Jump to content

[HELP] Two player sync (PvP)


Sergo86

Recommended Posts

Posted (edited)

Hello, please tell me how to organize the synchronization of the movement of an object between two players in 3D, like this

JbyjOV.png

L1000?cb=20100513201439

Edited by Sergo86
  • Moderators
Posted

@Sergo86

 


You should script this most partly serverside. And tell each client the direction of the ball.

 

Synchronisation

Server: ball position + direction > streamed to the clients.

Client: position of the bars (to block the ball) > streamed to server

 

Physics

Server: mathematically collision calculations.

Client: same mathematically collision calculations, except it gets overruled by the server in certain events. Like: ball misses bar or game ends.

 

Key component: getTickCount()

 

 

 

Posted
37 minutes ago, IIYAMA said:

@Sergo86

 


You should script this most partly serverside. And tell each client the direction of the ball.

 

Synchronisation

Server: ball position + direction > streamed to the clients.

Client: position of the bars (to block the ball) > streamed to server

 

Physics

Server: mathematically collision calculations.

Client: same mathematically collision calculations, except it gets overruled by the server in certain events. Like: ball misses bar or game ends.

 

Key component: getTickCount()

 

 

 

Ok, thx, maybe there is some sort of example?

  • Moderators
Posted
37 minutes ago, Sergo86 said:

Ok, thx, maybe there is some sort of example?

 

Not really, but in the past I did create an experimental tablet / mobile / desktop version of the game as web app. This is exclusive serverside multiplayer, only local multiplayer. Note: Docs is in Dutch.
Download

JavaScript

Spoiler

