Game Development Community

Rotate player around X or Y axis

by Eric den Boer · in Torque 3D Professional · 07/28/2009 (1:52 pm) · 23 replies

Hi all,

What I want to create is the following: have a player collide with a TSStatic, and when a certain flag on that static is set to true, have it scale the object, or walk on the wall of the object. Now, I've got the flags going and all that but now I'm running into the problem that I cannot rotate the player around its X or Y axis. Only around the Z-axis, using the mRot member.

Now, naturally I went looking for what caused this and I found out that the X and Y of mRot is never taken into account. Okay, good... so I changed things in setPosition and setRenderPosition etc. to have it take the X and Y of mRot into account.

void Player::setPosition(const Point3F& pos,const Point3F& rot)
{
   MatrixF mat;
   if (isMounted()) {
      // Use transform from mounted object
      MatrixF nmat,zrot;
      mMount.object->getMountTransform(mMount.node,&nmat);
      zrot.set(EulerF(rot.x, rot.y, rot.z)); // CHANGED THIS
      mat.mul(nmat,zrot);
   }
   else {
      mat.set(EulerF(rot.x, rot.y, rot.z)); // CHANGED THIS
      mat.setColumn(3,pos);
   }
   Parent::setTransform(mat);
   mRot = rot;

   if ( mPhysicsPlayer )
      mPhysicsPlayer->setPosition( mat );
}


void Player::setRenderPosition(const Point3F& pos, const Point3F& rot, F32 dt)
{
   MatrixF mat;
   if (isMounted()) {
      // Use transform from mounted object
      MatrixF nmat,zrot;
      mMount.object->getRenderMountTransform(mMount.node,&nmat);
      zrot.set(EulerF(rot.x, rot.y, rot.z)); // CHANGED THIS
      mat.mul(nmat,zrot);
   }
   else {
      EulerF   orient(rot.x, rot.y, rot.z); // CHANGED THIS 

      mat.set(orient);
      mat.setColumn(3, pos);

      if (inDeathAnim()) {
         F32   boxRad = (mDataBlock->boxSize.x * 0.5f);
         if (MatrixF * fallMat = mDeath.fallToGround(dt, pos, rot.z, boxRad))
            mat = * fallMat;
      }
      else
         mDeath.initFall();
   }
   Parent::setRenderTransform(mat);
}

However, when I set the rotation around the X axis to say 0.5, it won't work. How do I get the player objec to rotate around the X-axis, so I can then start working on having it run over the verticle surface of the object.

Thanks!
Page «Previous 1 2
#1
07/28/2009 (2:04 pm)
Nevermind it's not working.

This is odd.

When I do the following:
void Player::setPosition(const Point3F& pos,const Point3F& rot)
{
// CONSTANTLY ADD A NEW VALUE
	Point3F myrot = EulerF(rot.x, rot.y, rot.z);
	myrot.x += 0.01f;

   MatrixF mat;
   if (isMounted()) {
      // Use transform from mounted object
      MatrixF nmat,zrot;
      mMount.object->getMountTransform(mMount.node,&nmat);
      zrot.set(EulerF(rot.x, rot.y, rot.z));
      mat.mul(nmat,zrot);
   }
   else {
      mat.set(EulerF(myrot.x, myrot.y, myrot.z));
      mat.setColumn(3,pos);
   }
   Parent::setTransform(mat);
   mRot = myrot;

   if ( mPhysicsPlayer )
      mPhysicsPlayer->setPosition( mat );
}

I see my model suddenly rotating over it's X-axis.. however, when I set the mRot in the onCollision method, as in... "I'm making contact with a climbable object, do your rotate thing", it won't rotate. I checked with the debugger and it is passing along the updated rotation.

void Player::onCollision( SceneObject *object, const VectorF &vec )
{
	Parent::onCollision(object, vec);

	// UT
	// Check if it's a TSStatic and if we're allowed to scale
	// the object.
	if(object->getTypeMask() & StaticTSObjectType)
	{
		// cast da biatch!
		TSStatic* obj = dynamic_cast<TSStatic*>(object);

		// can we scale this sucker?
		if(obj->allowPlayerScale())
		{
			F32 curVel = mVelocity.len();
			F32 maxVel = mSqrt(mDataBlock->maxForwardSpeed);
			// is the player up to speed and jumping?
 			if(mInAir)
			{
  				mRot.x += 0.5f;
			}
		}
	}
}

What's going on?
#2
07/29/2009 (7:21 am)
There's a big resource on this. You should check it before rolling it on your own. It's far more complicated than you think because the player collision code will not work with rotated players, since it uses a simple AABB.
#3
07/29/2009 (11:56 am)
The error is that you use the parent setrendertransform.
Just create your own render transform and set it.
And the collision stuff will need a few corrections,Manoel is right.
#4
07/30/2009 (4:55 am)
Thank you so much. I did check out the resource, but I thought this can't be as complicated, I mean all you do is pass along a rotational value of the mRot.x...

So what's the real difference between setPosition, setRenderPosition and setRenderTransform? I mean, all three functions update the player's position and take a transformation matrix into account. I mean, huh?

I'm sure re-calculating the player's AABB will suffice? Or at least transpose the AABB's coordinates to fit the player's 90 degree turn to walk on a wall?

Also, any way to display the player's AABB?
#5
07/30/2009 (8:47 am)
Yes, it is complicated. Collision detection of freely rotating convex shapes *is* complicated.

AABB = Axis-Aligned Bounding Box. This means they cannot be rotated.

The Player collision code is based entirely around its movement direction, by checking if there's something to collide with in the displacement path for each tick, and adjusting the velocity so the player doesn't go inside other objects. You need to write a whole new collision detection and handling routine to deal with the rotations.

You can see the collision bounding box in the editor, when the player is selected, and I think you can force it always visible when setting $PlayerConvex::DebugRender to true.

Every object has two transforms: the real transform and the render transform. The first one is used for simulation, and is updated at every tick, the second one is used for rendering and is updated every frame (usually interpolating between the current transform and the previous one). The setPosition/setRenderPosition are helpers some class (like the player) use to simplify setting their transform matrices.
#6
07/30/2009 (11:34 am)
Okay... thennnn, what about just having an animation as the player moves upwards?
#7
07/30/2009 (12:08 pm)
The Bounding Box is axis aligned,because it uses an OrthoBoxConvex.
You should use a BoxConvex.

The render transform is the interpolated transform.
It uses an interpolation between the old state and the new delta state.
It interpolates linear between two ticks.
interpolatetick() happens each frame on client,that's why render transform works each frame,the normal transform works each tick.
When rotating the player,you will see that using the render transform will work fine,but you also need to do the same corrections on the normal transform,because you may get in trouble with some other objects (like projectile).What you do is done on client,the server needs the corrections too.
#8
07/30/2009 (12:15 pm)
@Eric: yes, animations would work. You could also have a blend animation with the X rotations in its own animation thread.

@Picasso: just changing the box type doesn't solve the problems. It works if the rotation never changes, but the current code isn't capable of handling collisions caused by rotations. I know because this is exactly what I tried in my very first TGE game, years ago.
#9
07/30/2009 (12:21 pm)
Manoel,
rotating the box convex is very simple:
just use convex.getboundingBox() with the rotated player transform and scale.
#10
07/30/2009 (1:33 pm)
What about just changing the size / coordinates / vertices of the AABB to conform to the player rotating 90 degrees? Then it'll be an AABB, just different dimensions :)

@Picasso: so if I use the setRenderTransform() function, I *should* be able to just rotate the player without too much trouble?
#11
07/30/2009 (1:43 pm)
@Picasso: there is a OOBB convex, and it can be used, yes. The box will rotate, yes it will. You could also re-calculate the AABB so it fits the rotated player. But expect your player to get stuck often when rotating, because the collision code doesn't handle rotations unless you change it.

The collision code prevents the player from moving (linear movement only, I might add) if something blocks its movement. It cannot, say, prevent a player from rotating if during the rotation the player "collides" with something. You'll get stuck until you rotate enough so the box is no longer colliding.

If it's only visual, yes, just using the setTransform (not Player, but the SceneObject one) should do it.
#12
07/30/2009 (2:16 pm)
Okay, so we now have two contradicting sollutions? On the one hand, I'm not supposed to use the SceneObject's setTransform and now I am? :/
#13
07/30/2009 (2:54 pm)
The sceneObject class setTransform does just that: it sets the transform. I don't recall if the ShapeBase setTransform does any kind of funny checks to your matrix, but the Player's undoubtedly does:

void Player::setTransform(const MatrixF& mat)
{
   // This method should never be called on the client.

   // This currently converts all rotation in the mat into
   // rotations around the z axis.
   Point3F pos,vec;
   mat.getColumn(1,&vec);
   mat.getColumn(3,&pos);
   Point3F rot(0.0f, 0.0f, -mAtan2(-vec.x,vec.y));
   setPosition(pos,rot);
   setMaskBits(MoveMask | NoWarpMask);
}


Also, Player::packUpdate() and Player::unpackUpdate() both need to be changed too, since only the mRot.z is sent to the client.
#14
07/30/2009 (3:38 pm)
That would explain why my Rot wouldn't update based on collision :)
#15
07/30/2009 (4:22 pm)
After doing some debugging, I found out that Player::setPosition() only gets called once or twice. The rest of the player's transform is being handled by Player::setPosition() and Player::setRenderPosition().

Now, I've got the following changes:

Player::onCollision()
This is supposed to trigger when a player is jumping up against a wall. It triggers properly, no problem.

void Player::onCollision( SceneObject *object, const VectorF &vec )
{
	Parent::onCollision(object, vec);

	// UT
	// Check if it's a TSStatic and if we're allowed to scale
	// the object.
	if(object->getTypeMask() & StaticTSObjectType)
	{
		// cast da biatch!
		TSStatic* obj = dynamic_cast<TSStatic*>(object);

		// can we scale this sucker?
		if(obj->allowPlayerScale())
		{
			F32 curVel = mVelocity.len();
			F32 maxVel = mSqrt(mDataBlock->maxForwardSpeed);
			
			// is the player up to speed and jumping?
 			if(mInAir)
			{

				if(mRot.x > -M_2PI_F)
 					mRot.x -= M_2PI_F;
			}
		}
	}
}

Player::setPosition()
I have replaced the X and Y values with the rot.x and rot.y respectively. No effect.

void Player::setPosition(const Point3F& pos,const Point3F& rot)
{
   MatrixF mat;
   if (isMounted()) {
      // Use transform from mounted object
      MatrixF nmat,zrot;
      mMount.object->getMountTransform(mMount.node,&nmat);
      zrot.set(EulerF(rot.x, rot.y, rot.z));
      mat.mul(nmat,zrot);
   }
   else {
      mat.set(EulerF(rot.x, rot.y, rot.z));
      mat.setColumn(3,pos);
   }
   Parent::setTransform(mat);
   mRot = rot;

   if ( mPhysicsPlayer )
      mPhysicsPlayer->setPosition( mat );
}

Player::setRenderPosition()
Same thing as Player::setPosition(), I replaced the X and Y values with the rotation values. They get passed along properly.

void Player::setRenderPosition(const Point3F& pos, const Point3F& rot, F32 dt)
{
   MatrixF mat;
   if (isMounted()) {
      // Use transform from mounted object
      MatrixF nmat,zrot;
      mMount.object->getRenderMountTransform(mMount.node,&nmat);
      zrot.set(EulerF(rot.x, rot.y, rot.z));
      mat.mul(nmat,zrot);
   }
   else {
      EulerF   orient(rot.x, rot.y, rot.z);

      mat.set(orient);
      mat.setColumn(3, pos);

      if (inDeathAnim()) {
         F32   boxRad = (mDataBlock->boxSize.x * 0.5f);
         if (MatrixF * fallMat = mDeath.fallToGround(dt, pos, rot.z, boxRad))
            mat = * fallMat;
      }
      else
         mDeath.initFall();
   }
   Parent::setRenderTransform(mat);
}

Player::packUpdate() & Player::unpackUpdate()
Now transmits and reads all three the values of the rotation vector.

stream->writeFloat(mRot.x / M_2PI_F, 7);
	  stream->writeFloat(mRot.y / M_2PI_F, 7);
	  stream->writeFloat(mRot.z / M_2PI_F, 7);

	  rot.x = stream->readFloat(7) * M_2PI_F;
	  rot.y = stream->readFloat(7) * M_2PI_F;
          rot.z = stream->readFloat(7) * M_2PI_F;

Yet, still no effect. The new matrix including the X and Y rotation is properly being passed onto its parent.
#16
08/01/2009 (3:26 pm)
Anyone?
#17
08/02/2009 (12:21 am)
replace
Parent::setRenderTransform(mat);

with
setRenderTransform(mat);
#18
08/02/2009 (4:41 am)
Thanks.

Well, I got stuff working, sorta :) Combining the conform resource and my own stuff. Now I gotta make sure my players stick to the walls :)
#19
08/03/2009 (8:26 pm)
Nice work. I tried this once a long time ago by replacing the player with a modified vehicle class, which has roll and pitch movement available. Just another way of looking at the problem.
- Colin
#20
09/11/2009 (3:41 pm)
Oh almighty thread, I resurrect thee!! MUHUHUAHHAHAHAAHA

Hi guys! Got the whole flipping against the wall going except for two things:

When I jump into the wall, my character does make a 90 degree flip however, the character is laying on its side looking left or right. The controls are also messed. The controls still behave as if he's standing on the floor (Forward meaning walking into the wall instead of over it).

Anyone know where I should look? I thought the transforms should've taken care of this?
Page «Previous 1 2