Game Development Community

Player Rotation using the Mouse cursor

by Callum Coffin · in Torque Game Engine Advanced · 09/02/2008 (7:21 pm) · 54 replies

I am trying to make it so that the mouse cursor rotates the player. By this I mean that the player will turn to face the cursor where ever it is on the screen. I have been slugging at this for ages and haven't got very far, so I am desperatley in need of help.

So far Timothy Castagna has managed to modify this tutorial (www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3645), so that the mouse turns the player, but I cannot get him to face the cursor, it doesnt really turn properly at all, its kind of random. But I have no idea why, and dont know how I can sort it out. This is the code which I carnt get to work properly, I am dealing with the onMouseMove(..) function in the gameTSCtrl.cc file:

void afxTSCtrl::onMouseMove(const GuiEvent& evt)
{
Point2I ext = getExtent()- evt.mousePoint;
	Point2I pos = evt.mousePoint - getPosition();
	F32	YawSpeed = MoveManager::mYawRightSpeed;
	F32	PitchSpeed = MoveManager::mPitchUpSpeed;

	
	GameConnection* conn = GameConnection::getConnectionToServer();
	if (!conn)
		return;
	ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
	if (!control || !(control->getType()))
		return;
	Player*	Psubj =	dynamic_cast<Player*>(control);

	MatrixF cam_xfm;
	Point3F dummy_pt;
	if (GameGetCameraTransform(&cam_xfm, &dummy_pt))    
	{
		Point3F cameraPoint; cam_xfm.getColumn(3,&cameraPoint);
		Point3F screen_pt((F32)evt.mousePoint.x, (F32)evt.mousePoint.y, 1.0f); 
		Point3F screen_pt_temp((F32)evt.mousePoint.x, 1.0f, 1.0f);
		Point3F world_pt;      
		if (unproject(screen_pt, &world_pt))       
		{         
			  Point3F mouseVec = world_pt - cameraPoint;         
			  mouseVec.normalizeSafe();    
		      
			  mMouse3DPos = cameraPoint;
			  mMouse3DVec = mouseVec;

			  F32 selectRange = arcaneFX::sTargetSelectionRange;
			  Point3F mouseScaled = mouseVec*selectRange;
			  Point3F rangeEnd = cameraPoint + mouseScaled;
			  
			  Psubj->setCameraFov(360);
			  //MoveManager::mYaw = evt.mousePoint.x;
			  MoveManager::mYaw = mouseVec.x;
			  //Psubj->setTransform(cam_xfm);
			  //MoveManager::mXAxis_L = mouseVec.x;
			  //Point2I CursorPos = GuiCanvas::getCursorPos();
			  //PLAYER TRANSFORM
			  //Psubj->mRot = screen_pt;
			  //Psubj->setRenderTransform(cam_xfm);
			  //Psubj->setPosition(Psubj->getPosition(),mouseVec);
			 
			  //arcaneFX::rolloverRayCast(cameraPoint, rangeEnd, arcaneFX::sTargetSelectionMask);
		}   
	}

}

Any help or advice would be greatly appretiated.

Thanks,
Page «Previous 1 2 3
#1
09/02/2008 (8:28 pm)
I'm really sorry that I can't be too detailed, but hopefully someone else can or you can figure this out on your own.

What we did for our game (which is losely based on gameplay from the Diablo/NWN series) is the following:

* When the player clicks on the screen, the coordinates are projected into 3D space and passed on to the MoveManager. The MoveManager packs this position into the next move update. All players get this update as well.
* In updateMove () we use logic from AIPlayer to calculate how much we have to turn to face the position. If it's far away enough, we move there. Since you're using Move updates, the Client will predict as you'd expect and there will be no visible delay when you command your player to go there even if you have lag. That's what we really wanted when we implemented this.

From there you could implement pathfinding and picking up items. Remember, AIPlayer has a lot of the math needed in it already so read trough it and make use of what you can.
#2
09/02/2008 (9:05 pm)
Thanks Stefan, I am a bit confused, but I will have a look.
When you say the player clicking on the screen generates the coordinates, would this work if I didnt use the mouse button (clicking) at all. What I ideally want to achieve when/if I ever manage it is to use the w and s keys to move (a,f to strafe), and use the mouse cursor purely to rotate the character and control where he faces. The kind of thing I am trying to achieve is as follows: www.youtube.com/watch?v=wA2sxPqK_wg&feature=related

Thanks Again
#3
09/03/2008 (6:53 am)
@Callum

In the code you posted above you are getting a normalized 3d point representing a 2d spot you clicked on your GUI. It is crucial to understand that this 3d point is not a representation of a world point. All the 3d point does is help you get what you need. From the code above, you are assuming the 3d point is what you need.

From what you're saying and from the video, you want to make the mouse point into a position on the ground. To do that you need to cast a ray from the 3d point and stop when you hit terrain/interior/ground. You can then use the position you get from the ray to transform your players rotation.

From your above code you can do:
...
if(unproject()) // is the point clicked actually in this GUI
...
// the following gives us our STARTING point.
mMouse3DVec = mouseVec; // this convention is primarily used to make this easily scriptable. You can skip it, 
                                             // but I recommend continuing it.
...
// Next we can either do this in script, or in C++. For simplicity we'll continue in the engine.
// We use our 3d mouse point as the start, and scale that same point for the end.
if(gServerContainer.castRay(mMouse3DVec,mMouse3DVec * 500.0f,SomeTerrainMask,&info))

If the castRay function returns true, we now have the point you want (info.point). This is the position in the world you want your player to face. If you are an AI object, you simply call setAimLocation(info.point).

If you are not an AI object, then you need to set movemanagers yaw. There is a perfect example of how to do this in AIPlayer::getAIMove. The section of code you want is under the comments:

// Orient towards the aim point, aim object, or towards
// our destination.

To be clear, I am sure there are many other ways of doing what you want to do. The example I provided is based on the work you have already done, as I am sure you are already very familiar with those related sections of code.

Let me know how it turnes out!
#4
09/04/2008 (9:30 am)
We have something working right now, originally we modified the gameConnectionMoves files as well, but i've been able to push most of the fun functionality off to this function which overrides the PlayGui. I am assuming you have top down view and you want the mouse to control your yaw.

F32 HnMTSCtrl::getYaw()
{
    GameConnection *con = GameConnection::getConnectionToServer();
    
    if (con->isFirstPerson())
    {
       return 0.0f;
    }

    if (!con->getControlObject())
    {
       return 0.0f;
    }

    GuiCanvas* Canvas = getRoot();

    if (Canvas == NULL)
    {
       return 0.0f;
    }

    // rotate the player to look at where the mouse pointer is pointing to
	// If in 3rd person
	F32 yaw, pitch;
	Point2I point = Canvas->getCursorPos();
	Point2I size =Canvas->getWindowSize();
	Point2I center = size / 2;

	// get the vector for the mouse pointer from the center of the screen
   MatrixF camTrans = con->getCameraObject()->getRenderTransform();      
		 
	VectorF camF;
   camTrans.getColumn(1, &camF);
   VectorF newVec;

   // Check if camera is straight down or on an angle
   if (mFabs(camF.z) == 1.0f) 
   {
      newVec = VectorF(-(point.y - center.y), -(point.x-center.x), 0.0f);
   }
   else 
   {
      newVec = VectorF(point.x-center.x, -(point.y-center.y), 0.0f);
   }
		
		// get the yaw of the vector
   MathUtils::getAnglesFromVector(newVec, yaw, pitch);

	
   MatrixF conTrans = con->getControlObject()->getWorldTransform();

	// get the current rotation around the Z-axis
	F32 curYaw = conTrans.toEuler().z;

   yaw = curYaw + yaw;

	// Check if we are taking the correct way round
    
	if( yaw > M_PI_F )
   {
      yaw -= M_2PI_F;
   }
   else
   if( yaw < -M_PI_F )
   {
      yaw += M_2PI_F;
   }
  			
	return yaw;
}

If you have afx installed, it does the job of passing events through the so that the actionmap can get at it. All it all I think this is a better solution as it allows input to be handle at the correct input end. Make sure you have this:
GuiCanvas* Canvas = getRoot();
    Canvas->setConsumeLastInputEvent(false);

in your onMouseMove.

Doing this you just have to modify your default.bind.cs:
function yaw(%val)
{
   if (ServerConnection.isFirstPerson())
   {
      $mvYaw += getMouseAdjustAmount(%val);
   }
   else
   {
      $mvYaw = PlayGui.getYaw();
   }
}

function pitch(%val)
{
   if (ServerConnection.isFirstPerson())
   {
      $mvPitch += getMouseAdjustAmount(%val);
   }
   else
   {
      $mvYaw = PlayGui.getYaw();
   }
}
#5
09/04/2008 (10:00 am)
That's a good example to follow if you don't want predicted* control object moves, which was a big deal for us. With low ping you won't notice, but with higher ping the latter will be smoother.

[* Where moves are used on the controlling clients machine before they are processed by the server, giving a smoother look.]
#6
09/04/2008 (3:34 pm)
Wow, thanks a bunch Terence, I have only had a chance to look through the code but it seems great. I carnt thank you enough.

I am probably just being stupid, but I am not sure which file am I dealing with to the implement the large section of code, I dont have a gameconnectionmove file.

The section of code I am reffering to is:
F32 HnMTSCtrl::getYaw()
{
    GameConnection *con = GameConnection::getConnectionToServer();
    
    if (con->isFirstPerson())
    {
       return 0.0f;
    }

    if (!con->getControlObject())
    {
       return 0.0f;
    }

    GuiCanvas* Canvas = getRoot();

    if (Canvas == NULL)
    {
       return 0.0f;
    }

    // rotate the player to look at where the mouse pointer is pointing to
	// If in 3rd person
	F32 yaw, pitch;
	Point2I point = Canvas->getCursorPos();
	Point2I size =Canvas->getWindowSize();
	Point2I center = size / 2;

	// get the vector for the mouse pointer from the center of the screen
   MatrixF camTrans = con->getCameraObject()->getRenderTransform();      
		 
	VectorF camF;
   camTrans.getColumn(1, &camF);
   VectorF newVec;

   // Check if camera is straight down or on an angle
   if (mFabs(camF.z) == 1.0f) 
   {
      newVec = VectorF(-(point.y - center.y), -(point.x-center.x), 0.0f);
   }
   else 
   {
      newVec = VectorF(point.x-center.x, -(point.y-center.y), 0.0f);
   }
		
		// get the yaw of the vector
   MathUtils::getAnglesFromVector(newVec, yaw, pitch);

	
   MatrixF conTrans = con->getControlObject()->getWorldTransform();

	// get the current rotation around the Z-axis
	F32 curYaw = conTrans.toEuler().z;

   yaw = curYaw + yaw;

	// Check if we are taking the correct way round
    
	if( yaw > M_PI_F )
   {
      yaw -= M_2PI_F;
   }
   else
   if( yaw < -M_PI_F )
   {
      yaw += M_2PI_F;
   }
  			
	return yaw;
}

Thanks again,
#7
09/04/2008 (4:38 pm)
Hey Callum,
it looks to me like you could prolly do this where you are doing it now just change yaw to MoveManager::yaw

i could be wrong but give it a shot.
#8
09/04/2008 (4:55 pm)
It works
#9
09/04/2008 (4:56 pm)
In afxTSCtrl.cpp
add
#include "math/mathUtils.h"

replace onMouseMove with
void afxTSCtrl::onMouseMove(const GuiEvent& evt)
{
	//TCASTAGNA
	GameConnection *con = GameConnection::getConnectionToServer();
    
    if (con->isFirstPerson())
    {
       return;// 0.0f;
    }

    if (!con->getControlObject())
    {
       return;// 0.0f;
    }

    GuiCanvas* Canvas = getRoot();

    if (Canvas == NULL)
    {
       return;// 0.0f;
    }

    // rotate the player to look at where the mouse pointer is pointing to
	// If in 3rd person
	F32 yaw, pitch;
	Point2I point = Canvas->getCursorPos();
	Point2I size =Canvas->getWindowSize();
	Point2I center = size / 2;

	// get the vector for the mouse pointer from the center of the screen
   MatrixF camTrans = con->getCameraObject()->getRenderTransform();      
		 
	VectorF camF;
   camTrans.getColumn(1, &camF);
   VectorF newVec;

   // Check if camera is straight down or on an angle
   if (mFabs(camF.z) == 1.0f) 
   {
      newVec = VectorF(-(point.y - center.y), -(point.x-center.x), 0.0f);
   }
   else 
   {
      newVec = VectorF(point.x-center.x, -(point.y-center.y), 0.0f);
   }
		
		// get the yaw of the vector
   MathUtils::getAnglesFromVector(newVec, yaw, pitch);

	
   MatrixF conTrans = con->getControlObject()->getWorldTransform();

	// get the current rotation around the Z-axis
	F32 curYaw = conTrans.toEuler().z;
	
   yaw = curYaw + yaw;

	// Check if we are taking the correct way round
    
	if( yaw > M_PI_F )
   {
      yaw -= M_2PI_F;
   }
   else
   if( yaw < -M_PI_F )
   {
      yaw += M_2PI_F;
   }
MoveManager::mYaw = yaw;
}
#10
09/04/2008 (5:04 pm)
Its still a little off but pretty close.
#11
09/04/2008 (5:52 pm)
Arh thats a shame, I thought terence's code would do it, it seems to have the same problem as we had before tim, just a bit better.
#12
09/04/2008 (6:01 pm)
Yea, its really close though. just a little tweak here and there should fix it. changing
Point2I point = Canvas->getCursorPos();
to
Point2I point = evt.mousepoint;

helps a little.
#13
09/04/2008 (6:37 pm)
Tim, To me it seems that the main problem is that the camera rotates when the player rotates. If you for instance take the cursor, place it in front of the player and then bring it straight down the player turns around 180 as he should, but as the camera rotates also, it means that the player is facing the opposite direction to the cursor. Have you any idea how to stop the camera from rotating?
#14
09/04/2008 (6:50 pm)
I'm using advanced camera so mine does not rotate.
#15
09/04/2008 (6:53 pm)
How did you alter the afx camera so that it doesnt rotate?
#16
09/04/2008 (7:06 pm)
How did you alter the afx camera so that it doesnt rotate when the player turns? i tried, but failed.
#17
09/04/2008 (7:35 pm)
I didnt alter the afx camera at all. i used the advanced camera resource instead.
#18
09/04/2008 (7:46 pm)
Have you transfered the advanced camera resource to tgea 1.7.1? and what did you do with the camera files that afx ships with?
#19
09/04/2008 (8:56 pm)
Yes i have advanced camera ported over. i think there were only like 2 changes, it was really easy. I didn't do anything with afx. just added advanced camera then in game.cs changed what camera it uses. This way if i need to add content (advanced camera does not have fly mode) i can switch back to afx camera and use the fly mode.
#20
09/04/2008 (9:11 pm)
Could you possible show me what you use to get the advanced camera to work because I couldnt make heads or tails of it, that was the reason I bought afx in the first place.
Page «Previous 1 2 3