360° shooter game example code

Show Index

This page demonstrates a very simple 360° shooter style game.  Some of its features include:

-Players ship can turn or fire 360° in 4° increments
-Players ship can fly at varying speeds
-NPC (Non-Player Character) ships fly around and automatically turn around to stay within playing field
-NPC ships can be fired upon and destroyed
-Background scrolls in all directions
-Use of sounds when firing or destroying ships
-Sounds emit from left or right speaker (or anywhere in between) respective with each ships position onscreen
360° shooter style game - Click to enlarge

Jump ahead to see the final result...

 

Start with setting up the graphics and then displaying a "Loading..." message at the center of the screen while the game does some initial set up work.  Typically we always draw to the back buffer and we'd usually set it to the back buffer now, but since we're going to have to do some image manipulation first (which will require using different image buffers), we might as well just draw directly to the front buffer.

Graphics 800,600, 0, 1					;2D graphics at a resolution of 800x600
Text 400, 300, "Loading...", 1				;Write a temp message (could display an image here too)

 

Then set up the Types that your game will use.  Each object type basically stores that object's x and y location, and its current rotation.   The shipTYPE Type also has ID and velocity fields.   The bullet type doesn't have a velocity field but has fields that split up the rotation in to X and Y vectors so that we can save some processing time by not performing as much redundant math.  The bullet's speeds are constant so there is no need to keep recalculating them, whereas the ships can (potentially) change speed and so it's necessary to always calculate the new change in their distance covered.

;Define Types						;Use Types to store ship and bullet information
Type ShipTYPE						;Set up the Ship type
	Field ID					;Type of ship (1=Player, 2=Enemy_type1, etc., ...)
	Field X#					;X World position
	Field Y#					;Y World position
	Field rotation					;Current rotation of ship
	Field velocity#					;Ship velocity
End Type

Type BulletTYPE						;Set up the Bullet type
	Field X#					;X World position
	Field Y#					;Y World position
	Field rotation					;Current rotation of bullet
	Field xvector#					;X, Y vectors of the bullets rotation, both are used to
	Field yvector#					; reduce the math required when updating the bullets position
End Type

 

Load in and create your game graphics.  Each game object's image is a single image that we convert into an entire 'animation' strip of rotated images.  By creating the animation strips at the startup of our game, (versus just loading in an animation strip file) we are reducing the space that our game media uses on disk.  Also, it could allow the game to be more flexible and use more or less video memory depending on the end users system.  Note that in this example, we are creating animation strips of 90 tiles (90 rotations of each game object).  We could easily do less if necessary, such as only 60, or 30 tiles each, which of course would require less and less video memory.

;Load and create images
background = LoadImage("background.jpg")		;Stores handle to image of background

shipImage = LoadImage("ship.png")			;Stores handle to image of players ship
MidHandle shipImage					;'Handle' the ship image at its center
playerStrip = CreateImage(ImageWidth(shipImage), ImageHeight(shipImage), 90)	;Create image strip of 90 tiles
For iter = -45 To 44					;Perform a 'circular' loop (-180° to 176°)
	rotatedImage = CopyImage(shipImage)		;Copy ship image into temporary image
	RotateImage(rotatedImage, iter*4)		;Rotate the temp image (4° intervals)
	SetBuffer ImageBuffer(playerStrip, iter+45)	;Set image strip tile (0-89) to draw to
	DrawBlock rotatedImage, ImageWidth(shipImage)/2, ImageHeight(shipImage)/2	;Draw temp image to (center of) tile
	FreeImage rotatedImage				;Free memory used by temp image
Next
MidHandle playerStrip					;'Handle' each tile at its center

enemyImage = LoadImage("enemyship.png")			;Stores handle to image of enemy ship
MidHandle enemyImage					;'Handle' the ship image at its center
enemyStrip = CreateImage(ImageWidth(enemyImage), ImageHeight(enemyImage), 90)	;Create image strip of 90 tiles
For iter = -45 To 44					;Perform a 'circular' loop (-180° to 176°)
	rotatedImage = CopyImage(enemyImage)		;Copy enemy image into temporary image
	RotateImage(rotatedImage, iter*4)		;Rotate the temp image (4° intervals)
	SetBuffer ImageBuffer(enemyStrip, iter+45)	;Set image strip tile (0-89) to draw to
	DrawBlock rotatedImage, ImageWidth(enemyImage)/2, ImageHeight(enemyImage)/2	;Draw temp image to (center of) tile
	FreeImage rotatedImage				;Free memory used by temp image
