I tried adding <code> </code> tags around your code but that blew the margins out so have tried with <codebox> instead which I don't really like either, this one's a mix of the two:
A "step by step" guide on how to make a simple game by Wave.
Contains:
- Short on gamedesign and planing
- Code structure
- Vector Physics (you can always copy N paste it)
Dowload ZIP with source,images, pdf and doc here:
How to make Asteroids in an hour or so!And in crappy html, without tabs because they don't copy?!:
How to make asteroids in an hour or soThis is a step by step example of how to make a simple asteroids clone. I’ll be simple and should show a good practical example for people new to BlitzMax.
First we need a plan, a design plan to be exact. Asteroids are a simple game so it’s not going to be very long. The design part is one of the most important steps in making a game. Without it or if it lacks in detail you’ll have to remake and change stuff which in turn will result in a longer production time.
Overview Design
Visual (Design)
• Explain how it would be to play the game
• The goal of the game
• Draw sample images of the game in action
Technical (Programming)
• Types Overview
• Methods/functions Overview
• Image/sound files
In a bigger game this would be much more complicated and involve many more steps. Start your stopwatch (on your mobile) and get ready to make asteroids in an hour or so.
Asteroid Wars – Design Overview
Overview – what’s the game about?
This game is similar to the classic asteroids. This version will be simple, fast-paced and difficult.
Controls – How does the player interact?
The player can rotate this ship, clockwise and counter-clockwise with the left and right keys. He can control his thrust with up and reverse thrust (80%) by pressing down. The player can fire in the direction of their ship by pressing space.
Game play – What does the player do?
The game is played in levels and the goal of each level is to destroy stones. Stones come in three sizes, small, medium and large. You start each level with one ship. When you hit a stone it breaks into smaller stones except small stones that evaporate into dust. The first level you meet one medium stone, as you progress through levels the starting stones increases in numbers while getting larger. The game has infinite levels. If you hit a stone you will destroy your ship = game over. One life only...
Goal
The long term goal of the game is to get the highest score possible.
That was the design part of our game plan. At “Game Play” you should draw on paper some ideas of the “look and feel” so that the game gets easier to visualize later on when you are making the graphics. I skipped that part this time... Now it’s time for the technical stuff. We are using BlitzMax so let’s get down to the types we want to use and what graphics we require.
Data structures:
TShip, TShot, TRock each inherit TSpaceObject
I have decided each of these should have their own file named “NameofType.bmx”
This is actually not redundant in this simple game, it will give your code more structure and in the end it will save us time. See it as good practice. I’ll have one file called AsteroidWars, in which the actual game loop will be and from which the game is going to be run.
The methods and functions of SpaceObject are:
Create – Function, New, Update, UpdateAll – Function, Destroy, Draw
The rest I’ll make when I see they are required.
Start to code
Ok let’s get down to programming, start by creating all bmx-files we need. Open the new and empty “TSpaceObject.bmx”
The first thing to do would be to define the Type’s fields.
We need:
X and Y for position
Direction for rotation
Thrust for speed in current direction.
Implement the methods, they should be empty for now. Keep this file open so that you can easily check back later.
TSpaceObject.bmx
Type TSpaceObject
Field X#,Y#
Field XSpeed#,YSpeed#
Field Direction
Function Create() EndFunction
Function UpdateAll() EndFunction
Method New() EndMethod
Method Update() EndMethod
Method Destroy() EndMethod
Method Draw() EndMethod
End Type
Create the files TShip.bmx, TRock.bmx, TShot.bmx and AsteroidWars.bmx.
Have them all open at the same time. Go to main and import TSpaceObject, TShip, TRock and TShot.
AsteroidWars.bmx
Include "SpaceObject.bmx"
Include "Ship.bmx"
Include "Shot.bmx"
Include "Rock.bmx"
Jump to TShip, because now we are going to get things moving. Create the Type (guess the name yourself) and extend it from (guess again).
TShip.bmx
Type TShip Extends TSpaceObject
End Type
So far so good, when you are making you own game try using a similar approach with multiple files. If this game would have been made in one file it would have been much harder to get a good overview. As soon as you have several types you should jump to several files.
Our type TShip might look very empty but don’t get fooled by that! TShip extends TSpaceObject therefore it contains every field and method that TSpaceObject contains.
I’ll start with Create() – It needs to spawn a new Ship type and add that type to a list. We need someplace to keep this list so create a global in the TShip Type called List, it need to be of the TList Type. We also need to create the list the first time it is used. We are only going to have one ship so I don’t really need to add it to a list, but I want to be open in case someone wants to add more players, AI, enemies or perhaps even multiplayer support and it makes the example more general. We also want to setup some default values, startX and startY. Make a method New() and set the default values of X and Y. New() is called when you create a new instance of the type it lies in.
TShip.bmx
Type TShip Extends TSpaceObject
Global List:TList
Global Image:Timage
Function Create()
Ship:TShip = New TShip
If List = Null List = CreateList()
List.AddLast Ship 'Add Ship Last in List
EndFunction
Method New()
X = 300
Y = 400
EndMethod
End Type
Time for a break... This part can take forever but I’m confident that if you focus you can make it within 20min and 30sec. Open up your favorite paint/graphics program. With favorite I mean the program you are best at. How you make the graphics are up to you, this is just the guide lines. Make a new picture at a good ship-size, I’ll make it 25x25 pixels. Paint a high-tech space ship in this small image while using black as your background color. Save as.. ”ship.png” if you can’t save as png try jpg or bmp. We got two more images we need, shot and rock. Make a new image 20x10, this is going to be our shot or missile. Use black as background. I saved it as ”missile.png” in the same folder. Next image is the rock. It need to be much bigger than our ship. I’ll make only one rock and then I will scale it to the smaller sizes. Make it 100x100 and fill it with a stone texture or something.
Now load our ”ship.png” image in the Function create() you will also have to center the image using MidHandleImage( Image ). Our image is set to a global; all ships will look the same. Make a new method called Draw(). In draw we want to set image rotation to our ship’s rotation, and then draw it at our current position.
TShip.bmx
Type TShip Extends TSpaceObject
Global List:TList
Global Image:Timage
Function Create()
Local Ship:TShip = New TShip
If List = Null List = CreateList()
List.AddLast Ship 'Add Ship Last in List
If Not Image
Image = LoadImage("ship.png")
MidHandleImage( Image )'Center
EndIf
EndFunction
Method New()'Starting Values
X = 300
Y = 400
EndMethod
Method Draw()
SetRotation( Direction )
DrawImage( Image,X,Y )
EndMethod
End Type
Now we are going to fix the update method and the updateAll function. In update we want to update the movement and controls and finally draw the ship. Just so that we can do a test-run let’s put in the method Draw() in method Update. In updateAll() we want to loop every ship we have, we only got one, but in case, if we ever add more of them then no extra code will be required. Make an eachin-loop with the global List and make each object run its Update() method.
TShip.bmx
Type TShip Extends TSpaceObject
Global List:TList
Global Image:Timage
Function Create()
Local Ship:TShip = New TShip
If List = Null List = CreateList()
List.AddLast Ship 'Add Ship Last in List
If Not Image
Image = LoadImage("ship.png")
MidHandleImage( Image )'Center
EndIf
EndFunction
Method New()'Starting Values
X = 300
Y = 400
EndMethod
Method Draw()
SetRotation( Direction )
DrawImage( Image,X,Y )
EndMethod
Method Update()
Draw()
'To add later:
'GetInput, Controls
'Update physics, Movement
EndMethod
Function UpdateAll()
For Ship:TShip = EachIn List
Ship.Update()
Next
EndFunction
End Type
Everything clear? No questions? Are you wondering if there could have been ”another way” to do it? Don’t hesitate to ask in the forum! Feedback = Good. Questions = Better. Asking = Learning.
Let’s see what we got so far. Jump to your main file. Setup a graphicsmode, I choose 800,600,0. Call the function Create() in TShip. Remember that to access a function that lies inside a type you need to access the function from the type; Type.Function() – or in our case - TShip.Create(). Create a basic loop and exit when someone presses Esc. Run the function UpdateAll() in the middle of it and before the end of the loop call Flip and Cls.
AsteroidWars.bmx
Include "TSpaceObject.bmx"
Include "TShip.bmx"
Include "TShot.bmx"
Include "TRock.bmx"
Graphics 800,600,0
TShip.Create()
'Main Loop
Repeat' - - - - - - - - - - - - - - - - -
TShip.UpdateAll()
Flip
Cls
Until KeyDown(Key_Escape)'- - - - - - - -
Build and Run! You can’t do anything except watch your pretty ship but it’s a start and we’ve built a solid base for years to come, or more exact ~42min.
Now we need interactivity. Jump to TShip again. We are going to implement the actual physics, the control feeling of the ship. This part is an important and a bit advanced, I’ll try to explain it as simple as thorough as possible. Still you need to know some about vectors to get it, or I’ll have to explain vectors to, but I won’t, not this time. Nothing strange with vectors there it’s just out-of-scope for this guide. So here is the code for method Update, Step One:
In file TShip.bmx
Method Update()
Draw()
'Possible Actions
Local Up,Down,Left,Right,Fire
'Controls affect actions
If KeyDown(Key_Up) Up = True
If KeyDown(Key_Down) Down = True
If KeyDown(Key_Left) Left = True
If KeyDown(Key_Right) Right = True
If KeyDown(Key_Space) Fire = True
' P H Y S I C S
'---------------------------------------
'Alter these to alter the ships movement
Local Acceleration# = 0.04
Local Friction# = 0.014
Local TopSpeed# = 2.0
Local TurnAcceleration# = 0.18
Local TurnFriction# = 0.06
Local TurnMax# = 3
'---------------------------------------
What I do when I set Left,Right,Up,Down and Fire is to separate the actions from the keys. It’s not required in this game unless we want to add more players or AI.
That wasn’t that hard, was it? I hope not because the actual physics/trouble is yet to come, Step Two:
In file TShip.bmx in method Update()
'M O V E M E N T P H Y S I C S
'--------------------------------------
If Up
'Create a Acceleration Vector and
'add it to the Speed Vector
XSpeed:+ Cos(Direction)*Acceleration
YSpeed:+ Sin(Direction)*Acceleration
EndIf
If Down
'Create a Acceleration Vector and
'subtract it from the Speed Vector
XSpeed:- Cos(Direction)*Acceleration
YSpeed:- Sin(Direction)*Acceleration
EndIf
'Calculate the length of the Speed Vector
Local SpeedVectorLength# = Sqr(XSpeed*XSpeed + YSpeed*YSpeed)
If SpeedVectorLength > 0
'Decrease Speed with Friction if we are moving
XSpeed:- (XSpeed/SpeedVectorLength)*Friction
YSpeed:- (YSpeed/SpeedVectorLength)*Friction
EndIf
If SpeedVectorLength > TopSpeed
'If we are going beyond the speed barrier then reduce our speed
'with the amount in which it surpasses TopSpeed
XSpeed:+ (XSpeed/SpeedVectorLength)*(TopSpeed - SpeedVectorLength)
YSpeed:+ (YSpeed/SpeedVectorLength)*(TopSpeed - SpeedVectorLength)
EndIf
'Move
X:+ XSpeed
Y:+ YSpeed
'Rem 'Visualize the vectors
SetRotation 0
SetColor 255,0,0 'Red
DrawLine X,Y,X,Y + YSpeed*50
SetColor 0,255,0 'Green
DrawLine X,Y,X+XSpeed*50,Y
SetColor 0,0,255 'Blue
'This is the SpeedVector's Length
'Note that this vector is built by
'Adding the Red and the Green
DrawLine X,Y,X+XSpeed*50,Y+YSpeed*50
SetColor 255,255,255
'EndRem
Here comes a set of turning events, further down is an explanation of the code. Step Three:
'R O T A T I O N P H Y S I C S
'--------------------------------------
'
If Left TurnSpeed:-TurnAcceleration
If Right TurnSpeed:+TurnAcceleration
'Limit TurnSpeed
If TurnSpeed > TurnMax TurnSpeed = TurnMax
If TurnSpeed < -TurnMax TurnSpeed = -TurnMax
Direction:+TurnSpeed
If Direction > 360 Direction:- 360
If Direction < 0 Direction:+ 360
'Apply Friction To Rotation
If TurnSpeed > TurnFriction TurnSpeed:- TurnFriction
If TurnSpeed < -TurnFriction TurnSpeed:+ TurnFriction
'If Friction is Greater than Speed then Stop
If TurnSpeed < TurnFriction And TurnSpeed > -TurnFriction TurnSpeed =0
EndMethod
Before you try to understand this and before you go any further I would strongly suggest you try the above addition. Compile and run. Test.
So what is this all about, with one word: Vectors. Vectors it the key to physics. Vectors work in 2D and 3D. Without telling you what a vector is I’m going to explain what I did, the problem is that… well you’ll find out.
When we press Up or Down we want our thruster to give us a push forward. One could refer to this push over time as force. What is a force then? A force is something that accelerates things. You have probably seen the equation: Force = Mass x Acceleration. This means that our thruster will accelerate a heavier ship (bigger mass) much slower than a light one. Acceleration = Force / Mass. In this game I have set Mass = 1 and it was never provided in the equations. So in this game our thruster gives a force equal to the acceleration. Let’s look at the code:
If Up
XSpeed:+ Cos(Direction)*Acceleration
YSpeed:+ Sin(Direction)*Acceleration
EndIf
XSpeed and YSpeed is the values of our speed vector. Acceleration in this case is the Ship-thruster’s “push” value. I take this value and assume it’s a vector in the direction of the ship and add it too the speed-vector.
If I had been using a vector library/module the above could have looked something like this:
If Up
Local Thruster:Vector = NewVector()
Thruster.SetLength( Acceleration )
Thruster.SetDir( Direction )
Speed.Add( Thruster )
EndIf
To be able to reduce our speed we need to reduce the speed-vectors length and not the values directly. To get the length we use Pythagoras Theorem.
Local SpeedVectorLength# = Sqr(XSpeed*XSpeed + YSpeed*YSpeed)
Friction is applied to our SpeedVectors length like this:
If SpeedVectorLength > 0
XSpeed:- (XSpeed/SpeedVectorLength)*Friction
YSpeed:- (YSpeed/SpeedVectorLength)*Friction
EndIf
The calculation (XSpeed/SpeedVectorLength) creates a unit vector of the speed vector. This means I set the length of the speedvector to one but I keep the direction. When I then multiply this unitvector with Friction I create a vector with the same direction as our speedvector but with the length of our friction value. Then I substract this vector from the speed vector.
With a vector library it would have looked something like this:
Speed.Sub( Speed.Unit().Multiply(Friction) )
Or
Speed.Decrease( Friction )
We also need to keep a speed limit. This speed limit would in a real-world be dependant on friction, in space we don’t have any friction. In my asteroids-world I dictate the physics and I use both friction and a speedlimit, both without breaking the laws of physics, I merly bend them.
If SpeedVectorLength > TopSpeed
XSpeed:+ (XSpeed/SpeedVectorLength)*(TopSpeed - SpeedVectorLength)
YSpeed:+ (YSpeed/SpeedVectorLength)*(TopSpeed - SpeedVectorLength)
EndIf
If our speed surpasses a certain limit I will substract the speed with the amount that it was surpassed, (TopSpeed - SpeedVectorLength). It’s the same as friction only that the amount of friction I add is depending on the speed.
Vector Lib equvivalent:
If Speed.Length() > MaxSpeed Then Speed.SetLength( MaxSpeed )
The last part is to update your position based on your speed
X:+ XSpeed
Y:+ Yspeed
In a vector lib that would have been:
Position.Add( Speed )
The rotation part is easier and I assume it don’t need much futher explination. Please go ahead and change any/all of the six values and try for different physics behaviors. This model could be used for most topdown games to give a fair feel. If you have a race game I suggest having a higher turn acceleration to get more responsive controls, the turn friction would in a car game be equvivalent to the grip of the tires. Test and try for your self.
TShip.bmx so far
Type TShip Extends TSpaceObject
Global List:TList
Global Image:Timage
Field TurnSpeed#
Function Create()
Local Ship:TShip = New TShip
If List = Null List = CreateList()
List.AddLast Ship 'Add Ship Last in List
If Not Image
Image = LoadImage("ship.png")
MidHandleImage( Image )'Center
EndIf
EndFunction
Method New()'Starting Values
X = 300
Y = 400
EndMethod
Method Draw()
SetRotation( Direction )
DrawImage( Image,X,Y )
EndMethod
Method Update()
Draw()
'Possible Actions
Local Up,Down,Left,Right,Fire
'Controls affect actions, Player 1
If KeyDown(Key_Up) Up = True
If KeyDown(Key_Down) Down = True
If KeyDown(Key_Left) Left = True
If KeyDown(Key_Right) Right = True
If KeyDown(Key_Space) Fire = True
' P H Y S I C S
'---------------------------------------
'Alter these to alter the ships movement
Local Acceleration# = 0.04
Local Friction# = 0.014
Local TopSpeed# = 2.0
Local TurnAcceleration# = 0.18
Local TurnFriction# = 0.03
Local TurnMax# = 5
'---------------------------------------
'M O V E M E N T P H Y S I C S
'--------------------------------------
'
If Up
'Create a Acceleration Vector and
'add it to the Speed Vector
XSpeed:+ Cos(Direction)*Acceleration
YSpeed:+ Sin(Direction)*Acceleration
EndIf
If Down
'Create a Acceleration Vector and
'substract it from the Speed Vector
XSpeed:- Cos(Direction)*Acceleration
YSpeed:- Sin(Direction)*Acceleration
EndIf
'Calculate the length of the Speed Vector
Local SpeedVectorLength# = Sqr(XSpeed*XSpeed + YSpeed*YSpeed)
If SpeedVectorLength > 0
'Decrease Speed with Friction if we are moving
XSpeed:- (XSpeed/SpeedVectorLength)*Friction
YSpeed:- (YSpeed/SpeedVectorLength)*Friction
EndIf
If SpeedVectorLength > TopSpeed
'If we are going beyond the speed barrier then reduce our speed
'with the amount in which it surpases TopSpeed
XSpeed:+ (XSpeed/SpeedVectorLength)*(TopSpeed - SpeedVectorLength)
YSpeed:+ (YSpeed/SpeedVectorLength)*(TopSpeed - SpeedVectorLength)
EndIf
'Move
X:+ XSpeed
Y:+ YSpeed
'Rem 'Visualize the vectors
SetRotation 0
SetColor 255,0,0 'Red
DrawLine X,Y,X,Y + YSpeed*50
SetColor 0,255,0 'Green
DrawLine X,Y,X+XSpeed*50,Y
SetColor 0,0,255 'Blue
'This is the SpeedVector's Length
'Note that this vector is built by
'Adding the Red and the Green
DrawLine X,Y,X+XSpeed*50,Y+YSpeed*50
SetColor 255,255,255
'EndRem
'R O T A T I O N P H Y S I C S
'--------------------------------------
'
If Left TurnSpeed:-TurnAcceleration
If Right TurnSpeed:+TurnAcceleration
'Limit TurnSpeed
If TurnSpeed > TurnMax TurnSpeed = TurnMax
If TurnSpeed < -TurnMax TurnSpeed = -TurnMax
Direction:+TurnSpeed
If Direction > 360 Direction:- 360
If Direction < 0 Direction:+ 360
'Apply Friction To Rotation
If TurnSpeed > TurnFriction TurnSpeed:- TurnFriction
If TurnSpeed < -TurnFriction TurnSpeed:+ TurnFriction
'If Friction is Greater than Speed then Stop
If TurnSpeed < TurnFriction And TurnSpeed > -TurnFriction TurnSpeed = 0
EndMethod
Function UpdateAll()
Local Ship:TShip
For Ship = EachIn List
Ship.Update()
Next
EndFunction
End Type
Now I’m going to add shots. Open up Tshot.bmx and create a TShot type that extends TSpaceObject. We are going to need a list for the shots. Make the create method similar to the ship’s create method. We’ll load the image we made, shot.png.
TShot.bmx
Type TShot Extends TSpaceObject
Global List:TList
Global Image:TImage
Function Create()
Local Shot:TShot = New TShot
If List = Null List = CreateList()
List.AddLast Shot 'Add Ship Last in List
If Not Image
Shot.Image = LoadImage("shot.png")
MidHandleImage( Shot.Image )
EndIf
EndFunction
Function UpdateAll() EndFunction
Method Update() EndMethod
Method Destroy() EndMethod
Method Draw() EndMethod
EndType
Now we can create shots, but where will the shots appear? The shots should always be created at the front of the ship who fired it. This requires us to add some parameters to create().
Function Create( X, Y, Direction, XSpeed, YSpeed )
Local Shot:TShot = New TShot
If List = Null List = CreateList()
List.AddLast Shot 'Add Ship Last in List
Shot.X = X
Shot.Y = Y
Shot.Direction = Direction
Shot.XSpeed = XSpeed
Shot.YSpeed = YSpeed
If Not Image
Shot.Image = LoadImage("shot.png")
MidHandleImage( Shot.Image )
EndIf
EndFunction
Let’s fix Update() and UpdateAll(), I copied what I have in these methods in TShip.bmx and altered them to fit our shot. This is what came out of it:
Type TShot Extends TSpaceObject
Global List:TList
Global Image:TImage
Function Fire( X, Y, Direction, XSpeed, YSpeed )
Local Shot:TShot = New TShot
If List = Null List = CreateList()
List.AddLast Shot
If Not Image'First Time
Image = LoadImage("missile.png")
MidHandleImage( Image )
EndIf
Local ShotSpeed#= 8
Shot.X = X
Shot.Y = Y
Shot.Direction = Direction
'Add Shot Start Speed
Shot.XSpeed= Cos(Direction)*ShotSpeed + XSpeed
Shot.YSpeed= Sin(Direction)*ShotSpeed + YSpeed
EndFunction
Method Draw()
SetRotation( Direction )
DrawImage( Image,X,Y )
EndMethod
Method Update()
Draw()
X:+ XSpeed
Y:+ YSpeed
If X > 800 Or Y > 600 Or X < 0 Or Y < 0 Destroy()
EndMethod
Function UpdateAll()
If Not List Return
Local Shot:TShot
For Shot = EachIn List
Shot.Update()
Next
EndFunction
Method Destroy()
List.Remove( Self )
EndMethod
EndType
The code above should say most just by itself, except:
Shot.XSpeed= Cos(Direction)*ShotSpeed + XSpeed
Shot.YSpeed= Sin(Direction)*ShotSpeed + YSpeed
Here is the vector code again =)
It’s the same as when we thrust forward. The shots in this game are not affected by friction and they can’t rotate so the update code is therefore much more simple than that in TShip.bmx.
To fire a shot we need to add one line to our Ship’s Update method:
If Fire TShot.Fire( X, Y, Direction, XSpeed, YSpeed )
Ok cool we can move around and fire. Now we need some targets – rocks. But before that I’ll add a border so that if you go out on one side you appear on the other.
Add to TShip.bmx in the update method:
If X > 800 X = 0
If Y > 600 Y = 0
If X < 0 X = 800
If Y < 0 Y = 600
Jump to TRock.bmx. This type is very similar to Tshot. We have to take into account that the rocks come in different sizes, I added one field Size and one called Rotation.
Type TRock Extends TSpaceObject
Global List:TList
Global Image:TImage
Field Size
Field Rotation#
I will also add three constants. Whenever I want to refere to a size I could write Size = 1 or 2. With constants It would be: Size = SMALL or MEDIUM. To have constants in all caps is just very common in programming and it’s not required by BMax.
Const LARGE = 3
Const MEDIUM = 2
Const SMALL = 1
Instead of Create I call this function Spawn, it does almost the same thing as create in Shot and Ship.
Function Spawn( X, Y ,Size )
Local Rock:TRock = New TRock
If Not List List = CreateList()
List.AddLast Rock
If Not Image'First Time
Image = LoadImage("rock.png")
MidHandleImage( Image )
EndIf
Local RockSpeed# = 0
Select Size
Case LARGE'Large
RockSpeed = 0.2
Case MEDIUM'Medium
RockSpeed = 0.5
Case SMALL'Small
RockSpeed = 1
EndSelect
Rock.X = X
Rock.Y = Y
Rock.Direction = Rand(360)
Rock.Rotation = Rock.Direction
Rock.Size = Size
'Add Rock Start Speed
Rock.XSpeed= Cos(Rock.Direction)*RockSpeed
Rock.YSpeed= Sin(Rock.Direction)*RockSpeed
EndFunction
The above code should speak for itself, the rocks start with a constant speed that it keep aslong as it stays alive.
Method Draw()
Select Self.Size
Case LARGE
SetScale 2,2
Case MEDIUM'Medium
SetScale 1,1
Case SMALL'Small
SetScale 0.5, 0.5
EndSelect
SetRotation( Rotation )
Rotation:+0.5
DrawImage( Image,X,Y )
SetScale 1,1
EndMethod
Note the collision() method below, I will come to that in a moment.
Method Update()
Draw()
X:+ XSpeed
Y:+ YSpeed
Collision()
If X > 800 X = 0
If Y > 600 Y = 0
If X < 0 X = 800
If Y < 0 Y = 600
EndMethod
Function UpdateAll()
If Not List Return
For Local Rock:TRock = EachIn List
Rock.Update()
Next
EndFunction
Method Destroy()
List.Remove( Self )
EndMethod
Until now the rock would just tumble around in space. We want it to interact with the shp and your shots, so that they pose a threat and can be destroyed.
Method Collision()
Local Radius
Select Size
Case LARGE
Radius = 100
Case MEDIUM'Medium
Radius = 50
Case SMALL
Radius = 20
EndSelect
'If a rock hit a ship
If TShip.List
For Local Ship:TShip = EachIn TShip.List
If Distance( X, Y, Ship.X, Ship.Y ) < Radius
Ship.Destroy()'Ship = Dead
EndIf
Next
EndIf
Collision Rock versus Ship. I use the simple fact that if the ship ever gets closer to the rocks center than the rocks radius then it will collide with the rock. The rock is round so this should work very good. The same goes for the shots, if they are close enought to the rock the rock is hit.
'If a shot hit a rock
If Not TShot.List Return
For Local Shot:TShot = EachIn TShot.List
If Distance( X, Y, Shot.X, Shot.Y ) < Radius
Shot.Destroy()
Select Size
Case LARGE
Destroy()'Destory the rock
Spawn( X, Y , MEDIUM )
Spawn( X, Y , MEDIUM )
Case MEDIUM
Destroy()
Spawn( X, Y , SMALL )
Spawn( X, Y , SMALL )
Case SMALL
Destroy()'
EndSelect
EndIf
Next
EndMethod
If the rocks is Large we destroy the large and spawn two medium. This part of the code is very easy to follow. This is the great about max, some part of the programs almost becomes trival. And of course you need a way to calculate the Distance from the Rock to whatever. I made a quick function for that:
Function Distance#( X1, Y1, X2, Y2 )
Local DX# = X2 - X1
Local DY# = Y2 - Y1
Return Sqr(Dx*Dx + Dy*Dy)
EndFunction
Dx is the Distance from X1 to X2. Dy is the distance from Y1 to Y2. I want to know the diagonal length of these and therefore use pythogeras again. You’ll need to know some basic math to get this.
Now that’s about it. Now it’s a good time to tweak the game. Alter the ships physics and the rock speed and size. Try to make everything fit together as smooth as possible.
Time to make a game out of this engine. Here is what we need for that:
1. Introduction Screen with controls – Press Space to Start Game , Q Quit–
2. If you kill all rocks spawn a new level
3. Keep track of your score. Only small rocks give score, 1point each.
4. If the player dies, tell them their score. Restart game with R, Quit Q.
To implement this we need to go to our main file, AsteroidWars.bmx
First I create a global called Mode, and then three constants, one for each mode. Intro, Death and Playing. Also a global for Score and one for highest Score.
Const Intro = 1
Const Death = 2
Const Playing = 3
Global Mode = Intro
Global Score, HighestScore
Now I added the main stuff, nothing really special but it required a lot of tweaking. Therefore I can’t write it step by step. Instead I’ll finnish off by giving you the complete source. I recommend that you make your own version. Use this as a base. Tweak it, add features. My gamepley for this game sucks. It’s an example and nothing more. I hope it prove a good base for anyone wanting to try out game programming. Have fun with it!
AsteroidWars.bmx
Strict
AppTitle = "Asteroid Wars Example"
Rem
This is the file of a step by step example of how to make a simple asteroids clone.
The guide can be found at www.blitzbasic.com - forums - blitzmax - tutorials
EndRem
Include "TSpaceObject.bmx"
Include "TShip.bmx"
Include "TShot.bmx"
Include "TRock.bmx"
Graphics 800,600,0
Const Intro = 1
Const Death = 2
Const Playing = 3
Global Mode = Intro
Global Score, HighestScore
Global Level , Rank$
TRock.Spawn( 50,50, TRock.LARGE )
For Local NR = 1 To 20
TRock.Spawn( 400,300, TRock.SMALL )
Next
'Main Loop
Repeat' - - - - - - - - - - - - - - - - -
TShot.UpdateAll()
TRock.UpdateAll()
Select Mode
Case Intro
DrawText "Welcome to Asteroids Wars Beta Version",50,100
DrawText "Highest Score this run: "+HighestScore+" rocks.",50,130
DrawText "Steer with LEFT, RIGHT ",50,170
DrawText "Thrust with UP and DOWN ",50,190
DrawText "Fire with SPACE ",50,210
DrawText " --------- P R E S S S P A C E T O S T A R T ---------- ",50,260
If KeyHit(Key_Space) StartGame()
Case Playing
DrawText "Score: "+Score, 20, 20
TShip.UpdateAll()
If TShip.List.Count() = 0
Mode = Death
GetRanking()
Level = 0
FlushKeys
EndIf
If TRock.List.Count() = 0 StartGame()
Case Death
DrawText "~~ You Lost Your Ship - G A M E O V E R ~~ ",50,100
DrawText "You managed to destroy "+Score+" rocks in "+level+" different zones.",50,130
DrawText "------------------------ ",50,170
DrawText " Your RANK : "+Rank$ ,50,190
DrawText "------------------------ ",50,210
DrawText " --------- P R E S S [R] T O R E T R Y ---------- ",50,260
If KeyHit(Key_R) Mode = Intro
EndSelect
Flip
Cls
If KeyHit(Key_Q) End
Until KeyHit(Key_Escape)'- - - - - - - -
End
Function StartGame()
If TRock.List.Count() > 0 'First Time
'Clear
For Local Rock:TRock = EachIn TRock.List
Rock.Destroy()
Next
'Start
TShip.Create()
Else'In play
Level:+1
Score:+13
EndIf
CreateRocks()
Mode = Playing
EndFunction
Function CreateRocks()
Local Danger = Level*20 + Rand(0,10)
Local Ship:TShip = TShip( TShip.List.First() )
Repeat
Local X,Y
Repeat
X = Rand(0,800)
Y = Rand(0,600)
Until Distance(Ship.X, Ship.Y, X, Y ) > 200
Select Rand(1,2)
Case 1
TRock.Spawn( X, Y, TRock.LARGE ) ;Danger:- 5
Case 2
TRock.Spawn( X, Y, TRock.MEDIUM ) ;Danger:- 3
EndSelect
Until Danger <= 0
EndFunction
Function GetRanking()
Rank$ = "Classified"
If Level = 1 Rank = "You didn't get a single one?!"
If Level = 1 Rank = "ROFL"
If Level = 2 Rank = "LOL"
If Level = 3 Rank = "Newbie"
If Level = 4 Rank = "Beginner"
If Level = 5 Rank = "Average"
If Level = 6 Rank = "Fair"
If Level = 7 Rank = "Almost got one"
If Level = 8 Rank = "Starfighter"
If Level = 9 Rank = "Rock Destroyer"
If Level = 10 Rank = "Duplicator"
'...
'....
HighestScore = Score
EndFunction
Feel free to post your suggestions, improvements, questions and comments.
Next I'm going to make a vector physics tutorial. But it's a long long time until next time.