Commit 80773dd4 by Alexander Bazo

Client side rendering of enemies and gaze points

parent 6160d3ed
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"postinstall": "node build.js", "postinstall": "node build.js",
"start": "http-server ./build -o" "start": "http-server ./build -o -c-1"
}, },
"author": "Alexander Bazo <alexanderbazo@googlemail.com>", "author": "Alexander Bazo <alexanderbazo@googlemail.com>",
"license": "MIT", "license": "MIT",
......
import Logger from "../utils/Logger.js";
import Observable from "../utils/Observable.js";
const DEFAULT_GAME_ROOM = "star_gazer_lab"; const DEFAULT_GAME_ROOM = "star_gazer_lab";
class NetClient { class NetClient extends Observable {
constructor() {
super();
}
connect(url) { connect(url) {
this.client = new Colyseus.Client('ws://localhost:2567'); this.client = new Colyseus.Client(url);
} }
joinGame() { joinGame() {
this.client.joinOrCreate(DEFAULT_GAME_ROOM).then(this.onConnect.bind(this)).catch( this.client.joinOrCreate(DEFAULT_GAME_ROOM).then(this.onConnect.bind(this))
this.onError.bind(this)); .catch(
this.onError.bind(this));
}
sendGazeData(gazeData) {
this.room.send({
type: "gaze",
data: gazeData
});
} }
onConnect(room) { onConnect(room) {
console.log(room.sessionId, "joined", room.name); Logger.log(`Player ${room.sessionId} joined ${room.name}`, "NetClient");
room.onStateChange(this.onUpdate); this.room = room;
room.onMessage(this.onMessage); this.room.onStateChange(this.onUpdate.bind(this));
room.onError(this.onError); this.room.onMessage(this.onMessage.bind(this));
room.onLeave(this.onLeave); this.room.onError(this.onError.bind(this));
this.room.onLeave(this.onLeave.bind(this));
this.notifyAll({
type: "connect",
ownID: this.room.sessionId
});
} }
onUpdate(state) { onUpdate(state) {
console.log("received new state from server"); this.notifyAll({
console.log(state); type: "stateupdate",
state: state
});
} }
onMessage(message) { onMessage(message) {
console.log("Received new message from server") Logger.log("Received new message from server", "NetClient");
} }
onError(error) { onError(error) {
...@@ -33,7 +56,7 @@ class NetClient { ...@@ -33,7 +56,7 @@ class NetClient {
} }
onLeave() { onLeave() {
console.log("Client left room") Logger.log("Local client left room", "NetClient");
} }
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
var version, var version,
gazeServerURL, gazeServerURL,
gameServerURL,
fps, fps,
fpsBufferLength, fpsBufferLength,
screenWidth, screenWidth,
...@@ -27,7 +28,8 @@ class GameConfiguration { ...@@ -27,7 +28,8 @@ class GameConfiguration {
reset() { reset() {
version = "$VERSION"; version = "$VERSION";
gazeServerURL = "ws://localhost:8001/gaze"; gazeServerURL = "ws://localhost:8001/gaze";
fps = 60; gameServerURL = "ws://localhost:2567",
fps = 60;
fpsBufferLength = 60; fpsBufferLength = 60;
screenWidth = screen.width; screenWidth = screen.width;
screenHeight = screen.height; screenHeight = screen.height;
...@@ -62,6 +64,14 @@ class GameConfiguration { ...@@ -62,6 +64,14 @@ class GameConfiguration {
return gazeServerURL; return gazeServerURL;
} }
setGameServerURL(value) {
gameServerURL = value;
}
getGameServerURL() {
return gameServerURL;
}
setFPS(value) { setFPS(value) {
fps = value; fps = value;
} }
......
...@@ -6,91 +6,28 @@ import Enemy from "./objects/Enemy.js"; ...@@ -6,91 +6,28 @@ import Enemy from "./objects/Enemy.js";
import Planet from "./objects/Planet.js"; import Planet from "./objects/Planet.js";
var gameLoop, var gameLoop,
playerID,
stage, stage,
planet, state,
enemies, lastUpdate = 0,
gazePoints,
lastUpdate,
lastEnemySpawn; lastEnemySpawn;
function onTick() { function onTick() {
let now = Date.now(), let now = Date.now(),
delta = now - lastUpdate; delta = now - lastUpdate,
updateGazePoints(now); adjustment = (Time.ONE_SECOND_IN_MS / GameConfig.getFPS()) / delta;
updateEnemies(now, delta); if (state) {
GameRenderer.render(getCurrentState(delta)); predictStateChanges();
lastUpdate = Date.now(); GameRenderer.render(state, delta)
}
lastUpdate = now;
} }
function getCurrentState(delta) {
return {
delta: delta,
objects: [...enemies, planet],
gazePoints: gazePoints,
};
}
function updateGazePoints(now) { function predictStateChanges() {
for (let i = gazePoints.length - 1; i >= 0; i--) { // TODO: Predict local changes to enemy positions
let age = now - gazePoints[i].createdAt;
if (age > GameConfig.getMaxGazePointAge()) {
gazePoints.splice(i, 1);
} else {
gazePoints[i].relativeAge = age / GameConfig.getMaxGazePointAge();
}
}
} }
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() { function onWorldDestroyed() {
Logger.log(`World destroyed [Current health ${planet.health}]`, "Game"); Logger.log(`World destroyed [Current health ${planet.health}]`, "Game");
...@@ -100,13 +37,11 @@ class GameManager { ...@@ -100,13 +37,11 @@ class GameManager {
init(canvasStage) { init(canvasStage) {
stage = canvasStage; stage = canvasStage;
enemies = []; state = null;
gazePoints = [];
planet = new Planet(GameConfig.getScreenWidth()/2, GameConfig.getScreenHeight());
lastEnemySpawn = 0;
} }
start() { start(newPlayerID) {
playerID = newPlayerID;
GameRenderer.init(stage); GameRenderer.init(stage);
lastUpdate = Date.now(); lastUpdate = Date.now();
gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / GameConfig.getFPS())); gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / GameConfig.getFPS()));
...@@ -116,6 +51,10 @@ class GameManager { ...@@ -116,6 +51,10 @@ class GameManager {
gazePoints.push(point); gazePoints.push(point);
} }
syncState(serverState) {
state = serverState;
}
} }
export default new GameManager(); export default new GameManager();
\ No newline at end of file
import GameConfig from "../config/GameConfiguration.js"; import GameConfig from "../config/GameConfiguration.js";
import Time from "../utils/Time.js"; import Time from "../utils/Time.js";
import Enemy from "./objects/Enemy.js";
var canvas, var canvas,
context, context,
fpsBuffer, fpsBuffer,
currentFPS; currentFPS,
lastDebugFrame = 0;
// TODO: Look for values to be copied and stored from GameConfig to reduce access time function draw(state, delta) {
function draw(state) { updateFPS(delta);
updateFPS(state.delta);
context.clearRect(0, 0, canvas.width, canvas.height); context.clearRect(0, 0, canvas.width, canvas.height);
drawObjects(state.objects); drawEnemies(state.enemies);
drawGazePoints(state.gazePoints); drawGazePoints(state.gazePoints);
if (GameConfig.getShowDebugInfoOnScreen() === true) { if (GameConfig.getShowDebugInfoOnScreen() === true) {
drawDebugInfo(); drawDebugInfo();
...@@ -28,9 +29,9 @@ function updateFPS(delta) { ...@@ -28,9 +29,9 @@ function updateFPS(delta) {
} }
} }
function drawObjects(objects) { function drawEnemies(enemies) {
for (let i = 0; i < objects.length; i++) { for (let i = 0; i < enemies.length; i++) {
objects[i].draw(context); Enemy.draw.call(enemies[i], context);
} }
} }
...@@ -42,12 +43,19 @@ function drawGazePoints(gazePoints) { ...@@ -42,12 +43,19 @@ function drawGazePoints(gazePoints) {
} }
function drawSingleGazePoint(gazePoint) { 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, let inversedAge = 1 - gazePoint.relativeAge,
radius = inversedAge * GameConfig.getGazePointRadius(); radius = inversedAge * GameConfig.getGazePointRadius();
context.save(); context.save();
context.globalAlpha = inversedAge; context.globalAlpha = inversedAge;
context.globalAlpha = 1;
context.beginPath(); 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); 4, 0, 2 * Math.PI);
context.closePath(); context.closePath();
context.fill(); context.fill();
...@@ -58,7 +66,8 @@ function drawDebugInfo() { ...@@ -58,7 +66,8 @@ function drawDebugInfo() {
context.beginPath(); context.beginPath();
context.font = GameConfig.getDebugInfoFont(); context.font = GameConfig.getDebugInfoFont();
context.fillStyle = GameConfig.getDebugInfoFonColor(); 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(); context.closePath();
} }
...@@ -76,8 +85,8 @@ class GameRenderer { ...@@ -76,8 +85,8 @@ class GameRenderer {
canvas.style.backgroundColor = GameConfig.getWorldBackgroundColor(); canvas.style.backgroundColor = GameConfig.getWorldBackgroundColor();
} }
render(state) { render(state, delta) {
draw(state); draw(state, delta);
} }
} }
......
import Logger from "../utils/Logger.js"; import Logger from "../utils/Logger.js";
import NetClient from "../com/NetClient.js";
import GameManager from "./GameManager.js"; import GameManager from "./GameManager.js";
var stage, provider; var stage, client, provider;
function init(options) { function init(options) {
Logger.log("Starting StarGazer game", "Game"); Logger.log("Starting StarGazer game", "Game");
stage = options.canvas; initGazeProvider(options);
GameManager.init(stage); initStage(options);
GameManager.start(); initConnection(options);
}
function initGazeProvider(options) {
provider = options.gazeDataProvider; provider = options.gazeDataProvider;
provider.addEventListener("dataavailable", onGazeUpdate); 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) { function onGazeUpdate(event) {
let gazePoint = event.data; let gazePoint = event.data;
gazePoint.linkTo(stage); gazePoint.linkTo(stage);
if (gazePoint.hasLink) { if (gazePoint.hasLink) {
GameManager.addGazePoint(gazePoint); client.sendGazeData(gazePoint);
//GameManager.addGazePoint(gazePoint);
} }
} }
......
import GameObject from "./GameObject.js";
const DEFAULT_VELOCITY = 1, const DEFAULT_VELOCITY = 1,
DEFAULT_HEALTH = 100, DEFAULT_HEALTH = 100,
DEFAULT_WIDTH = 50, DEFAULT_WIDTH = 50,
...@@ -12,17 +10,9 @@ const DEFAULT_VELOCITY = 1, ...@@ -12,17 +10,9 @@ const DEFAULT_VELOCITY = 1,
DEFAULT_ENEMY_HEALTH_WIDTH = 10, DEFAULT_ENEMY_HEALTH_WIDTH = 10,
DEFAULT_ENEMY_HEALTH_RADIUS = 50; DEFAULT_ENEMY_HEALTH_RADIUS = 50;
class Enemy extends GameObject { class Enemy {
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;
}
draw(context) { static draw(context) {
context.save(); context.save();
// Draw health bar if focused // Draw health bar if focused
context.translate(this.x, this.y); context.translate(this.x, this.y);
...@@ -36,7 +26,7 @@ class Enemy extends GameObject { ...@@ -36,7 +26,7 @@ class Enemy extends GameObject {
context.closePath(); context.closePath();
} }
// Draw enemy // Draw enemy
context.fillStyle = this.color; context.fillStyle = this.DEFAULT_ENEMY_COLOR;
context.beginPath(); context.beginPath();
context.rotate((90 + this.direction) * Math.PI / 180); context.rotate((90 + this.direction) * Math.PI / 180);
context.moveTo(0, -this.height / 2); context.moveTo(0, -this.height / 2);
...@@ -55,21 +45,6 @@ class Enemy extends GameObject { ...@@ -55,21 +45,6 @@ class Enemy extends GameObject {
context.restore(); 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; export default Enemy;
\ No newline at end of file
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
import GameObject from "./GameObject.js";
const DEFAULT_HEALTH = 100, const DEFAULT_HEALTH = 100,
DEFAULT_WIDTH = 500, DEFAULT_WIDTH = 500,
DEFAULT_HEIGHT = 500, DEFAULT_HEIGHT = 500,
...@@ -11,17 +9,9 @@ const DEFAULT_HEALTH = 100, ...@@ -11,17 +9,9 @@ const DEFAULT_HEALTH = 100,
DEFAULT_PLANET_TEXT_FONT = "32px ShareTech", DEFAULT_PLANET_TEXT_FONT = "32px ShareTech",
DEFAULT_PLANET_TEXT_COLOR = "#FFF"; DEFAULT_PLANET_TEXT_COLOR = "#FFF";
class Planet extends GameObject { class Planet {
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;
}
draw(context) { static draw(context) {
context.save(); context.save();
// Draw planet // Draw planet
context.fillStyle = this.color; context.fillStyle = this.color;
......
...@@ -3,7 +3,6 @@ import Logger from "./utils/Logger.js"; ...@@ -3,7 +3,6 @@ import Logger from "./utils/Logger.js";
import StarGazer from "./game/StarGazer.js"; import StarGazer from "./game/StarGazer.js";
import FakeGazeDataProvider from "./gaze/FakeGazeDataProvider.js"; import FakeGazeDataProvider from "./gaze/FakeGazeDataProvider.js";
import GazeDataProvider from "./gaze/GazeDataProvider.js"; import GazeDataProvider from "./gaze/GazeDataProvider.js";
import NetClient from "./com/NetClient.js";
var canvas = document.querySelector("canvas"), var canvas = document.querySelector("canvas"),
starScreen = document.querySelector("#startScreen"); starScreen = document.querySelector("#startScreen");
...@@ -27,13 +26,6 @@ function loadOptions() { ...@@ -27,13 +26,6 @@ function loadOptions() {
GameConfig.setShowVerboseDebugInfoInConsole(logToConsole); GameConfig.setShowVerboseDebugInfoInConsole(logToConsole);
} }
function testServer() {
console.log("Testing Server ...");
let client = new NetClient();
client.connect("ws://localhost:2567");
client.joinGame();
}
function prepareGame() { function prepareGame() {
loadOptions(); loadOptions();
StarGazer.init({ StarGazer.init({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment