Computer Science Canada

[tutorial] spaceship movement: asteroids style

Author:  zylum [ Mon Feb 16, 2004 6:03 pm ]
Post subject:  [tutorial] spaceship movement: asteroids style

This tutorial is meant to teach you how to program an image to respond to key input and to create an effect of movement in space ie you keep moving it the same direction even if you aren't pressing the throttle key...

to start off, here's the full source:
code:

setscreen ("offscreenonly")

var spaceship : int := Pic.FileNew ("spaceship.bmp")
Pic.Draw (spaceship, 15, 20, picMerge)
spaceship := Pic.New (0, 0, Pic.Width (spaceship) + 30, Pic.Height (spaceship) + 20)

const rotX := Pic.Width (spaceship) div 2
const rotY := 35
const forwardSpd := 0.3
const backwardSpd := 0.2
const turnSpd := 5
const decay := 0.99

var x : real := maxx div 2
var y : real := maxy div 2
var ax, ay : real
var vx, vy : real := 0
var spd : real := 0
var ang : int := 0
var ship : int
var move : array char of boolean

procedure moveShip
    if x > maxx - rotX then
        x := -rotX
    elsif x < -rotX then
        x := maxx - rotX
    elsif y > maxy - rotY then
        y := -rotY
    elsif y < -rotY then
        y := maxy - rotY
    end if
    ax := cos (ang * (Math.PI / 180)) * spd
    ay := sin (ang * (Math.PI / 180)) * spd
    vx += ax
    vy += ay
    vx *= decay
    vy *= decay
    x += vx
    y += vy
    ship := Pic.Rotate (spaceship, ang - 90, rotX, rotY)
    Pic.Draw (ship, round (x), round (y), picMerge)
    Pic.Free (ship)
end moveShip

procedure getKeyStrokes
    Input.KeyDown (move)
    if move (KEY_UP_ARROW) then
        spd := forwardSpd
    elsif move (KEY_DOWN_ARROW) then
        spd := -backwardSpd
    else
        spd := 0
    end if
    if move (KEY_LEFT_ARROW) then
        ang += turnSpd
    elsif move (KEY_RIGHT_ARROW) then
        ang -= turnSpd
    end if
end getKeyStrokes

loop
    drawfillbox (0, 0, maxx, maxy, black)
    getKeyStrokes
    moveShip
    View.Update
    delay (10)
end loop


lets look at the first bit of code:

code:

var spaceship : int := Pic.FileNew ("spaceship.bmp")
Pic.Draw (spaceship, 15, 20, picMerge)
spaceship := Pic.New (0, 0, Pic.Width (spaceship) + 30, Pic.Height (spaceship) + 20)


in the above code, we store the image "spaceship.bmp" in a variable for later use. next we draw the spaceship on the screen and store the picture in the same variable. there is a reason for this... this time when we store the image we select and area greater than the actual image which leaves a border of nothingness around it. this is important since later when we rotate the ship, some clipping occures at the edge of the picture. with the edge being further away, the cliping doesnt occure.

code:

const rotX := Pic.Width (spaceship) div 2
const rotY := 35
const forwardSpd := 0.3
const backwardSpd := 0.2
const turnSpd := 5
const decay := 0.99


this chunk of code controls all the characteristics of the movement such as the speed of the ship when its accelerating forwrad or how fast the ship turns. the constants rotX and rotY is where the ship will pivot when it's being rotated.

code:

procedure moveShip
    if x > maxx - rotX then
        x := -rotX
    elsif x < -rotX then
        x := maxx - rotX
    elsif y > maxy - rotY then
        y := -rotY
    elsif y < -rotY then
        y := maxy - rotY
    end if
    ax := cos (ang * (Math.PI / 180)) * spd
    ay := sin (ang * (Math.PI / 180)) * spd
    vx += ax
    vy += ay
    vx *= decay
    vy *= decay
    x += vx
    y += vy
    ship := Pic.Rotate (spaceship, ang - 90, rotX, rotY)
    Pic.Draw (ship, round (x), round (y), picMerge)
    Pic.Free (ship)
