Damn, I just finished typing up that gravity post, and managed to delete it by accident while I was editing some code. This next version may be shorter, terser and less well explained, sorry about that.
First an introduction to gravity:
Gravity is a force, and will be taken in our game to act with a constant magnitude and straight down. By Newton's second law,
Force = Mass * Acceleration
Mass (the man's) is constant, so if force is too then acceleration must also be. So we can decide a single value for acceleration due to gravity and leave it alone. This also means that in order to model gravity fairly realistically, all we have to do is add that constant value to the man's vertical speed each loop, and add that vertical speed to his vertical position. So: in the "setup player" section we add
And in "update player" we add this:
0.05 is just a value that works well on my computer; you may need to adjust it. vs# is 0 at the moment because we don't want the guy to start off falling. Try running the code now. The man should fall straight through the floor. While this allows you to gauge the strength of gravity that using 0.05 gives you and allows you to fiddle it till you have the right value, clearly it's not something we want to keep, so our next step is to add code checking that all squares underneath the guy's feet are sky squares, and stopping him from falling if any of them are. The square directly beneath his left hand edge is given by screen(int(x#/20),int(y#/20)+1) as x# and y# are in pixels which are 1/20 * as big as the squares, and the square below has the same x-coordinate and a y-coordinate one greater than the guy. The int()'s are there because we can't ask for the 12 and a halfth box in an array; only whole numbers are defined. However, this isn't good enough. For one thing, there may be no part of the man's foot region touching that bottom left hand edge - in my stick-drawing there are 8 pixesl before the first foot appears, so I need to add 8 to x#. screen(int((x#+8)/20,int(y#/20)+1). For another, the square underneath that edge isn't necessarily the only one we need to check. If he was standing between two squares then having ground on either side of him would be enough to make him stop. Therefore we need to check the right edge too (there is no need to check in his middle because he is only as wide as one square). In my drawing there are 17 pixels from the left edge to his right foot, so the code referencing this square is screen(int((x#+16)/20,int(y#/20)+1).
The code we want then, looks like this (in the "update player" section)
rem Gravity Code
rem ************
vs#=vs#+g#
y#=y#+vs#
if screen(int((x#+8)/20),int(y#/20)+1)=3 and screen(int((x#+17)/20),int(y#/20)+1)=3
else
y#=y#-vs#
vs#=0
endif
This asks if both of the two possible squares is sky, and if it gets the answer no, reduces y# by the amount it was previously increased by so that the man doesn't move overall, and instantly cuts vertical speed to 0. So far so good, but running the program now would be pretty boring. Nothing would have changed at all since the version I posted last week, because the guy's not falling anymore, and there's no way to make him jump yet. So, here's the jump code (in "update player")
if spacekey()=1 and vs#=0 and space=0 then vs#=-2:space=1
if spacekey()=0 then space=0
Again, you might have to fiddle with the value vs# is set to to make it work well on your computer. vs# is negative because increasing the y-coordinate indicates a move down, not up. I added "and vs#=0" so you can't jump while in midair. The use of the new variable 'space' means that you can only jump once per spacebar-press.
And that's gravity just about finished. Now we need to test the jump code with a bit of a playground. Add the following to the "make level data" section, after the loop that sets up the level how we've had it so far.
screen(14,21)=1:screen(14,20)=2:screen(16,21)=1:screen(16,20)=1:screen(16,19)=2
screen(19,16)=2
screen(21,21)=1:screen(21,20)=2
screen(22,21)=1:screen(22,20)=1:screen(22,19)=2
screen(24,21)=1:screen(24,20)=1:screen(24,19)=1:screen(24,18)=1:screen(24,17)=2
screen(25,21)=1:screen(25,20)=1:screen(25,19)=1:screen(25,18)=2
screen(26,21)=1:screen(26,20)=1:screen(26,19)=1:screen(26,18)=2
screen(27,21)=1:screen(27,20)=2
This'll make some hills and a floating platform to play on. Run it, and you should quickly notice a stupid problem - we can run straight through walls, and jump into them too. This is because so far we never check, anywhere in our code, whether we have run into a wall, or jumped into one from below. This is understandable - until we added those extra features to the level this was never a risk, so it never occurred to us. We do need to fix it though, with another block of code in the "update player" section. You can either just add the code below, or you can try to work it out yourself, and use the code below if you get stuck. Up to you. Again, some of the numbers will have to be adjusted to make them fit your guy graphic.
if screen(int((x#+8)/20),int((y#+3)/20))=3 and screen(int((x#+14)/20),int((y#+1)/20))=3
else
y#=y#-vs#
vs#=0
endif
if screen(int((x#+17)/20),int((y#+3)/20))=3 and screen(int((x#+17)/20),int((y#/20)+1))=3
else
rem The number used here should be the same as that by which x# is altered if the left
rem or right arrows are pressed in the "Controls" section
x#=x#-1.5
endif
if screen(int((x#+3)/20),int((y#+3)/20))=3 and screen(int((x#+3)/20),int(y#/20+1))=3
else
rem This one too
x#=x#+1.5
endif
My code is still a bit bug-ridden. Depending on which way you're running it may or may not be possible to jump when you're touching a wall. If you jump into a wall you can hang there by walking towards the wall, stuff like that. I'll work on them sometime later and get them fixed. You may be able to fix them for your code yourself.
After that I'm not sure what to add to this little tutorial... you can probably work out code for scoring systems and collecting items yourself, AI/enemies sounds rather too much like hard work, although I could do it if you'd like me to. I could make you a bitmap based level editor if you like... dunno.
Anyhow, here's my code so far. Yours will look somewhat different obviously, but if you don't know where bits of code go or I've missed out some crucial step that's in my code but I forgot to write into this post you should be able to work it out from there.
cls 0
hide mouse
sync on
rem Load Images
rem ***********
load image "underground.jpg",1,1
load image "floor.jpg",2,1
load image "sky.jpg",3,1
rem Make Level Data
rem ***************
dim screen(31,23)
for y=0 to 23
if y=23 or y=22 then tile=1
if y=21 then tile=2
if y<21 then tile=3
for x=0 to 31
screen(x,y)=tile
next x
next y
screen(11,18)=2
screen(14,21)=1:screen(14,20)=2:screen(16,21)=1:screen(16,20)=1:screen(16,19)=2
screen(19,16)=2
screen(21,21)=1:screen(21,20)=2
screen(22,21)=1:screen(22,20)=1:screen(22,19)=2
screen(24,21)=1:screen(24,20)=1:screen(24,19)=1:screen(24,18)=1:screen(24,17)=2
screen(25,21)=1:screen(25,20)=1:screen(25,19)=1:screen(25,18)=2
screen(26,21)=1:screen(26,20)=1:screen(26,19)=1:screen(26,18)=2
screen(27,21)=1:screen(27,20)=2
rem Setup Player
rem ************
rem DBPro Code:
`create animated sprite 1,"guyPRO.bmp",8,1,4
rem DBC Code:
load bitmap "guy.bmp",1
for t=0 to 3
get image 4+t,t*20,0,t*20+20,20
next t
mirror bitmap 1
for t=0 to 3
get image 8+t,60-t*20,0,80-t*20,20
next t
frame=1
set current bitmap 0
x#=0
y#=320
g#=0.05
vs#=0
rem *************
rem * Main Loop *
rem *************
do
gosub updatePlayer
gosub drawScreen
sync
loop
rem ***************
rem * Subroutines *
rem ***************
rem DrawScreen Sub
rem **************
drawScreen:
for x=0 to 31
for y=0 to 23
paste image screen(x,y),x*20,y*20
next y
next x
return
rem UpdatePlayer Sub
rem ****************
updatePlayer:
rem Gravity Code
rem ************
vs#=vs#+g#
y#=y#+vs#
if screen(int((x#+8)/20),int(y#/20)+1)=3 and screen(int((x#+17)/20),int(y#/20)+1)=3
else
y#=y#-vs#
vs#=0
endif
if screen(int((x#+8)/20),int((y#+3)/20))=3 and screen(int((x#+14)/20),int((y#+1)/20))=3
else
y#=y#-vs#
vs#=0
endif
if screen(int((x#+17)/20),int((y#+3)/20))=3 and screen(int((x#+17)/20),int((y#/20)+1))=3
else
rem The number used here should be the same as that by which x# is altered if the left
rem or right arrows are pressed in the "Controls" section
x#=x#-1.5
endif
if screen(int((x#+3)/20),int((y#+3)/20))=3 and screen(int((x#+3)/20),int(y#/20+1))=3
else
rem This one too
x#=x#+1.5
endif
rem Controls
rem ********
if leftkey()=1 then dec x#,1.5
if rightkey()=1 then inc x#,1.5
if x#>620 then x#=620
if x#<0 then x#=0
if spacekey()=1 and vs#=0 and space=0 then vs#=-2:space=1
if spacekey()=0 then space=0
rem Sprite Update
rem *************
rem DBPro Code:
`sprite 1,x#,y#,4
`if rightkey()=1 then play sprite 1,1,4,100:lastDirection=0
`if leftkey()=1 then play sprite 1,5,8,100:lastDirection=1
`if rightkey()=0 and leftkey()=0 then play sprite 1,1+4*lastDirection,1+4*lastDirection,100
rem DBC Code:
if rightkey()=1 then sprite 1,x#,y#,frame+3:lastDirection=0
if leftkey()=1 then sprite 1,x#,y#,frame+7:lastDirection=1
if rightkey()=0 and leftkey()=0 then sprite 1,x#,y#,4+4*lastDirection
if timer()-time>100 then frame=frame+1:time=timer()
if frame=5 then frame=1
return
Why do you sink I ave zees outRAGEOUS accente?!