Game Development Community

Conform Player to Terrain

by Joe Maruschak · 09/02/2002 (12:07 pm) · 67 comments

Here is a little bit of code that we added to the engine to make the player conform to the terrain. It was originally written to work with the tank model seen at the bottom of this gallery image: www.garagegames.com/mg/snapshot/view.php?qid=302

Have fun, and let us know if you find any interesting ways to use it.

To get your player to confrom to the terrain, in player.cc:

At the bottom of PlayerData::PlayerData() add:
conformToGround = false;
   smoothCamera = 0.0f;
At the bottom of PlayerData::initPersistFields() add:
addField("conformToGround", TypeBool, Offset(conformToGround, PlayerData));
   addField("smoothCamera", TypeF32, Offset(smoothCamera,PlayerData));
At the bottom of PlayerData::packData(BitStream* stream) add:
stream->write(conformToGround);
   stream->write(smoothCamera);
At the bottom of PlayerData::unpackData(BitStream* stream) add:
stream->read(&conformToGround);
   stream->read(&smoothCamera);
To Player::setPosition(const Point3F& pos,const Point3F& rot) add the following code to the end of the "else" clause:
if (mDataBlock && mDataBlock->conformToGround)
      {
         RayInfo surfaceInfo;
         Point3F above = Point3F(pos.x,pos.y,pos.z + 0.5f);
         Point3F below = Point3F(pos.x,pos.y,pos.z - 100.0f);
         if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
         {
            Point3F x,y,z;
            z = surfaceInfo.normal;
            z.normalize();
            mat.getColumn(1,&y);
            mCross(y,z,&x);
            x.normalize();
            mCross(z,x,&y);
            mat.setColumn(0,x);
            mat.setColumn(1,y);
            mat.setColumn(2,z);
         }
      }
To Player::setRenderPosition(const Point3F& pos, const Point3F& rot, F32 dt) add the following code to the end of the outer else clause:
if (mDataBlock && mDataBlock->conformToGround)
      {
         RayInfo surfaceInfo;
         Point3F above = Point3F(pos.x,pos.y,pos.z + 0.5f);
         Point3F below = Point3F(pos.x,pos.y,pos.z - 100.0f);
         if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
         {
            Point3F x,y,z;
            z = surfaceInfo.normal;
            z.normalize();
            mat.getColumn(1,&y);
            mCross(y,z,&x);
            x.normalize();
            mCross(z,x,&y);
            mat.setColumn(0,x);
            mat.setColumn(1,y);
            mat.setColumn(2,z);
         }
      }
In Player::setRenderPosition(const Point3F& pos, const Point3F& rot, F32 dt) change:
Parent::setRenderTransform(mat);
to
[code]
   setRenderTransform(mat);
Add the following method to player.cc:
void Player::setRenderTransform(const MatrixF & mat)
{
   if (mDataBlock && mDataBlock->smoothCamera>0.0001f)
   {
      Point3F pos, x,y,z;
      mat.getColumn(3,&pos);

      // keep compass direction (assume not looking straight up)
      mat.getColumn(1,&y);
      y.z = 0;
      y.normalize();

      // smooth out camera
      MatrixF mat2 = getRenderTransform();
      QuatF q1(mat);
      QuatF q2(mat2);
      F32 k = mDataBlock->smoothCamera;

      q2.x = q2.x * k + (1.0f-k) * q1.x;
      q2.y = q2.y * k + (1.0f-k) * q1.y;
      q2.z = q2.z * k + (1.0f-k) * q1.z;
      q2.w = q2.w * k + (1.0f-k) * q1.w;
      q2.normalize();
      q2.setMatrix(&mat2);
      mat2.setColumn(3,pos);

      // keep up-vector, refigure y & z based on saved off y
      mat2.getColumn(2,&z);
      mCross(y,z,&x);
      x.normalize();
      mCross(z,x,&y);
      mat2.setColumn(0,x);
      mat2.setColumn(1,y);

      Parent::setRenderTransform(mat2);
   }
   else
      Parent::setRenderTransform(mat);
}
In player.h add the following lines to the bottom of the PlayerData struct:
bool conformToGround;
   F32 smoothCamera;

In player.h just after the line:
[code]
   void setPosition(const Point3F& pos,const Point3F& viewRot);
add the following line:
void setRenderTransform(const MatrixF &);
Now you can add the following two fields to the player datablock:

conformToGround = 1; // or 0 by default
smoothCamera = 0.95; // or 0 by default -- 0 is no smoothing, 1 is so smooth the camera won't rotate...
Page «Previous 1 2 3 4 Last »
#1
09/04/2002 (9:36 am)
Could this be used for something like putting a player in a prone position perhaps?
#2
09/04/2002 (11:18 am)
Never thought of that, but it would work for that purpose if I am understanding you correctly. All this does is align the player character to the terrain tile he is standing on (instead of always being level)
#3
09/07/2002 (3:04 am)
now all we need is to modify it to make a new vehicle class, no wait I see use add the conformToGround variable in the vehicle datablock right. Joe is there any special animation that the tank uses for the treads?
#4
09/07/2002 (7:52 am)
The tank I made is implemented a player shape, not a vehicle shape.

In the shape that I made, the forward motion has wheels rotating and the treads animating via IFL using the "run" sequence, the backward animation using "back" sequence, and the gun elevation animating using the "look" blend sequence. There is also a "root" sequence in there for when you are standing still that basically has no aniamtion in it.

The the treads don't animate while rotating in place when rotating using mouselook.

The player sidestep movement speed has been set to 0.

It isn't the perfect solution, but it works well enough to demonstrate that you can get something close to what you want working without digging into the code too deeply.
#5
09/07/2002 (2:07 pm)
oh looks like I'll have to hack at it to make a new treadedVehicleClass :-)
#6
09/07/2002 (6:35 pm)
I tried this and for some reason my screen sits there and shakes frequently, something I did wrong or is this usual?
#7
09/07/2002 (6:53 pm)
If the player is moving very fast and the smoothing is not set high enough, the camera will shake.

This is because the player is changing his orientation every time you move to a new terrain tile. The faster you go the more violent the trasition is.

Play with the smoothing parameter to get more smoothing.

Does this fix your problem?
#8
09/08/2002 (7:40 pm)
so is it easy to add this to a vehicle? or is there alot of work needed to do so?
#9
09/08/2002 (8:47 pm)
Not understanding the question. The vehicles already conform to the terrain. This bit of code just allows player models to conform to the terrain as well.

It will work for anything that you want to be a player.
#10
09/09/2002 (12:17 am)
oh cool... sorry i haven't done much with vehicles so...
#11
09/09/2002 (4:24 pm)
@Joe: so how would this make a tread conform to the ground?
Also... do IFL's work on torque? I thought they were broken?
#12
09/09/2002 (5:33 pm)
Will this make corpses conform to the terrain so when they die they don't lay there straight out on the terrain?
#13
09/09/2002 (11:54 pm)
This code snippet will not make the treads conform to the ground.

We are working on that for our own game but the additions will not be all that compatible with what everyone else is working with.

IFL's work fine in Torque (at least for me they do)

This does indeed make the corpses conform to the terrain..
#14
09/11/2002 (3:16 pm)
Thank you so much got tired of that when people died.
#15
04/05/2003 (1:37 pm)
One minor point, when you have a steep terrain,
and you make something stick out/flow above that terrain which is not sloped, the player will conform to the terrain instead of the object it is standing on :S is there a way to fix that?
#16
04/06/2003 (11:01 am)
Just a little addition from me,
if you use Static Shapes to walk on,
change (2 times!)
if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
to
if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType | StaticObjectType,&surfaceInfo))

If you only want to conform to terrain if the player touches the terrain (eg when its not in the air), change (2 times!)
if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
to
VectorF contactNormal;
bool jumpSurface = false, runSurface = false;
if (!isMounted())
findContact(&runSurface,&jumpSurface,&contactNormal);
if (runSurface && gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType ,&surfaceInfo))

WATCH OUT!:
This addition (only conform when touch) will give really shaky camera when moving down a hill :)

You can also use both additions together :)
#17
04/06/2003 (12:10 pm)
Oops error :P
when you apply the code above for the static shapes,
you will just walk straight , even if its a sloped thingie,
but at least you won't walk according the terrain :P
#18
04/14/2003 (5:30 am)
If you want to properly conform to statics, you should add

MatrixF smat = surfaceInfo.object->getTransform();
z = surfaceInfo.normal;
z.normalize();
smat.setPosition(Point3F(0,0,0));
smat.mulP(z);

where in the code snippit it just said:

z.normalize();

I personally would like to see camera smoothing actually work on the camera and not player+camera.
#19
04/14/2003 (9:55 am)
Yes :D then it will conform ok to statics, but not to interiors :S
going to test some things now, will post back ASAP here
#20
04/14/2003 (9:59 am)
Found some interresting variable inside player.cc :) might be useful to use that in our castRay thingie
static const U32 sPlayerConformMask =  InteriorObjectType|StaticShapeObjectType
                                       |StaticObjectType|TerrainObjectType;
Page «Previous 1 2 3 4 Last »