Bullet Hell engine.

Community Forums/Showcase/Bullet Hell engine.

Ryan Burnside(Posted 2007) [#1]
I've been working on a bullet hell engine for my upcoming shmup. If you don't know what "bullet hell" is google please.

The code works on some base principals.

You would provide an enemy boss or otherwise.

This enemy contains a "burst_timeline" object. This timeline holds "shot_burst" objects which define one burst of enemy shots and how long to delay befor activation. The "burst_timeline" counts up and activates "shot_burst" objects in time. These shot bursts define how many shots are created, and at what angles and speeds.

I'll provide the source code. I hope sombody can gleam something from it. These games aren't played much in America but they have a very solid funfactor. They are simple and require mastery.



and now the code for your adaptation:

You will need the bullet image. Name "image.png"


Strict
 

Global enemy_shots:TList= New TList ' list for our enemy shots
Global cell_groups:TList = New TList ' this is for the cell groups
Type enemy_shot
	Field x:Float, y:Float, direction:Float, speed:Float, radius:Float
	Function Create(x:Float, y:Float, direction:Float, speed:Float, radius:Float) 
	Local temp:enemy_shot= New enemy_shot
	temp.x=x
	temp.y=y
	temp.speed=speed
	temp.direction = direction
	temp.radius = radius
	ListAddLast(enemy_shots,temp)
	EndFunction
	Method update()
		outside_screen()
		move()
		draw()
	End Method
	Method move()
		x:+Cos(direction)*speed
		y:+Sin(direction)*speed
	End Method
	Method outside_screen()
		If x<0
			destroy()
		End If
		If y<0
			destroy()
		End If
			If x>640
			destroy()
		End If
		If y>480
			destroy()
		End If
	End Method
	Method destroy()
		ListRemove(enemy_shots,Self)
	End Method
	Method draw()
		DrawImageRect(image, x - radius, y - radius, radius * 2, radius * 2) 
	End Method
End Type ' basic bullet

Type shot_burst 
Field x:Float, y:Float, fill_angle:Float, numshots:Float, direction:Float, step_delay:Int, shot_speed:Float, shot_radius = 16
	Method fire()
		Local start_angle:Float = direction - (fill_angle / 2.0) 
		Local sub_angle:Float = fill_angle / numshots
			For Local i = 0 To numshots - 1
				enemy_shot.Create(x, y, start_angle + (i * sub_angle) + (sub_angle *.5), shot_speed, shot_radius) 
			Next
	End Method
End Type  ' resides witin a burst timeline  'describes a pattern

Type burst_timeline
	 Field num_bursts#=0,bursts:shot_burst[],event_time%,reset_time=0
	 Method add_burst(burst:shot_burst) ' add a burst and time from last shot to be executed
	 	Local new_length%=bursts.length+1
		bursts=bursts[..new_length]' holds bursts
		bursts[new_length-1]=burst
		num_bursts:+1
		If burst.step_delay>reset_time ' if the new burst needs more time add to the reset time
			reset_time=burst.step_delay
		End If
	 End Method
	 Method update()
	 
		For Local i:shot_burst=EachIn(bursts)
			If i.step_delay=event_time
				i.fire()
			EndIf
		Next
		event_time:+1
		If event_time>reset_time
			event_time=0
		End If
	 End Method
End Type

'test

Local p:burst_timeline = New burst_timeline ' here is a burst timelie-
' that might be stored in an enemy or boss type



' use a series of for loops to add burst patterns to our burst_timeline object
	
For Local i:Float = 1 To 11
Local d:shot_burst = New shot_burst 'Left side

	d.step_delay = i * 10
	 
	d.x = 320 - 120
	d.y = 128
	d.shot_speed = 2
	d.direction = 180
	d.numshots = 5
	d.fill_angle = i * 36
	p.add_burst(d) 
Local e:shot_burst = New shot_burst

	e.step_delay = i * 10 'right side
	 
	e.x = 320 + 120
	e.y = 128
	e.shot_speed = 2
	e.direction = 0
	e.numshots = 5
	e.fill_angle = i * 36
	p.add_burst(e) 
Next ' add twin burst pattern
Local m = p.reset_time ' this local var will give us 
'a continue count from the last for loop
For Local i:Float = 1 To 30
	Local e:shot_burst = New shot_burst
	
	Local n = i
	If n > 12
		n = 12
	End If
	e.step_delay = m + i * 2
	 
	e.x = 320
	e.y = 240
	e.shot_speed = 2
	e.direction = 90
	e.numshots = n
	e.fill_angle =M+ i * 12
	p.add_burst(e) 
Next ' add bottom bloom
 m = p.reset_time ' update our left off count
For Local i:Float = 1 To 30
	Local e:shot_burst = New shot_burst
	
	e.step_delay = m + i * 2
	 
	e.x = 320
	e.y = 120
	e.shot_speed = 2
	e.direction = i * 12
	e.numshots = 12
	e.fill_angle = 360
	p.add_burst(e) 
Next ' add top radial burst
 m = p.reset_time ' update last ticks again
For Local i:Float = 1 To 10

	For Local j = 0 To 11
	Local e:shot_burst = New shot_burst
	
	e.step_delay = m + i * 10
	 
	e.x = 320 + Cos(j * 30) * 32
	e.y = 240 + Sin(j * 30) * 32
	e.shot_speed = 3
	e.direction = i * 15
	e.numshots = 6
	e.fill_angle = 360
	e.shot_radius = 8
	p.add_burst(e) 
	Next
Next ' add ring shots
m = p.reset_time ' update last ticks again
For Local i:Float = 1 To 10
	Local e:shot_burst = New shot_burst
	
	e.step_delay = m
	 
	e.x = 320
	e.y = 240
	e.shot_speed = 2 + (I *.05) 
	e.direction = 0
	e.numshots = 36
	e.fill_angle = 360
	e.shot_radius = 32
	p.add_burst(e) 
Next ' add exanding line ring

Graphics 640, 480
SetMaskColor(0, 0, 0) 
SetBlend(ALPHABLEND) 
 
Global image:TImage = LoadImage("image.png") 
While not KeyDown(KEY_ESCAPE) 
Cls

	p.update()
	For Local e:enemy_shot=EachIn(enemy_shots)
		e.update()
	Next
	DrawText("Bullets: :" + CountList(enemy_shots), 12, 12) 
Flip
Wend



MGE(Posted 2007) [#2]
Very nice, thanks for letting us have a peek!