Game Development Community

Multiple player at the same keyboard

by J. Alan Atherton · in Torque Game Engine · 01/29/2005 (10:44 am) · 16 replies

I would like to allow two players to control two different players in the game. I'm not using the default fps setup, I've got a custom game, so things need to be done through GameConnection. I've figured out how to spawn two different clients (which is what I think I need to do). What I don't know is how to bind keys to different clients.

Well, a thought came while I wrote this. The moves are issued as commandToServer's, (which might change), so I can use bindCmd and pass arguments to specify it's player 2.

Now the question is, is there a better way? Is there a way I can specify keys for a different client?

#1
01/29/2005 (12:50 pm)
How about one client that controls two players? Unless a client is only allowed one player...
#2
01/29/2005 (1:18 pm)
That sounds like a good way to do it. If you wanted to work hard, you could probably get parallel GuiTSCtrls, parallel NetConnections, and so forth going, but your way sounds a lot quicker.
#3
01/29/2005 (4:50 pm)
@Eric:
The rest of the code is simplified greatly if each player is a separate client. I'm not using the Torque Player class at all... just GameConnection, so everything is stored inside the GameConnection basically. Thanks for the idea though.
#4
01/29/2005 (5:28 pm)
Sounds like doubling up your functions would be a possible sollution. Meaning, instead of having a function called "turnRight" you would just copy paste a duplicate set and call them "turnRightPlayerOne" and "turnRightPlayerTwo" and have each respective control function work specifically on that game connection. Then you can bind your controls without conflict and you can make a "allowed list" for each player that specifies which keys each can bind to and devide your keyboard in half giving one side to each client. That way players can rebind their keys to their own desire if they would like to do so.
#5
01/29/2005 (8:27 pm)
@Gonzo:
Yes, that's generally what I was thinking. The only part I don't know how to do is "have each respective control function work specifically on that game connection." How can I tell one set of functions to work on one game connection and not another? The only way I can think of is to pass an argument that will say player1 or player2 in a commandToServer function. I'd gladly accept news of another way.
#6
01/29/2005 (8:57 pm)
Why is that latter approach bad? It seems like it would work fine to me...
#7
01/29/2005 (9:10 pm)
I'm not sure I understand fully what your having a problem with, but if it's just a matter of somehow separating who is controlling what without having to do massive amounts of coding then I would suggest using a couple of global variables. Make the first logged in connection default to player one via a global like...

$PlayerOne = %client;

And then if $PlayerOne is greater than zero when another player logs in, they go to player two via...

$PlayerTwo = %client;


and then no one else can join till one of the other drops.

Declaring a global like that allows you to share specific information on a global scale that is accessable by both the engine and the scripts at any time. So you can easily distinguish inside the engine which client is inputing which actions.
#8
01/29/2005 (10:41 pm)
Wouldn't it be wildly challenging to maintain two gameconnections in a loopback situation?
#9
01/30/2005 (12:03 am)
@Gonzo:
Yeah, you've missed what's troubling me. I understand how to separate the client objects. I don't understand how to bind a command that will invoke a function call with a specific client when both players are on the same machine in the same game. So I guess really that doubling the functions and keeping track of both client IDs is the simplest way. I was trying to get an answer that the actionmap.bind stuff could include extra information that would say which client pushed the button. But that's just complicating the situation. Thanks for the simpler advice.

