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.
Page«First 1 2 3 4 Next»
#61
02/06/2008 (11:12 am)
Sry I didn't explain !!
a possible explanation for that is that when u approximate the values while u r on a rough terrain the point is most probably under the terrain surface, making it rather unreachable, so the character keeps moving in all directions to try to make it there
#62
02/06/2008 (4:46 pm)
Nope, I made a flat smooth terrain.
Using the original aiplayer getaimove routine ,
I noticed that the jerky movement is somewhat sort of slowing something,
as evident of the spaceorc animation is also affected that is kinda jugged
and millisecond slow, but enough to cause you a discomport watching.
#63
02/07/2008 (12:05 am)
I suppose that u r using the 1st person view right ??
but the camera looks fine in 3rd person ??

well I'm using another camera other than the default one Advanced Camera by Thomas "Man of Ice" Lund
it might come in handy, & I\ve got a nice resource if u r working on a RPG, it might prove to be useful in several ways
[url=http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=13617]Mouse, Camera & Selection Combined Resource/url]
#64
02/07/2008 (4:35 pm)
I am also using the Advanced Camera by Thomas "Man of Ice" Lund,
plus the Object selection and Mouse Controlled player resources.
It is fine in 1st person view but jerkiness can be seen on 3rd person or Orbit mode.
Maybe it work fine on TGE but on TGEA something has gone wrong.
Anyway I already got a workaround and happy with it.

And also yes I am working on an MMORPG system,
next stop login and character selection system however
my login/char select screen will not be placed in a hud panel
but within like a loaded mission ,
just like in RF, Lineage, GuildWars, Tantra login/char select style ;)
#65
10/26/2008 (5:29 am)
Hi Ariel!
Can You post Your solution to jerky movement od player?
#67
09/16/2009 (9:43 pm)
Ok, I'm trying to make this work in TGEA, I get everything kinda ok (camera somewhat works, player movement... no) So i seem to have forgot to add in this:
1. bool fp = bstream->readFlag();
2. if(fp)
3. mCameraPos = 0;
4. else
5. mCameraPos = 1;
But when I searched through the file, it didn't include that. So i tried to include the necessary changes into the file (mCameraPos = mCameraDist; etc.)
It compiles fine, but i get this error when trying to create a new mission:
(invalid packet. (bad event class id))

It only started happening when i added in those lines of code. Can anyone help?
Page«First 1 2 3 4 Next»