From 80773dd4357a21472b17d7890262f1d522dd8340 Mon Sep 17 00:00:00 2001 From: Alexander Bazo Date: Thu, 10 Oct 2019 12:19:09 +0200 Subject: [PATCH] Client side rendering of enemies and gaze points --- package.json | 2 +- resources/js/com/NetClient.js | 49 ++++++++++++++++++++++++++++++++++++------------- resources/js/config/GameConfiguration.js | 12 +++++++++++- resources/js/game/GameManager.js | 99 +++++++++++++++++++-------------------------------------------------------------------------------- resources/js/game/GameRenderer.js | 33 +++++++++++++++++++++------------ resources/js/game/StarGazer.js | 37 ++++++++++++++++++++++++++++++++----- resources/js/game/objects/Enemy.js | 31 +++---------------------------- resources/js/game/objects/GameObject.js | 38 -------------------------------------- resources/js/game/objects/Planet.js | 14 ++------------ resources/js/index.js | 8 -------- 10 files changed, 125 insertions(+), 198 deletions(-) delete mode 100644 resources/js/game/objects/GameObject.js diff --git a/package.json b/package.json index e3a2037..cb1bcf7 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "postinstall": "node build.js", - "start": "http-server ./build -o" + "start": "http-server ./build -o -c-1" }, "author": "Alexander Bazo ", "license": "MIT", diff --git a/resources/js/com/NetClient.js b/resources/js/com/NetClient.js index 9fd410e..e660a10 100644 --- a/resources/js/com/NetClient.js +++ b/resources/js/com/NetClient.js @@ -1,31 +1,54 @@ +import Logger from "../utils/Logger.js"; +import Observable from "../utils/Observable.js"; + const DEFAULT_GAME_ROOM = "star_gazer_lab"; -class NetClient { +class NetClient extends Observable { + + constructor() { + super(); + } connect(url) { - this.client = new Colyseus.Client('ws://localhost:2567'); + this.client = new Colyseus.Client(url); } joinGame() { - this.client.joinOrCreate(DEFAULT_GAME_ROOM).then(this.onConnect.bind(this)).catch( - this.onError.bind(this)); + this.client.joinOrCreate(DEFAULT_GAME_ROOM).then(this.onConnect.bind(this)) + .catch( + this.onError.bind(this)); + } + + sendGazeData(gazeData) { + this.room.send({ + type: "gaze", + data: gazeData + }); } onConnect(room) { - console.log(room.sessionId, "joined", room.name); - room.onStateChange(this.onUpdate); - room.onMessage(this.onMessage); - room.onError(this.onError); - room.onLeave(this.onLeave); + Logger.log(`Player ${room.sessionId} joined ${room.name}`, "NetClient"); + this.room = room; + this.room.onStateChange(this.onUpdate.bind(this)); + this.room.onMessage(this.onMessage.bind(this)); + this.room.onError(this.onError.bind(this)); + this.room.onLeave(this.onLeave.bind(this)); + this.notifyAll({ + type: "connect", + ownID: this.room.sessionId + }); + } onUpdate(state) { - console.log("received new state from server"); - console.log(state); + this.notifyAll({ + type: "stateupdate", + state: state + }); } onMessage(message) { - console.log("Received new message from server") + Logger.log("Received new message from server", "NetClient"); } onError(error) { @@ -33,7 +56,7 @@ class NetClient { } onLeave() { - console.log("Client left room") + Logger.log("Local client left room", "NetClient"); } } diff --git a/resources/js/config/GameConfiguration.js b/resources/js/config/GameConfiguration.js index 70ea3ac..51fd910 100644 --- a/resources/js/config/GameConfiguration.js +++ b/resources/js/config/GameConfiguration.js @@ -2,6 +2,7 @@ var version, gazeServerURL, + gameServerURL, fps, fpsBufferLength, screenWidth, @@ -27,7 +28,8 @@ class GameConfiguration { reset() { version = "$VERSION"; gazeServerURL = "ws://localhost:8001/gaze"; - fps = 60; + gameServerURL = "ws://localhost:2567", + fps = 60; fpsBufferLength = 60; screenWidth = screen.width; screenHeight = screen.height; @@ -62,6 +64,14 @@ class GameConfiguration { return gazeServerURL; } + setGameServerURL(value) { + gameServerURL = value; + } + + getGameServerURL() { + return gameServerURL; + } + setFPS(value) { fps = value; } diff --git a/resources/js/game/GameManager.js b/resources/js/game/GameManager.js index 5433857..1f85617 100644 --- a/resources/js/game/GameManager.js +++ b/resources/js/game/GameManager.js @@ -6,91 +6,28 @@ import Enemy from "./objects/Enemy.js"; import Planet from "./objects/Planet.js"; var gameLoop, +playerID, stage, - planet, - enemies, - gazePoints, - lastUpdate, + state, + lastUpdate = 0, lastEnemySpawn; function onTick() { let now = Date.now(), - delta = now - lastUpdate; - updateGazePoints(now); - updateEnemies(now, delta); - GameRenderer.render(getCurrentState(delta)); - lastUpdate = Date.now(); + delta = now - lastUpdate, + adjustment = (Time.ONE_SECOND_IN_MS / GameConfig.getFPS()) / delta; + if (state) { + predictStateChanges(); + GameRenderer.render(state, delta) + } + lastUpdate = now; } -function getCurrentState(delta) { - return { - delta: delta, - objects: [...enemies, planet], - gazePoints: gazePoints, - }; -} -function updateGazePoints(now) { - for (let i = gazePoints.length - 1; i >= 0; i--) { - let age = now - gazePoints[i].createdAt; - if (age > GameConfig.getMaxGazePointAge()) { - gazePoints.splice(i, 1); - } else { - gazePoints[i].relativeAge = age / GameConfig.getMaxGazePointAge(); - } - } +function predictStateChanges() { + // TODO: Predict local changes to enemy positions } -function updateEnemies(now, delta) { - let currentGazePosition = gazePoints[gazePoints.length - 1]; - if ((now - lastEnemySpawn) >= GameConfig.getEnemySpawnDelay()) { - if (enemies.length < GameConfig.getMaxNumberOfEnemies()) { - Logger.log("Add new enemy", "Game"); - enemies.push(Enemy.createEnemy(GameConfig.getScreenWidth(), 100, planet.x, planet.y)); - lastEnemySpawn = now; - } - } - for (let i = enemies.length - 1; i >= 0; i--) { - // Remove focus from enemy - enemies[i].removeFocus(); - // Update enemy's position - enemies[i].update(delta); - // Check if enemy has hit target - if (planet.isHitBy(enemies[i])) { - Logger.log(`Enemy hit target for ${enemies[i].damage} dmg`, "Game"); - // Reduce planet health after enemy has hit - planet.health -= enemies[i].damage; - if (planet.health <= 0) { - onWorldDestroyed(); - } - // Remove enemy after it has hit target - enemies.splice(i, 1); - // Skip other checks if enemy has hit target - // TODO: Add exposion at last position - continue; - } - // Skip other checks if no gaze data is available - if (currentGazePosition === undefined) { - continue; - } - // Check if enemy was hit - if (enemies[i].isHitBy({ - x: currentGazePosition.targetX, - y: currentGazePosition.targetY, - })) { - // Set focus on currently watched enemy - enemies[i].setFocus(); - // Update enemy's health - enemies[i].health -= GameConfig.getPlayerDamage() / (1000/delta); - // Remove enemy if destroyed - if (enemies[i].health <= 0) { - Logger.log(`Enemy destroyed by player`, "Game"); - enemies.splice(i, 1); - // TODO: Add exposion at last position - } - } - } -} function onWorldDestroyed() { Logger.log(`World destroyed [Current health ${planet.health}]`, "Game"); @@ -100,13 +37,11 @@ class GameManager { init(canvasStage) { stage = canvasStage; - enemies = []; - gazePoints = []; - planet = new Planet(GameConfig.getScreenWidth()/2, GameConfig.getScreenHeight()); - lastEnemySpawn = 0; + state = null; } - start() { + start(newPlayerID) { + playerID = newPlayerID; GameRenderer.init(stage); lastUpdate = Date.now(); gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / GameConfig.getFPS())); @@ -116,6 +51,10 @@ class GameManager { gazePoints.push(point); } + syncState(serverState) { + state = serverState; + } + } export default new GameManager(); \ No newline at end of file diff --git a/resources/js/game/GameRenderer.js b/resources/js/game/GameRenderer.js index df0bd0e..7e44409 100644 --- a/resources/js/game/GameRenderer.js +++ b/resources/js/game/GameRenderer.js @@ -1,16 +1,17 @@ import GameConfig from "../config/GameConfiguration.js"; import Time from "../utils/Time.js"; +import Enemy from "./objects/Enemy.js"; var canvas, context, fpsBuffer, - currentFPS; + currentFPS, + lastDebugFrame = 0; -// TODO: Look for values to be copied and stored from GameConfig to reduce access time -function draw(state) { - updateFPS(state.delta); +function draw(state, delta) { + updateFPS(delta); context.clearRect(0, 0, canvas.width, canvas.height); - drawObjects(state.objects); + drawEnemies(state.enemies); drawGazePoints(state.gazePoints); if (GameConfig.getShowDebugInfoOnScreen() === true) { drawDebugInfo(); @@ -28,9 +29,9 @@ function updateFPS(delta) { } } -function drawObjects(objects) { - for (let i = 0; i < objects.length; i++) { - objects[i].draw(context); +function drawEnemies(enemies) { + for (let i = 0; i < enemies.length; i++) { + Enemy.draw.call(enemies[i], context); } } @@ -42,12 +43,19 @@ function drawGazePoints(gazePoints) { } function drawSingleGazePoint(gazePoint) { + if(Date.now() - lastDebugFrame > 1000) { + console.log(gazePoint); + lastDebugFrame = Date.now(); + console.log((1 - gazePoint.relativeAge) * GameConfig.getGazePointRadius()); + console.log((1 - gazePoint.relativeAge)); + } let inversedAge = 1 - gazePoint.relativeAge, radius = inversedAge * GameConfig.getGazePointRadius(); context.save(); context.globalAlpha = inversedAge; + context.globalAlpha = 1; context.beginPath(); - context.ellipse(gazePoint.targetX, gazePoint.targetY, radius, radius, Math.PI / + context.ellipse(gazePoint.screenX, gazePoint.screenY, radius, radius, Math.PI / 4, 0, 2 * Math.PI); context.closePath(); context.fill(); @@ -58,7 +66,8 @@ function drawDebugInfo() { context.beginPath(); context.font = GameConfig.getDebugInfoFont(); context.fillStyle = GameConfig.getDebugInfoFonColor(); - context.fillText(`${GameConfig.getVersion()} | ${currentFPS}fps`, GameConfig.getDebugInfoPosition().x, GameConfig.getDebugInfoPosition().y); + context.fillText(`${GameConfig.getVersion()} | ${currentFPS}fps`, GameConfig.getDebugInfoPosition() + .x, GameConfig.getDebugInfoPosition().y); context.closePath(); } @@ -76,8 +85,8 @@ class GameRenderer { canvas.style.backgroundColor = GameConfig.getWorldBackgroundColor(); } - render(state) { - draw(state); + render(state, delta) { + draw(state, delta); } } diff --git a/resources/js/game/StarGazer.js b/resources/js/game/StarGazer.js index ce1e935..7aa1d86 100644 --- a/resources/js/game/StarGazer.js +++ b/resources/js/game/StarGazer.js @@ -1,22 +1,49 @@ import Logger from "../utils/Logger.js"; +import NetClient from "../com/NetClient.js"; import GameManager from "./GameManager.js"; -var stage, provider; +var stage, client, provider; function init(options) { Logger.log("Starting StarGazer game", "Game"); - stage = options.canvas; - GameManager.init(stage); - GameManager.start(); + initGazeProvider(options); + initStage(options); + initConnection(options); +} + +function initGazeProvider(options) { provider = options.gazeDataProvider; provider.addEventListener("dataavailable", onGazeUpdate); } +function initStage(options) { + stage = options.canvas; + GameManager.init(stage); +} + +function initConnection(options) { + client = new NetClient(); + client.addEventListener("connect", onConnect); + client.addEventListener("stateupdate", onStateUpdate) + client.connect(options.gameServerURL); + client.joinGame(); +} + +function onConnect(event) { + Logger.log("Connected to game server", "Game"); + GameManager.start(event.ownID); +} + +function onStateUpdate(event) { + GameManager.syncState(event.state); +} + function onGazeUpdate(event) { let gazePoint = event.data; gazePoint.linkTo(stage); if (gazePoint.hasLink) { - GameManager.addGazePoint(gazePoint); + client.sendGazeData(gazePoint); + //GameManager.addGazePoint(gazePoint); } } diff --git a/resources/js/game/objects/Enemy.js b/resources/js/game/objects/Enemy.js index bf21c4c..6ba1e6a 100644 --- a/resources/js/game/objects/Enemy.js +++ b/resources/js/game/objects/Enemy.js @@ -1,5 +1,3 @@ -import GameObject from "./GameObject.js"; - const DEFAULT_VELOCITY = 1, DEFAULT_HEALTH = 100, DEFAULT_WIDTH = 50, @@ -12,17 +10,9 @@ const DEFAULT_VELOCITY = 1, DEFAULT_ENEMY_HEALTH_WIDTH = 10, DEFAULT_ENEMY_HEALTH_RADIUS = 50; -class Enemy extends GameObject { - - constructor(x, y, direction) { - super(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS, - DEFAULT_VELOCITY, direction, DEFAULT_ENEMY_COLOR); - this.focus = false; - this.health = DEFAULT_HEALTH; - this.damage = DEFAULT_DAMAGE; - } +class Enemy { - draw(context) { + static draw(context) { context.save(); // Draw health bar if focused context.translate(this.x, this.y); @@ -36,7 +26,7 @@ class Enemy extends GameObject { context.closePath(); } // Draw enemy - context.fillStyle = this.color; + context.fillStyle = this.DEFAULT_ENEMY_COLOR; context.beginPath(); context.rotate((90 + this.direction) * Math.PI / 180); context.moveTo(0, -this.height / 2); @@ -55,21 +45,6 @@ class Enemy extends GameObject { context.restore(); } - setFocus() { - this.focus = true; - } - - removeFocus() { - this.focus = false; - } - - static createEnemy(rangeX, rangeY, targetX, targetY) { - let xPos = parseInt(Math.random() * rangeX), - yPos = parseInt(Math.random() * rangeY), - direction = Math.atan2(targetY - yPos, targetX - xPos) * 180 / Math.PI; - return new Enemy(xPos, yPos, direction); - } - } export default Enemy; \ No newline at end of file diff --git a/resources/js/game/objects/GameObject.js b/resources/js/game/objects/GameObject.js deleted file mode 100644 index 77cc087..0000000 --- a/resources/js/game/objects/GameObject.js +++ /dev/null @@ -1,38 +0,0 @@ -class GameObject { - - constructor(x, y, width, height, hitBoxRadius, velocity, direction, color) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.hitBoxRadius = hitBoxRadius; - this.velocity = velocity; - this.direction = direction; - this.color = color; - } - - update(delta) { - if(this.velocity === 0) { - return; - } - this.x += this.velocity * Math.cos(this.direction * Math.PI / 180); - this.y += this.velocity * Math.sin(this.direction * Math.PI / 180); - } - - isHitBy(object) { - let distance = this.distanceTo(object); - if (distance <= this.hitBoxRadius) { - return true; - } - return false; - } - - distanceTo(target) { - let deltaX = Math.abs(this.x - target.x), - deltaY = Math.abs(this.y - target.y); - return Math.sqrt(deltaX * deltaX + deltaY * deltaY); - } - -} - -export default GameObject; \ No newline at end of file diff --git a/resources/js/game/objects/Planet.js b/resources/js/game/objects/Planet.js index 427f306..c4ed107 100644 --- a/resources/js/game/objects/Planet.js +++ b/resources/js/game/objects/Planet.js @@ -1,5 +1,3 @@ -import GameObject from "./GameObject.js"; - const DEFAULT_HEALTH = 100, DEFAULT_WIDTH = 500, DEFAULT_HEIGHT = 500, @@ -11,17 +9,9 @@ const DEFAULT_HEALTH = 100, DEFAULT_PLANET_TEXT_FONT = "32px ShareTech", DEFAULT_PLANET_TEXT_COLOR = "#FFF"; -class Planet extends GameObject { - - constructor(x, y) { - super(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS, - 0, 0, DEFAULT_PLANET_COLOR); - this.health = DEFAULT_HEALTH; - this.borderColor = DEFAULT_PLANET_BORDER_COLOR; - this.borderWidth = DEFAULT_PLANET_BORDER_WIDTH; - } +class Planet { - draw(context) { + static draw(context) { context.save(); // Draw planet context.fillStyle = this.color; diff --git a/resources/js/index.js b/resources/js/index.js index 881f1ac..eaf0f4e 100644 --- a/resources/js/index.js +++ b/resources/js/index.js @@ -3,7 +3,6 @@ import Logger from "./utils/Logger.js"; import StarGazer from "./game/StarGazer.js"; import FakeGazeDataProvider from "./gaze/FakeGazeDataProvider.js"; import GazeDataProvider from "./gaze/GazeDataProvider.js"; -import NetClient from "./com/NetClient.js"; var canvas = document.querySelector("canvas"), starScreen = document.querySelector("#startScreen"); @@ -27,13 +26,6 @@ function loadOptions() { GameConfig.setShowVerboseDebugInfoInConsole(logToConsole); } -function testServer() { - console.log("Testing Server ..."); - let client = new NetClient(); - client.connect("ws://localhost:2567"); - client.joinGame(); -} - function prepareGame() { loadOptions(); StarGazer.init({ -- libgit2 0.26.0