Multiplayer game - synchronizing ball

2019-06-06 11:25发布

问题:

I'm developing a very simple game in HTML5 with the amazing phaser.io. The game is very simple: a 1vs1 volley game in 2D... Actually it is 'copy' of the great "pikachu volley"(http://imagenes.es.sftcdn.net/es/scrn/12000/12531/pikachu-volleyball-2.jpg).

As I said, the game is very easy, I have:

  • Player1
  • Player2
  • Static elements
  • Ball

And I have just to control: - Players movements - Ball movements(actually controlled by Arcade Physics) - Players-Ball collisions

I made it just for fun, adding the faces of my friends to the players. The game works pretty well, and I had really fun playing it with friends. So I thought, why not to make it online multiplayer, so we can play remotely from different locations?

After reading about multiplayer HTML5 games I started developing it with websocket(socket.io) having a nodeJS server. The implementation of socket.io is really simple and the comunication works fine.

The thing is to make the game actually playable.

Those are my steps by now:

On client connection, it is created:

  • Player1(myself)
  • Static elements

And the client waits for a new client conection to create:

  • Remote player
  • Ball

So, after two clients are connected, on each client I have:

  • OwnPlayer
  • RemotePlayer
  • Static elements
  • Ball

Then the game starts... on this stage it's not very fair because Remote player it's not moving at all. So, in order to make the remote player move(after some tries) I decided to implement a kind of authoritative server, working like that.

  • Local player moves(input registered) -> client send the input to the server -> server send the input to both clients -> each client apply movement(by changing velocity)

This mechanic make the movements of the players works on the past but 'synchronized'(the waiting time it's acceptable).

This looks great, every client it's playing moving their player and there is another player moving by a remote client.

The problem is the ball...

On each client there is a ball, moving with Arcade physics(bouncing in the net or in the head of each player)... so after very few movements, because the synchronization of the players position is not perfect, the ball position on each client is not the same.

How would you implement the ball synchronization?

Some options I'm thinking:

  • Periodically send the ball position to the server -> server send ball position to the clients -> clients update the ball position(with some interpolation)

  • Enabling the ball physics only in one client(master) and then send the ball position from 'master client' to 'slave client' periodically (with webRTC, maybe)

  • Start over and make a real 'authoritative server', having the Arcade Physics on server(if this is possible) and just interpolation on clients?

回答1:

To solve your problem you can go an easy way, you transfer to the Server, the state of attacking of the player which is attaching, Now, all Player subscribe from the server the Information of attacking and now the "clints" have to render the bullet.

Here a litte code example

Part1: [Publish the data]

            transferData = [
            {
                id: id,
                name: Player.name,
                position: Player.position,
                facing: Player.facing,
                hitFacing: Player.hitFacing,
                health: Player.health,
                energie: Player.energie,
                healtbar: {width: Player.healthbar.width},
                energiebar: {width: Player.energiebar.width},
                isAttacking: Attack.isAttacking
            }
        ];

        session.publish('org.example.character.data', transferData);
        Attack.isAttacking = false;

Part2: [Subscribe the data]

 // get player position
    session.subscribe('org.example.character.data',function (args) {
        var player = args[0];
        var exists = false;
        for (var i = 0; i < onlinePlayer.length; i++) {

            if (onlinePlayer[i].uid == player.uid) {
                var tmp = onlinePlayer[i];
                player.sprite = tmp.sprite;
                player.label = tmp.label;
                player.status = tmp.status;


                if (player.isAttacking && player.sprite != undefined) {
                    // HERE RENDER THE BALL
                    renderBall(player, this.game);
                }

                onlinePlayer[i] = player;
                exists = true;
            }
        }

        if (!exists)
            onlinePlayer.push(player);

    }).then(
        function (sub) {
            //console.log('subscribed to topic');
        },
        function (err) {
            console.log('failed to subscribe to topic', err);
        }
    );

This example is for an websocket-server, like crossbar.io with autobahn.js on node base.

http://crossbar.io/docs/Quick-Start/ But you can also make it with other servers



回答2:

Your problem reminds me a past problem of mine which is smilar. I was working on a map application on web. Map object has to be same for all clients (synchronized).

How I solved the problem is I have moved class library to server-side and made map object singleton. Have a look at singleton pattern. Singleton objects can not instantiate more than once. I mean there will be exactly one ball object for each game and clients will updates their local object with it.

Here is wikipedia page: https://en.wikipedia.org/wiki/Singleton_pattern

After that, first thing to do in client side is getting the recent instance of the map (that is the ball in your case), modify and update on server.

Another point is that, more than one client may want to update the shared object on server at the sime time. That would cause consistency problems. Many implementations includes locks on variables to restrict access. Other clients wait the lock to release and update.

Anyway, having multiple instances of the same object at clients is not a good approach.