var gameData = {
    playerData: {
        score: {
            player1: 0,
            player2: 0,
        },
        givePlayerScore: function(player, score) {
            var self = gameData.playerData;
            self.score[player] += score;
            document.getElementById("score-" + player).getElementsByTagName("span")[0].textContent = self.score[player];
        }
    },
    gameSpeed: {
        gameSpeedMultiplier: 1,
        increaseSpeed: function () {
            var self = gameData.gameSpeed;
            self.gameSpeedMultiplier += 0.1;
        },
        resetSpeed: function () {
            var self = gameData.gameSpeed;
            self.gameSpeedMultiplier = 1;
        }
    },
    controller: {
        sensitivity: 15,
        enabled: {
            keyPress: true,
            mouseMove: true,
            pointerMove: true,
            touchMove: true
        },
        functions: {
            setControllerConfig: function (controller) {
                switch (controller) {
                    case "keyPress":
                        return true;
                    case "mouseMove":
                        return true;
                    case "pointerMove":
                        // console.log("pointerMove 222", gameData.controller.functions.isControllerEnabled("mouseMove"));
                        if (gameData.controller.functions.isControllerEnabled("mouseMove")) {
                            gameData.controller.touchAndCursorElement.removeEventListener("mousemove",gameData.controller.functions.mouseMove, false);
                        }
                        gameData.controller.enabled.mouseMove = false;

                        return true;
                    case "touchMove":
                        if (gameData.controller.functions.isControllerEnabled("mouseMove")) {
                            gameData.controller.touchAndCursorElement.removeEventListener("mousemove",gameData.controller.functions.mouseMove, false);
                        }
                        if (gameData.controller.functions.isControllerEnabled("pointerMove")) {
                            gameData.controller.touchAndCursorElement.removeEventListener("pointermove",gameData.controller.functions.pointerMove, false);
                        }

                        gameData.controller.enabled.mouseMove = false;
                        gameData.controller.enabled.pointerMove = false;
                        return true;
                    default:
                }
                return false;
            },
            isControllerEnabled: function (controller) {
                return gameData.controller.enabled[controller];
            },
            buttons: function (e) {
                var source = e.target;
                var value = source.value;
                if (value != "") {
                    var self = gameData.controller;

                    var player;
                    if (source.parentElement.id === "player-controls-1") {
                        player = "player1";
                    } else if (source.parentElement.id === "player-controls-2") {
                        player = "player2";
                    }
                    if (player != undefined) {

                        var position = self.position[player];
                        position += (value === "up" ? -self.sensitivity : self.sensitivity);

                        position = gameData.utility.setValueLimit(position, 0, 100);

                        self.position[player] = position;
                    }
                }
            },
            keyPress: function (e) {

                var keyName = e.key;

                var self = gameData.controller;

                var sensitivity = self.sensitivity;

                switch (keyName) {
                    case "ArrowUp":
                        var position = self.position.player2;
                        position -= sensitivity;
                        position = gameData.utility.setValueLimit(position, 0, 100);

                        self.position.player2 = position;
                        break;
                    case "ArrowDown":
                        var position = self.position.player2;
                        position += sensitivity;
                        position = gameData.utility.setValueLimit(position, 0, 100);
                        self.position.player2 = position;
                        break;
                    case "w":
                        var position = self.position.player1;
                        position -= sensitivity;
                        position = gameData.utility.setValueLimit(position, 0, 100);
                        self.position.player1 = position;

                        break;
                    case "s":
                        var position = self.position.player1;
                        position += sensitivity;
                        position = gameData.utility.setValueLimit(position, 0, 100);
                        self.position.player1 = position;
                        break;
                }
            },
            mouseMove: function (e) {
                var source = e.target;

                if (gameData.controller.functions.isControllerEnabled("mouseMove")) {
                    gameData.controller.functions.setControllerConfig("mouseMove");
                }


                var cursorPositionY = e.clientY - source.getBoundingClientRect().top;
                var cursorPositionX = e.clientX - source.getBoundingClientRect().left;
                // https://stackoverflow.com/questions/3234256/find-mouse-position-relative-to-element

                if (cursorPositionY > 0 && cursorPositionX > 0) {
                    var self = gameData.controller;
                    var canvasData = gameData.canvas.data;
                    var sizeScaleFactor = canvasData.size / 640;
                    var barHeight = 100 * sizeScaleFactor;

                    var position = ((cursorPositionY - (barHeight / 2)) / (canvasData.size - barHeight)) * 100;

                    position = gameData.utility.setValueLimit(position, 0, 100);

                    self.position.player1 = position;
                    self.position.player2 = position;
                }
            },
            pointerMove: function (e) {
                var source = e.target;

                if (gameData.controller.functions.isControllerEnabled("pointerMove")) {
                    gameData.controller.functions.setControllerConfig("pointerMove");
                }

                gameData.controller.functions.touchMoveDetection(source, e.clientX, e.clientY);

                e.preventDefault();
            },
            touchMove: function (e) {
                var source = e.target;

                if (gameData.controller.functions.isControllerEnabled("touchMove")) {
                    gameData.controller.functions.setControllerConfig("touchMove");
                }

                // max 2 fingers
                for (var i = 0; i < Math.min(2, e.targetTouches.length); i++ ) {
                    var clientY = parseInt(e.targetTouches[i].clientY);
                    var clientX = parseInt(e.targetTouches[i].clientX);
                    gameData.controller.functions.touchMoveDetection(source, clientX, clientY);
                }
                e.preventDefault();
            },
            touchMoveDetection: function (source, clientX, clientY) {

                if (clientY != undefined && clientX != undefined) {
                    var self = gameData.controller;
                    var canvasData = gameData.canvas.data;

                    var cursorPositionY = clientY - source.getBoundingClientRect().top;
                    var cursorPositionX = clientX - source.getBoundingClientRect().left;

                    if (cursorPositionY > 0 && cursorPositionY < canvasData.size) {

                        var sizeScaleFactor = canvasData.size / 640;
                        var barHeight = 100 * sizeScaleFactor;


                        var position = ((cursorPositionY - (barHeight / 2)) / (canvasData.size - barHeight)) * 100;


                        position = gameData.utility.setValueLimit(position, 0, 100);
                        if (cursorPositionX < canvasData.size / 2) {
                            self.position.player1 = position;
                        } else {
                            self.position.player2 = position;
                        }
                    }
                }
            }
        },
        position: {
            player1: 100,
            player2: 100
        }
    },
    canvas: {
        data: {
            components: {
                player1: {
                    position: 100
                },
                player2: {
                    position: 100
                },
                ball: {
                    position: {
                        x: 50,
                        y: 50
                    },
                    velocity: {
                        x: (1 - Math.random() * 2) > 0 ? 0.5 : -0.5,
                        y: (1 - Math.random() * 2) > 0 ? 0.5 : -0.5
                    }
                }
            }
        },

        render: {
            colors: {
                components:{
                    ball: "black",
                    barLeft: "black",
                    barRight: "black",
                },
                setComponentColor: function (component, color) {
                    var self = gameData.canvas.render.colors;
                    self.components[component] = color;
                    return true;
                },
                getComponentColor: function (component, color) {
                    var self = gameData.canvas.render.colors;
                    return self.components[component];
                }
            },

            func: function (timeStamp) {



                var speedFactor = 1;
                if (gameData.canvas.render.lastTimeStamp != undefined) {
                    speedFactor = (timeStamp - gameData.canvas.render.lastTimeStamp) / 17;
                }

                var colors = gameData.canvas.render.colors;


                gameData.canvas.render.lastTimeStamp = timeStamp;

                var canvasData = gameData.canvas.data;

                var context = gameData.canvas.element.getContext('2d');

                var sizeScaleFactor = canvasData.size / 640;



                var ball = canvasData.components.ball;

                var ballRadius = 10 * sizeScaleFactor;
                var ballRadiusPercentage = ballRadius * 100 / canvasData.size;

                var ballPosition = ball.position;
                var ballVelocity = ball.velocity;

                ballPosition.x = ballPosition.x + (ballVelocity.x * speedFactor * gameData.gameSpeed.gameSpeedMultiplier);
                ballPosition.y = ballPosition.y + (ballVelocity.y * speedFactor * gameData.gameSpeed.gameSpeedMultiplier);

                var ballAbsolutePositionX = ballPosition.x / 100 * canvasData.size;
                var ballAbsolutePositionY = ballPosition.y / 100 * canvasData.size;


                var barWidth = 20 * sizeScaleFactor;
                var barHeight = 100 * sizeScaleFactor;


                context.fillStyle = "white";

                context.strokeStyle = "black";

                context.fillRect(0, 0, canvasData.size, canvasData.size);
                //
                context.strokeRect(1, 1, canvasData.size - 2, canvasData.size - 2);

                context.fillStyle = colors.getComponentColor("barLeft");

                context.fillRect(0, 0, barWidth, canvasData.size);

                context.fillStyle = colors.getComponentColor("barRight");

                context.fillRect(canvasData.size - barWidth, 0, canvasData.size, canvasData.size);






                // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc
                context.fillStyle = "orange";

                var barSpeed = speedFactor * 1.7;
                var players = ["player1", "player2"];
                for (var i = 0; i < players.length; i++) {
                    var player = players[i];
                    var controllerPosition = gameData.controller.position[player];

                    var playerComponent = canvasData.components[player];
                    var componentPosition = playerComponent.position;

                    if (controllerPosition > componentPosition) {
                        componentPosition = componentPosition + barSpeed;
                        if (componentPosition > controllerPosition) {
                            componentPosition = controllerPosition;
                        }

                    } else if (controllerPosition < componentPosition) {
                        componentPosition = componentPosition - barSpeed;
                        if (componentPosition < controllerPosition) {
                            componentPosition = controllerPosition;
                        }
                    }

                    canvasData.components[player].position = componentPosition;

                    context.fillRect(i * (canvasData.size - barWidth), (canvasData.size - barHeight) * (componentPosition / 100) ,  barWidth, barHeight);
                }

                var sideOffsetX = barWidth / canvasData.size * 100;
                var barDetectionArea = barHeight / canvasData.size * 100;

                if (ballPosition.x < sideOffsetX || ballPosition.x > 100 - sideOffsetX) {
                    if (ballPosition.x < sideOffsetX) {
                        ballPosition.x = sideOffsetX;
                    } else {
                        ballPosition.x = 100 - sideOffsetX;
                    }

                    ballVelocity.x = -ballVelocity.x;



                    if (ballPosition.x == sideOffsetX) {
                        var playerComponent = canvasData.components.player1;
                        var componentPosition = playerComponent.position;

                        componentPosition = (100 - barDetectionArea) / 100 * componentPosition;

                        if (ballPosition.y + (ballRadiusPercentage / 2) > componentPosition && ballPosition.y - ballRadiusPercentage < componentPosition + barDetectionArea) {
                            gameData.gameSpeed.increaseSpeed();

                            colors.setComponentColor("ball", "orange");
                            setTimeout(colors.setComponentColor, 300, "ball", "black");
                        } else {

                            gameData.playerData.givePlayerScore("player2", 1);

                            ballPosition.x = 50;
                            ballPosition.y = 50;
                            gameData.gameSpeed.resetSpeed();

                            colors.setComponentColor("barLeft", "red");
                            setTimeout(colors.setComponentColor, 300, "barLeft", "black");
                        }
                    } else if (ballPosition.x == 100 - sideOffsetX) {

                        var playerComponent = canvasData.components.player2;
                        var componentPosition = playerComponent.position;

                        componentPosition = (100 - barDetectionArea) / 100 * componentPosition;

                        if (ballPosition.y + (ballRadiusPercentage / 2) > componentPosition && ballPosition.y - ballRadiusPercentage < componentPosition + barDetectionArea) {
                            gameData.gameSpeed.increaseSpeed();

                            colors.setComponentColor("ball", "orange");
                            setTimeout(colors.setComponentColor, 300, "ball", "black");
                        } else {
                            gameData.playerData.givePlayerScore("player1", 1);

                            ballPosition.x = 50;
                            ballPosition.y = 50;

                            gameData.gameSpeed.resetSpeed();

                            colors.setComponentColor("barRight", "red");
                            setTimeout(colors.setComponentColor, 300, "barRight", "black");
                        }
                    }
                } else if (ballPosition.y < 0 || ballPosition.y > 100) {
                    if (ballPosition.y < 0) {
                        ballPosition.y = 0;
                    } else {
                        ballPosition.y = 100;
                    }
                    ballVelocity.y = -ballVelocity.y;
                }



                context.fillStyle = colors.getComponentColor("ball");

                context.beginPath();
                context.arc(ballRadius + (canvasData.size - ballRadius * 2) * (ballPosition.x / 100), ballRadius + (canvasData.size - ballRadius  * 2) * (ballPosition.y / 100), ballRadius, 0, 2 * Math.PI);
                context.fill();
                // context.stroke();
                if ("requestAnimationFrame" in window) {
                    gameData.canvas.render.animationFrameRequest = window.requestAnimationFrame(gameData.canvas.render.func);
                } else {
                    gameData.canvas.render.animationTimer = setTimeout(gameData.canvas.render.func, 30, new Date().getTime());
                }
            },
            start: function () {
                if (this.animationFrameRequest == undefined) {
                    // this.lastTimeStamp = new Date().getTime();
                    if ("requestAnimationFrame" in window) {
                        this.animationFrameRequest= window.requestAnimationFrame(this.func);
                    } else {
                        gameData.canvas.render.animationTimer = setTimeout(gameData.canvas.render.func, 30, new Date().getTime());
                    }
                }
            },
            stop: function () {
                if (this.animationFrameRequest != undefined) {
                    window.cancelAnimationFrame(this.animationFrameRequest);
                    delete this.animationFrameRequest;
                } else if (this.animationTimer != undefined) {
                    clearTimeout(this.animationTimer);
                    delete this.animationTimer;
                }
            }

        }
    },
    utility: {
        setValueLimit: function (value, minValue, maxValue) {
            if (value < minValue) {
                value = minValue;
            } else if (value > maxValue )  {
                value = maxValue;
            }
            return value;
        }
    }
};
var gamePreview = document.getElementById("game-preview");

if ("addEventListener" in window) {
    window.addEventListener("load", function () {




        var canvasElement = document.createElement("canvas");
        if ("getContext" in canvasElement) {

            if (gamePreview != undefined) {
                gamePreview.parentElement.removeChild(gamePreview);
            }

            var mainElement = document.getElementsByTagName("main")[0];
            var domRect = mainElement.getBoundingClientRect();
            if (domRect.width != undefined) {
                canvasElement.width = domRect.width * 0.7;
                canvasElement.height = domRect.width * 0.7;
            } else { // IE 8
                canvasElement.width = 600;
                canvasElement.height = 600;
            }

            gameData.canvas.data.size = canvasElement.width;

            gameData.canvas.element = canvasElement;


            // Controller section
            var controlsSectionElement = document.createElement("section");
            controlsSectionElement.id = "controls";

            // Add a div for styling
            var controlsContainerElement = document.createElement("div");

            var controlsHeadingElement = document.createElement("h2");
            controlsHeadingElement.textContent = "Controls";
            controlsSectionElement.appendChild(controlsHeadingElement);

            // Buttons per player.
            var buttonsData = [
                {
                    value: "up",
                    nodeValue: "up"
                },
                {
                    value: "down",
                    nodeValue: "down"
                }
            ];

            var keyData = [
                ["w", "s"],
                ["Arrow up", "Arrow down"]
            ];

            // Make the controller content info
            for (var i = 0; i < 2; i++) {
                var playerSectionElement = document.createElement("section");

                // Controls section headings
                var headingElement = document.createElement("h3");
                headingElement.textContent = "Player" + (i + 1);
                playerSectionElement.appendChild(headingElement);
                playerSectionElement.id = "player-controls-" + (i + 1);
                // Make the buttons
                for (var j = 0; j < buttonsData.length; j++) {
                    var buttonElement = document.createElement("button");
                    var buttonData = buttonsData[j];
                    buttonElement.addEventListener("click", gameData.controller.functions.buttons);

                    // Set the properties
                    buttonElement.value = buttonData.value;
                    buttonElement.textContent = buttonData.nodeValue;

                    playerSectionElement.appendChild(buttonElement);
                }

                var keyInfoElement = document.createElement("p");
                keyInfoElement.textContent = keyData[i].reduce(function (accumulator, currentValue, index) {
                    return accumulator + currentValue + (keyData[i].length - 1 != index ? ", " : "");
                }, "Keys: ");


                playerSectionElement.appendChild(keyInfoElement);
                controlsContainerElement.appendChild(playerSectionElement);
            }

            controlsSectionElement.appendChild(controlsContainerElement);

            // Player 1 score
            var scorePlayer1Element = document.createElement("p");
            scorePlayer1Element.textContent = "Score player1: ";
            var scoreIndicatorPlayer1Element = document.createElement("span");
            scoreIndicatorPlayer1Element.textContent = "0";
            scorePlayer1Element.appendChild(scoreIndicatorPlayer1Element);
            scorePlayer1Element.id = "score-player1";
            mainElement.appendChild(scorePlayer1Element);

            // Player 2 score
            var scorePlayer2Element = document.createElement("p");
            scorePlayer2Element.textContent = "Score player2: ";
            var scoreIndicatorPlayer2Element = document.createElement("span");
            scoreIndicatorPlayer2Element.textContent = "0";
            scorePlayer2Element.appendChild(scoreIndicatorPlayer2Element);
            scorePlayer2Element.id = "score-player2";
            mainElement.appendChild(scorePlayer2Element);

            // Touch and cursor area
            var touchAndCursorElement = document.createElement("div");
            touchAndCursorElement.appendChild(canvasElement);
            touchAndCursorElement.id = "touch-and-cursor-element";
            mainElement.appendChild(touchAndCursorElement);

            mainElement.appendChild(controlsSectionElement);
            gameData.canvas.render.start();


            document.addEventListener("keypress",gameData.controller.functions.keyPress);
            touchAndCursorElement.addEventListener("mousemove",gameData.controller.functions.mouseMove, false);
            touchAndCursorElement.addEventListener("touchmove",gameData.controller.functions.touchMove, false);
            touchAndCursorElement.addEventListener("pointermove",gameData.controller.functions.pointerMove, false);

            gameData.controller.touchAndCursorElement = touchAndCursorElement;
        } else {
            gamePreview.getElementsByTagName("p")[0].textContent = "Canvas is unfortunately not supported by your browser, which is required for the game.";
        }
    });
} else {
    gamePreview.getElementsByTagName("p")[0].textContent = "Your browser doesn't support the basic JavaScript functionality to play this game.";
}

 

 

 

 

  • Like 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...