New character controller

Share with us how are you using the powerrrr of the force

Moderator: Alain

New character controller

Postby Yezide » Thu Jun 19, 2008 3:51 am

Here is a little demo of my new character controller, using a specially designed "physics system". The main difference compared to my previous (which was used in the Penumbra games) is that the other bodies do not "know" about it. Previously I used a static body that the other bodies would bump into but this was not very good when I wanted more interaction, so I choose instead to have the character be a kind of force field in the world and add forces on bodies that comes near it. For it's own collision it simply do a world collision check.

The reason I did not use a dynamic body to simulate the character is that I want 100% control over behavior and when using a physically correct body there is a lot of problems one runs into. Using this approach I can control the behavior very exactly which one can see especially at slopes and stair climbing. The main problem is that when connecting other bodies to the character there are some stability issues and also the character can go through walls and floor if a heavy enough object hits it. These are just special cases though and generally it works very well.

In the video, watch out for the two way interaction with the seesaw. The character can push it down and when enough force is applied on the opposite side it will push the character up. This is something I do not know any other game that does (except for marble madness clones, car games and other games that use a physically correct object as "characters"). Mostly games uses a kinetic body that pushes everything out of the way (like my first implementation).

Now for the video:
http://www.youtube.com/watch?v=S26HphuXioU
User avatar
Yezide
 
Posts: 173
Joined: Tue Oct 18, 2005 4:31 am

Postby Dave Gravel » Thu Jun 19, 2008 5:04 am

Nice video, I very like it :shock: :wink:
Good work.
You search a nice physics solution, if you can read this message you're at the good place :wink:
OrionX3D Projects & Demos:
https://www.facebook.com/dave.gravel1
http://orionx3d.googlepages.com/
https://www.youtube.com/user/EvadLevarg/videos
User avatar
Dave Gravel
 
Posts: 690
Joined: Sat Apr 01, 2006 9:31 pm
Location: Quebec in Canada.

Postby Yezide » Thu Jun 19, 2008 11:53 am

Glad ye like :)

If you wanna know some more specifics I will be happy to give more details.
User avatar
Yezide
 
Posts: 173
Joined: Tue Oct 18, 2005 4:31 am

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 3:10 am

How are you controlling where the character can walk? I am using a combination of a convex raycast and a dynamic body, but it has problems.
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 3:19 am

What I mean is, if you perform a collision test and see there is a collision at some position and normal, how does that tell you how to make the character slide?
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Yezide » Wed Jan 21, 2009 11:22 am

To see if the character should slide or not I calculate the the angle between the negative gravity direction (normally 0,1,0) and the ground normal. When over a certain distance, then I skip doing any "anti-slide" stuff. Was that the answer you where looking for? Or do you mean the stair climbing?
User avatar
Yezide
 
Posts: 173
Joined: Tue Oct 18, 2005 4:31 am

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 12:02 pm

I mean I assume you are just doing a collision test between the player cylinder and the scene. This results in some number of collisions, each with a position and normal, and maybe some other data. I take it you are moving the player yourself with a simple physics system, and using the collision data to determine where the player should be if it runs into a wall? It would be pretty simple because there is no rotation to worry about with the player.

Is this correct? Are you just performing an intersection test between the player and the scene, using NewtonCollisionCollide(), and using that data to manually move the player where they should be? If so, can you say anything about how the collision data is used to make the player slide off walls?

I just realized my player controller has some fundamental flaws that will prevent it from doing what I need, so I am looking around for alternatives. Yours is the best approach I have come across so far. If I can implement something pretty generic, maybe Julio can add it to the joint library.

Thanks.
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 1:10 pm

I am trying this. It is extremely jumpy. I just find the collisions, and then move the player by the vector times the penetration:
Code: Select all
      count=NewtonBodyCollide(TWorld.current.Newtonworld,maxsize,Current.newtonBody,newtonBody,contacts,normals,penetration)
      
      For i=0 To count-1
         MemCopy position,Varptr contacts[i*3],12
         MemCopy normal,Varptr normals[i*3],12
         MoveEntity Current,[normal.x*penetration[i]*0.5,normal.y*penetration[i]*0.5,normal.z*penetration[i]*0.5]
      Next


I understand I can't expect everything to work perfectly on the first try, but I have been tweaking and adjusting player controllers for a year and mine still has problems. I have got to find a method that is fundamentally sound.

So much fail:
http://www.leadwerks.com/post/fail.wmv
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby agi_shi » Wed Jan 21, 2009 4:59 pm

Just to throw in my own $0.02, I use an extremely basic system. A sphere (fake but free stair climbing) with a contact callback - if it encounters anything with an "up" normal (or close to it by N degrees) then it stores that body as the floor - if not, then it's either sliding or in the air (contact callback also disables friction and elasticity for all the contacts). In the force/torque callback, if the floor body is not NULL, I just apply a force to move the character. Having access to the 'floor' is pretty useful to fake friction between the player and moving objects (like trains, etc.)
agi_shi
 
Posts: 263
Joined: Fri Aug 17, 2007 6:54 pm

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 5:41 pm

The whole point of this approach is to not use a dynamic body, since the movement we are trying to produce is fundamentally unrealistic.
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Yezide » Wed Jan 21, 2009 7:27 pm

Not sure why you get that stuttering. Gonna go through fairly briefly how I do it:

1) Get the movement. I calculate this from acceleration, velocity that are floats and then use the direction of the charcter to get the final vector. Not realistic, but gives good movement.
2) Check collision for the movement and if there is a collision I:
- Push back the character by a certain amount (more on this later) given by the newton collision algo.
- Calculate the reflection vector given movement and push back amount. This makes the character slide along walls.
- Check if there is any step to climb ahead, do this by casting 1 or 3 rays (depending on accuracy)
3) Get the movement from gravity and check collision and do the same steps as in 2, except the step climbing. I also get the ground normal here and use it to check if sliding should be counter-acted on and I also realign the forward vector so the characters walks _up_ slopes instead of _into_ slopes.
4) Update body interaction, which is not 100% working at this point, and is done by treating the character as a force field + allowing all colliding bodies to push the character. There are situations where this screw up stuff and character can be pushed through walls etc. All that can be tweaked to avoid though. In Penumbra I just treated the player like static object and no objects could push it, this works very well but depends on the game.
5) Add friction and stuff like that.

Now as for the newton collide stuff, what I do there basically:
1) Get all nearby bodies and iterate them
2) For each body that is collided with:
- Fix the normal so that it points towards the body testing for collision.
- Store the push back vector (normal*depth) in a "summed" push back where each axis is the largest encountered.
- the "summed" push back is returned and use to push back character body.

That is basically it :)
User avatar
Yezide
 
Posts: 173
Joined: Tue Oct 18, 2005 4:31 am

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 8:14 pm

Thanks, I am getting there now. The summed vector trick is what I needed. Will post an update after I play with this for a while.
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 9:23 pm

Here is my source code. This works really well, for one day's work! I have implemented simple movement and a maximum slope the player can walk on without sliding. I am having problems with the player getting stuck on corners.

