Versatile Sliding Collision by G Man18th Feb 2005 15:54
|
---|
Summary This is a versatile sliding collision routine that can be used inside .X level maps, on advanced terrain or .X terrain models, or even combinations there-of... Description Here is a sliding collision routine developed by myself and Lost In Thought. The code carries with it copious amounts of commenting that should allow anyone to quickly read into the code and understand what we are doing. As this is a starter piece of code, media is not required, but I've none-the-less attached the media we tested it with. Code ` This code was downloaded from The Game Creators ` It is reproduced here with full permission ` http://www.thegamecreators.com `Main Source File remstart ========================================================================================= * A basic sliding collision algorythm. A joint product of the efforts of G-Man and * * Lost In Thought. Feel free to use this code as you wish, but credits would cerainly * * be appreciated. * * * * Before you ask, no, there isn't any jump code... We have to let something for you to * * figure out! :-) * * * * Revision History: * * 1.) Initially coded 12-26-04 GM/LIT * * * * Known Bugs/Issues: * * 1.) No Jumping * * 2.) Ocasional glitches when falling * * 3.) Camera falls up when the camera's Y position < 0 * ========================================================================================= remend `First set up some of the screen particulars Sync On : Sync Rate 0 Autocam Off : Hide Mouse `Set up the Camera's view frustrum Set Camera Range .1, 5000 `this global variable holds the player's movement rate `in quants per second. This equates roughly to inches `per second Global MovePerSec As Float `set the movement rate to 20 feet per second MovePerSec = 240 `This next variable determines If a reticle will be drawn on the screen Global UseReticle As Boolean `we'll set it to the default value, switching the reticle on. A value of `0 would switch it off UseReticle = 1 `This variable holds the height from the floor to the player's eye `this is again in quants, with 1 quant equating roughly to an inch Global EyeHeight As Integer `set the eye height to 64 inches EyeHeight = 64 `This next variable set the mouse's sensitivity for the mouse look Global MouseSens As integer `we'll set the default mouse sensitivity value to 3. Valid values range from 1 `(very sensitive)upwards growing less sensitive with each increase MouseSens = 3 `This variable is used to determine if the character is jumping or falling Global Airborn as Boolean `This variable contains the speed at which the player will fall in inches per second Global Gravity as Float Gravity = 512 `now we load the map. Swap the name of your map file in here LoadMap: Load Object "colltest.x", 1 `we set collision to polygons here. Normally collsion is set to boxes or spheres `but because we are gouing to be walking around inside the map, we'd be colliding `all of the time using either of those methods because the collision horizon is set `to the model's maximum extents Set Object Collision To Polygons 1 `we will create an Object to do our colliding for the Camera. We'll use a sphere and position it around `the Camera. Because its polygons all face out, it won't be rendered by the Camera. `normally, this would be your player model `first let's create a global variable to hold the Object number Global ColliderObjNum As Integer `...and fill it ColliderObjNum = 2 `now we'll create the actual collider Object Make Object Sphere ColliderObjNum, EyeHeight/2 `set up its collision method Set Object Collision On ColliderObjNum Hide Object ColliderObjNum `now we'll place the Camera Position Camera 0, 0, 0, 0 `then place the collider Object around the Camera Position Object ColliderObjNum, 0, 0, 0 `at this point we'll set the ambient light level so we can see where we're going `this will probably need to be tweaked to your liking Set Ambient Light 100 Fog Color RGB(128,128,128) Fog Distance 3000 Fog On `this is our main loop MainLoop: Do `call our sliding collision function if Airborn=0 then CheckCollision() if Airborn=1 then Fall() `render our scene Sync Loop End Function Fall() `This function handles falling. During a fall, the user can not control `the character's actions until they land. `We'll start by collecting the collider's current position ox#=Object Position x(ColliderObjNum) oy#=Object Position y(ColliderObjNum) oz#=Object Position z(ColliderObjNum) `We'll calculate how far we're going to fall based upon the frame rate Delta#=Gravity/Screen FPS() `We'll figure out the Y value we're falling to ny#=EyeHeight + (oy#-Intersect Object(1, ox#, oy#, oz#, ox#, -1000, oz#)) `We check if the player is done falling If (oy#-ny#)<=Delta# `If the difference between the old Y position and the new Y position is equal to or `less than the amount we are going to let the player fall this iteraction, then `the fall is done. `Position the camera and collider at the final position Position Object ColliderObjNum, ox#, ny#, oz# Position Camera 0, ox#, ny#, oz# `Shut off the AirBorn flag Airborn = 0 Else `Since the player is not done falling, we'll drop the player's Y value `according to the calculated amount Position Object ColliderObjNum, ox#, oy#-delta#, oz# Position Camera 0, ox#, oy#-delta#, oz# EndIf `we'll draw the reticle during the fall If UseReticle = 1 circle screen width()/2,screen height()/2,8 EndIf EndFunction Function CheckCollision() `This is the meat of the sliding collision routine. It is based on a simple premise. `any move the player makes through the map will be composed of an X and Z component. `If the player attempts to make a move that causes a collision with the map, we will `seperate the requested move into these two components and give them the portion of `the move that does not create a collsion condition. If neither portion of the move `can be allowed, the player will be simply halted. This final case could only occur `If the player's move was exactly aligned with either the X or Z axis. Moving = 0 `Begin Routine `First we need to record the current Camera position before attempting the move ox#=Object Position x(ColliderObjNum) oy#=Object Position y(ColliderObjNum) oz#=Object Position z(ColliderObjNum) `next we need to know how fast the machine is running so that `we can move our player at a consistent speed Delta#=MovePerSec/Screen FPS() `Because the collider could pass through a wall without triggering a collision `condition If the allowable move is too large, we need to ensure that the delta `pacing formula does not allow it to exceed the collider's size. If Delta#>EyeHeight Delta#=EyeHeight EndIf `accept the user's input Rotate Object ColliderObjNum, Object Angle x(ColliderObjNum)+(MouseMovey()/MouseSens),Object Angle y(ColliderObjNum)+(MouseMovex()/MouseSens),0 `this code constitutes the mouselook functionality If Object Angle x(ColliderObjNum)>90 then XRotate Object ColliderObjNum,90 If Object Angle x(ColliderObjNum)<-90 then XRotate Object ColliderObjNum,-90 cx#=Object Angle x(ColliderObjNum) cy#=Object Angle y(ColliderObjNum) cz#=Object Angle z(ColliderObjNum) Rotate Camera cx#, cy#, cz# Rotate Object ColliderObjNum, cx#, cy#, cz# If UseReticle = 1 circle screen width()/2,screen height()/2,8 EndIf k$=upper$(Inkey$()) `This code constitutes the movement functionality If UpKey()=1 or k$="W" `move the player forward XRotate Object ColliderObjNum,0 Move Object ColliderObjNum,Delta# XRotate Object ColliderObjNum,cx# EndIf If DownKey()=1 or k$="S" `move the player backwards XRotate Object ColliderObjNum,0 Move Object ColliderObjNum,0-Delta# XRotate Object ColliderObjNum,cx# EndIf If LeftKey()=1 or k$="A" `strafe left XRotate Object ColliderObjNum,0 YRotate Object ColliderObjNum,cy#-90 Move Object ColliderObjNum,Delta# YRotate Object ColliderObjNum,cy# XRotate Object ColliderObjNum,cx# EndIf If RightKey()=1 or k$="D" `strafe right XRotate Object ColliderObjNum,0 YRotate Object ColliderObjNum,cy#-90 Move Object ColliderObjNum,0-Delta# YRotate Object ColliderObjNum,cy# XRotate Object ColliderObjNum,cx# EndIf `with that done, we'll record the collider's new X/Z position nx#=Object Position x(ColliderObjNum) nz#=Object Position z(ColliderObjNum) `retrieve the floor height at the new position ny#=EyeHeight + (oy#-Intersect Object(1, nx#, oy#, nz#, nx#, -1000, nz#)) `there appears to be an inconsistancy in the way DBPro registers collsions `in certain circumstances. The following code remedies that situation `To fix the west wall problem check to see if the distance to collision `is less than half the collider's size and if it is then nx# = ox# if nx# > ox# newxwest# = intersect object(1,nx#,ny#,nz#,nx#+(EyeHeight/2),ny#,nz#) endif if newxwest# > 0 if newxwest# < object size x(2)/2 nx# = ox# endif endif `now that we're done moving the collider around, we'll check to see if there's `a collision if object collision(ColliderObjNum,0) <> 0 `The player's requested move caused a collision, so we'll try to give them `as much of their move as we can without allowing them to collide `So first off we'll see if using the old X value eliminates the collision condition position object ColliderObjNum, ox#, ny#, nz# if object collision(ColliderObjNum, 0)<>0 `Using the old X value did not eliminate the collision. Now we'll try the `old Z value. Position object ColliderObjNum, nx#, ny#, oz# If Object Collision(ColliderObjNum, 0)<>0 `Using the old Z value did not eliminate the collision, so now we'll `see if the new Y value is the one causing the problem. Position Object ColliderObjNum, nx#, oy#, nz# If Object Collision(ColliderObjNum, 0)<>0 `Ok, nothing we've tried will eliminate the collision condition, so `we'll just put the player back where they started from. ny#=oy# nx#=ox# nz#=oz# Else `The new Y value, was the problem, so we'll just set the Y value back to OY# ny#=oy# EndIf Else `The New Z value was the problem so we'll set it back to OZ# nz#=oz# EndIf else `The new X value was the culprit, so we'll set it back to OX# nx# = ox# endif EndIf if (oy#-ny#) > EyeHeight `If the Y value is lower than the current Y value, by more than the Eyeheight, `we need to start the falling process. To do that, we'll reset ny# to oy# because `the falling routine will handle changing it. Also, we'll set the AirBorn flag to 1 `to tell the program to use the falling routine. AirBorn = 1 ny#=oy# `========================================================================= `NOTE: If the player is going to be damaged by falls, here is where that `damage is best calculated. `========================================================================= `Before we leave, let's just check and make sure that we can `safely allow the player to fall at this location. CheckFall() EndIF `move the Camera and collider to the final adjudicated position Position Camera 0, nx#, ny#, nz# Position Object ColliderObjNum, nx#, ny#, nz# EndFunction Function CheckFall() `this function checks to see if the place you are going to fall to is going to `create a collision. ox#=Object Position x(ColliderObjNum) oy#=Object Position y(ColliderObjNum) oz#=Object Position z(ColliderObjNum) `to do that, we'll determine where the final resting location is after the fall is `complete ny#=EyeHeight + (oy#-Intersect Object(1, ox#, oy#, oz#, ox#, -1000, oz#)) `We'll quickly position the collider there position object ColliderObjNum, ox#, ny#, oz# `then see if there is a collision at the proposed location if object collision(ColliderObjNum,0) <> 0 `If there is a collision, we'll simply turn off the AirBorn flag Airborn=0 EndIf EndFunction |