Next
MidHandle enemyStrip					;'Handle' each tile at its center

bulletImage = LoadImage("bullet.png")			;Stores handle to image of bullets
MidHandle bulletImage					;'Handle' the bullet image at its center
bulletStrip = CreateImage(ImageWidth(bulletImage), ImageHeight(bulletImage), 90)	;Create image strip of 90 tiles
For iter = -45 To 44					;Perform a 'circular' loop (-180° to 176°)
	rotatedImage = CopyImage(bulletImage)		;Copy bullet image into temporary image
	RotateImage(rotatedImage, iter*4)		;Rotate the temp image (4° intervals)
	SetBuffer ImageBuffer(bulletStrip, iter+45)	;Set image strip tile (0-89) to draw to
	DrawBlock rotatedImage, ImageWidth(bulletImage)/2, ImageHeight(bulletImage)/2	;Draw temp image to (center of) tile
	FreeImage rotatedImage				;Free memory used by temp image
Next
MidHandle bulletStrip					;'Handle' each tile at its center

 

Load some other media:

;Load Sounds
sndBlast = LoadSound("blast.wav")
sndExplode = LoadSound("explode.wav")

 

Create your game objects.  We'll start the player off in the middle of the screen.   Then we'll create, oh, say, 100 enemy ships to shoot down and randomly place them around the playing field (with random rotations and velocities).  There are no actual boundaries in this example, but we'll limit the distance the enemy ships can travel later on in the program.

;Create the players ship
player.ShipTYPE = New ShipTYPE				;Create the players ship
player\ID = 1						;Set ship ID to represent the player
player\X# = 400						;Set players initial X position
player\Y# = 350						;Set players initial Y position
player\velocity = 1					;Set players initial velocity

;Create enemies
For iter = 1 To 100					;Set up a loop to create some enemies
	thisShip.ShipTYPE = New ShipTYPE		;Create the new enemy ship
	thisShip\ID = 2					;Set ship ID to represent an enemy ship
	thisShip\X# = Rnd(-1500, 1500)			;Randomly set ships initial X position
	thisShip\Y# = Rnd(-1500, 1500)			;Randomly set ships initial Y position
	thisShip\rotation# = Rnd(0, 359) - 180		;Randomly set ships rotation (direction of travel)
	thisShip\velocity# = .5+Rnd(.9)			;Set ships velocity (pixels per frame)
Next

 

And now we can finally set the back buffer as the drawing buffer since we'll be drawing to it for the remainder of the game:

SetBuffer BackBuffer()					;Now do all drawing to the back drawing buffer

 

Create a timer to set the games pace and then create a loop that will update the screen every tick of the timer.  Add all of the remaining code inside of the loop where the purple comment is.

Gametimer = CreateTimer(60)				;Set and start the game timer (60 fps)

;Start game loop
While Not KeyHit(1)					;Repeat main loop until the user presses the Esc key
	WaitTimer GameTimer				;Wait for the timer to tick

	;;; insert additional code here ;;;
 
	Flip						;Flip the back buffer to the front to be displayed
Wend							;Start the game loop over again

 

Collect the inputs from the player.  Rotate the players ship slightly if left/right was input.  Increase or decrease the ships velocity if either of those inputs were given.

;*** Process Inputs ***
;Collect user inputs
rotateleft = KeyDown(203)	;Left arrow		;It's a good practice to collect these inputs only once
rotateright = KeyDown(205)	;Right arrow		; per game loop. This will prevent odd behaviors from happening,
thrust = KeyDown(200)		;Up arrow		; for instance if the state of a key changes between multiple
airbrake = KeyDown(208)		;Down arrow		; checks of that key while still in the same loop.
shoot = KeyDown(57)		;Spacebar

;Apply inputs to player
If rotateleft And (Not rotateright)			;If left key is pressed but not right key then decrement players rotation
	player\rotation = player\rotation - 2 +360*(player\rotation <= -179); by 2° (but add 360° if already less than -179°)
EndIf							; (this is to always keep the rotation between -179° and 178°)
If rotateright And (Not rotateleft)			;If right key is pressed but not left key then increment players rotation
	player\rotation = player\rotation + 2 -360*(player\rotation >= 178) ; by 2° (but subtract 360° if already greater than 178°)
EndIf

If thrust And player\velocity < 10			;If thrust key and player is moving slower than 10 (pixels per frame)
	player\velocity = player\velocity + .1		;Increase velocity by 1/10 (pixel per frame)
ElseIf player\velocity > 1				;Otherwise slow player down (but not slower than 1 (pixel per frame)
	If airbrake					;Check if airbrake key is pressed
		player\velocity = player\velocity - .17 ; and if so, then decrease speed drastically
	Else						; otherwise if not pressing airbrake
		player\velocity = player\velocity - .05 ; then just slow down gradually
	EndIf
EndIf

 

If the fire button was pressed and enough time has passed since the last shot, then we need to create a new 'bullet'.  We place the bullet at the players location.   Then we orientate the bullet to face the same way as the player and give it a velocity greater than the players current velocity (so that the player can never pass up his own bullets).  Lastly, we determine when the next shot can be fired.  Oh yeah, and now is a pretty good time to play the bullets firing sound...

If shoot And MilliSecs() > lastshot			;If shoot key is pressed and enough time has passed since last shot was taken
	thisBullet.BulletTYPE = New
	BulletTYPE					;Create a new bullet
	thisBullet\rotation = player\rotation		;Set its rotation the same as the players rotation
	thisBullet\X# = player\X#			;Set its position the same as the players position
	thisBullet\Y# = player\Y#
	thisBullet\xvector# = -(Sin(player\rotation)) * (player\velocity + 9)	;Set X vector (combine with bullet vel. & players vel.)
	thisBullet\yvector# = Cos(player\rotation) * (player\velocity + 9)	;Set Y vector (combine with bullet vel. & players vel.)
	lastshot = MilliSecs() + 200			;Store time of shot so next shot is delayed
	PlaySound sndBlast				;Play the gunblast sound
EndIf

 

Advance all of the game objects according to their current rotations and velocities.   If the enemy ships go too far in any direction, make them turn around.  If the bullets go offscreen, delete them.

;Process Logic
;Advance players ship
player\X# = player\X# + player\velocity * -(Sin(player\rotation));Move player in X according to ships rotation and velocity
player\Y# = player\Y# + player\velocity * Cos(player\rotation)	;Move player in Y according to ships rotation and velocity

;Advance enemy ships
For thisShip.ShipTYPE = Each ShipTYPE				;Iterate through each of the enemy ships
	If thisShip\ID = 2					;Determine if this ship is an enemy
		thisShip\X# = thisShip\X# - thisShip\velocity * Sin(thisShip\rotation);Move ship in X according to its rot. and vel.
		thisShip\Y# = thisShip\Y# + thisShip\velocity * Cos(thisShip\rotation);Move ship in Y according to its rot. and vel.
		If thisShip\X# < -2000 Or thisShip\X# > 2000 Or thisShip\Y# < -2000 Or thisShip\Y# > 2000 ;If ship is out of bounds
			thisShip\rotation = thisShip\rotation + 1 ;Rotate ship slowly but subract 360° if rotation is greater than 179°
			If thisShip\rotation > 179 Then thisShip\rotation = thisShip\rotation - 360 ; to keep rotation between -179° and 178°
		EndIf
	EndIf
Next

;Advance bullets
For thisBullet.BulletTYPE = Each BulletTYPE			;Iterate through each of the bullets in play
	thisBullet\X# = thisBullet\X# + thisBullet\xvector#	;Move bullet in X according to its X vector
	thisBullet\Y# = thisBullet\Y# + thisBullet\yvector#	;Move bullet in Y according to its Y vector
	If thisBullet\X# < player\X#-440 Or thisBullet\X# > player\X#+440 Or thisBullet\Y# < player\Y#-340 Or thisBullet\Y# > player\Y#+340
		Delete thisBullet				;If bullet went off-screen then delete it
	EndIf
Next

 

Now compare each bullet's image with each enemy ship to see if their images overlap.   If two images do overlap, then compare them even more closely to see if any non-transparent pixels overlap (collide).  If a bullet did collide with a ship, then delete the bullet and the ship it collided with and then play an explosion sound.   For an added cool effect, we'll pan the sound of the explosion between the left or right speakers in relation to how far the ship was left or right of the center of the screen (Obviously, the numbers used for calculating the offset of the sound need to be proportional to the screen's width.).

;Check for bullet collisions
For thisBullet.BulletTYPE = Each BulletTYPE		;Iterate again through each of the bullets
	exitloop = False				;Set false now in case we set it true later
	For thisShip.ShipTYPE = Each ShipTYPE		;Iterate through each ship in play
		If thisShip\ID = 2			;Make sure its an enemy ship
			If ImagesOverlap(bulletStrip, thisBullet\X, thisBullet\Y, enemyStrip, thisShip\X+400, thisShip\Y+300);Check if bullet & ship overlap (faster than ImagesCollide)
				If ImagesCollide(bulletStrip, thisBullet\X, thisBullet\Y, ((thisBullet\rotation+180)/4), enemyStrip, thisShip\X+400, thisShip\Y+300, ((thisShip\rotation+180)/4))
					Delete thisBullet		;Check if bullet image and ship image collide (checking against correct tiles), and if so, delete the bullet
					channel = PlaySound(sndExplode)	;Play an explosion and assign it to a channel so we can pan the sound left/right based on
					ChannelPan channel, ((player\X-400)-thisShip\X)/350.0 ; ships position onscreen (making 350 pixels off-center extreme left/right)
					ChannelPitch channel, Rnd(16000, 28000) ; and randomly adjust the pitch slightly to give the sounds some variety
					Delete thisShip			;Delete the ship now
					exitloop = True			;Exit the loop since this bullet is destroyed
				EndIf
			EndIf
		EndIf
		If exitloop = True Then Exit		;Exit loop if bullet is destroyed and check next bullet
	Next						;Check next ship
Next							;Check next bullet

 

The only thing remaining is to draw the screen.  Obviously we need to draw it from 'back' to 'front'.

;*** Paint the screen ***
;Draw background image
TileImage background, player\X#, player\Y#		;Draw background image offset according to players position

;Draw enemy ships
shipCount = 0						;Reset counter variable
For thisShip.ShipTYPE = Each ShipTYPE			;Iterate through each ship
	If thisShip\ID = 2				;Check if its an enemy ship
		shipCount = shipCount + 1		;Keep count of how many there are
		DrawImage enemyStrip, player\X#-thisShip\X#, player\Y#-thisShip\Y#, ((thisShip\rotation+180)/4)
	EndIf						;Draw the ship at the correct position and rotation
Next							; in relation to the players ship

;Draw bullets
For thisBullet.BulletTYPE = Each BulletTYPE		;Iterate through each bullet
	DrawImage bulletStrip, player\X#-thisBullet\X#+400, player\Y#-thisBullet\Y#+300, ((thisBullet\rotation+180)/4)
Next							;Draw bullet at correct positon & rotation in relation to players ship

;Draw players ship
DrawImage playerStrip, 400, 300, ((player\rotation+180)/4);Draw players ship in middle of screen at correct rotation

Color 100, 10, 250					;Set text color
Text 2, 565, "Keys: Arrows, Spacebar, Esc"		;Draw some stats for the players reference...
Text 2, 575, "Pos:"+(player\X#-400)+" "+(player\Y#-300)
Text 2, 585, "Enemy Remaining: " + shipCount

 

And lastly, once the player has chosen to end the game (by way of the 'Esc' key) we should free all of the images and game objects we created for the game before we end the program.  Add this after the gameloop's Wend command.

;Clean up before exiting
FreeImage playerStrip					;Upon exit, free the images from memory...
FreeImage enemyStrip
FreeImage bulletStrip
FreeImage shipImage
FreeImage enemyImage
FreeImage bulletImage
FreeImage background
Delete player
Delete Each ShipTYPE					;And delete all of the objects...
Delete Each BulletTYPE

End							;And that's the end

 

And here is the entire code pieced together so that you can copy&paste it and immediately run it to see how it works.

Keys used in this example:

keys used in this example

	
Left key	Rotate left
Right key	Rotate right
Spacebar	Fire
Up key		Thrust
Down key	Air brake
Esc key		Exits program
Files used in this example:

files used in this example

	
right click and select
"Save Target As..."
background.jpg
ship.png
enemyship.png
bullet.png
blast.wav
explode.wav

 

 

If you've reached this page and there's no index on the left, Click here to show the Index Page