Sergo86 Posted April 11, 2020 Share Posted April 11, 2020 (edited) Hello, please tell me how to organize the synchronization of the movement of an object between two players in 3D, like this L Edited April 11, 2020 by Sergo86 Link to comment
Moderators IIYAMA Posted April 11, 2020 Moderators Share Posted April 11, 2020 @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() Link to comment
Sergo86 Posted April 11, 2020 Author Share Posted April 11, 2020 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? Link to comment
Moderators IIYAMA Posted April 11, 2020 Moderators Share Posted April 11, 2020 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."; } 1 Link to comment
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now