Commit 81eef9d5 by Alexander Bazo

Add enemies and allow player to focus and destroy them

parent dc172fd6
var Config = { var Config = {
GAZE_SERVER_URL: "ws://localhost:8001/gaze", GAZE_SERVER_URL: "ws://localhost:8001/gaze",
TARGET_FPS: 120, TARGET_FPS: 60,
SHOW_DEBUG_INFO: true, SHOW_DEBUG_INFO: true,
USE_MOUSE_INPUT_AS_FAKE_GAZE_DATA: true, USE_MOUSE_INPUT_AS_FAKE_GAZE_DATA: true,
USE_LOGGER: true, USE_LOGGER: true,
......
const DEFAULT_SPEED = 1,
DEFAULT_HEALTH = 100,
DEFAULT_WIDTH = 20,
DEFAULT_HEIGHT = 20,
DEFAULT_DAMAGE = 10,
DEFAULT_HIT_BOX_RADIUS = 30;
class Enemy {
constructor(x, y, direction) {
this.x = x;
this.y = y;
this.width = DEFAULT_WIDTH;
this.height = DEFAULT_HEIGHT;
this.speed = DEFAULT_SPEED;
this.health = DEFAULT_HEALTH;
this.damage = DEFAULT_DAMAGE;
this.direction = direction;
}
update(target, adjustment) {
this.direction = Math.atan2(target.y - this.y, target.x - this.x);
this.x = this.x + Math.cos(this.direction) * this.speed * adjustment;
this.y = this.y + Math.sin(this.direction) * this.speed * adjustment;
}
isHitBy(pos) {
let distance = this.distanceTo(pos);
if (distance <= DEFAULT_HIT_BOX_RADIUS) {
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);
}
static createEnemy(area) {
let xPos = parseInt(Math.random() * area.width),
yPos = parseInt(Math.random() * area.height),
direction = Math.atan2(area.center.y - yPos, area.center.x - xPos);
return new Enemy(xPos, yPos, direction);
}
}
export default Enemy;
\ No newline at end of file
var Config = {
MAX_GAZE_POINT_AGE: 500,
MAX_NUMBER_OF_ENEMIES: 10,
TARGET_RADIUS: 100,
DEFAULT_PLAYER_DAMAGE: 5,
ENEMEY_SPAWN_DELAY: 1000,
FPS_BUFFER_LENGTH: 50,
BACKGROUND_COLOR: "#333",
DEFAULT_ENEMEY_COLOR: "#dd3939",
DEFAULT_GAZE_POINT_RADIUS: 15,
DEFAULT_GAZE_POINT_COLOR: "#4cd494",
DEBUG_POSITION_OFFSET: 10,
DEBUG_FONT: "16px ShareTech",
DEBUG_COLOR: "#ffffff",
};
Object.freeze(Config);
export default Config;
\ No newline at end of file
import Logger from "../utils/Logger.js";
import Config from "./GameConfig.js";
import Time from "../utils/Time.js";
import GameRenderer from "./GameRenderer.js"; import GameRenderer from "./GameRenderer.js";
import Enemy from "./Enemy.js";
var gameLoop,
gameArea,
lastUpdate,
lastEnemySpawn,
options,
enemies,
gazePoints;
function onTick() {
let now = Date.now(),
delta = now - lastUpdate,
adjustment = (Time.ONE_SECOND_IN_MS / options.frameRate) / delta;
updateEnemies(now, adjustment);
updateGazePoints(now);
GameRenderer.render(getCurrentState(now));
lastUpdate = Date.now();
}
function getCurrentState(now) {
return {
delta: now - lastUpdate,
frameRate: options.frameRate,
enemies: enemies,
gazePoints: gazePoints,
debug: options.debug,
};
}
function updateEnemies(now, adjustment) {
let currentGazePosition = gazePoints[gazePoints.length - 1];
if ((now - lastEnemySpawn) >= Config.ENEMEY_SPAWN_DELAY) {
if (enemies.length < Config.MAX_NUMBER_OF_ENEMIES) {
Logger.log("Add new enemy", "Game");
enemies.push(Enemy.createEnemy(gameArea));
lastEnemySpawn = now;
}
}
for (let i = enemies.length - 1; i >= 0; i--) {
// Update enemy's position
enemies[i].update(gameArea.center, adjustment);
// Check if enemy has hit target
if (enemies[i].distanceTo(gameArea.center) < Config.TARGET_RADIUS) {
onEnemyHitsTarget(enemies[i].damage);
enemies.splice(i, 1);
}
// 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,
})) {
// Update enemy's health
enemies[i].health -= Config.DEFAULT_PLAYER_DAMAGE;
// 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 onEnemyHitsTarget(damage) {
Logger.log(`Enemy hit target for ${damage} dmg`, "Game");
}
function updateGazePoints(now) {
let lastIndexForRemoval = -1;
for (let i = 0; i < gazePoints.length; i++) {
gazePoints[i].age = now - gazePoints[i].createdAt;
gazePoints[i].inversedAge = 1 - (gazePoints[i].age / Config.MAX_GAZE_POINT_AGE);
if (gazePoints[i].age > Config.MAX_GAZE_POINT_AGE) {
lastIndexForRemoval = i;
}
}
if (lastIndexForRemoval !== -1) {
gazePoints.splice(0, lastIndexForRemoval + 1);
}
}
class GameManager { class GameManager {
setOptions(options) { constructor() {
enemies = [];
gazePoints = [];
lastEnemySpawn = 0;
}
setOptions(gameOptions) {
options = gameOptions;
gameArea = {
width: options.width,
height: options.height,
center: {
x: options.width / 2,
y: options.height / 2,
},
};
}
} start() {
GameRenderer.init(options.canvas, options.width, options.height, options.version);
lastUpdate = Date.now();
gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / options.frameRate));
}
start() { addGazePoint(point) {
gazePoints.push(point);
} }
} }
......
const BACKGROUND_COLOR = "#333", import Config from "./GameConfig.js";
DEFAULT_GAZE_POINT_RADIUS = 15, import Time from "../utils/Time.js";
DEFAULT_GAZE_POINT_COLOR = "#3f0d76",
DEBUG_POSITION_OFFSET = 10,
DEBUG_FONT = "16px ShareTech",
DEBUG_COLOR = "#ffffff",
MAX_GAZE_POINT_AGE = 1000;
var lastUpdate, var canvas,
lastDelta,
fpsBuffer,
gazePoints,
version,
debug,
frameRate,
canvas,
context, context,
loop; gameVersion,
fpsBuffer,
currentFPS;
function onTick() { function draw(state) {
setDelta(); updateFPS(state.delta);
updateGazePoints();
context.clearRect(0, 0, canvas.width, canvas.height); context.clearRect(0, 0, canvas.width, canvas.height);
drawGazePoints(); drawEnemies(state.enemies);
if (debug === true) { drawGazePoints(state.gazePoints);
if (state.debug === true) {
drawDebugInfo(); drawDebugInfo();
} }
setUpdateTime();
} }
function setUpdateTime() { // Move fps callculation to manager since we need it there to use delta
lastUpdate = Date.now(); function updateFPS(delta) {
} let fps = Time.ONE_SECOND_IN_MS / delta;
fpsBuffer.push(fps);
function setDelta() { if (fpsBuffer.length === Config.FPS_BUFFER_LENGTH) {
lastDelta = Date.now() - lastUpdate; currentFPS = parseInt(fpsBuffer.reduce((a, b) => a + b, 0) /
fpsBuffer.length);
fpsBuffer.shift();
}
} }
function addGazePoint(point) { function drawEnemies(enemies) {
gazePoints.set(point.id, point); context.fillStyle = Config.DEFAULT_ENEMEY_COLOR;
for (let i = 0; i < enemies.length; i++) {
drawSingleEnemey(enemies[i]);
}
} }
function removeGazePoint(point) { function drawSingleEnemey(enemy) {
gazePoints.delete(point.id); context.save();
context.beginPath();
context.translate(enemy.x, enemy.y + enemy.height/2);
context.rotate(enemy.direction);
context.moveTo(0,-enemy.height/2);
context.lineTo(-(enemy.width/2), enemy.height/2);
context.lineTo(enemy.width/2, enemy.height/2);
context.closePath();
context.fill();
context.restore();
} }
function updateGazePoints() { function drawGazePoints(gazePoints) {
let now = Date.now(); context.fillStyle = Config.DEFAULT_GAZE_POINT_COLOR;
for (let item of gazePoints) { for (let i = 0; i < gazePoints.length; i++) {
let point = item[1]; drawSingleGazePoint(gazePoints[i]);
if (now - point.createdAt > MAX_GAZE_POINT_AGE) {
removeGazePoint(point);
}
} }
} }
function drawGazePoints() { function drawSingleGazePoint(gazePoint) {
let now = Date.now(); let radius = gazePoint.inversedAge * Config.DEFAULT_GAZE_POINT_RADIUS;
context.fillStyle = DEFAULT_GAZE_POINT_COLOR; context.save();
for (let item of gazePoints) { context.globalAlpha = gazePoint.inversedAge;
let relativeAge = 1 - ((now - item[1].createdAt) / context.beginPath();
MAX_GAZE_POINT_AGE); context.ellipse(gazePoint.targetX, gazePoint.targetY, radius, radius, Math.PI /
context.save(); 4, 0, 2 * Math.PI);
context.globalAlpha = relativeAge; context.closePath();
context.beginPath(); context.fill();
context.ellipse(item[1].targetX, item[1].targetY, context.restore();
relativeAge * DEFAULT_GAZE_POINT_RADIUS, relativeAge *
DEFAULT_GAZE_POINT_RADIUS, Math.PI / 4,
0, 2 * Math.PI);
context.fill();
context.closePath();
context.restore();
}
} }
function drawDebugInfo() { function drawDebugInfo() {
let fps = parseInt(1000 / lastDelta),
averageFps;
fpsBuffer.push(fps);
if (fpsBuffer.length === frameRate) {
averageFps = parseInt(fpsBuffer.reduce((a, b) => a + b, 0) / fpsBuffer.length);
fpsBuffer.shift();
}
context.beginPath(); context.beginPath();
context.font = DEBUG_FONT; context.font = Config.DEBUG_FONT;
context.fillStyle = DEBUG_COLOR; context.fillStyle = Config.DEBUG_COLOR;
context.fillText(`${version} | ${averageFps}fps`, DEBUG_POSITION_OFFSET, canvas.height-DEBUG_POSITION_OFFSET); context.fillText(`${gameVersion} | ${currentFPS}fps`, Config.DEBUG_POSITION_OFFSET,
canvas.height - Config.DEBUG_POSITION_OFFSET);
context.closePath(); context.closePath();
} }
class GameRenderer { class GameRenderer {
constructor() { init(gameCanvas, width, height, version) {
fpsBuffer = []; fpsBuffer = [];
gazePoints = new Map(); currentFPS = 0;
} gameVersion = version;
canvas = gameCanvas;
start(options) { canvas.width = width;
canvas = options.canvas; canvas.height = height;
canvas.width = options.width; canvas.style.width = `${width}px`;
canvas.height = options.height; canvas.style.height = `${height}px`;
canvas.style.width = `${options.width}px`;
canvas.style.height = `${options.height}px`;
context = canvas.getContext("2d", { alpha: false }); context = canvas.getContext("2d", { alpha: false });
canvas.style.backgroundColor = BACKGROUND_COLOR; canvas.style.backgroundColor = Config.BACKGROUND_COLOR;
frameRate = options.frameRate;
version = options.version;
debug = options.debug;
setUpdateTime();
loop = setInterval(onTick, (1000 / frameRate));
}
addGazePoint(point) {
addGazePoint(point);
} }
removeGazePoint(point) { render(state) {
removeGazePoint(point); draw(state);
} }
} }
......
import Logger from "../utils/Logger.js"; import Logger from "../utils/Logger.js";
import GameManager from "./GameManager.js"; import GameManager from "./GameManager.js";
import GameRenderer from "./GameRenderer.js";
var canvas, provider; var canvas, provider;
function init(options) { function init(options) {
Logger.log("Starting StarGazer game"); Logger.log("Starting StarGazer game", "Game");
canvas = options.canvas; canvas = options.canvas;
GameRenderer.start({ GameManager.setOptions({
canvas: canvas, canvas: canvas,
frameRate: options.fps, frameRate: options.fps,
version: options.version, version: options.version,
...@@ -15,6 +14,7 @@ function init(options) { ...@@ -15,6 +14,7 @@ function init(options) {
width: options.width, width: options.width,
height: options.height, height: options.height,
}); });
GameManager.start();
provider = options.gazeDataProvider; provider = options.gazeDataProvider;
provider.addEventListener("dataavailable", onGazeUpdate); provider.addEventListener("dataavailable", onGazeUpdate);
} }
...@@ -23,7 +23,7 @@ function onGazeUpdate(event) { ...@@ -23,7 +23,7 @@ function onGazeUpdate(event) {
let gazePoint = event.data; let gazePoint = event.data;
gazePoint.linkTo(canvas); gazePoint.linkTo(canvas);
if (gazePoint.hasLink) { if (gazePoint.hasLink) {
GameRenderer.addGazePoint(gazePoint); GameManager.addGazePoint(gazePoint);
} }
} }
......
...@@ -2,24 +2,37 @@ ...@@ -2,24 +2,37 @@
class Logger { class Logger {
constructor() { constructor() {
this.enabled = false; this.enabled = false;
} }
enable() { enable() {
this.enabled = true; this.enabled = true;
} }
disable() { disable() {
this.enabled = false; this.enabled = false;
} }
log(msg) { log(msg, label) {
if(this.enabled === false) { let time = new Date(),
return; hours = time.getUTCHours(),
} minutes = time.getUTCMinutes(),
console.log(msg); seconds = time.getUTCSeconds();
} if (hours < 10) {
hours = "0" + hours;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 10) {
seconds = "0" + seconds;
}
if (this.enabled === false) {
return;
}
console.log(`[${label}]\t${hours}:${minutes}:${seconds} - ${msg}`);
}
} }
......
var Time = {
ONE_SECOND_IN_MS: 1000,
};
Object.freeze(Time);
export default Time;
\ No newline at end of file
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