About Monkey 2 › Forums › Monkey 2 Programming Help › QCollision test
Tagged: collisions, mojo3d
This topic contains 2 replies, has 1 voice, and was last updated by 
 Ethernaut
 9 months, 2 weeks ago.
- 
		AuthorPosts
 - 
		
			
				
July 3, 2018 at 4:48 am #14979
I took the “QCollide” (Quake collider?) function from the Castle Mojo3d-loader demo and am trying to generalize it so that it can provide collision in an “arcade style” (top down, side scroller, first person, etc.) movement controller that can be used in any Mojo3d model.
It’s mostly working, but there’s some odd behavior and I can’t figure out where it’s coming from:
- When using the capsule or box collider, horizontal collisions and vertical collisions going down slide properly along the collision surface, but vertical collisions going up get stuck
 - When using the sphere collider, upon collision the sphere jumps away from the collision object. Bug? Doesn’t look like the collision response distance is right.
 
Hit space bar to change the shape, c to turn on/off collision for the shape (so that you can try colliding from outside the torus, etc.). Gravity, jumping and Z movement are disabled for now.
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368Namespace myapp3d#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"Using std..Using mojo..Using mojo3d..Function Main()New AppInstanceNew MyWindowApp.Run()End'---------------------------------------- Window ----------------------------------------Class MyWindow Extends WindowField speed := 0.1Field _scene:SceneField _camera:CameraField _light:LightField _current := 0Field _collide := TrueField _ball:ModelField _capsule:ModelField _box:ModelField _obstacle:ModelField _controllers:= New Stack<CharacterController>Method New( title:String="Simple mojo3d app",width:Int=800,height:Int=600,flags:WindowFlags=WindowFlags.Resizable )Super.New( title,width,height,flags )EndMethod OnCreateWindow() Override'create (current) scene_scene=New Scene_scene.ClearColor = New Color( 0.25, 0.28, 0.3 )_scene.AmbientLight = _scene.ClearColor_scene.EnvColor = _scene.ClearColor'create camera_camera=New Camera( Self )_camera.Move( 0, 0, -25 )_camera.FOV = 35'create light_light=New Light_light.CastsShadow=True_light.Rotate( 45, 45, 0 )'Green materialLocal material1:=New PbrMaterial( Color.Green, 0.1, 0.1 )'Create ball_ball = Model.CreateSphere(1.0, 24,24, material1 )Local colBall:= _ball.AddComponent<SphereCollider>()colBall.Radius = 1.0Local _controlBall := _ball.AddComponent<CharacterController>()_controllers.Push( _controlBall )'Create Capsule_capsule = Model.CreateCapsule( 0.5, 0.5, Axis.Y, 24, material1 )Local colCapsule:= _capsule.AddComponent<CapsuleCollider>()colCapsule.Radius = 0.5colCapsule.Length = 0.5colCapsule.Axis = Axis.YLocal controlCapsule := _capsule.AddComponent<CharacterController>()_controllers.Push( controlCapsule )'Create cubeLocal boxSize := New Boxf(-.5, -.5, -.5, .5, .5, .5 )_box = Model.CreateBox( boxSize, 1, 1, 1, material1 )Local colBox:= _box.AddComponent<BoxCollider>()colBox.Box = boxSizeLocal controlBox := _box.AddComponent<CharacterController>()_controllers.Push( controlBox )'create obstacleLocal material4:=New PbrMaterial( Color.Red, 0.05, 0.2 )_obstacle=Model.CreateTorus( 5, .5, 64, 24, material4 )_obstacle.Rotate( 90, 0, 0)Local obstCol:= _obstacle.AddComponent<MeshCollider>()obstCol.Mesh = _obstacle.MeshLocal obstBody:= _obstacle.AddComponent<RigidBody>()obstBody.Mass = 0EndMethod OnRender( canvas:Canvas ) OverrideRequestRender()_scene.Update()If Keyboard.KeyHit( Key.Space )_current += 1If _current > 2 Then _current = 0EndFor Local n := 0 Until _controllers.LengthIf n = _current_controllers[n].Entity.Visible = TrueElse_controllers[n].Entity.Visible = FalseEndNext_camera.Render( canvas )canvas.DrawText( "FPS="+App.FPS, Width-10, Height-10, 1.0, 1.0 )canvas.DrawText( "Hit C to toggle Collision, SpaceBar to change shape", 10, Height-10, 0.0, 1.0 )canvas.DrawText( "Collision:" + _controllers[_current].allowCollision + ", Shape:" + _current, 10, 10 )EndEnd'---------------------------------------- Components ----------------------------------------Class CharacterController Extends BehaviourField speed := 0.15Field turnRate := 2.5Field horizontal := Axis.XField vertical := Axis.YField allowGravity := FalseField allowJump := TrueField allowCollision := TrueField lockXMovement := FalseField lockYMovement := FalseField lockZMovement := TrueMethod New( entity:Entity )Super.New( entity )EndProperty OnGround:Bool()Return _ongroundEndProperty OnWall:Bool()Return _onwallEndProperty StepDown:Float()Return _stepDownSetter( stepDown:Float )_stepDown=stepDownEndProtectedField _jumping:BoolField _stepDown:Float=.5 '50 cmsField _onground:BoolField _onwall:BoolField _vel:Vec3fField _col:ConvexColliderMethod OnStart() Override_col = Cast<ConvexCollider>( Entity.Collider )Assert( _col, "~nCharacterController: Error, Entity collider required.~n")EndMethod OnUpdate( elapsed:Float ) OverrideIf Not Entity.Visible ReturnLocal src:=Entity.PositionLocal moving:=FalseIf Keyboard.KeyHit( Key.C )allowCollision = Not allowCollisionEndifIf Keyboard.KeyDown( Key.Left )Entity.MoveX( -speed )moving=TrueElse If Keyboard.KeyDown( Key.Right )Entity.MoveX( speed )moving=TrueEndifIf Keyboard.KeyDown( Key.Up )Entity.MoveY( speed )moving=TrueElse If Keyboard.KeyDown( Key.Down )Entity.MoveY( -speed )moving=TrueEndif' If allowGravity' If _onground _vel.y=-Entity.Collider.Margin' _vel+=Entity.Scene.World.Gravity/60.0/60.0' End' If Keyboard.KeyHit( Key.Space ) And allowJump' _jumping=True' _vel.y=.125' EndifIf Not allowCollision Then ReturnEntity.Move( _vel )Local dst:=Entity.PositionLocal qres:=QCollide( _col,src,dst,True )'moving Or Not _onground )dst=qres.position' If Not _jumping And Not qres.hitground And _onground' src=dst' dst.y-=_stepDown' qres=QCollide( Cast<ConvexCollider>( Entity.Collider ),src,dst,False )' dst=qres.position' Endif_onground=qres.hitground_onwall=qres.hitwallIf allowJump And allowGravityIf _onground_jumping=False_vel.y=0Else_vel.y=dst.y-src.yEndifEnd' Entity.Position=dstIf Not lockXMovement Then Entity.X = dst.XIf Not lockYMovement Then Entity.Y = dst.YIf Not lockZMovement Then Entity.Z = dst.ZEndEnd'---------------------------------------- QCollider ----------------------------------------Struct QResultField position:Vec3fField hitground:BoolField hitwall:BoolEndFunction QCollide:QResult( collider:ConvexCollider,src:Vec3f,dst:Vec3f,moving:Bool )Local margin:=collider.MarginLocal world:=collider.Entity.Scene.WorldLocal start:=srcLocal plane0:Planef,plane1:Planef,state:=0,casts:=0Local qresult:QResultLocal debug:=""RepeatIf src.Distance( dst )<.0001dst=srcExitEndifcasts+=1Local cresult:=world.ConvexSweep( collider, src, dst, 1 )If Not cresult ExitIf cresult.normal.y> 0.7qresult.hitground=TrueEndifIf cresult.normal.y< 0.1qresult.hitwall=TrueEndifLocal plane:=New Planef( cresult.point, cresult.normal )plane.d-=marginLocal d0:=plane.Distance( src ), d1:=plane.Distance( dst )Local tline:=New Linef( src, dst-src )Local t:=plane.TIntersect( tline )If t>0src=tline * tEndifIf Not moving Or t>=1dst=srcExitEndifSelect stateCase 0Local tdst:=plane.Nearest( dst )If plane.n.y<.1 And tdst.y>src.ydst=srcExitLocal t:=(tdst.y-src.y)/(tdst-src).Lengthtdst=(tdst-src)*t+srcPrint "Here!"Endifdst=tdstplane0=planestate=1Case 1Local v:=plane0.n.Cross( plane.n )If v.Length>.00001Local groove:=New Linef( src,v )dst=groove.Nearest( dst )plane1=planestate=2ElsePrint "QCollide OOPS2"dst=srcExitEndifCase 2dst=srcExitEndForeverIf casts>3 Print debug.Slice( 2 )+"QCOLLIDE OOPS3 casts="+castsqresult.position=dstReturn qresultEndThanks!
July 4, 2018 at 7:43 pm #14987Ok, I figured why the sliding behavior depended on movement axis and fixed it. Also added more collision shapes.
The sphere is the only one not behaving correctly, seems like a bug to me! It looks like the collision result is using the diameter value as if it’s the radius, or something like that. Will file a bug.
Here’s the updated code. Hit space to toggle different collision shapes.
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393Namespace myapp3d#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"Using std..Using mojo..Using mojo3d..Function Main()New AppInstanceNew MyWindowApp.Run()End'---------------------------------------- Window ----------------------------------------Class MyWindow Extends WindowField speed := 0.1Field _scene:SceneField _camera:CameraField _light:LightField _current := 0Field _collide := TrueField _ball:ModelField _capsule:ModelField _box:ModelField _cone:ModelField _cylinder:ModelField _obstacle:ModelField _controllers:= New Stack<CharacterController>Method New( title:String="Simple mojo3d app",width:Int=800,height:Int=600,flags:WindowFlags=WindowFlags.Resizable )Super.New( title,width,height,flags )EndMethod OnCreateWindow() Override'create (current) scene_scene=New Scene_scene.ClearColor = New Color( 0.25, 0.28, 0.3 )_scene.AmbientLight = _scene.ClearColor_scene.EnvColor = _scene.ClearColor'create camera_camera=New Camera( Self )_camera.Move( 0, 0, -25 )_camera.FOV = 35'create light_light=New Light_light.CastsShadow=True_light.Rotate( 45, 45, 0 )'Green materialLocal material1:=New PbrMaterial( Color.Green, 0.1, 0.1 )'Create ball_ball = Model.CreateSphere(1.0, 24,24, material1 )Local colBall:= _ball.AddComponent<SphereCollider>()colBall.Radius = 1.0Local _controlBall := _ball.AddComponent<CharacterController>()_controllers.Push( _controlBall )'Create Capsule_capsule = Model.CreateCapsule( 0.5, 0.5, Axis.Y, 24, material1 )Local colCapsule:= _capsule.AddComponent<CapsuleCollider>()colCapsule.Radius = 0.5colCapsule.Length = 0.5colCapsule.Axis = Axis.YLocal controlCapsule := _capsule.AddComponent<CharacterController>()_controllers.Push( controlCapsule )'Create cubeLocal boxSize := New Boxf(-.5, -.5, -.5, .5, .5, .5 )_box = Model.CreateBox( boxSize, 1, 1, 1, material1 )Local colBox:= _box.AddComponent<BoxCollider>()colBox.Box = boxSizeLocal controlBox := _box.AddComponent<CharacterController>()_controllers.Push( controlBox )'Create cone_cone = Model.CreateCone( 1.0, 1.0, Axis.Y, 24, material1 )Local colCone:= _cone.AddComponent<ConeCollider>()colCone.Radius = 1.0colCone.Length = 1.0colCone.Axis = Axis.YLocal controlCone := _cone.AddComponent<CharacterController>()_controllers.Push( controlCone )'Create cylinder_cylinder = Model.CreateCylinder( 0.5, 2.0, Axis.Y, 16, material1 )Local colCylinder:= _cylinder.AddComponent<CylinderCollider>()colCylinder.Radius = 0.5colCylinder.Length = 2.0colCylinder.Axis = Axis.YLocal controlCylinder := _cylinder.AddComponent<CharacterController>()_controllers.Push( controlCylinder )'create obstacleLocal material4:=New PbrMaterial( Color.Red, 0.05, 0.2 )_obstacle=Model.CreateTorus( 1, .1, 64, 24, material4 )_obstacle.Rotate( 90, 0, 0)_obstacle.Mesh.FitVertices( New Boxf(-5,-5,-5,5,5,5) )Local obstCol:= _obstacle.AddComponent<MeshCollider>()obstCol.Mesh = _obstacle.MeshLocal obstBody:= _obstacle.AddComponent<RigidBody>()obstBody.Mass = 0EndMethod OnRender( canvas:Canvas ) OverrideRequestRender()_scene.Update()If Keyboard.KeyHit( Key.Space )_current += 1If _current >= _controllers.Length Then _current = 0EndFor Local n := 0 Until _controllers.LengthIf n = _current_controllers[n].Entity.Visible = TrueElse_controllers[n].Entity.Visible = FalseEndNext_camera.Render( canvas )canvas.DrawText( "FPS="+App.FPS, Width-10, Height-10, 1.0, 1.0 )canvas.DrawText( "Hit C to toggle Collision, SpaceBar to change shape", 10, Height-10, 0.0, 1.0 )canvas.DrawText( "Collision:" + _controllers[_current].allowCollision + ", Shape:" + _current, 10, 10 )EndEnd'---------------------------------------- Components ----------------------------------------Class CharacterController Extends BehaviourField speed := 0.15Field turnRate := 2.5Field horizontal := Axis.XField vertical := Axis.YField allowGravity := FalseField allowJump := TrueField allowCollision := TrueField lockXMovement := FalseField lockYMovement := FalseField lockZMovement := TrueMethod New( entity:Entity )Super.New( entity )EndProperty OnGround:Bool()Return _ongroundEndProperty OnWall:Bool()Return _onwallEndProperty StepDown:Float()Return _stepDownSetter( stepDown:Float )_stepDown=stepDownEndProtectedField _jumping:BoolField _stepDown:Float=.5 '50 cmsField _onground:BoolField _onwall:BoolField _vel:Vec3fField _col:ConvexColliderMethod OnStart() Override_col = Cast<ConvexCollider>( Entity.Collider )Assert( _col, "~nCharacterController: Error, Entity collider required.~n")EndMethod OnUpdate( elapsed:Float ) OverrideIf Not Entity.Visible ReturnLocal src:=Entity.PositionLocal moving:=FalseIf Keyboard.KeyHit( Key.C )allowCollision = Not allowCollisionEndifIf Keyboard.KeyDown( Key.Left )Entity.MoveX( -speed )moving=TrueElse If Keyboard.KeyDown( Key.Right )Entity.MoveX( speed )moving=TrueEndifIf Keyboard.KeyDown( Key.Up )Entity.MoveY( speed )moving=TrueElse If Keyboard.KeyDown( Key.Down )Entity.MoveY( -speed )moving=TrueEndif' If allowGravity' If _onground _vel.y=-Entity.Collider.Margin' _vel+=Entity.Scene.World.Gravity/60.0/60.0' End' If Keyboard.KeyHit( Key.Space ) And allowJump' _jumping=True' _vel.y=.125' EndifIf Not allowCollision Then ReturnEntity.Move( _vel )Local dst:=Entity.PositionLocal qres:=QCollide( _col,src,dst,True )'moving Or Not _onground )dst=qres.position' If Not _jumping And Not qres.hitground And _onground' src=dst' dst.y-=_stepDown' qres=QCollide( Cast<ConvexCollider>( Entity.Collider ),src,dst,False )' dst=qres.position' Endif_onground=qres.hitground_onwall=qres.hitwallIf allowJump And allowGravityIf _onground_jumping=False_vel.y=0Else_vel.y=dst.y-src.yEndifEnd' Entity.Position=dstIf Not lockXMovement Then Entity.X = dst.XIf Not lockYMovement Then Entity.Y = dst.YIf Not lockZMovement Then Entity.Z = dst.ZEndEnd'---------------------------------------- QCollider ----------------------------------------Struct QResultField position:Vec3fField hitground:BoolField hitwall:BoolEndFunction QCollide:QResult( collider:ConvexCollider,src:Vec3f,dst:Vec3f,moving:Bool )Local margin:=collider.MarginLocal world:=collider.Entity.Scene.WorldLocal start:=srcLocal plane0:Planef,plane1:Planef,state:=0,casts:=0Local qresult:QResultLocal debug:=""RepeatIf src.Distance( dst )<.0001dst=srcExitEndifcasts+=1Local cresult:=world.ConvexSweep( collider, src, dst, 1 )If Not cresult ExitIf cresult.normal.y> 0.7qresult.hitground=TrueEndifIf cresult.normal.y< 0.1qresult.hitwall=TrueEndifLocal plane:=New Planef( cresult.point, cresult.normal )plane.d-=marginLocal d0:=plane.Distance( src ), d1:=plane.Distance( dst )Local tline:=New Linef( src, dst-src )Local t:=plane.TIntersect( tline )If t>0src=tline * tEndifIf Not moving Or t>=1dst=srcExitEndifSelect stateCase 0Local tdst:=plane.Nearest( dst )' If plane.n.y<.1 And tdst.y>src.y' dst=src' Exit' Local t:=(tdst.y-src.y)/(tdst-src).Length' tdst=(tdst-src)*t+src' Print "Here!"' Endifdst=tdstplane0=planestate=1Case 1Local v:=plane0.n.Cross( plane.n )If v.Length>.00001Local groove:=New Linef( src,v )dst=groove.Nearest( dst )plane1=planestate=2ElsePrint "QCollide OOPS2"dst=srcExitEndifCase 2dst=srcExitEndForeverIf casts>3 Print debug.Slice( 2 )+"QCOLLIDE OOPS3 casts="+castsqresult.position=dstReturn qresultEndJuly 4, 2018 at 11:41 pm #14988Problem solved, here’s Mark’s explanation in case anyone runs into the same issue.
https://github.com/blitz-research/monkey2/issues/402
I hope to post the finished Character Controller eventually to the code library section, since it’s such an essential component when you’re making action games.
Cheers.
 - 
		AuthorPosts
 
You must be logged in to reply to this topic.