Commit 1889eba2 by Alexander Bazo

Merge branch 'dev' into 'master'

Add Features: Enemies

See merge request !1
parents 38964de7 81eef9d5
...@@ -43,8 +43,8 @@ body { ...@@ -43,8 +43,8 @@ body {
border-radius: 5px; border-radius: 5px;
border-width: 5px; border-width: 5px;
border-style: solid; border-style: solid;
background-color: #1b744a; background-color: #3f0d76;
border-color: #aaffd8; border-color: #d2ccf3;
text-align: center; text-align: center;
line-height: 10vh; line-height: 10vh;
font-size: 5vh; font-size: 5vh;
...@@ -53,9 +53,9 @@ body { ...@@ -53,9 +53,9 @@ body {
#startButton:hover { #startButton:hover {
cursor: pointer; cursor: pointer;
background-color: #aaffd8; background-color: #d2ccf3;
border-color: #1b744a; border-color: #3f0d76;
color: #1b744a; color: #3f0d76;
} }
canvas { canvas {
......
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_FPS: 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,
SCREEN_WIDTH: screen.width, SCREEN_WIDTH: screen.width,
SCREEN_HEIGHT: screen.height, SCREEN_HEIGHT: screen.height,
GAME_VERSION: 0.1,
}; };
Object.freeze(Config); Object.freeze(Config);
......
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
const BACKGROUND_COLOR = "#333", import Logger from "../utils/Logger.js";
DEFAULT_GAZE_POINT_RADIUS = 15, import Config from "./GameConfig.js";
DEFAULT_GAZE_POINT_COLOR = "#ff007b", import Time from "../utils/Time.js";
FPS_FONT = "16px ShareTech", import GameRenderer from "./GameRenderer.js";
FPS_COLOR = "#ffffff", import Enemy from "./Enemy.js";
MAX_GAZE_POINT_AGE = 500;
var gameLoop,
var lastUpdate, gameArea,
lastDelta, lastUpdate,
fpsBuffer = []; lastEnemySpawn,
options,
class GameManger { enemies,
gazePoints;
constructor() {
this.gazePoints = new Map(); function onTick() {
} let now = Date.now(),
delta = now - lastUpdate,
setCanvas(canvas) { adjustment = (Time.ONE_SECOND_IN_MS / options.frameRate) / delta;
this.canvas = canvas; updateEnemies(now, adjustment);
this.context = this.canvas.getContext("2d", { alpha: false }); updateGazePoints(now);
this.canvas.style.backgroundColor = BACKGROUND_COLOR; GameRenderer.render(getCurrentState(now));
} lastUpdate = Date.now();
}
setFrameRate(fps) { function getCurrentState(now) {
this.fps = fps; return {
} delta: now - lastUpdate,
frameRate: options.frameRate,
enemies: enemies,
gazePoints: gazePoints,
debug: options.debug,
};
}
showFPS() { function updateEnemies(now, adjustment) {
this.shouldDisplayFrameRate = true; 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
} }
hideFPS() {
this.shouldDisplayFrameRate = false;
} }
setSize(width, height) {
this.canvas.width = width;
this.canvas.height = height;
this.canvas.style.width = `${width}px`;
this.canvas.style.height = `${height}px`;
} }
}
start() { function onEnemyHitsTarget(damage) {
let targetFrameTime = 1000 / this.fps; Logger.log(`Enemy hit target for ${damage} dmg`, "Game");
this.setUpdateTime(); }
this.loop = setInterval(this.onTick.bind(this), targetFrameTime);
}
setUpdateTime() { function updateGazePoints(now) {
lastUpdate = Date.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;
} }
setDelta() {
lastDelta = Date.now() - lastUpdate;
} }
if (lastIndexForRemoval !== -1) {
addGazePoint(point) { gazePoints.splice(0, lastIndexForRemoval + 1);
this.gazePoints.set(point.id, point);
} }
}
removeGazePoint(point) { class GameManager {
this.gazePoints.delete(point.id);
}
onTick() { constructor() {
this.setDelta(); enemies = [];
this.updateGazePoints(); gazePoints = [];
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); lastEnemySpawn = 0;
this.drawGazePoints();
if (this.shouldDisplayFrameRate === true) {
this.drawFrameRate();
}
this.setUpdateTime();
} }
updateGazePoints() { setOptions(gameOptions) {
let now = Date.now(); options = gameOptions;
for (let item of this.gazePoints) { gameArea = {
let point = item[1]; width: options.width,
if (now - point.createdAt > MAX_GAZE_POINT_AGE) { height: options.height,
this.removeGazePoint(point); center: {
} x: options.width / 2,
} y: options.height / 2,
},
};
} }
drawGazePoints() { start() {
let now = Date.now(); GameRenderer.init(options.canvas, options.width, options.height, options.version);
this.context.fillStyle = DEFAULT_GAZE_POINT_COLOR; lastUpdate = Date.now();
for (let item of this.gazePoints) { gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / options.frameRate));
let relativeAge = 1 - ((now - item[1].createdAt) /
MAX_GAZE_POINT_AGE);
this.context.save();
this.context.globalAlpha = relativeAge;
this.context.beginPath();
this.context.ellipse(item[1].targetX, item[1].targetY,
relativeAge * DEFAULT_GAZE_POINT_RADIUS, relativeAge * DEFAULT_GAZE_POINT_RADIUS, Math.PI / 4,
0, 2 * Math.PI);
this.context.fill();
this.context.closePath();
this.context.restore();
}
} }
drawFrameRate() { addGazePoint(point) {
let fps = parseInt(1000 / lastDelta), gazePoints.push(point);
averageFps;
fpsBuffer.push(fps);
if(fpsBuffer.length === this.fps) {
averageFps = parseInt(fpsBuffer.reduce((a,b) => a + b, 0) / fpsBuffer.length);
fpsBuffer.shift();
}
this.context.beginPath();
this.context.font = FPS_FONT;
this.context.fillStyle = FPS_COLOR;
this.context.fillText(`FPS: ${averageFps}`, 30, 30);
this.context.closePath();
} }
} }
export default GameManger; export default new GameManager();
\ No newline at end of file \ No newline at end of file
import Config from "./GameConfig.js";
import Time from "../utils/Time.js";
var canvas,
context,
gameVersion,
fpsBuffer,
currentFPS;
function draw(state) {
updateFPS(state.delta);
context.clearRect(0, 0, canvas.width, canvas.height);
drawEnemies(state.enemies);
drawGazePoints(state.gazePoints);
if (state.debug === true) {
drawDebugInfo();
}
}
// Move fps callculation to manager since we need it there to use delta
function updateFPS(delta) {
let fps = Time.ONE_SECOND_IN_MS / delta;
fpsBuffer.push(fps);
if (fpsBuffer.length === Config.FPS_BUFFER_LENGTH) {
currentFPS = parseInt(fpsBuffer.reduce((a, b) => a + b, 0) /
fpsBuffer.length);
fpsBuffer.shift();
}
}
function drawEnemies(enemies) {
context.fillStyle = Config.DEFAULT_ENEMEY_COLOR;
for (let i = 0; i < enemies.length; i++) {
drawSingleEnemey(enemies[i]);
}
}
function drawSingleEnemey(enemy) {
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 drawGazePoints(gazePoints) {
context.fillStyle = Config.DEFAULT_GAZE_POINT_COLOR;
for (let i = 0; i < gazePoints.length; i++) {
drawSingleGazePoint(gazePoints[i]);
}
}
function drawSingleGazePoint(gazePoint) {
let radius = gazePoint.inversedAge * Config.DEFAULT_GAZE_POINT_RADIUS;
context.save();
context.globalAlpha = gazePoint.inversedAge;
context.beginPath();
context.ellipse(gazePoint.targetX, gazePoint.targetY, radius, radius, Math.PI /
4, 0, 2 * Math.PI);
context.closePath();
context.fill();
context.restore();
}
function drawDebugInfo() {
context.beginPath();
context.font = Config.DEBUG_FONT;
context.fillStyle = Config.DEBUG_COLOR;
context.fillText(`${gameVersion} | ${currentFPS}fps`, Config.DEBUG_POSITION_OFFSET,
canvas.height - Config.DEBUG_POSITION_OFFSET);
context.closePath();
}
class GameRenderer {
init(gameCanvas, width, height, version) {
fpsBuffer = [];
currentFPS = 0;
gameVersion = version;
canvas = gameCanvas;
canvas.width = width;
canvas.height = height;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context = canvas.getContext("2d", { alpha: false });
canvas.style.backgroundColor = Config.BACKGROUND_COLOR;
}
render(state) {
draw(state);
}
}
export default new GameRenderer();
\ No newline at end of file
import Logger from "../utils/Logger.js"; import Logger from "../utils/Logger.js";
import GameManager from "./GameManager.js"; import GameManager from "./GameManager.js";
var canvas, gm, provider; var canvas, provider;
function init(config) { function init(options) {
Logger.log("Starting StarGazer game"); Logger.log("Starting StarGazer game", "Game");
canvas = config.canvas; canvas = options.canvas;
gm = new GameManager(); GameManager.setOptions({
gm.setCanvas(canvas); canvas: canvas,
gm.setFrameRate(config.fps); frameRate: options.fps,
if (config.showFPS === true) { version: options.version,
gm.showFPS(); debug: options.showDebug,
} width: options.width,
gm.setSize(config.width, config.height); height: options.height,
gm.start(); });
provider = config.gazeDataProvider; GameManager.start();
provider = options.gazeDataProvider;
provider.addEventListener("dataavailable", onGazeUpdate); provider.addEventListener("dataavailable", onGazeUpdate);
} }
...@@ -22,7 +23,7 @@ function onGazeUpdate(event) { ...@@ -22,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) {
gm.addGazePoint(gazePoint); GameManager.addGazePoint(gazePoint);
} }
} }
......
...@@ -20,7 +20,8 @@ function prepareGame() { ...@@ -20,7 +20,8 @@ function prepareGame() {
StarGazer.init({ StarGazer.init({
canvas: canvas, canvas: canvas,
fps: Config.TARGET_FPS, fps: Config.TARGET_FPS,
showFPS: Config.SHOW_FPS, version: `Star Gazer, build ${Config.GAME_VERSION}`,
showDebug: Config.SHOW_DEBUG_INFO,
width: Config.SCREEN_WIDTH, width: Config.SCREEN_WIDTH,
height: Config.SCREEN_HEIGHT, height: Config.SCREEN_HEIGHT,
gazeDataProvider: dataProvider, gazeDataProvider: dataProvider,
......
...@@ -14,11 +14,24 @@ class Logger { ...@@ -14,11 +14,24 @@ class Logger {
this.enabled = false; this.enabled = false;
} }
log(msg) { log(msg, label) {
if(this.enabled === false) { let time = new Date(),
hours = time.getUTCHours(),
minutes = time.getUTCMinutes(),
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; return;
} }
console.log(msg); 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