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 {
border-radius: 5px;
border-width: 5px;
border-style: solid;
background-color: #1b744a;
border-color: #aaffd8;
background-color: #3f0d76;
border-color: #d2ccf3;
text-align: center;
line-height: 10vh;
font-size: 5vh;
......@@ -53,9 +53,9 @@ body {
#startButton:hover {
cursor: pointer;
background-color: #aaffd8;
border-color: #1b744a;
color: #1b744a;
background-color: #d2ccf3;
border-color: #3f0d76;
color: #3f0d76;
}
canvas {
......
var Config = {
GAZE_SERVER_URL: "ws://localhost:8001/gaze",
TARGET_FPS: 120,
SHOW_FPS: true,
TARGET_FPS: 60,
SHOW_DEBUG_INFO: true,
USE_MOUSE_INPUT_AS_FAKE_GAZE_DATA: true,
USE_LOGGER: true,
SCREEN_WIDTH: screen.width,
SCREEN_HEIGHT: screen.height,
GAME_VERSION: 0.1,
};
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",
DEFAULT_GAZE_POINT_RADIUS = 15,
DEFAULT_GAZE_POINT_COLOR = "#ff007b",
FPS_FONT = "16px ShareTech",
FPS_COLOR = "#ffffff",
MAX_GAZE_POINT_AGE = 500;
var lastUpdate,
lastDelta,
fpsBuffer = [];
class GameManger {
constructor() {
this.gazePoints = new Map();
}
setCanvas(canvas) {
this.canvas = canvas;
this.context = this.canvas.getContext("2d", { alpha: false });
this.canvas.style.backgroundColor = BACKGROUND_COLOR;
}
import Logger from "../utils/Logger.js";
import Config from "./GameConfig.js";
import Time from "../utils/Time.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();
}
setFrameRate(fps) {
this.fps = fps;
}
function getCurrentState(now) {
return {
delta: now - lastUpdate,
frameRate: options.frameRate,
enemies: enemies,
gazePoints: gazePoints,
debug: options.debug,
};
}
showFPS() {
this.shouldDisplayFrameRate = true;
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
}
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() {
let targetFrameTime = 1000 / this.fps;
this.setUpdateTime();
this.loop = setInterval(this.onTick.bind(this), targetFrameTime);
}
function onEnemyHitsTarget(damage) {
Logger.log(`Enemy hit target for ${damage} dmg`, "Game");
}
setUpdateTime() {
lastUpdate = Date.now();
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;
}
setDelta() {
lastDelta = Date.now() - lastUpdate;
}
addGazePoint(point) {
this.gazePoints.set(point.id, point);
if (lastIndexForRemoval !== -1) {
gazePoints.splice(0, lastIndexForRemoval + 1);
}
}
removeGazePoint(point) {
this.gazePoints.delete(point.id);
}
class GameManager {
onTick() {
this.setDelta();
this.updateGazePoints();
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawGazePoints();
if (this.shouldDisplayFrameRate === true) {
this.drawFrameRate();
}
this.setUpdateTime();
constructor() {
enemies = [];
gazePoints = [];
lastEnemySpawn = 0;
}
updateGazePoints() {
let now = Date.now();
for (let item of this.gazePoints) {
let point = item[1];
if (now - point.createdAt > MAX_GAZE_POINT_AGE) {
this.removeGazePoint(point);
}
}
setOptions(gameOptions) {
options = gameOptions;
gameArea = {
width: options.width,
height: options.height,
center: {
x: options.width / 2,
y: options.height / 2,
},
};
}
drawGazePoints() {
let now = Date.now();
this.context.fillStyle = DEFAULT_GAZE_POINT_COLOR;
for (let item of this.gazePoints) {
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();
}
start() {
GameRenderer.init(options.canvas, options.width, options.height, options.version);
lastUpdate = Date.now();
gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / options.frameRate));
}
drawFrameRate() {
let fps = parseInt(1000 / lastDelta),
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();
addGazePoint(point) {
gazePoints.push(point);
}
}
export default GameManger;
\ No newline at end of file
export default new GameManager();
\ 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 GameManager from "./GameManager.js";
var canvas, gm, provider;
var canvas, provider;
function init(config) {
Logger.log("Starting StarGazer game");
canvas = config.canvas;
gm = new GameManager();
gm.setCanvas(canvas);
gm.setFrameRate(config.fps);
if (config.showFPS === true) {
gm.showFPS();
}
gm.setSize(config.width, config.height);
gm.start();
provider = config.gazeDataProvider;
function init(options) {
Logger.log("Starting StarGazer game", "Game");
canvas = options.canvas;
GameManager.setOptions({
canvas: canvas,
frameRate: options.fps,
version: options.version,
debug: options.showDebug,
width: options.width,
height: options.height,
});
GameManager.start();
provider = options.gazeDataProvider;
provider.addEventListener("dataavailable", onGazeUpdate);
}
......@@ -22,7 +23,7 @@ function onGazeUpdate(event) {
let gazePoint = event.data;
gazePoint.linkTo(canvas);
if (gazePoint.hasLink) {
gm.addGazePoint(gazePoint);
GameManager.addGazePoint(gazePoint);
}
}
......
......@@ -20,7 +20,8 @@ function prepareGame() {
StarGazer.init({
canvas: canvas,
fps: Config.TARGET_FPS,
showFPS: Config.SHOW_FPS,
version: `Star Gazer, build ${Config.GAME_VERSION}`,
showDebug: Config.SHOW_DEBUG_INFO,
width: Config.SCREEN_WIDTH,
height: Config.SCREEN_HEIGHT,
gazeDataProvider: dataProvider,
......
......@@ -14,11 +14,24 @@ class Logger {
this.enabled = false;
}
log(msg) {
if(this.enabled === false) {
log(msg, label) {
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;
}
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