UnderwareDesign
July 29, 2010, 06:50:15 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
News: PlayValidate V0.01 Released (Checks the status your PB Install) (20th,May,2010)
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: Platformer Pixel Collisions Via Ray Casting  (Read 1116 times)
kevin
Development Team
Administrator
Hero Member
*****
Offline Offline

Posts: 9341



WWW
« on: August 24, 2009, 06:14:55 PM »

  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.. Smiley
 
   Anyway, have fun.


Related Examples

   Advanced Platform Collision (Vector based)
   Walking On Sprites
   Walking on Sprite Collision Map



Download
   Attached.

* Pixel_Collision_Ray_Casting_V001.zip (11.57 KB - downloaded 126 times.)

* Platformer_Pixel_Collision_Ray_Casting_V001.png (10.13 KB, 404x317 - viewed 166 times.)
« Last Edit: January 20, 2010, 09:44:17 PM by kevin » Logged

micky4fun
Sr. Member
****
Offline Offline

Posts: 401


« Reply #1 on: August 25, 2009, 05:22:20 AM »

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 Smiley
Logged
markel422
Newbie
*
Offline Offline

Posts: 46


« Reply #2 on: May 07, 2010, 10:43:34 PM »

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:
Code:
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
     player.JumpState=JumpState_FallingDown
   endif
  
   case JumpState_FallingDown
   player.accelY#=player.accelY#+player.accelStepY#
   EndSelect
print player.y#
endfunction

function Draw_Collision_Map()
   w=getscreenwidth()
   h=getscreenheight()
  
   img=newfximage(w,h)
   rendertoimage img
   line 0,h-50,w,h-50
   line 200,h-200,600,h-100
   line 0,h/2,150,h/2
   line 650,h/2,w,h/2
   line 200,(h/2)-100,600,(h/2)-200
   rendertoscreen
endfunction img

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

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?
« Last Edit: May 07, 2010, 10:45:18 PM by markel422 » Logged
kevin
Development Team
Administrator
Hero Member
*****
Offline Offline

Posts: 9341



WWW
« Reply #3 on: May 15, 2010, 03:15:10 AM »


Quote
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).

    Just in case you're not sure, The point of the asking PB what the current surface is, then restoring it at the end of the function.   Is cleanliness.  If your writing a function that alters the current surface, ink colour, current font  or whatever.   Then it's best to preseve these before you change them, so the exit state 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.     
 

Code:
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.   


Code:
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. 

Logged

kevin
Development Team
Administrator
Hero Member
*****
Offline Offline

Posts: 9341



WWW
« Reply #4 on: June 30, 2010, 11:38:53 AM »


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

      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)




* Ray_Casting_To_Map_V002.zip (15.37 KB - downloaded 7 times.)

* PBV164M_PLatformer_Using_Ray_To_Map_V002.png (11.7 KB, 808x634 - viewed 11 times.)
Logged

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.10 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!