end moveShip


this is the procedure which controls all the movements of the ship...

code:

if x > maxx - rotX then
    x := -rotX
elsif x < -rotX then
    x := maxx - rotX
elsif y > maxy - rotY then
    y := -rotY
elsif y < -rotY then
    y := maxy - rotY
end if


this is where we check whether the ship has reached the edge of the screen, if so, we "rollover" the ship to the other side of the screen.

code:

ax := cos (ang * (Math.PI / 180)) * spd
ay := sin (ang * (Math.PI / 180)) * spd
vx += ax
vy += ay
vx *= decay
vy *= decay
x += vx
y += vy


since this type of movement is a bit more complex than the tradition point and go movement, we need a few more variables.... the ax and ay variables store the value of the acceleration upon that axis. this value is calculated with a bit of trig

code:

cos (ang * (Math.PI / 180))
sin (ang * (Math.PI / 180))


this returns a value betwee 0 and 1 depending on the angle spcified. the angle is converted into radians by multiplying it by PI/180. this number is then multiplied by the speed and you end up with your acceleration values.

code:

vx += ax
vy += ay
vx *= decay
vy *= decay
x += vx
y += vy


this bit is fairly self explanitory especially if you've taken physics. it adds the acceleration vectors to the velocity vectors and then the velocities are added to the x and y coordinates of the spaceship.

code:

ship := Pic.Rotate (spaceship, ang - 90, rotX, rotY)
Pic.Draw (ship, round (x), round (y), picMerge)
Pic.Free (ship)


here we rotate our original spaceship image around the rotX and rotY coordinates (the pivot point) by the specified angle and then we draw it and free the memory. we free the image from the memory because that's the only time we will use it -> the next loop will update it.

code:

procedure getKeyStrokes
    Input.KeyDown (move)
    if move (KEY_UP_ARROW) then
        spd := forwardSpd
    elsif move (KEY_DOWN_ARROW) then
        spd := -backwardSpd
    else
        spd := 0
    end if
    if move (KEY_LEFT_ARROW) then
        ang += turnSpd
    elsif move (KEY_RIGHT_ARROW) then
        ang -= turnSpd
    end if
end getKeyStrokes


this simply checks which keys are pressed and alters the appropriate variables accordingly.

code:

loop
    drawfillbox (0, 0, maxx, maxy, black)
    getKeyStrokes
    moveShip
    View.Update
    delay (10)
end loop


finally we put the two procedures together in a loop.
everytime it loops, we erase our old screen by drawing a blackbox over it. then we check which keys are being pressed by calling the getKeyStrokes procedure. then we move and draw the space ship by calling the moveShip procedure. finally we update the screen and add a delay to control the speed of the game.

phew!!! that took some time! hope you guys enjoy this tutorial Wink

-zylum

by the way here's the graphic for the spaceship:

Author:  zylum [ Mon Feb 16, 2004 6:05 pm ]
Post subject: 

the graphic:

[edit] can't seem to get the bmp to attach... is bmp a valid file type???
here's a url to the image:
http://zylum1.tripod.com/spaceship2.bmp
[/edit]

Author:  Cervantes [ Mon Feb 16, 2004 6:18 pm ]
Post subject: 

link doesn't work either Eh
just change it to a jpg file.

Author:  zylum [ Mon Feb 16, 2004 6:25 pm ]
Post subject: 

damn...
try to copy and paste the url into your browser if that doesnt work here's the jpg:

Author:  AsianSensation [ Mon Feb 16, 2004 6:31 pm ]
Post subject: 

i like it, add to this some FPS, add some nice gfx, and you can have a nice and easy asteroid game.

one thing though, when you were doing this:
code:
ship := Pic.Rotate (spaceship, ang - 90, rotX, rotY)
Pic.Draw (ship, round (x), round (y), picMerge)
Pic.Free (ship)
just like the cosine-sine problem you had with the 3d thingie, it's not as fast as generating all the pictures and then call them when you needed it.

here, have some bits for your effort.

+25 bits

Edit: it seems the donate and the +- bits option are messed, I'll owe you 25 bits for now, remind me later.

Author:  Cervantes [ Mon Feb 16, 2004 6:35 pm ]
Post subject: 

niiiice...
This is really similar to Asians tutorial but meh, still good Smile

This brings up memories of comet busters Crying or Very sad

Author:  zylum [ Mon Feb 16, 2004 6:41 pm ]
Post subject: 

what do you mean by:
"just like the cosine-sine problem you had with the 3d thingie, it's not as fast as generating all the pictures and then call them when you needed it. "

my 3d engine doesnt draw pics, it draws polygons... further more, it just has to generate 1 pic every frame which isnt that bad.

btw i already made an asteroids game and it runs just fine... i'll post it tomorrow since the latest draft is stored there, but for now heres a basic version:

Author:  AsianSensation [ Mon Feb 16, 2004 6:43 pm ]
Post subject: 

I meant that instead of generating on the spot and then freeing the picture memory, it's faster to pre-generate everything and then call on the appropriate picture. Much like instead of generating sine and cosine values, it's faster to pre-generate a table and then call it instead of calling it on the spot. The difference being Pic.Rotate takes up alot of time, so when you have asteroid flying all over, it's going to lag the game down very badly.

Author:  zylum [ Mon Feb 16, 2004 6:47 pm ]
Post subject: 

well, i'd have to store 72 images if i wanted im increment of 5 degrees while turning -_- i think one Pic.Rotate isn't that bad (the asteroids themselves dont rotate)

Author:  Cervantes [ Mon Feb 16, 2004 7:18 pm ]
Post subject: 

An increment of 10 looks fine. that's 36 different images. If you think about it, that's a lot. It wouldn't look all that smooth if the image you were rotating was a long thin line, but with a small fat ship like that (jk it looks really cool) you don't really notice it.

Author:  AsianSensation [ Mon Feb 16, 2004 7:27 pm ]
Post subject: 

I was assuming that you were also rotating the asteroid, then you would need to generate pics pre-hand. Also, from my experience, 10 degree increments would look fine on most pictures (not extremely large ones)

but generating pictures and storing them won't take up that much memory at all, if you want, you could even declare them as const int instead of int.

Author:  paladin style [ Wed Feb 18, 2004 10:25 am ]
Post subject: 

a cool program Twisted Evil Evil or Very Mad

Author:  Jodo Yodo [ Tue Mar 02, 2004 10:53 pm ]
Post subject: 

How come when I use Math.PI, it says it's not an export of 'Math'?

Author:  zylum [ Tue Mar 02, 2004 10:59 pm ]
Post subject: 

you need version 4.0.5 to use Math.PI... you can always replace that with 3.14159 if you dont want to update

-zylum

Author:  Paul [ Sat Jan 08, 2005 9:44 am ]
Post subject: 

Sorry for bringing this old one up, but I'm making one right now. Basically what I've got so far is your code (but I made it myself from understanding every part of your code first). And a second picture for when the ship is thrusting, it has exhaust. But there are still a couple of things I don't understand:
code:

AccX := cos (angle * (Math.PI / 180))*speed
AccY := sin (angle * (Math.PI / 180))*speed

since I haven't learnt radians yet, how does "cos (angle * (Math.PI / 180))*speed" give you the component of acceleration for x, similarly for y. Could you possibly explain this in non-technical terms?
also: why "angle - 90"? why 90? I think this also has something to do with not knowing radians. Confused

Author:  zylum [ Tue Jan 25, 2005 7:54 pm ]
Post subject: 

i didnt know about cosd and sind when i wrote this so instead of using

code:
AccX := cos (angle * (Math.PI / 180))*speed
AccY := sin (angle * (Math.PI / 180))*speed


you can use

code:
AccX := cosd (angle)*speed
AccY := sind (angle)*speed


which is normal trig with angles instead of radians.

also the -90 is there to correct the angle of the spaceship. since the picture is oriented with the ship facing up and the 0th degree points right, you need to draw the pic with -90 degrees to correct this... or i suppose you can make the default angle 90. yeah i should have thought of that before but meh this was a long time ago Wink


: