Game Development Community

dev|Pro Game Development Curriculum

Mouse controlled player movement

by Ron Yacketta · 12/14/2002 (7:35 am) · 67 comments

RTS/RPG Mouse controlled player movement

The following is a long awaited resource that I have been struggling to get out the door for a couple of the months now. The idea of the resource came from playing the likes of Dungeon Siege, Diablo, Baldurs Gate and Never Winter Nights.

I started off by utilizing a couple resources located on the GarageGames web site as well as document sent to me by Jason from 21-6 productions (still needs to be worked in) and one sent my way for review from Davide (do not recall the chaps Surname).

The first resource I used was "Object Selection in Torque" (www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=2173). The only exception was that I did not add the selection code mentioned in the resource. I also made a few modifications to the serverCmdSelectObject() function, which I will outline

function serverCmdSelectObject(%client, %mouseVec, %cameraPoint)
{
//Determine how far should the picking ray extend into the world?
   %selectRange = 200;
// scale mouseVec to the range the player is able to select with mouse
   %mouseScaled = VectorScale(%mouseVec, %selectRange);
// cameraPoint = the world position of the camera
// rangeEnd = camera point + length of selectable range
   %rangeEnd = VectorAdd(%cameraPoint, %mouseScaled);


   %searchMasks = ($TypeMasks::TerrainObjectType      | $TypeMasks::InteriorObjectType    |
                   $TypeMasks::PlayerObjectType       | $TypeMasks::StaticShapeObjectType |
  	           $TypeMasks::VehicleObjectType      | $TypeMasks::ForceFieldObjectType  |
                   $TypeMasks::PhysicalZoneObjectType | $TypeMasks::StaticTSObjectType);

   %scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks, %player);
   
// a target in range was found so select it
if (%scanTarg)
{
$pos = getWords(%scanTarg,1,3);
%client.player.setMoveDestination($pos);
}
}

One could change the name of the function to something more suitable like "serverCmdSetDestination", but I will leave that up to you to decide.

The following bit of information was taken from Davide's document with additions and modifications made by me.

How to make an RPG-like interface

Instead of using the default "first person" or "fixed third person" that cames with the Torque demo, you can
program a classic RPG interface, that is, the camera follows the player and to move the camera around you
have to move the cursor to the end of the screen, and the camera turn around the player but he doesn't change
his direction.

In the FILE "fps/client/ui/playgui.gui" remove the following:

noCursor=true;

This will enable the cursor to be displayed at all times.

In the FILE "fps/client/scripts/default.bind.cs" INSERT BOTTOM these functions:

function incCameraDistance()
{
   $CameraDist = ($CameraDist + 1.0) > 5.0 ? 5.0: $CameraDist+1.0;
   $CameraZooming = true;
}

function decCameraDistance()
{
   $CameraDist = ($CameraDist - 1.0) < 1.0 ? 1.0 : $CameraDist-1.0;
   $CameraZooming = true;
}

function changeCameraDistance(%val)
{
   if (%val>0) incCameraDistance();
   else decCameraDistance();
}

The functions are self explanatory, the variable $CameraDist and $CameraZooming will be added later in the
engine code.

Now in the FILE "config.cs" and "default.bind.cs", bind the keys with these function (really you have to write these lines also in the default.bind.cs
but if you already have the config.cs file you have to write these lines in this file because is the last file loaded and it delete
the previsious moveMap), to do this INSERT BOTTOM

moveMap.bind(keyboard, "m", toggleFirstPerson);
moveMap.bind(mouse, "xaxis", yaw);
moveMap.bind(mouse, "yaxis", pitch);
moveMap.bind(mouse, "zaxis", changeCameraDistance);	//mousewheel
moveMap.bind(keyboard, "i", incMaxCameraDistance);	//these two lines are here if you don't have a 
mousewheel
moveMap.bind(keyboard, "k", decMaxCameraDistance);
moveMap.bind(keyboard, "c", toggleMouseLock);

Note that I have changed the bind of the toggleFirstPerson, don't use the tab key, because when the cursor is
on, when you press the tab key, the engine thinks that you want to change control and it doesn't execute the
toggleFirstPerson function.

Ok, now let's go into the engine code
First we add the two variable's to

File : gameConnection.h
class : GameConnection
in the private section, after the mCameraSpeed add:

static F32 mCameraDist;
static bool mCameraZooming;

File : gameConnection.cc
after the S32 GameConnection::smVoiceConnections[MaxClients]; add:

F32 GameConnection::mCameraDist = 3.0f;			//3.0 is the distance  I have chosed, change it as you will
bool GameConnection::mCameraZooming = false;

File : gameConnection.cc
Function: getControlCameraTransform(......)

change the block that starts with if (dt) { .... } with

if (dt) 
   {
  	if (mFirstPerson) mCameraDist = 0.0;	//When you change to first person the camera has a distance of 0.0
  	else
	  if (mCameraDist == 0.0)		//If you aren't in first person but the distance is 0.0
	  {
		  mCameraDist = 3.0f;				//move the camera back
		  mCameraZooming = true;
	  }

      if (mCameraZooming || mFirstPerson || obj->onlyFirstPerson()) 
      {
         if (mCameraPos > mCameraDist)
         {
            if ((mCameraPos -= mCameraSpeed * dt) <= mCameraDist)
	    {
               mCameraPos = mCameraDist;
	       mCameraZooming = false;
	    }
        }
        else 
	if (mCameraPos < mCameraDist)
            if ((mCameraPos += mCameraSpeed * dt) > mCameraDist)
	{
	 mCameraPos = mCameraDist;
	 mCameraZooming = false;
	}
    }
 }

change the next line from
if (!sChaseQueueSize || mFirstPerson || obj->onlyFirstPerson())

to

if (!sChaseQueueSize || mCameraZooming || mFirstPerson || obj->onlyFirstPerson())
	      obj->getCameraTransform(&mCameraPos,mat);


File : gameConnection.cc
Funciton: GameConnection::readPacket( ....)

find the block

bool fp = bstream->readFlag();
      if(fp)
         mCameraPos = 0;
      else
         mCameraPos = 1;

and replace the mCameraPos = 1; with mCameraPos = mCameraDist;

Now we export the variables so we can use them in the script

File : gameConnection.cc
Function: GameConnection::consoleInit()

add these 2 lines

Con::addVariable("CameraDist", TypeF32, &mCameraDist);
Con::addVariable("CameraZooming", TypeBool, &mCameraZooming);

Now, we have to manage the mousemove event, so when the cursor is near the margin of the window, the
camera has to move.

File: : gameTSCtrl.h
Class : GameTSCtrl

add the following variable decleration in the public section

F32 mMouseMove;

File : gameTSCtrl.cc
Function: GameTSCtrl::GameTSCtrl()

add the following

mMouseMove = 0.009f;

File : gameTSCtrl.cc
Function: onMouseMove(..)

Comment out (or remove) the code already present and replace it with this:

Point2I ext = getExtent()- evt.mousePoint;
	Point2I pos = evt.mousePoint - getPosition();
	F32	YawSpeed = MoveManager::mYawRightSpeed;
	F32	PitchSpeed = MoveManager::mPitchUpSpeed;

	if (ext.x < 50 )						//50 is the distance from the margin 
	{
		MoveManager::mYawRightSpeed -= mMouseMove ;
	} 
	else
	if (pos.x < 50)
	{
		MoveManager::mYawRightSpeed += mMouseMove ;

	}

	if (ext.y < 50)
	{
		MoveManager::mPitchUpSpeed += mMouseMove ;
	}
	else
	if (pos.y < 50)
	{
		MoveManager::mPitchUpSpeed -= mMouseMove ;
	}


	//if you enter this function and the speed isn't changed this means that the cursor is out of the "moveble area" so
	//stop the movement

	if (MoveManager::mYawRightSpeed == YawSpeed)
		MoveManager::mYawRightSpeed = 0;

	if (MoveManager::mPitchUpSpeed == PitchSpeed)
		MoveManager::mPitchUpSpeed = 0;

Ok, the last step. We have to send the MouseMove event to this class, which is the correct class to manage this
type of event (at least I think so!! :-)), but in the script playGui.gui is defined the GuiShapeNameHud that is
almost the same size as the canvas itself, so every mouse message is sent to this class, so we have to pass the
message to the parent of this class

File : guiShapeNameHud.cc (located ./engine/game/fps ver. 1.1.2)
class : GuiShapeNameHud

In the public section add:

virtual void onMouseMove(const GuiEvent& event);

then in the same file, for example before the onRender method, add:

void GuiShapeNameHud::onMouseMove(const GuiEvent &event)
{
	//Let's let the parent execute its event handling (if any)

	GuiTSCtrl* parent = dynamic_cast<GuiTSCtrl*>(getParent());
	if (parent) parent->onMouseMove(event);
}

Ok, that's all.
Try to run the game, if everything is ok, you can move the mouse at the edge of the screen and you should see the camera rotating around the
player, use the mousewheel to zoom in and out.
Davide

I noticed that the rotation is clamped, that is you can not do a full 360 rotation around the player. To solve this I modified the following

FILE : Player.cc
Function: updateMove

I replace the following block of code

if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
      {
         mHead.z = mClampF(mHead.z + y,
                           -mDataBlock->maxFreelookAngle,
                           mDataBlock->maxFreelookAngle);
      }

with

if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
      {
         mHead.z += y;
      }

Now onto the part you all have been waiting for. Mouse controlled player movement.
I started out with a copy of aiPlayer.cc and went forth, after a few emails to Tim Gift; he sent me down the road to glory. On the way he snuck in behind me (just kidding Tim) and released a new aiPlayer.cc that contained basicly what I was setting out to-do. Wonder if that boils down to great minds think alike? Nah, I am not even in the same league as Tim, he is up their with the likes of Carmak!!

Anways, on to the changes

FILE : fps\client\server\scripts\game.cs
Function : createPlayer

Replace the entire function with

function GameConnection::createPlayer(%this, %spawnPoint)
{
   if (%this.player > 0)  {
      // The client should not have a player currently
      // assigned.  Assigning a new one could result in 
      // a player ghost.
      error( "Attempting to create an angus ghost!" );
   }

   // Create the player object
   %player = new AIPlayer() {
      dataBlock = LightMaleHumanArmor;
      client = %this;
   };
   MissionCleanup.add(%player);
   
   // Player setup...
   %player.setTransform(%spawnPoint);
   
   %player.setEnergyLevel(60);
   %player.setShapeName(%this.name);
   
   
   // Update the camera to start with the player
   %this.camera.setTransform(%player.getEyeTransform();
   
   
   // Give the client control of the player
   %this.player = %player;
   %this.setControlObject(%this.camera);
}

I think that should wrap it up for this tutorial.
#41
07/25/2007 (8:43 am)
Ow crap ;-) ok thnx, just learning everything... btw it worked I can get ingame again after 1,5 day but....
here it comes I get a grey screen... and it doesn't seem to be any gui wich is infront of it.
Any suggestions ?

btw the luck I need
#42
07/25/2007 (8:57 am)
glad to help, but I think would better take this issue outside this thread since it has nothing to do with mouse controlled player movement, make a new thread or contact me directly Ehab.Amer@gmail.com

(Till u do that here is my reply)
95% there was a problem in creating the player, so when u set the transform of the camera with the eye transform of the player which doesn't exist there is a problem, so most probably the camera is underground.
to make sure abt that press F11 to go to the editor then move upwards with the camera till u become above the ground.

the part that failed to create the Player is this
%player = new AIPlayer() {
dataBlock = LightMaleHumanArmor;
client = %this;
};

double check the LightMaleHumanArmor Datablock, if this is the correct spelling or the datablock failed to load, the console will help u

(dont forget to start another thread & to tell me its name or link, or contact me directly)

best of luck :D
#43
09/08/2007 (1:13 am)
What if all I want is a camera zoom function? I have already used the tutorial found at this thread for my game controls and want to stick with them: www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=11152
#44
09/08/2007 (4:40 am)
well let me check that resource u've mentioned 1st, but unfortunately that wont be any time soon :-s
so well let me think with u abt the zoom thing
first: u want a zoom like a sniper rifle zoom ?? then ur answer is to change the field of view to a smaller angle, that will give u the effect u want
OR u want the camera to get closer thats all, then u could also use the field of view or u could change the camera possition

the function to change the camera field of view is "setCameraFOV( fov )" & to get is "getCameraFOV()"

I used the function %this.camera.setOrbitMode(%player, %player.getTransform(), 10.0, 15.0, 10.0, true); to set the camera in its position, the way it is mentioned in this resource didn't work out for me very well (I dont remember y actually :D :D )
so u could do the same & according to the zoom buttons u want change the distance the camera orbits around the player

I hope that helps enough :)
#45
09/08/2007 (1:37 pm)
i think thats what i need thx!

Now I just have to play around with that in my default.binds.cs. :P
#46
09/08/2007 (1:44 pm)
Ok, so what would I put in for "mousewheel"?

Pseudo-code:

bindCmd( mouse, wheel+, adjustCam(0) );
bindCmd( mouse, wheel-, adjustCam(1) );
#47
09/08/2007 (1:53 pm)
moveMap.bind(mouse, "xaxis", yaw);
moveMap.bind(mouse, "yaxis", pitch);

this code was mentioned in the resource, the functions are yaw & pitch but i suppose u already knew that :D

BTW thnx alot for the resource u mentioned it will be very handy I'm sure ( I went with my eyes through it but didn't try it yet)

good luck :)
#48
09/08/2007 (1:54 pm)
That resource is awesome! Combined with what you gave me, and I am a happy camper! :P Thx!


Actually...

function handleMouseWheel(%val)
{
if( %val )
zoomCam(0);
else
zoomCam(1)
}

moveMap.bind( mouse, zaxis, handleMouseWheel );
#49
09/08/2007 (2:45 pm)
ok i cant get any of this to work. i basically want the mouse wheel to scroll my 3rd person cam closer and farther away from my character.
#50
09/08/2007 (4:24 pm)
yeah sry abt the yaw & pitch thing, my bad
well actually try using the setFOV( FOV ) function instead, MY APPOLOGY ITS NOT "setCameraFOV ( fov )" which is something else, instead of zoomCam(0) and zoomCam(1)

u can also set the zoom speed using "setZoomSpeed( msec )" which is the speed of changing the FOV by 90 degrees in the time u specified.

to know the current camera FOV the global variable "$cameraFOV" does that for u. NOTICE treat it like a read only variable
& there is camera speed "$camera::movementSpeed"

in short thats how i think the code should b

function handleMouseWheel(%val)
{
if( %val )
setFOV ( $cameraFOV + 5);
else
setFOV ( $cameraFOV - 5);
}

u could use some boundaries for the values, making it shouldn't b a problem

& I'm sry 4 the VERY l8t reply but i got the last 3 mails u've sent while I wasn't @ home & I replied from there too, I needed some references to be of use to u in this reply.
#51
09/08/2007 (4:30 pm)
well, the code works and it does what i want, but...

it doesnt work with the mousewheel. I am trying:


moveMap.bind( mouse, zaxis, handleMouseWheel );
#52
09/08/2007 (4:38 pm)
u'll have to bing this in default.bind.cs & config.cs BOTH files not one of them
if still didn't work
after u run ur game type in the console "trace(1)" without the quotes & scroll ur wheels & c how the function calls go thats if ur function was called or not, it can help to know whats wrong
#53
09/08/2007 (4:42 pm)
WOW! Thx for that "trace(1)" thing! Wonder why my instructor never showed me that? I got it all working wonderfully now! :D
#54
09/08/2007 (4:48 pm)
gr8 :D now u have something to INSTRUCT him with :D
& now I can add a zoom too in my project :D :D I've always been very lazy to add that & always had something else to work on, & I've never thought of the FieldOfView thing till u asked abt it, & its MUCH better than moving the camera & I believe that it has always been recommended over moving the camera

I'm glad I could help & best of luck with ur project :)

oh and "trace(0)" brings things to normal
#55
02/01/2008 (3:20 am)
Anybody got this working on TGEA?
#56
02/04/2008 (6:46 pm)
okey I got this working, problem is the Jerky Movement ...
(i already applied aiplayer animation fix, but still jerky)
#57
02/04/2008 (9:14 pm)
darn, I ended up creating my own setmovedestination routine..
anyway it works the way I want it to be
#58
02/05/2008 (8:12 am)
Sry for not being around everyone !!
@Ariel Its good news that it works out fine on TGEA, I would be very greatful if u would explain the changes u made to the player movement
#59
02/05/2008 (4:56 pm)
As I noticed, when using the AIPlayer with object selection and advance camera in orbit mode or 3rd person,
clicking on the terrain so that the player run to it makes a jerky movement , this kinda make your eye tired.
However doing a normal moveforward via cursor keys the movement are smooth.

Now I see that calling the AIPlayer GetAIMove routine, it sets the moveptr->y = (value) is the culprit.
I still dont know why this moveptr->y=(value) causes jerky movement, but I am tired of finding
solutions anymore. I made my own routine that instead of setting moveptr->y , I tried to set
the MoveManager::moveforward = 1 ( i also just copy the rotation routine of the aiplayer).
I also add a console function and done some changes in player.cpp, but I its not perfect yet I am still
dealing with stuck routine. Someday I will post a resource.
#60
02/06/2008 (4:05 am)
dont approximate the values u've got from the mouse click