|
Show Index |
|---|
This page demonstrates a very simple 360° shooter style game. Some of its features include:
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.
|
|
| If you've reached this page and there's no index on the left, Click here to show the Index Page | |