Commit 80773dd4 by Alexander Bazo

Client side rendering of enemies and gaze points

parent 6160d3ed
......@@ -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 <alexanderbazo@googlemail.com>",
"license": "MIT",
......
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");
}
}
......
......@@ -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;
}
......
......@@ -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
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);
}
}
......
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);
}
}
......
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
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,
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;
......
......@@ -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({
......
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