News:

Building a 3D Ray Tracer  By stevmjon

Main Menu

Platformer Pixel Collisions Via Ray Casting

Started by kevin, August 24, 2009, 06:14:55 PM

Previous topic - Next topic

kevin

  Platformer Pixel Collisions Via Ray Casting

 This example demo's a basic platform game control system with acceleration, jumping, falling and collision. The collision is really the only interesting part of the demo (since the rest of is cut and pasted from an example you can find the PlayBasic/Projects/Tutorials/Physics folder.

  In this demo, we're using an image as our collision environment, the image is a 2 colour representation of the 'hard floor' areas that a character can stand upon. When the character is moved/updated we're using the ray Casting function PixelRunLength() on this collision image to detect our position relative to the floor.  Then applying some simple logic to determine what that character should do, given how far from the height it is.

 Characters have 3 modes.  They're either Walking, Jumping or Falling.

   If the character is Walking then we need to get some idea of the terrain they're walking upon, so we run a ray down from the characters origin point  until it hits the ground.  If you're walking upon a slope, within this demo  we're going to assume the slope never raises / drops move than the characters  height in gradient.  So if we're running up/down hill, then this assumption  will hold us upon the ground, until the ground if too far away.  In that  situation, the character changes from walk mode to falling mode.  

  If the character is Jumping, then we don't need to check for ground collisions at all, only when it's falling.

  When the character is falling, we're casting a ray down from it's current position. This time we're assuming the ground is further away then the characters height.  If it is, we keep falling, if it isn't, then it's landed upon something.   So we use the distance from ground  to reposition  the character relatively.  

  We could expand upon this logic further and detect situations were the character is only just above the ground perhaps.  Which can happen if they try and jump up onto something and almost miss.  Here, you could trigger a climbing animation or perhaps the character  moves into a hanging by hands mode or something.   In the demo it just  forces a reposition,  which can be a bit clunky.. :)

  Anyway, have fun.


Related Examples

  Sonic the Hedgehog Loop Mock Up
  Platform Game Movement Based on Ray Intersection
  Advanced Platform Collision (Vector based)
  Walking On Sprites
  Walking on Sprite Collision Map
  Simple platformer with slopes


Download
  Attached.

micky4fun

Hi Kevin

This looks and plays nice , i will look through code and try to suss out how you did it
i love the semi-circle collision detection part of demo , i think this looks a lot nicer in a platform game than just straight lines as platform

mick :)

markel422

#2
Not sure if this is allowed but this is my attempt to do it but without the addimage functioning, I'm glad that I was able to get how it works...(sort of). But it took me months just to get this far.

Here is the code:
PlayBASIC Code: [Select]
setfps 60


Constant Gravity# = 9.8

Constant JumpState_Off = 0
Constant JumpState_JumpingUp = 1
Constant JumpState_FallingDown = 2

//Create Screen Type
type tScreenBounds
Left
Right
Floor
endtype
Dim bounds as tScreenBounds
bounds.left = 32
bounds.right = getscreenwidth()-bounds.left
bounds.floor = getscreenheight()

//Create Player Type
type tPlayer
x#, y#

AccelX#
AccelStepX#
TopSpeedX#

AccelY#
AccelStepY#
TopSpeedY#

JumpState

CollisionImg
endtype
Dim Player as tplayer
player.x# = getscreenwidth()/2
player.y# = getscreenheight()/2

player.AccelX#=0
player.AccelStepX#=0.75
player.TopSpeedX#=10

player.AccelY#=0
player.AccelStepY#=Gravity#/30
player.TopSpeedY#=Gravity#*2

player.JumpState=JumpState_Off

CollisionImg=Draw_Collision_Map()

player.Collisionimg=CollisionImg

Do
cls 0

Drawimage Collisionimg,0,0,true

Handle_Player()

sync
loop

function Handle_Player()

If Rightkey()=true
player.accelx#=player.accelx#+player.accelstepx#
if player.accelX#>player.topspeedx# then player.accelX#=player.topspeedx#
endif

if LeftKey()=true
player.accelx#=player.accelx#-player.accelstepx#
if player.accelx#<-player.topspeedx# then player.accelx#=-player.topspeedX#
endif

If SpaceKey()
if player.Jumpstate=Jumpstate_off
player.jumpstate=JumpState_JumpingUp
player.accely#=-Gravity#
endif
endif

player.x#=player.x#+player.accelx#
player.y#=player.y#+player.accely#

if player.x#<bounds.left then player.x#=bounds.left
if player.x#>bounds.right then player.x#=bounds.right
width=64
height=32
height2=height*2