@Ben:
I haven't tried it out yet, but I'll tell you how it goes. The idea I had (which I haven't tested) is to add some code to startMissionGui.gui inside the SM_StartMission function. (The function that is called when the user pushes "Start mission" in the gui).
// existing block
   %conn = new GameConnection(ServerConnection);
   RootGroup.add(ServerConnection);
   %conn.setConnectArgs($pref::Player::Name);
   %conn.setJoinPassword($Client::Password);
   %conn.connectLocal();
  // added block
   %conn = new GameConnection(ServerConnection2);
   RootGroup.add(ServerConnection2);
   %conn.setConnectArgs($pref::Player2::Name);
   %conn.setJoinPassword($Client::Password);
   %conn.connectLocal();

So that simply doubles up the ServerConnection. I'm new at this area of Torque, so I can't see a problem with this. I haven't gone through and traced what this code does yet, so I could be wildly wrong. Is this a bad way of setting up a 2-player game? I plan to also have internet multiplayer, if that effects the answer. If it's going to add a lot of hairiness to the game, I'll just skip it and add it in a future version or something :)
#10
01/30/2005 (12:52 pm)
Yeagh!

That's the really really really complicated way to do it. The way you should be doing it if you want to get it done quickly is "I was trying to get an answer that the actionmap.bind stuff could include extra information that would say which client pushed the button." There are two ways to do that - the first is to have moveLeft1 and moveLeft2, and the second is to just pass a parameter using bindCmd that indicates which you're talking about.

You might consider extending the moveManager (game/moveManager.cc) to transmit an extra set of move parameters.

But maintaining two client connections is probably more pain than you want.
#11
01/31/2005 (8:25 am)
@Ben:
Remember I'm planning to go completely multiplayer with this thing, the 2 player mode is just an extra. So if you mean complicated because I have to isolate the client from the server, that's already in the works. I would think it would be more complicated to try to manage two players in the same client connection when I already have the client/server stuff talking correctly. Perhaps the code above is the bad way. I decided to look at the joinServer gui stuff, because that would make more sense. So I'd connect the first player the usual way above, locally, then the second player as if they were remote. I'll post the code for easy reference:
%conn = new GameConnection(ServerConnection);
      %conn.setConnectArgs($pref::Player::Name);
      %conn.setJoinPassword($Client::Password);
      %conn.connect($ServerInfo::Address);

Is this still the really really really complicated way? If so, could you please give a reason why? And how am I supposed to manage two different players on the same connection?
#12
01/31/2005 (12:49 pm)
Ok, I thought you weren't also trying for network support, too. Since that's the case, I think you're taking a reasonable tack on the whole thing. Sorry for freaking out. :)
#13
01/31/2005 (1:58 pm)
Hey, no problem about the freaking out. I like doing that too. Thanks everyone for your help and advice. I'll try to remember to post how it goes.

I know that right now it doesn't entirely work (in fact, the connectLocal method works better than the second, so far... that makes sense). It creates two GameConnections, sure enough, but only one (the second created one) has all of the functionality. But I haven't finished isolating the client and server (i.e., placing the necessary initialization on the client). Once it works for network play, I hope it will work in 2 player mode using the methods discussed in the thread. If so, that's an almost no-brainer way to add 2 player crammed on the same keyboard support. The only problem I foresee is the "remote" player might have lag while the "local" player doesn't, but I'm trying to engineer the networking stuff so lag is minimal even across the world.
#14
02/13/2007 (2:23 am)
@J. Alan Atherton

how did it go? did you have no problem to do that?
i really wonder if it's possible to do and not so difficult to implement it.
#15
02/22/2007 (12:47 am)
I've been working on something similar to this and have gotten pretty decent results. I used AIPlayer as a base to create a different subclass of Player which I called VCPlayer (for Virtual Controller Player). Instead of trying to add more actual players, I just add an extra VCPlayer for each extra person.

I'm using x-box controllers, so how I do my input probably wouldn't apply...but I'll toss it out there anyway.

Each VCPlayer has an ANALOG (float) array and a DIGITAL (boolean) array. The actual 1st player has a normal action map. Each additional player's action map (although all of these are technically contained within one actionmap) simply transmits the analog/digital values of the controllers to the VCPlayer's control arrays. Every time getAIMove() is called, my guys check the "current" state of their controller and build the appropriate move from there.
#16
11/15/2007 (8:18 am)
Will, have you got any example code that I could take a look at? I like your idea, but I'm having a problem with implementation.