Best way to do collisions in 2d platformer
Blitz3D Forums/Blitz3D Programming/Best way to do collisions in 2d platformer
| ||
Hey All, I know that this is probably a really basic question, but I've not done a 2d platformer before and it seems like there are quite a few options for doing collisions. I really want the controls and gameplay to feel good, so I'd love to hear from the pros what the best approach is. I am open to doing pure 2d, sprites (2d in 3d), or using a lib like fastimage... it'd be great if it could be pixel perfect, though. I have tried in the past to do this using standard collision methods, like imagescollide(), etc., but always find that i get jumpy and poor quality collisions. What's worked for you? thanks, roland |
| ||
Imagescollided() should do pixel perfect checks. But a few tricks can help you out. I don't think it's the commands you use to obtain collision information, it's the techinques (useless with a spell checker) you can use. For instances, a character going up a slope wouldn't use the image of the character, but rather a pixel, not drawn, to get the position of the feet, to obtain smooth slope moving. Really depends what your after. Can you elaborate any? |
| ||
Hi Ross -- that's exactly the type of stuff that i'd love to know more about... It seems like there are probably some tried and true tricks to getting good-feeling platform gameplay working right. I was just hoping for a head start. The game that I'm going to be working on will want to feel kind of like super mario 2. I'll want a little bit of friction / slide on the ground, smooth jumping and most likely the majority of the level will be flat -- i haven't even thought about slopes yet. Seems like whenever I try to use collisions, I end up fighting with my gravity and movement so that my controls look really jittery. So once I finish a jump, or walk into a block from the side i just keep bouncing back and forth when i collide instead of walking into it and stopping or falling and standing still. Does that make sense? Any help at all would be greatly appreciated! |
| ||
Hmmm, seems more like a problem with your code. For instance, when jumping, you should set a flag saying your jumping, so you can't press the jump key again. The flag should only be unset, if you LAND on the ground, which would mean, you have to test the players feet. Sounds complicated, but what i do, or have done in the past is too move the players along the horizontal axis first, test for collisions, then move on the vertical, and test for collisions. That way, you can check for collisions in many ways: Check for jumping into platforms left and right of the characters position. Check to see if the players jump is upwards, then check for banging the head off the roof, then you can immediately change the jump direction to downwards. Check to see if the players direction is downwards, then check to see if he has collided with the ground, stopping the fall, and resetting the jump flag, therefore enabling the player to press the jump key again. With regards to checking if the player has hit the ground, you can at the same time, check to see if he has fallen in water, onto spikes, jumped on an enemies head. Some things that would result in a different action if the player had run into those things. Another important thing is to check collisions, THEN move your image. player_x player_y If ImagesCollide(player_image,player_x + move_x ,player_y + move_y , player_image_frame, ground , groundx, groundy , groundframe) = false then player_x = player_x + move_x player_y = player_y + move_y End If Note i don't move the image, until i've checked it's future co-ordinates (where the image will be). You should do this with all movements of the player, and enemies if they move. If your new to 2d games and blitz, or programming in general, you should check out 2d tilemaps. It's basically the whole screen made up of smaller images. This is done to save memory mainly, and make things easier to deal with. Also makes level construction easy too, especially with an editor. http://www.blitzbasic.com/toolbox/toolbox.php?tool=76 I have never used it, but seen many people recommend it :o) Hope some of this helps you. Feel free to ask more stuff. I'm sure others more experienced in this field will post some good stuff too. |
| ||
Hi again, and thanks for the quick reply! This is really helpful. I haven't implemented a regular tile map yet, but here's a version using just a simple image type instead. Not optimized, but it's a start. The controls are definitely feeling more solid -- they need to be tweaked a little. Arrows to move, space to jump. the whole drag thing is a little wonky right now and definitely needs refinement. Any suggestions would be really appreciated!!!! thanks again, roland Graphics 800,600,0,2 SeedRnd MilliSecs() Global gravity = 2 Global drag# = .8 Global tilewidth = 32 Type block Field image Field x Field y End Type Type player Field image Field x# Field y# Field xspeed# Field yspeed# Field jumpstrength Field jumping Field maxjump Field topimage Field bottomimage Field leftimage Field rightimage Field speed# Field maxspeed# End Type For x=0 To 50 b.block = New block b\x = Rand(25)*tilewidth b\y = (Rand(6)*tilewidth)+(11*tilewidth) b\image = CreateImage(tilewidth,tilewidth) SetBuffer(ImageBuffer(b\image)) grey = Rand(50)+205 ClsColor grey,grey,grey Cls SetBuffer BackBuffer() Next For x=0 To 24 b.block = New block b\x = x*tilewidth b\y = 18*tilewidth b\image = CreateImage(tilewidth,tilewidth) SetBuffer(ImageBuffer(b\image)) grey = Rand(50)+205 ClsColor grey,grey,grey Cls SetBuffer BackBuffer() Next p.player = New player p\image = CreateImage(tilewidth,tilewidth) p\x = 9*tilewidth p\y = 10*tilewidth p\jumpstrength = -5 p\maxjump = -14 p\speed = 1 p\maxspeed =5 SetBuffer ImageBuffer(p\image) Color 255,0,0 Oval 0,0,tilewidth,tilewidth p\topimage = CreateImage(tilewidth/2,3) SetBuffer ImageBuffer(p\topimage) Color 0,255,0 Rect 0,0,tilewidth/2,3 p\bottomimage = CreateImage(tilewidth/2,3) SetBuffer ImageBuffer(p\bottomimage) Color 0,255,0 Rect 0,0,tilewidth/2,3 p\rightimage = CreateImage(3,tilewidth/2) SetBuffer ImageBuffer(p\rightimage) Color 0,255,0 Rect 0,0,3,tilewidth/2 p\leftimage = CreateImage(3,tilewidth/2) SetBuffer ImageBuffer(p\leftimage) Color 0,255,0 Rect 0,0,3,tilewidth/2 SetBuffer(BackBuffer()) ClsColor 0,0,0 While Not KeyHit(1) Cls drawblocks() updateplayer() Flip() Wend Function drawblocks() For b.block = Each block DrawImage b\image,b\x,b\y,0 Next End Function Function updateplayer() For p.player = Each player p\yspeed = p\yspeed+gravity ;If p\xspeed > 0 Then p\xspeed = p\xspeed-drag ;If p\xspeed < 0 Then p\xspeed = p\xspeed+drag p\xspeed = p\xspeed*drag If KeyDown(203) p\xspeed = p\xspeed-p\speed EndIf If KeyDown(205) p\xspeed = p\xspeed+p\speed EndIf If p\xspeed > p\maxspeed Then p\speed = p\maxspeed If p\xspeed < -p\maxspeed Then p\speed = -p\maxspeed p\y = p\y+p\yspeed p\x = p\x+p\xspeed Repeat collidey = False For b.block = Each block If ImagesCollide(p\bottomimage,p\x+tilewidth/4,p\y+tilewidth-3,0,b\image,b\x,b\y,0) collidey = True p\yspeed=0 EndIf Next If collidey = True p\y = p\y-1 EndIf Until collidey = False Repeat collideup = False For b.block = Each block If ImagesCollide(p\topimage,p\x+tilewidth/4,p\y,0,b\image,b\x,b\y,0) collideup = True p\yspeed=0 p\jumping = False EndIf Next If collideup = True p\y = p\y+1 EndIf Until collideup = False Repeat collidel = False For b.block = Each block If ImagesCollide(p\leftimage,p\x,p\y+tilewidth/4,0,b\image,b\x,b\y,0) collidel = True p\xspeed=0 EndIf Next If collidel = True p\x = p\x+1 EndIf Until collidel = False Repeat collider = False For b.block = Each block If ImagesCollide(p\rightimage,p\x+tilewidth-3,p\y+tilewidth/4,0,b\image,b\x,b\y,0) collider = True p\xspeed=0 EndIf Next If collider = True p\x = p\x-1 EndIf Until collider = False If KeyHit(57) And p\yspeed = 0 And p\jumping = False p\jumping = True EndIf If KeyDown(57) If p\jumping = True If p\yspeed > p\maxjump p\yspeed = p\yspeed+p\jumpstrength Else p\jumping = False EndIf EndIf Else p\jumping = False EndIf DrawImage p\topimage,p\x+tilewidth/4,p\y DrawImage p\bottomimage,p\x+tilewidth/4,p\y+tilewidth-3 DrawImage p\leftimage,p\x,p\y+tilewidth/4 DrawImage p\rightimage,p\x+tilewidth-3,p\y+tilewidth/4 DrawImage p\image,p\x,p\y,0 Next End Function |
| ||
Ok, i'll check this out :o) |
| ||
Ok, here we go. First the code!Graphics 800,600,0,2 SeedRnd MilliSecs() Global gravity# = 0.7 Global drag# = .8 Global tilewidth = 32 Type block Field image Field x Field y End Type Type player Field image Field x# Field y# Field xspeed# Field yspeed# Field jump_speed_angle# Field jump_speed# Field jump_strength# Field jumping Field topimage Field top_x Field top_y Field bottomimage Field bottom_x Field bottom_y Field leftimage Field left_x Field left_y Field rightimage Field right_x Field right_y Field speed# Field maxspeed# End Type For x=0 To 50 b.block = New block b\x = Rand(25)*tilewidth b\y = (Rand(6)*tilewidth)+(11*tilewidth) b\image = CreateImage(tilewidth,tilewidth) SetBuffer(ImageBuffer(b\image)) grey = Rand(50)+205 ClsColor grey,grey,grey Cls SetBuffer BackBuffer() Next For x=0 To 24 b.block = New block b\x = x*tilewidth b\y = 18*tilewidth b\image = CreateImage(tilewidth,tilewidth) SetBuffer(ImageBuffer(b\image)) grey = Rand(50)+205 ClsColor grey,grey,grey Cls SetBuffer BackBuffer() Next p.player = New player p\image = CreateImage(tilewidth,tilewidth) p\x = 9*tilewidth p\y = 10*tilewidth ; new variables for jumping p\jump_strength = 10 p\jump_speed_angle# = 0 ; leave this at zero p\jump_speed# = 5 p\speed = 1 p\maxspeed =5 SetBuffer ImageBuffer(p\image) Color 255,0,0 Oval 0,0,tilewidth,tilewidth ; DrawImage p\topimage , p\x+3 , p\y ; DrawImage p\bottomimage , p\x+3 , p\y+tilewidth-3 ; DrawImage p\leftimage , p\x , p\y+3 ; DrawImage p\rightimage , p\x+tilewidth-3 , p\y+3 p\topimage = CreateImage(tilewidth-6,3) p\top_x = p\x + 3 p\top_y = p\y SetBuffer ImageBuffer(p\topimage) Color 0,255,0 Rect 0,0,tilewidth-6,3 p\top_x = p\x + 3 p\top_y = p\y p\bottom_x = p\x + 3 p\bottom_y = p\y + tilewidth - 3 p\left_x = p\x p\left_y = p\y + 3 p\right_x = p\x + tilewidth - 3 p\right_y = p\y + 3 p\bottomimage = CreateImage(tilewidth-6,3) p\bottom_x = p\x + 3 p\bottom_y = p\y + tilewidth - 3 SetBuffer ImageBuffer(p\bottomimage) Color 0,255,0 Rect 0,0,tilewidth-6,3 p\rightimage = CreateImage(3,tilewidth-6) p\right_x = p\x + tilewidth - 3 p\right_y = p\y + 3 SetBuffer ImageBuffer(p\rightimage) Color 0,255,0 Rect 0,0,3,tilewidth-6 p\leftimage = CreateImage(3,tilewidth-6) p\left_x = p\x p\left_y = p\y + 3 SetBuffer ImageBuffer(p\leftimage) Color 0,255,0 Rect 0,0,3,tilewidth-6 SetBuffer(BackBuffer()) ClsColor 0,0,0 While Not KeyHit(1) Cls drawblocks() updateplayer() Flip() Wend Function drawblocks() For b.block = Each block DrawImage b\image,b\x,b\y,0 Next End Function Function updateplayer() For p.player = Each player ; grab x and y co-ords before you move anything old_px = p\x old_py = p\y If KeyDown(57) And p\jumping = 0 p\jumping = 1 EndIf If KeyDown(203) p\xspeed = p\xspeed-p\speed ElseIf KeyDown(205) p\xspeed = p\xspeed+p\speed Else p\xspeed = p\xspeed * drag End If ; I have created 2 jump stages ; p\jumping = 1 - this is when the player is jumping upwards. ; p\jumping = 2 - this is when the player is falling ; Below i am using cos to make use of the sin waves. Basically, inputting a value into COS ; will produce a number, a ratio. If you were to drawing every single number from 0 to 180, you would come ; across a half circle. As you know, a circle curves, reaching an extremety. ; FOR EXAMPLE ; angle = 0 - cos(0) = 1 ; angle = 10 - cos(10) = 0.984 ; angle = 20 - cos(20) = 0.939 ; angle = 30 - cos(30) = 0.866 ; angle = 40 - cos(40) = 0.766 ; angle = 50 - cos(50) = 0.642 ; angle = 60 - cos(60) = 0.500 ; angle = 70 - cos(70) = 0.342 ; angle = 80 - cos(80) = 0.173 ; angle = 90 - cos(90) = 0.000 ; as you can see from above, inputting a linear number, outputs an ever decreasing number. So, if you use this ; for a jump, you can make the jump start off quick, and quickly dimishing, replicating the effect of gravity, ; without fiddling about with physics. For the falling, continue to angles for 100 - 180, this will start off, ; slowly, and increase exponentially, again, simulating the effect of gravity fairly well. If p\jumping = 1 Then p\jump_speed_angle = p\jump_speed_angle + p\jump_speed If p\jump_speed_angle >= 90 Then p\jump_speed_angle = 90 p\jumping = 2 End If ElseIf p\jumping = 2 Then p\jump_speed_angle = p\jump_speed_angle + p\jump_speed If p\jump_speed_angle => 180 Then p\jump_speed_angle = 180 End If End If If p\xspeed > p\maxspeed Then p\xspeed = p\maxspeed If p\xspeed < -p\maxspeed Then p\xspeed = -p\maxspeed ; below is where you add the jump/fall speed to the players co-ords. see below for expanation of COS ; Firstly check to see if the jumping flag has been set. If so, adjust the players y position. ; If NOT, then don't bother with them. If p\jumping <> 0 Then p\y = p\y-(Cos(p\jump_speed_angle) * p\jump_strength) End If p\x = p\x+p\xspeed ; you must update the image edges, so the collisions will register properly update_edges(p.player) ; you pass across the player object "p", as it's locally created to this function. ; The same applies with "old_px" and "old_py". The collision routine needs to reset to these co-ords. update_collisions(p.player,old_px,old_py) ; you must again update the image egdes, as the collision code may have reset the main image co-ords. update_edges(p.player) ; check collisions with blocks Text 0,0,"jumping : "+p\jumping Text 0,10,"jump_speed_angle : "+ jump_speed_angle Text 0,20,"jump_speed : "+p\jump_speed DrawImage p\topimage , p\top_x , p\top_y DrawImage p\bottomimage , p\bottom_x , p\bottom_y DrawImage p\leftimage , p\left_x , p\left_y DrawImage p\rightimage , p\right_x , p\right_y DrawImage p\image,p\x,p\y,0 Next End Function ; you must pass across the old p\x and p\y co-ords so the collision routine may reset positions, if future positions, ; cause a collision. Function update_collisions(p.player,old_px,old_py) collide_down = 0 collide_up = 0 collide_left = 0 collide_right = 0 For b.block = Each block If ImagesCollide(p\bottomimage,p\bottom_x,p\bottom_y,0,b\image,b\x,b\y,0) p\jumping = 0 ; switching jumping OFF p\jump_speed_angle = 0 ; reset jump_speed_angle to 0 collide_down = 1 ; set collide_down flag p\y = old_py EndIf If ImagesCollide(p\topimage,p\top_x,p\top_y,0,b\image,b\x,b\y,0) p\jumping = 2 p\jump_speed_angle = 90 collide_up = 1 p\y = old_py EndIf update_edges(p.player) If ImagesCollide(p\leftimage,p\left_x,p\left_y,0,b\image,b\x,b\y,0) p\xspeed=0 collide_left = 1 p\x = old_px EndIf If ImagesCollide(p\rightimage,p\right_x,p\right_y,0,b\image,b\x,b\y,0) p\xspeed=0 collide_right = 1 p\x = old_px EndIf Next If collide_down = 0 Then ; did not hit the ground If p\jumping = 0 Then p\jumping = 2 ; set to falling p\jump_speed_angle = 90 ; set the angle of cos to simulate falling End If End If End Function Function update_edges(p.player) p\top_x = p\x + 3 p\top_y = p\y p\bottom_x = p\x + 3 p\bottom_y = p\y + tilewidth - 3 p\left_x = p\x p\left_y = p\y + 3 p\right_x = p\x + tilewidth - 3 p\right_y = p\y + 3 End Function I reworked some of your code, created a few function to seperate everything. I also created some fields in your player type: Field top_x Field top_y Field left_x Field left_y Field right_x Field right_y Field bottom_x Field bottom_y Field jump_speed_angle Field jump_speed NOTE: the "jump_speed_angle" field isn't really an angle of such, it's just named as such, as it's put into COS to retrieve a value. I removed the max_jump field. I implemented your jumping and falling using COS. I have provided a more detailed explanation inside the code via comments. I have split the jumping into 3 stages. p\jumping = 0 - the player is on flat ground. This is set to zero when a collision is detected below the player, via "p\bottom_image" p\jumping = 1 - the player is actually jumping. Increments "jump_speed_angle" by "jump_speed" eg - jump_speed_angle = jump_speed_angle + jump+speed "jump_speed_angle" is limited to 90, as this is quater of a circle, and is the 0 point into COS wave. Once "jump_speed_angle" = 90, the p.jumping flag gets set to 2 p\jumping = 2 - the player is now falling. This variable is set to falling if: - the player jump has reached it's peak. - the player has walked off of a platform. This increases the "jump_speed_angle" up to 180. This is another quarter of a circle which leads to the lowest point in the COS wave. For example: "jump_speed_angle" = 0. Stick this into COS and it returns 1. So the jump starts at the highest speed. As "jump_speed_angle" = 90 the value COS returns is 0, meaning the speed is non-existant. Increasing "jump_speed_angle" beyond 90 cause the values returned to go below 0. When "jump_speed_angle" reaches 180, COS will return -1. It will peak at that, and beyond 180, will return to 0, then 1. That is why the code cuts off at 180, as it is the biggest minus number COS will return. The beauty of this, is all you need to do is plug in the "jump_speed_angle" and it will return value going from 1 (jumping upwards) to -1 (falling downwards), without you having to do anything, except change the jumping_flag. Additionally, COS will produce numbers have increase and decrease exponentially, acting like gravity :o) I have put all the co-ords of the edges images into their own variables, and made them longer, as the image was getting caught through the corners which were showing. I put them in their own variables just for reading sake. You need to update their co-ords though, as they are based on the main images co-ords. Call "update_edges(p.player" passing across the player object. You will need to do this anytime you update the main images co-ords. I have put the collisions all into the one loop and stuck them in a function, so to cut down the amount of code you have to read through and tidied up the code. I have also recorded the player images co-ords at the beginning of the "updateplayer" so you can reset them, if the the new co-ords fall within a collision. You must pass them across to the function, along with the player object "p.player". Added or changed the left/right movement to: If KeyDown(203) p\xspeed = p\xspeed-p\speed ElseIf KeyDown(205) p\xspeed = p\xspeed+p\speed Else p\xspeed = p\xspeed * drag End If This will check to see if the left or right key has been pressed. If not, then it will apply drag. Therefore not applying drag whilst moving. Just simplifies things a touch. Changed the jump code to a simple coupld of lines: If KeyDown(57) And p\jumping = 0 p\jumping = 1 EndIf Check to see if jump key is pressed (best with Keydown() because keyhit() only registers once per loop. So you couldn't check for jump keyhit() twice per loop). If so, also check to see if the p\jumping flag is at 0. If so, set to 1 (jumping) and let the rest of the code handle this. I think i've covered the things i have altered :o) Btw your code was very good. impressed by your implementation if this is a first go at it! I'll be hanging about here for a while, so feel free to post back about anything in there :o) Good luck! |
| ||
I have put in an Update_Edges() call between checking the up and down images, and the left and right images. This will enable the player to slide once he lands. |
| ||
I would recommend making the player slightly smaller than a tile too. Will reduce the problems you'll get with struggling to fit into tight spaces :o) |
| ||
Ok, i have updated the code again. It was getting tricky using the four different image to detect collisions. Perhaps they would only be useful for bullet collisions (hit in the back or front?) I have now split up the collision function into four different ones. The code will check to see the jump status. If p\jumping = 0 then check static collisions: This will check one pixel below the image, to see if the player is still on flat land. As falling is disabled, there is nothing to pull the player to the ground. It also makes sure that if you restore the orginal co-ods after a downward collision, the player will remain flush with the ground. If p\jumping = 1 then check upward collisions: Basically, if your jumping upwards, there is no point in check the players feet for platform collisions. Only exceptions would be moving platforms. If p\jumping = 2 then check downward collisions: Check to see if the players updated co-ords are causing a collision. If so, then set the jumping status to 0. I have made the player smaller, so you can make the jumps to tighter platforms. Graphics 800,600,0,2 SeedRnd MilliSecs() Global gravity# = 0.7 Global drag# = .8 Global tilewidth = 32 Type block Field image Field x Field y End Type Type player Field image Field x# Field y# Field xspeed# Field yspeed# Field jump_speed_angle# Field jump_speed# Field jump_strength# Field jumping Field topimage Field top_x Field top_y Field bottomimage Field bottom_x Field bottom_y Field leftimage Field left_x Field left_y Field rightimage Field right_x Field right_y Field speed# Field maxspeed# End Type For x=0 To 50 b.block = New block b\x = Rand(25)*tilewidth b\y = (Rand(6)*tilewidth)+(11*tilewidth) b\image = CreateImage(tilewidth,tilewidth) SetBuffer(ImageBuffer(b\image)) grey = Rand(50)+205 ClsColor grey,grey,grey Cls SetBuffer BackBuffer() Next For x=0 To 24 b.block = New block b\x = x*tilewidth b\y = 18*tilewidth b\image = CreateImage(tilewidth,tilewidth) SetBuffer(ImageBuffer(b\image)) grey = Rand(50)+205 ClsColor grey,grey,grey Cls SetBuffer BackBuffer() Next p.player = New player p\image = CreateImage(tilewidth/2,tilewidth/2) p\x = 9*tilewidth p\y = 10*tilewidth ; new variables for jumping p\jump_strength = 10 p\jump_speed_angle# = 0 ; leave this at zero p\jump_speed# = 5 p\speed = 1 p\maxspeed =5 SetBuffer ImageBuffer(p\image) Color 255,0,0 Oval 0,0,tilewidth/2,tilewidth/2 p\topimage = CreateImage(ImageWidth(p\image),3) p\top_x = p\x p\top_y = p\y SetBuffer ImageBuffer(p\topimage) Color 0,255,0 Rect 0,0,ImageWidth(p\image),3 p\bottomimage = CreateImage(ImageWidth(p\image),3) p\bottom_x = p\x p\bottom_y = p\y + ImageHeight(p\image) - 3 SetBuffer ImageBuffer(p\bottomimage) Color 0,255,0 Rect 0,0,ImageWidth(p\image),3 p\rightimage = CreateImage(3,ImageHeight(p\image)) p\right_x = p\x + ImageWidth(p\image) - 3 p\right_y = p\y SetBuffer ImageBuffer(p\rightimage) Color 0,255,0 Rect 0,0,3,ImageHeight(p\image) p\leftimage = CreateImage(3,ImageWidth(p\image)) p\left_x = p\x p\left_y = p\y SetBuffer ImageBuffer(p\leftimage) Color 0,255,0 Rect 0,0,3,ImageHeight(p\image) SetBuffer(BackBuffer()) ClsColor 0,0,0 While Not KeyHit(1) Cls drawblocks() updateplayer() Flip() Wend Function drawblocks() For b.block = Each block DrawImage b\image,b\x,b\y,0 Next End Function Function updateplayer() For p.player = Each player ; grab x and y co-ords before you move anything old_px = p\x old_py = p\y If KeyDown(57) And p\jumping = 0 p\jumping = 1 EndIf If KeyDown(203) p\xspeed = p\xspeed-p\speed ElseIf KeyDown(205) p\xspeed = p\xspeed+p\speed Else p\xspeed = p\xspeed * drag End If ; I have created 2 jump stages ; p\jumping = 1 - this is when the player is jumping upwards. ; p\jumping = 2 - this is when the player is falling ; Below i am using cos to make use of the sin waves. Basically, inputting a value into COS ; will produce a number, a ratio. If you were to drawing every single number from 0 to 180, you would come ; across a half circle. As you know, a circle curves, reaching an extremety. ; FOR EXAMPLE ; angle = 0 - cos(0) = 1 ; angle = 10 - cos(10) = 0.984 ; angle = 20 - cos(20) = 0.939 ; angle = 30 - cos(30) = 0.866 ; angle = 40 - cos(40) = 0.766 ; angle = 50 - cos(50) = 0.642 ; angle = 60 - cos(60) = 0.500 ; angle = 70 - cos(70) = 0.342 ; angle = 80 - cos(80) = 0.173 ; angle = 90 - cos(90) = 0.000 ; as you can see from above, inputting a linear number, outputs an ever decreasing number. So, if you use this ; for a jump, you can make the jump start off quick, and quickly dimishing, replicating the effect of gravity, ; without fiddling about with physics. For the falling, continue to angles for 100 - 180, this will start off, ; slowly, and increase exponentially, again, simulating the effect of gravity fairly well. If p\jumping = 1 Then p\jump_speed_angle = p\jump_speed_angle + p\jump_speed If p\jump_speed_angle >= 90 Then p\jump_speed_angle = 90 p\jumping = 2 End If ElseIf p\jumping = 2 Then p\jump_speed_angle = p\jump_speed_angle + p\jump_speed If p\jump_speed_angle => 180 Then p\jump_speed_angle = 180 End If End If If p\xspeed > p\maxspeed Then p\xspeed = p\maxspeed If p\xspeed < -p\maxspeed Then p\xspeed = -p\maxspeed ; below is where you add the jump/fall speed to the players co-ords. see below for expanation of COS ; Firstly check to see if the jumping flag has been set. If so, adjust the players y position. ; If NOT, then don't bother with them. If p\jumping <> 0 Then p\y = p\y-(Cos(p\jump_speed_angle) * p\jump_strength) End If If p\jumping = 0 Then ; check below, as player may run off of a platform update_static_vertical_collisions(p.player,old_py) ElseIf p\jumping = 1 Then ; check above update_collisions_up(p.player,old_py) ElseIf p\jumping = 2 Then ; check below the player, as he is falling update_collisions_down(p.player,old_py) End If update_edges(p.player) p\x = p\x+p\xspeed ; you must update the image edges, so the collisions will register properly update_edges(p.player) update_collisions_horizontal(p.player,old_px) update_edges(p.player) Text 0,0,"jumping : "+p\jumping Text 0,10,"jump_speed_angle : "+ jump_speed_angle Text 0,20,"jump_speed : "+p\jump_speed DrawImage p\topimage , p\top_x , p\top_y DrawImage p\bottomimage , p\bottom_x , p\bottom_y DrawImage p\leftimage , p\left_x , p\left_y DrawImage p\rightimage , p\right_x , p\right_y DrawImage p\image,p\x,p\y,0 Next End Function ; you must pass across the old p\x and p\y co-ords so the collision routine may reset positions, if future positions, ; cause a collision. Function update_static_vertical_collisions(p.player,old_py) collide_down = 0 collide_up = 0 For b.block = Each block If ImagesCollide(p\image,p\x,p\y+1,0,b\image,b\x,b\y,0) p\jumping = 0 ; switching jumping OFF p\jump_speed_angle = 0 ; reset jump_speed_angle to 0 collide_down = 1 ; set collide_down flag p\y = old_py update_edges(p.player) EndIf Next If collide_down = 0 Then ; did not hit the ground If p\jumping = 0 Then p\jumping = 2 ; set to falling p\jump_speed_angle = 90 ; set the angle of cos to simulate falling End If End If End Function Function update_collisions_down(p.player,old_py) collide_down = 0 collide_up = 0 For b.block = Each block If ImagesCollide(p\image,p\x,p\y,0,b\image,b\x,b\y,0) p\jumping = 0 ; switching jumping OFF p\jump_speed_angle = 0 ; reset jump_speed_angle to 0 collide_down = 1 ; set collide_down flag p\y = old_py update_edges(p.player) EndIf Next If collide_down = 0 Then ; did not hit the ground If p\jumping = 0 Then p\jumping = 2 ; set to falling p\jump_speed_angle = 90 ; set the angle of cos to simulate falling End If End If End Function Function update_collisions_up(p.player,old_py) For b.block = Each block If ImagesCollide(p\image,p\x,p\y,0,b\image,b\x,b\y,0) p\jumping = 2 p\jump_speed_angle = 90 collide_up = 1 p\y = old_py update_edges(p.player) EndIf Next End Function Function update_collisions_horizontal(p.player,old_px) For b.block = Each block If ImagesCollide(p\image,p\x,p\y,0,b\image,b\x,b\y,0) p\xspeed=0 p\x = old_px EndIf Next End Function Function update_edges(p.player) p\top_x = p\x p\top_y = p\y p\bottom_x = p\x p\bottom_y = p\y + ImageHeight(p\image) - 3 p\right_x = p\x + ImageWidth(p\image) - 3 p\right_y = p\y p\left_x = p\x p\left_y = p\y End Function Check it out and let me kwno what you think. One for detecting collisions below the player, using the main player image. |
| ||
Wow, this is really incredible! I think that i'm starting to get my head around it from reading your detailed instructions, but I haven't had a chance to try it out yet... hopefully i'll get to it tonight and let you know how it works out!!! Thanks again for taking the time to work this out for me -- i really appreciate your help... best, roland |
| ||
No problem man. Just as long as you pass on what you learn to other people if they need the help :o) |