;If JumpState_Off make the player equal the red line ray so that it prefectly stops at the created grounds...
if player.JumpState=Jumpstate_Off
x=int(player.x#)
y=int(player.y#)-height
DistancetoFloor=FindDistancetoImpact(player.Collisionimg,x,y)
linec x,y,x,y+DistancetoFloor,RGB(255,0,0)

if DistancetoFloor<=height
player.AccelY#=0
player.y#=y+DistancetoFloor
else
player.JumpState=JumpState_FallingDown
player.AccelY#=0
endif
endif

;If JumpState_JumpingUp draw the redline Ray so the player can compute how far they are from the ground in realtime...
If player.JumpState=JumpState_FallingDown
x=int(player.x#)
y=int(player.y#)-height
DistancetoFloor=FindDistancetoImpact(player.Collisionimg,x,y)
linec x,y,x,y+DistancetoFloor,RGB(255,0,0)

if DistancetoFloor<=height
GroundY=y+DistancetoFloor ;Create Variable GroundY to represent y+DistancetoFloor
player.JumpState=JumpState_Off
player.AccelY#=0
Player.y#=GroundY
endif
endif


if player.y#=>bounds.floor-height
player.JumpState=JumpState_Off
player.AccelY#=0
player.y#=bounds.floor-height
endif

circlec player.x#,player.y#-height,32,1,rgb(255,0,0)

if player.AccelX#<>0
if player.AccelX#<0
player.accelX#=player.accelX#+(player.accelStepX#/2)
if player.accelX#>0 then player.accelX#=0
endif
if player.accelX#>0
player.accelx#=player.accelX#-(player.accelstepX#/2)
if player.accelX#<0 then player.accelX#=0
endif
endif

Select player.JumpState

case JumpState_JumpingUp
player.accelY#=player.accelY#+player.accelStepY#
if player.accelY#>0
player.accelY#=0
Login required to view complete source code


But I'm still confused about some of the variables here, I was able to remove some code from your program and it still works perfectly like (Oldsurface). Also when it comes to the "FindDistancetoImpact" function, I can't seem to figure out what a few of the codes is doing, when I saw NullPixel=(0,0) and removed it, the ball would suddenly start flying up pass the top of the screen, so I would like to know what is this controlling???
And the rendertoimage img and rendertoimage oldsurface, usually I thought I was suppose to always type "rendertoscreen" when I am done, so how does this work?

Also my ball seems to bounce when it is going down the slope compared to yours, why is that?



kevin

#3
QuoteBut I'm still confused about some of the variables here, I was able to remove some code from your program and it still works perfectly like (Oldsurface).

   Just in case you're not sure, The point of the asking PlayBASIC what the current surface is, then restoring it at the end of a function.   Is cleanliness.  If your writing a function that alters the current surface, ink colour, current font  or whatever.   Then it's best to preserve these before you change them, so these states can be restored upon exit.   So when you call your function from other parts of your program, the function doesn't unexpectedly change the state in some way.


Quote
Also when it comes to the "FindDistancetoImpact" function, I can't seem to figure out what a few of the codes is doing, when I saw NullPixel=(0,0) and removed it, the ball would suddenly start flying up pass the top of the screen, so I would like to know what is this controlling???


 Bellow is taken from the manual,

 * Note: Before you can use PixelRunLength safely. You must lock the draw surface, then it's highly recommended you read a pixel from this surface using Point(). This will ensure the address of the surface are seeded correctly internally for PixelRunLength


  When we're drawing or reading from a graphics surface (the screen or an Image),  the Pb gfx engine has certain fixed overheads that come doing any operation.    Primarly it has to work out the correct rendering method for the surface your drawing to, then prepare it self for this operation. The preparation is seeding the internally buffer pointer and pixel format.    You can think of this overhead as nanny code.   So the nanny code protects you from the real world underneath!

  While most built in commands in PB have nanny code in them, some don't.    PixelRunLength is one of them.  FastPoint & FastDot are some others.    These commands have no nanny code protection, given they're going to be called in high frequency.    As while the nanny code might save you from poking your eye out,  it can add considerable overhead to every operation.      
 

PlayBASIC Code: [Select]
function FindDistancetoImpact(img,Xpos,Ypos)
if Xpos>-1 and Xpos<getimagewidth(img) //If Xpos is more than -1 and less than imagewidth()"800x600" do this...
if Ypos<0 then Ypos=0
OldSurface=getsurface()
rendertoimage img
lockbuffer
NullPixel = Point(0,0)
Runs=PixelRunLength(Xpos,Ypos,0,1,RGB(0,0,0))-1
unlockbuffer
rendertoimage oldsurface
endif
endfunction Runs



  In the function above,  what i'm doing here is first preserving the the current surface the GFX engine is rendering to, then I'm changing to the surface in question then locking it.     The thing is, since we haven't drawn anything to this surface, we can't be sure if the internal frame buffer pointers and pixel formats have been initialized.    But we can do this via using POINT().   When we call point, the nanny code within it (the core GFX engine) will work out the type of the surface and rendering routines we've selected , then initialize the pointers internally.    Now given that PixelRunLength() has no nanny code in it,  it simply uses gfx engines the current settings.    


Quote
And the rendertoimage img and rendertoimage oldsurface, usually I thought I was suppose to always type "rendertoscreen" when I am done, so how does this work?

  No.  


kevin

#4
  Platformer Pixel Collisions Via Ray Casting V0.02 (Requires PlayBasic V1.64M or newer)

     The attached example uses is an extension the above example,  where as the previous demo runs collision upon an image, this one builds a map representation and then runs collision upon that using the RayHitMapPixels() function.


      Requires: PlayBASIC V1.64M Beta 12 (or above)


 Video:




 Download: