Game Development Community

Multiple player objects, one connection

by Ian Morrison · in Torque Game Engine · 01/03/2006 (4:39 pm) · 8 replies

What I'd like to do is set up my game so that when a player joins, he controls a squad of players instead of a single avatar. To start, I've used Anthony Lovell's Actor resource so that the player will be able to hop between controlling individual members of his squad. That, and dealing with the players/aiplayers distinction would probably end up being annoying. I've also gotten some very basic team deathmatch AI going, so thats out of the way... my next task is getting AIs into a squad grouping.

The method that I'm thinking about using is simply hooking into the GameConnection::spawnActor function in server\scripts\game.cs, and creating all the members of the squad there. I'd then give each a pointer to each other member of the squad, and give the client control of one of them. Then, whenever a player wants to switch avatars (or dies) he'd get dumped into one of the linked AIs, where all the relevant information about the squad would be waiting.

However, that seems a little unwieldy. Though it'd have the advantage of being doable entirely in script, I worry about the potential for errors. Would there be a more elegant way of going about it?

Thanks.

#1
01/03/2006 (7:18 pm)
Mmm...

Well, you're already creating a player no? Why not shift from when the spawn player is called to a spawn group, that is an overseer class built prototyped on player that maps down the functionality to a given 'current' player object that it holds? The spawn group could then call a few cases of spawnActor to get the other AIs in on the group and have it all in one place. Of course, this is more C++ thinking as the scripting language wouldn't care where they were put together. Then again, this could just be lowered down into the c level, as if I recall, the player is. Just a tired thought anyhow.
#2
01/04/2006 (4:40 am)
I've dome something like this without going into any C++

in Game.cs
function GameConnection::spawnPlayer(%this)
{
   // Combination create player and drop him somewhere
   %spawnPoint = pickSpawnPoint();
   
   %this.createUnits(%spawnPoint);
}

function GameConnection::createUnits(%this, %spawnPoint)
{
   if (%this.unit1 > 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
   %unit1 = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
   MissionCleanup.add(%unit1);
   
   // Player setup...
   %unit1.setTransform(%spawnPoint);
   %unit1.setShapeName("1");//%this.name);

   %this.unit1 = %unit1;
   %this.selectedUnit = %unit1;
   

   %unit2 = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
   MissionCleanup.add(%unit2);


   %trans = getWord(%spawnPoint, 0)+50 SPC getWord(%spawnPoint, 1) SPC getWord(%spawnPoint, 2);
   %unit2.setTransform(%trans);
   %unit2.setShapeName("2");

   %this.unit2 = %unit2;
   

   %unit3 = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
   MissionCleanup.add(%unit3);

   // Player setup...
   %trans = getWord(%spawnPoint, 0)+100 SPC getWord(%spawnPoint, 1) SPC getWord(%spawnPoint, 2);
   %unit3.setTransform(%trans);
   %unit3.setShapeName("3");

   %this.unit3 = %unit3;
   

   %unit4 = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
   MissionCleanup.add(%unit4);

   // Player setup...
   %trans = getWord(%spawnPoint, 0)+150 SPC getWord(%spawnPoint, 1) SPC getWord(%spawnPoint, 2);
   %unit4.setTransform(%trans);
   %unit4.setShapeName("4");

   %this.unit4 = %unit4;
  
  %this.selectUnit(1);

}

function GameConnection::selectUnit( %this, %unitNumber )
{
      
   switch( %unitNumber )
   {
      case 1: %unit = %this.unit1;

      case 2: %unit = %this.unit2;

      case 3: %unit = %this.unit3;

      case 4: %unit = %this.unit4;

   }
      
  %this.selectedUnit = %unit;
      
   // Update the camera to start with the player
   %this.camera.setTransform(%unit.getEyeTransform());

   // Give the client control of the player
   %this.setControlObject(%unit);

  // We set the camera system to run in 3rd person mode around the %player
  %this.advCamera.setPlayerObject(%unit);
  %this.advCamera.setThirdPersonMode();
  %this.advCamera.setFollowTerrainMode(false);
  %this.advCamera.setVerticalFreedomMode(false);
  %this.setCameraObject(%this.advCamera);
  %this.advCamera.setOrbitMode();
  %this.advCamera.zoom = 200;
}

Then in commands.cs
// Unit Selection
function serverCmdSelectUnit1( %client )
{
   %client.selectUnit(1);
}

function serverCmdSelectUnit2( %client )
{
   %client.selectUnit(2);
}

function serverCmdSelectUnit3( %client )
{
   %client.selectUnit(3);
}

function serverCmdSelectUnit4( %client )
{
   %client.selectUnit(4);
}

Then unless I've missed something, it's just a matter of sending the correct command to the server when you press a key.
Something like:
These go in your client/config.cs (or one of the client files, can't remember exactly which one)

moveMap.bindCmd(keyboard, "1", "commandToServer(\'SelectUnit1\');", "");
moveMap.bindCmd(keyboard, "2", "commandToServer(\'SelectUnit2\');", "");
moveMap.bindCmd(keyboard, "3", "commandToServer(\'SelectUnit3\');", "");
moveMap.bindCmd(keyboard, "4", "commandToServer(\'SelectUnit4\');", "");


If I've not missed anything, you should get 4 players appearing in a line on mission start and can select them using the 1-4 keys.

(edit: removed Game specific code.. sorry)
#3
01/04/2006 (4:41 am)
BTW, if anyone can improve on my code, please do. I'm not an expert by any means so anything which cuts down the amount of code I've used would be welcome.
#4
01/04/2006 (5:21 am)
Then if you wanted to make each avatar behave like an AI unit once they're de-selected, you could alter the Player class in C++ to include the AIPlayer code (unless AIPlayer is derived from Player... I haven't checked yet) which activates on a flag which you could expose to script.

I've not actually done that part myself, still a task to be done.. once I've decided how I'm going to handle the AI. But I see all of that code living in a version of the Player class.
#5
01/04/2006 (11:53 am)
The Actor resource combines Player and AIPlayer functionality, and allows Actors to be script controlled when not controlled by a player, so thats covered!

Thanks, that code will be extremely helpful! Thanks for sharing.
#6
01/04/2006 (12:19 pm)
If I may suggest some changes to your code:

Place your units into an array, and put their creation and handling into a loop. This'll let you change squad size, and change how many squadmates you want. On top of that, it'll cut down on the amount of code you'll need drastically... the separate unit creation blocks of code would be rolled into one.

Also, if I've read the torqueScript docs correctly, you've already gotten them into arrays, as unit1 == unit[1].

I'm no expert either, but there are my suggestions. :)
#7
01/04/2006 (12:24 pm)
How about paramaterising the Client to Server messages? Is that possible?
That would make a difference too I think, but I've not gotten around to looking into ways to improve the code just yet.. knocked this up as a Quick and Dirty solution.
#8
01/05/2006 (11:07 am)
Heres what I did:

function GameConnection::selectUnit(%this, %unit)
{
//Set the unit
if(isObject(%this.unit[%unit])) //Does the object exist?
if(!(%this.unit[%unit].getState() $= "Dead")) //No dead ones
{
%this.actor = %this.unit[%unit];
%this.setControlObject(%this.unit[%unit]);
}
}

...and in config.cs...

moveMap.bindCmd(keyboard, "1", "commandToServer(\'SelectUnit\',0);", "");
moveMap.bindCmd(keyboard, "2", "commandToServer(\'SelectUnit\',1);", "");
moveMap.bindCmd(keyboard, "3", "commandToServer(\'SelectUnit\',2);", "");
moveMap.bindCmd(keyboard, "4", "commandToServer(\'SelectUnit\',3);", "");

Does that help?