Marble / Ball rolling on mesh physics
Blitz3D Forums/Blitz3D Programming/Marble / Ball rolling on mesh physics
| ||
It is simple. I want a sphere to site on a mesh landscape and roll down the hills and up the other side etc realistically. I am pulling my hair out, are there any examples anywhere? |
| ||
This may or may not work: 1) set full sliding collision. 2) implement inertia. Each frame you check the position against the position you recorded the previous frame. The difference is then used to shift the ball with TranslateEntity 3) implement gravity by pulling the ball down a set amount each frame. The inertia system will make it accelerate downwards. |
| ||
So, how would the intertia system work on step 2? for example would I get entityx(),entityy(), and entityz() of the last position and subtract it from current values and add a fraction of this to the values for x,y, and z for tranlate entity? Sorry, do you have an example of how you would implement interia. In point 3, this would be applied to the 'y' axis, right? i.e. translateentity ball,0,-gravity,0? Could I implement step 2 and 3 in one translate entity? Any help would be most appreciated |
| ||
Use my simple Ball rolling code in code archives to allow you to get an idea how to roll it without affecting it. From here, you'll be needing it's collision normals. The Driver demo will help here. Check it out on the CD. Once you've obtained the normals under your sphere (these will likely be averaged), you can use this "push" data to create yourself some fake gravity. Let me explain... The steeper the angle of the normal, the greater the velocity of the ball if the ball is going down. If the ball is going up, the greater the angle, the greater the damping. failing that, look up the other Rob. He's done that sphereracers game that has just these properties... |
| ||
Sigh. :-) Try searching the forums people! This is like the third time this week I've posted this. :-) ; Check to see if the entity collided with the level last frame. Entity_Hit = EntityCollided(ballpos, COLLIDE_Level) ; Calculate motion friction: ; Calculate the entity's current velocity. Velocity# = Sqr(Vx#^2 + Vy#^2 + Vz#^2) ; If the entity is not traveling fast enough to be motion blurred: If Velocity# < 6.0 ; If motion blur is on, turn it off: If Motion_Blur = True SPS_DELETE_CHILD_EMITTERS(ballpos) Motion_Blur = False EndIf EndIf ; If the entity is moving, adjust it's velocity: If Velocity# > 0 ; Calculate the direction vector. ; The direction vector has a length of 1. Direction_X# = Vx# / Velocity# Direction_Y# = Vy# / Velocity# Direction_Z# = Vz# / Velocity# ; Compute air friction. ; Air friction is dependent on the speed of the entity, and will prevent it from accelerting forever. Air_Friction_Force# = AIR_FRICTION_CONSTANT# * Velocity#^2.0 Velocity# = Velocity# - (Air_Friction_Force# * Time_Delta_Sec#) ; If the entity collided with the level, apply ground friction. If Entity_Hit > 0 ; Compute ground friction. Ground friction is not dependent on the speed of the entity. Velocity# = Velocity# - (GROUND_FRICTION_CONSTANT# * Time_Delta_Sec#) EndIf ; Make sure the entity's velocity doesn't go below 0. ; It is impossible to have a negative velocity in physics and "bad things" happen if you try to. If (Velocity# < 0) Then Velocity# = 0 ; Convert the entity's velocity and direction back into a motion vector. Vx# = Direction_X# * Velocity# Vy# = Direction_Y# * Velocity# Vz# = Direction_Z# * Velocity# ; If the entity collided with the level, make it bounce. If Entity_Hit > 0 ; Calculate bounce: ; Get the normal of the surface which the entity collided with. Nx# = CollisionNX(ballpos, 1) Ny# = CollisionNY(ballpos, 1) Nz# = CollisionNZ(ballpos, 1) ; Compute the dot product of the entity's motion vector and the normal of the surface collided with. VdotN# = Vx#*Nx# + Vy#*Ny# + Vz#*Nz# ; Calculate the normal force. NFx# = -2.0 * Nx# * VdotN# NFy# = -2.0 * Ny# * VdotN# NFz# = -2.0 * Nz# * VdotN# ; Add the normal force to the direction vector. Vx# = Vx# + NFx# Vy# = Vy# + NFy# Vz# = Vz# + NFz# ; Do not allow the entity to move vertically. If Vy# > 0 Then Vy# = 0 EndIf EndIf ; Apply directional thrust: ; If the entity collided with the level, apply directional thrust. ;If Entity_Hit > 0 ; Take thrust in object space, and translates it to an XYZ vector in world space. ;TFormVector 0, 0, Thrust#, ballpos, 0 ; Add any thrust being applied this frame. ; There's a very good reason why this is done AFTER the friction is calculated. ; It involves inequalities in force cause by variable framerates. Vx# = Vx# + (Thrust_X# * Time_Delta_Sec#) Vz# = Vz# + (Thrust_Z# * Time_Delta_Sec#) ;EndIf ; Apply gravity: Vy# = Vy# - (GRAVITY# * Time_Delta_Sec#) ; Move and rotate the entity: ; We rotate the entity by the actual distance moved and not by the velocity because if we rotate according ; to the velocity then the entity will roll when it's up against a wall and not moving. OldX# = NewX# OldZ# = NewZ# TranslateEntity ballpos, Vx#*Time_Delta_Sec#, Vy#*Time_Delta_Sec#, Vz#*Time_Delta_Sec#, True NewX# = EntityX#(ballpos, True) NewZ# = EntityZ#(ballpos, True) Mx# = (NewX# - OldX#) Mz# = (NewZ# - OldZ#) ; Rotate the entity the right amount for it's radius and the distance it has moved along the X and Z axis. ; This is kinda a hack and only designed for rolling on planes but you won't notice the diffrence. XAngleAdjust# = (Mx# / BallRadius#) * (180.0/Pi) ZAngleAdjust# = (Mz# / BallRadius#) * (180.0/Pi) TurnEntity ball,ZAngleAdjust#,0,-XAngleAdjust#,True Return |
| ||
Btw, there's a line in there that says: ; Do not allow the entity to move vertically. If Vy# > 0 Then Vy# = 0 You want to comment that out if you want the ball to be able to roll up hills. Comenting it out will allow the ball to bounce when it hits a surface though. To counteract that what you might want to do is set the Y componenet of the normal force listed just above that line to 0. The normal force is the force with which the ground pushes back when you push against it. In real situations you'd probably multiply it by some dampening factor... say 0.5, before adding it to the object's velocity. |
| ||
You'll need this too: Const GRAVITY# = 9.8 Const AIR_FRICTION_CONSTANT# = 0.1 Const GROUND_FRICTION_CONSTANT# = 3.0 Oh, and btw, the whole system assumes that one unit = one meter. So if your world is in a diffrent scale than that you're gonna have to multiply the gravity by however many units are in a meter in your game, and when adding velocity to your entity you'll have to specify that in meters per second, but then multiply that by your world scale too. And you'll have to multuiply the ground friction constant too. And maybe the air friction constant though I'm not sure about that. Ground friction is the force which slows an object down quickly... air friction is a force which is expoential and keeps the object from acelerating forever. You can remove those, but you'll have to then check your entity's velocity to see if it's over a certain value. |
| ||
Give that man a candy cigar! :) |
| ||
HERE IS A DEMO. [CODE] ; thanks to SSWIFT for the nuts and bolts of this code AppTitle "Ball Movement Demo ","goodbye" ; check to see if a graphics mode exist then ; if so set it, if not display error If GfxMode3DExists (1024,768,16) Graphics3D 1024,768 HidePointer Else RuntimeError "UPGRADE YOUR VIDEO CARD!" EndIf ;-------------------------------------------------------------------------------------\ ; GLOBALS ;------------------------------------------------------------------------------------- ; world vars Global cam,lite,ball,world,ball_type,world_type ; Physics vars (thanks goes to sswift) Const GRAVITY# = .0098 Const AIR_FRICTION_CONSTANT# = 0.01 Const GROUND_FRICTION_CONSTANT# = .0030 Global bullet_life=600 Global VX#,VY#,VZ#,NX#,NY#,NZ#,oldx#,newx#,oldz#,newz#,MX#,mz# Global Velocity#,VdotN#,nfx#,nfy#,nfz#,Thrust_X#,Thrust_z# Global Direction_X#,Direction_y#,Direction_z# Global xAngleAdjust# ,ZAngleAdjust#,BallRadius# Global Entity_Hit Global jumping,player,vector_piv,balltex,BOX Global midw=GraphicsWidth()/2,midh=GraphicsHeight()/2 Global speed#,lateral_speed#,cam_mx#,cam_my#,pyvel# Global shot_timer Type ball Field entity,life,brush Field vx#,vy#,vz# Field oldx#,oldz#,newx#,newz# End Type SeedRnd MilliSecs initialize_world() For x= 1 To 32 b.ball= New ball b\entity =CopyEntity(ball) EntityType b\entity,ball_type EntityRadius b\entity,.5 b\brush=CreateBrush() BrushTexture b\brush,balltex BrushColor b\brush,Rnd(0,255),Rnd(0,255),Rnd(0,255) PaintEntity b\entity,b\brush b\life=2 EntityAlpha b\entity,0 Next ; ------------------------------------------------------------------------------- Repeat ; * * * * beginning of loop user_input() update_ball() fps_camera() UpdateWorld RenderWorld ; render the 3d scene draw_crosshairs() Flip ; flip the buffer Until KeyDown(1)=1 ; * * * * end of loop RuntimeError "goodbye" ClearWorld End ;===================================================================================== ; FUNCTIONS ;===================================================================================== Function create_checker_tex(red1,green1,blue1,red2,green2,blue2,scale_u#,scale_v#) texture_handle=CreateTexture(32,32) SetBuffer TextureBuffer(texture_handle) Color red1,green1,blue1 Rect 0,0,32,32 Color red2,green2,blue2 Rect 0,0,16,16,1 Rect 16,16,15,15,1 ScaleTexture texture_handle,scale_u#,scale_v# SetBuffer BackBuffer() Return texture_handle End Function ;===================================================================================== Function update_ball() shot_timer=shot_timer-1 If shot_timer<1 Then shot_timer=0 For b.ball=Each ball b\life=b\life-1 If b\life=1 HideEntity b\entity PositionEntity b\entity,EntityX(cam),EntityY(cam),EntityZ(cam) b\vx=0 b\vy=0 b\vz=0 b\life=0 EndIf Entity_Hit = EntityCollided(b\entity, world_type) For x=1 To CountCollisions(B\ENTITY) If CollisionEntity(b\entity,x)=box EntityColor box,Rnd(0,100),Rnd(0,100),Rnd(0,100) EndIf Next Velocity# = Sqr(b\Vx#^2 + b\Vy#^2 + b\Vz#^2) If Velocity# > 0 ; Calculate the direction vector. The direction vector has a length of 1. If b\life>1 ; only if there is life left Direction_X# = b\Vx# / Velocity# Direction_Y# = b\Vy# / Velocity# Direction_Z# = b\Vz# / Velocity# ; Compute air friction. ; Air friction is dependent on the speed of the entity, and will prevent it from accelerting forever. Air_Friction_Force# = AIR_FRICTION_CONSTANT# * Velocity#^2.0 Velocity# = Velocity# - (Air_Friction_Force# ) ; If the entity collided with the level, apply ground friction. If Entity_Hit > 0 ; Compute ground friction. Ground friction is not dependent on the speed of the entity. Velocity# = Velocity# - (GROUND_FRICTION_CONSTANT#) EndIf ; Make sure the entity's velocity doesn't go below 0. ; It is impossible to have a negative velocity in physics and "bad things" happen if you try to. If (Velocity# < 0) Then Velocity# = Velocity#+.001 ; Convert the entity's velocity and direction back into a motion vector. b\Vx# = Direction_X# * Velocity# b\Vy# = Direction_Y# * Velocity# b\Vz# = Direction_Z# * Velocity# ; If the entity collided with the level, make it bounce. If Entity_Hit > 0 ; Calculate bounce: ; Get the normal of the surface which the entity collided with. Nx# = CollisionNX(b\entity, 1) Ny# = CollisionNY(b\entity, 1) Nz# = CollisionNZ(b\entity, 1) ; Compute the dot product of the entity's motion vector and the normal of the surface collided with. VdotN# = b\Vx#*Nx# + b\Vy#*Ny# + b\Vz#*Nz# ; Calculate the normal force. NFx# = -2.0 * Nx# * VdotN# NFy# = -2.0 * Ny# * VdotN# NFz# = -2.0 * Nz# * VdotN# ; Add the normal force to the direction vector. b\Vx# = b\Vx# + NFx# b\Vy# = b\Vy# + NFy# b\Vz# = b\Vz# + NFz# ; Do not allow the entity to move vertically. ;If Vy# > 0 Then Vy# = 0 EndIf EndIf EndIf ; Apply directional thrust: ; If the entity collided with the level, apply directional thrust. ;If Entity_Hit > 0 ; Take thrust in object space, and translates it to an XYZ vector in world space. ;TFormVector 0, 0, Thrust#, ballpos, 0 ; Add any thrust being applied this frame. ; There's a very good reason why this is done AFTER the friction is calculated. ; It involves inequalities in force cause by variable framerates. b\Vx# = b\Vx# + (Thrust_X# ) b\Vz# = b\Vz# + (Thrust_Z# ) ;EndIf ; Apply gravity: b\Vy# = b\Vy# - (GRAVITY# ) ; Move and rotate the entity: ; We rotate the entity by the actual distance moved and not by the velocity because if we rotate according ; to the velocity then the entity will roll when it's up against a wall and not moving. b\OldX# = b\NewX# b\OldZ# = b\NewZ# TranslateEntity b\entity, b\Vx#, b\Vy#, b\Vz#, True b\NewX# = EntityX#(b\entity, True) b\NewZ# = EntityZ#(b\entity, True) Mx# = (b\NewX# - b\OldX#) Mz# = (b\NewZ# - b\OldZ#) ; Rotate the entity the right amount for it's radius and the distance it has moved along the X and Z axis. ; This is kinda a hack and only designed for rolling on planes but you won't notice the diffrence. XAngleAdjust# = (Mx# / BallRadius#) * (180.0/Pi) ZAngleAdjust# = (Mz# / BallRadius#) * (180.0/Pi) TurnEntity b\entity,ZAngleAdjust#,0,-XAngleAdjust#,True Next End Function ;===================================================================================== Function user_input() If KeyDown(17)=1 Or KeyDown(200) Then vz#=vz#+ .01 If KeyDown(30)=1 Or KeyDown(203) Then vx# = vx# - .01 If KeyDown(31)=1 Or KeyDown(208) Then vz# = vz# -.01 If KeyDown(32)=1 Or KeyDown(205) Then vx# = vx# + .01 If KeyDown(57)=1 Then VY#=VY#+.1 End Function ;===================================================================================== Function initialize_world() cam=CreateCamera() ; create a world camera player=CreatePivot() EntityRadius player,2 MoveEntity player,0,10,-10 vector_piv=CreatePivot() ;MoveEntity cam,0,10,-25 ; move the camera "back" 5 units ;MoveEntity lite,-10,10,-10 ball=CreateSphere(12) ; create a cube and call it blob HideEntity ball PositionEntity ball,-1,10,0 ; place the blob at world coordinate 0,0,3 AmbientLight(50,50,50) lite=CreateLight(3) ; create a light for our world PositionEntity lite,-20,20,-20 PointEntity lite,ball balltex=create_checker_tex(0,150,0,0,0,100,.25,.25) ;EntityColor ball,255,0,0 ; color our blob, red, green , blue world=CreateCube() ;RotateMesh world,0,0,0 ws=CreateSphere(12) EntityTexture ws,create_checker_tex(0,50,0,100,75,0,.125,.125) BOX=CreateCube() EntityAlpha BOX,.8 RotateMesh BOX,0,45,0 PositionEntity box,10,5,10 ScaleEntity box,3,6,3 ScaleEntity ws,7,3,7 ;MoveEntity ws,0,-5,0 ScaleEntity world,25,15,25 MoveEntity world,0,15,0 ;AddMesh ws,world EntityTexture world,create_checker_tex(200,200,0,0,0,0,.5,.5) FlipMesh world ; SETUP COLLISIONS ball_type=1 ; collision types world_type=2 BallRadius#=.5 EntityType world,world_type EntityType ws,world_type EntityType box,world_type EntityColor box,255,0,0 EntityType player,ball_type Collisions ball_type,world_type,2,2 ;sphere-to-poly collisions between the ball and the world and set response to "STOP" End Function ;===================================================================================== Function fps_camera() ;If KeyDown(57) And jumping=0 ; SPACE to JUMP ; jumping=1 ; pyvel#=.2 ;EndIf If KeyDown(17)=1 Or KeyDown(200) Then speed#=speed#+.005 +boost# If KeyDown(30)=1 Or KeyDown(203) Then lateral_speed# = lateral_speed# - .004 +boost# If KeyDown(31)=1 Or KeyDown(208) Then speed# = speed# -(.005 +boost#) If KeyDown(32)=1 Or KeyDown(205) Then lateral_speed# = lateral_speed# + .004 +boost# ; FRICTION FOR SMOOTH MOVEMENT lateral_speed#=lateral_speed#*.97 speed#=speed#*.95 PositionEntity cam,EntityX(player),EntityY(player)+1,EntityZ(player) ; CAMERA MOVEMENTS cam_MY#=curvevalue#(MouseYSpeed(),cam_MY#,4 ) cam_MX#=curvevalue#(MouseXSpeed(),cam_MX#,4 ) TurnEntity cam,cam_MY#,0,0 ; turn camera up and down TurnEntity player,0,-cam_mx,0 ; turn pivot left --right RotateEntity cam,EntityPitch(cam),EntityYaw(player),0 MoveMouse midw,midh; Bring mouse to middle of screen for mouselook to work If on_platform=0 Then pyvel#=pyvel#-gravity# ;pyvel#=pyvel#*.99 ;move the player --- the camera in this case! MoveEntity player,lateral_speed#,pyvel#,speed# TranslateEntity player,pxvel#,0,pzvel# pxvel#=pxvel#*.9 pzvel#=pzvel#*.9 If MouseDown(1)= 1 Then fire_cannon() FlushKeys FlushMouse End Function ;===================================================================================== Function curvevalue#(newvalue#,oldvalue#,increments# ) If increments>1 Then oldvalue#=oldvalue#-(oldvalue#-newvalue#)/increments If increments<=1 Then oldvalue=newvalue Return oldvalue# End Function ;===================================================================================== Function fire_cannon() If shot_timer=0 For b.ball = Each ball If b\life<1 ShowEntity b\entity EntityAlpha b\entity,1 PositionEntity b\entity,EntityX(cam),EntityY(cam)+.4,EntityZ(cam) PositionEntity vector_piv,EntityX(cam),EntityY(cam)+.4,EntityZ(cam) RotateEntity vector_piv,EntityPitch(cam),EntityYaw(cam),EntityRoll(cam) MoveEntity vector_piv,0,0,5 vectx#=EntityX(vector_piv)-EntityX(cam) vecty#=EntityY(vector_piv)-EntityY(cam) vectz#=EntityZ(vector_piv)-EntityZ(cam) TFormVector vectx#,vecty#,vectz#,vector_piv,cam b\vx=TFormedX() /8 b\vz=TFormedZ() /8 b\vy=TFormedY() /4 b\life=bullet_life shot_timer=20 Exit EndIf Next EndIf End Function ;===================================================================================== Function update_cannon() shot_timer=shot_timer-1 If shot_timer<1 Then shot_timer=0 For b.ball = Each ball Next End Function ;===================================================================================== Function draw_crosshairs() Color 0,235,0 Rect midw-6,midh-6,12,12,0 Line midw-6,midh,midw+6,midh End Function ;===================================================================================== ;===================================================================================== ;===================================================================================== ;===================================================================================== ;===================================================================================== ;===================================================================================== ;===================================================================================== ;===================================================================================== ;===================================================================================== [/CODE] |
| ||
Swift, Bill Your both my heroes, thank you so SO much |
| ||
sswift, or whoever else can help, in bradfords example the balls fire off in whatever direction the camera is facing. I need that. I've gone thru the demo thad bradford put up but I'm still not getting it. Can anyone sort it out in plain english?? Basically I need to my golf ball in my project to go the direction the camera is facing. Thanks in advance. |
| ||
TFormVector 0, 0, Magnitude#, Camera, 0 Transforms a velcoity of Magnitude# from camera space pointing along it's z axis into world space giving you a vector pointing in the direction the camera is pointing. Magnitude# is the force you want to apply. So if you want to impart an instant additional velocity of 10 meters per second, pass 10 as the magnitude. |
| ||
Works like a charm! Thanks! |
| ||
I'm really hoping that sswift or bradford6 look in here and can help me! I'm using a slightly modified version of the code posted above by bradford6 in my game, to fire cannonballs out of cannons. However, when I rotate the cannon to fire in any direction OTHER than straight down the Z axis I get an inconsistent (lower) velocity, meaning shots go short. Here is a snip of my code, can anyone tell me what I am doing wrong? PositionEntity vector_piv,EntityX(ball[pnum],1),EntityY(ball[pnum],1),EntityZ(ball[pnum],1),1 RotateEntity vector_piv,EntityPitch(ball[pnum],1)-(cannon_loft[pnum]/4),EntityYaw(ball[pnum],1),EntityRoll(ball[pnum],1),1 hitpower#=cannon_powr[pnum]*shot_power[pnum] MoveEntity vector_piv,0,0,hitpower# vectx#=EntityX(vector_piv)-EntityX(ball[pnum]) vecty#=EntityY(vector_piv)-EntityY(ball[pnum]) vectz#=EntityZ(vector_piv)-EntityZ(ball[pnum]) TFormVector vectx#,vecty#,vectz#,vector_piv,ball[pnum] ball_vx[pnum]=TFormedX() /8 ball_vz[pnum]=TFormedZ() /8 ball_vy[pnum]=TFormedY() /4 Please? Cheers, Ryan |
| ||
Dividing Y by a different amount? |
| ||
sswift, Thanks for responding! I've changed that (it was as per the code that Bill posted above) and that has sorted out the initial velocity issue, but I am still getting different distances dependent on which direction the ball is fired in, but I guess that must now be down to something in the update_ball() part of the function. I will have a further poke around and see what I can find... Cheers, Ryan |
| ||
Does everything (position, rotation, x,y,z) have to be in global coordinate system? That could definatly be a problem... hope that helps scooter43 |
| ||
scooter43, Thanks for the suggestion... TBH... I don't know. It wasn't working when I first adapted the code and I think that is because I used ScaleEntity on my balls. Putting in the global flags fixed that problem and got it working. Anyway, I've solved it now with a slightly different approach, when I've got time I'll post it here for others to use! Cheers, Ryan |
| ||
Ok, another question for sswift :) Say i want to give the ball a certain "power" when it's 'fired' ... ie: take a cannon ball and if you use so and so much gun powder, it would fly this far, use less and it will fly less far.. I can't seem to find this in your code. Tracer |
| ||
Hi ! ( Function fire_cannon().... ....MoveEntity vector_piv,0,0,5< Change this Value ) Bolo |
| ||
Kewl, never even saw that... thanks. Tracer |
| ||
Argh! Turns out I am not as smart as I thought I was and my solution created more problems than it fixed. So I am still having problems, if anyone else has any suggestions!? Cheers, Ryan |
| ||
I finally found the problem! It was this line: RotateEntity vector_piv,EntityPitch(ball[pnum],1)-(cannon_loft[pnum]/4),EntityYaw(ball[pnum],1),EntityRoll(ball[pnum],1),1 Which needed to be changed to this: RotateEntity ball[pnum],EntityPitch(ball[pnum],1)-(cannon_loft[pnum]/4),EntityYaw(ball[pnum],1),EntityRoll(ball[pnum],1),1 RotateEntity vector_piv,EntityPitch(ball[pnum],1)-(cannon_loft[pnum]/4),EntityYaw(ball[pnum],1),EntityRoll(ball[pnum],1),1 Cheers, Ryan |