This should be pretty simple to understand. The Update() method is called once per frame, and for each body, the UpdateCollision() method is called:
Code: Select all
SuperStrict

   Type TController
   
   Global NewtonBodyIteratorController:TController
   
   Field velocity:TVec3=vec3()
   Field body:TBody
   Field airborne:Int
   Field direction:TVec3[2]
   Field maxslope:Float=45
   Field hardness:Float=0.98
   
   Method New()
      direction[0]=New TVec3
      direction[1]=New TVec3
      body=CreateBodyCylinder(0.5,2)
      SetEntityCallback body,UpdateCallback,ENTITYCALLBACK_UPDATE
      body.userdata=Self
   EndMethod
   
   Function UpdateCallback(entity:TEntity)
      Local player:TController
      player=TController(entity.userdata)
      player.update()
   EndFunction
   
   Method Update()
      Local movement:TVec3=vec3(0)
      Local prevposition:TVec3
      
      'Key controls
      If Not airborne
         movement.x=(KeyDown(KEY_D)-KeyDown(KEY_A))*0.1*AppSpeed()
         movement.z=(KeyDown(KEY_W)-KeyDown(KEY_S))*0.1*AppSpeed()      
         movement=TFormVector(movement,body,Null)
         velocity.x=velocity.x*0.5+movement.x*0.5
         velocity.z=velocity.z*0.5+movement.z*0.5
         If KeyHit(KEY_SPACE) velocity.y:+0.16
      EndIf
      
      'Gravity
      velocity.y:-0.01
      prevposition=body.position.copy()
      
      'Move the entity by the velocity
      TranslateEntity body,velocity

      'Collision routine
      direction[0]=vec3(0)
      direction[1]=vec3(0)
      airborne=True
      NewtonBodyIteratorController=Self
      NewtonWorldForEachBodyInAABBDo(TWorld.Current.newtonWorld,Varptr body.aabb.x0,Varptr body.aabb.x1,NewtonBodyIterator)
      
      'Get collision results
      movement.x=(direction[1].x+direction[0].x)*hardness
      movement.y=(direction[1].y+direction[0].y)*hardness
      movement.z=(direction[1].z+direction[0].z)*hardness
      
      'Move entity for collision
      TranslateEntity body,movement   
      
      'Calculate new velocity
      velocity.x=body.position.x-prevposition.x
      velocity.y=body.position.y-prevposition.y
      velocity.z=body.position.z-prevposition.z
      
   EndMethod

   Method UpdateCollision(entity:TEntity)
      Const maxsize:Int=64
      Local contacts:Float[maxsize*3]
      Local normals:Float[maxsize*3]
      Local penetration:Float[maxsize]
      Local time:Float[maxsize]
      Local count:Int
      Local i:Int
      Local position:TVec3=New tVec3
      Local normal:TVec3=New TVec3
      Local timestep:Float=1.0
      Local p:TPlane=New TPlane
      
      'Perform collision
      count=NewtonBodyCollideContinue(TWorld.current.Newtonworld,maxsize,Varptr timestep,body.newtonBody,velocity,[0.0,0.0,0.0],entity.newtonBody,[0.0,0.0,0.0],[0.0,0.0,0.0],time,contacts,normals,penetration)
      
      For i=0 To count-1
         MemCopy position,Varptr contacts[i*3],12
         MemCopy normal,Varptr normals[i*3],12
         
         'Correct the normal so it points towards the body
         TPlane.FromPointNormal(position,normal,p)
         If p.DistanceToPoint(body.position)<0.0
            normal.x:*-1.0
            normal.y:*-1.0
            normal.z:*-1.0
         EndIf
         
         'Adjust normal if slope is too steep
         If 90-ASin(normal.y)<maxslope
            normal.x=0.0
            normal.z=0.0
            airborne=False
         EndIf
         
         'Adjust normal for penetration value
         normal.x:*penetration[i]
         normal.y:*penetration[i]
         normal.z:*penetration[i]
         
         'Get largest movement on each axis
         direction[0].x=Min(direction[0].x,normal.x)
         direction[0].y=Min(direction[0].y,normal.y)
         direction[0].z=Min(direction[0].z,normal.z)
         direction[1].x=Max(direction[1].x,normal.x)
         direction[1].y=Max(direction[1].y,normal.y)
         direction[1].z=Max(direction[1].z,normal.z)
      Next
   EndMethod
   
   Function NewtonBodyCollideContinue:Int(newtonworld:Byte Ptr,maxsize:Int,timestep:Byte Ptr,newtonBody0:Byte Ptr,velocity0:Byte Ptr,omega0:Byte Ptr,newtonBody1:Byte Ptr,velocity1:Byte Ptr,omega1:Byte Ptr,time:Byte Ptr,contacts:Byte Ptr,normals:Byte Ptr,penetration:Byte Ptr)
      Local collision:Byte Ptr[2]
      Local mat0:Float[16]
      Local mat1:Float[16]
      collision[0]=NewtonBodyGetCollision(newtonBody0)
      collision[1]=NewtonBodyGetCollision(newtonBody1)
      NewtonBodyGetMatrix(newtonBody0,mat0)
      NewtonBodyGetMatrix(newtonBody1,mat1)
      Return NewtonCollisionCollideContinue(newtonworld,maxsize,Varptr timestep,collision[0],Varptr mat0[0],velocity0,omega0,collision[1],Varptr mat1[0],velocity1,omega1,time,contacts,normals,penetration)
   EndFunction
   
   Function NewtonBodyCollide:Int(newtonworld:Byte Ptr,maxsize:Int,newtonBody0:Byte Ptr,newtonBody1:Byte Ptr,contacts:Byte Ptr,normals:Byte Ptr,penetration:Byte Ptr)
      Local collision:Byte Ptr[2]
      Local mat0:Float[16]
      Local mat1:Float[16]
      collision[0]=NewtonBodyGetCollision(newtonBody0)
      collision[1]=NewtonBodyGetCollision(newtonBody1)
      NewtonBodyGetMatrix(newtonBody0,mat0)
      NewtonBodyGetMatrix(newtonBody1,mat1)
      Return NewtonCollisionCollide(newtonworld,maxsize,collision[0],mat0,collision[1],mat1,contacts,normals,penetration)
   EndFunction
   
   Function NewtonBodyIterator(newtonBody:Byte Ptr)
      Local entity:TEntity
      entity=TEntity(NewtonBodyGetUserData(newtonBody))
      If Not entity Return
      If entity=NewtonBodyIteratorController.body Return
      NewtonBodyIteratorController.UpdateCollision(entity)
   EndFunction
         
EndType


When I commented out this code, the edge hanging seems to go away. In fact I can skip those collision completely.
Code: Select all
         TPlane.FromPointNormal(position,normal,p)
         If p.DistanceToPoint(body.position)<0.0
            normal.x:*-1.0
            normal.y:*-1.0
            normal.z:*-1.0
         EndIf
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Leadwerks » Wed Jan 21, 2009 11:26 pm

I am getting pretty good results, and the code is still very simple and generic. I think this can be made to work as a general purpose controller for all Newton programs:
http://www.leadwerks.com/post/controller2.wmv

Can you tell me more about the reflection vector? You can see in my code where I calculate the direction of the collision. I am only multiplying the collision normal by the penetration value. It would feel better if the player slid along walls more easily.
User avatar
Leadwerks
 
Posts: 515
Joined: Fri Oct 27, 2006 2:54 pm

Re: New character controller

Postby Yezide » Thu Jan 22, 2009 4:58 am

Reflection without bounce:

Vel = Vel - Normal * Dot(Normal, Vel);

Where Normal is the normalized pushback vec.
User avatar
Yezide
 
Posts: 173
Joined: Tue Oct 18, 2005 4:31 am

Next

Return to User Gallery

Who is online

Users browsing this forum: No registered users and 3 guests

cron