Quote
So in the observer pattern, the Player class will send the input? What if the Player class depends on the server answer? how will it fit with the observer pattern and the singleplayer?
You have to be careful to split the responsabilities well. Here I'm thinking of the player as the object that will represent what a player can or can not do. Thinking on the messages/methods a player could receive I thinking of:
-
Shoot()
-
Jump()
-
Crouch()
-
TakeHit(double damage)
-
RotateAim(double verticalAngle, double horizontalAngle)
So, in this way you can ensure that the state of the player will always be valid through those methods. As an example, on CounterStrike the player can't shoot while jumping, right? So, the object that will guarantee this is the player, the implementation options are many, you can do something like:
public boolean shoot() {
if(this.state == State.JUMPING) {
return false;
} else {
this.state = State.SHOOTING;
// do some logic like decrease ammo or something
return true;
}
}
This way, you are protecting the player state, if the player press the shoot button while jumping, the player will return false, indicating the player could not do the action. Ok, so I'm saying this just to be clear about the responsibility of the player class, it basically encapsulates the player state management. Those concepts are related to Object Oriented Programming, but getting back to the original problem...
Quote
So for an example - shooting, the player will send its aiming direction and its position. So on multiplayer i guess that the client will wait for the server for its response whenever the player hit or not. when on singleplayer after taking the shot everything is done.
As @frob said, ideally most of your code should be the same besides being played singleplayer or multiplayer. That's also related to Object Oriented Programming principles, you must try to do your architecture in a way that you reuse the code that is common. Ok, but how to do that? I'll make a kind of pseudo-code implementation trying to illustrate. First let's think a single player game, than we expand the idea.
So, you will need an object that will know all the players and will handle the interactions with each other, let's call this class GameWorld. Also let's imagine a class responsible to update the screen, the PlayerRenderer class. And finally we will have a class that knows how to listen to the keyboard (let's imagine that shooting and aiming is also done through Keyboard and that we have multiple players on the same keyboard), we can name this class as PlayerInput. Ok, the responsibilities are there, but how tie them together?
First of all, the PlayerInput must know the Player in order to command him to shoot, jump or crouch when the proper key are pressed. So to do that we can just pass the player instance and the keys on the keyboard on PlayerInput constructor and trust that the PlayerInput instance will listen to the keyboard and call the proper player proper methods. Something like that:
public class PlayerInput {
private Player player;
private String shootKey;
public PlayerInput(Player player, String shootKey) {
this.player = player;
this.shootKey = shootKey;
}
// This method may be called on gameloop update or something
public void checkKeys() {
if (Keyboard.isPressed(shootKey)) {
this.player.shoot();
}
}
}
// Usage
PlayerInput p1Input = new PlayerInput(player1, "CTRL");
PlayerInput p2Input = new PlayerInput(player2, "ALT");
while(true) {
p1Input.checkKeys();
p2Input.checkKeys();
sleep(1/60);
}
Ok, so our player is shooting now, but no one is being hit, neither the shooting animation are being played. The only one who know about all players is the GameWorld, so it would be a good idea to warn him too so he can check if any player was hit, we can do the same approach of making the player receive GameWorld on constructor and making him call a method on GameWorld like (playerShooting(player, direction)), but think that the PlayerRenderer will also need that information to play the animation, this means that the player would have to know PlayerRenderer and GameWorld. As multiple objects are interested on player events we can create a interface named PlayerObserver which will have the method shot(Player player, float direction). So now, GameWorld and PlayerRender will implement this interface and Player will broadcast the shoot to all interested objects. The implementation would be something like this:
public interface PlayerObserver {
public void shoot(Player player, double direction);
}
public class Player {
private double aimDirection;
private List<PlayerObserver> observers;
public Player(/* receive attributes */) {
// assign those attributes (health, player name, etc)
}
public void notify(PlayerObserver observer) {
this.observers.add(observer);
}
public void shoot() {
if (this.state == State.JUMPING) {
// do nothing
} else {
for(int i = 0; i < observers.getLength(); i++) {
observers.get(i).shoot(this, aimDirection);
}
}
}
}
public class GameWorld implements PlayerObserver {
public void shoot(Player player, double direction) {
Player damagedPlayer = this.findPlayerOnDirection(direction);
if(damagedPlayer != null) {
damagedPlayer.beingHit();
}
}
}
public class PlayerRenderer implements PlayerObserver {
public void shoot(Player player, double direction) {
this.renderShootingAnimation(direction);
}
}
// Usage
Player player1 = new Player();
Player player2 = new Player();
PlayerInput p1Input = new PlayerInput(player1);
PlayerInput p2Input = new PlayerInput(player2);
GameWorld world = new GameWorld(player1, player2);
PlayerRenderer p1Renderer = new PlayerRenderer();
PlayerRenderer p2Renderer = new PlayerRenderer();
player1.notify(world);
player2.notify(world);
player1.notify(p1Renderer);
player2.notify(p2Renderer);
while(true) {
p1Input.checkKeys();
p2Input.checkKeys();
gameWorld.update();
sleep(1/60);
}
This is some kind of java pseudo-code that I wrote from my head just to illustrate it probably will not compile in java (It has been a while since I stop working with java). But the main idea is that player is not tied with PlayerRenderer nor GameWorld. What we gain from that? Now we can have other kinds of listeners, like GameServer, who will listen a Player shoot and will send to the server, now talking about a multiplayer game.
And about the input on a multiplayer game? You will not need the player2 PlayerInput anymore, instead you will have a NetworkPlayerInput, which will listen to the server (instead of listening to the Keyboard) and will call the shoot(), jump() and the other methods.
So as you can seen, just thinking a little bit on the architecture we can switch to a multiplayer game reusing almost all the code we have done except for the network specifics. But this example are not considering network delay, this would work if the two GameWorlds on the two computers are always synced. That not the case at all, so instead of the server just forward the player inputs, it will have a GameWorld instance that will be used as source of truth, the GameWorld on the player computer will have to obey what the server GameWorld is saying as the truth. This will add a new way that the GameWorld on the client will be updated because the server state will override the client state.
Quote
We can approach this also from a different direction, on multiplayer when shooting the Player will observer to the server about its input, and without waiting for the server response will damage the player's target and trigger the blood animation, sound affects etc.. But the server still need to let the client know if the player actually hit, how will you consider that on the design?
I would adapt the client game world to receive state sync events which would basically override the current GameWorld state to the new one (sent by the game server). This way the NetworkPlayerInput will not be needed anymore.
Quote
Maybe you know a tutorial or just a UML design to post here?
The book Development and Deployment of Multiplayer Online Games explain in details the problems and most common techniques to deal with multiplayer online games.
The tutorial I know that explain in a simpler way is this one: http://buildnewgames.com/real-time-multiplayer/
This is a complicated topic, I suggest you start by a simpler game like a pacman online on a local network so you can learn how to propagate the state from server to the clients. After that you can try to put on a real server it will feel really laggy, then you use the book techniques to improve it. After understanding the techniques it will be much easier to create your FPS.
PS.: I'm not an expert on the subject, I just work with web applications for a long time and I'm studying game servers specific techniques right now. hehe
Hope it helped