Commit 0d39b6c7 by Alexander Bazo

Add destroyable world object

parent 91d05c72
......@@ -16,4 +16,4 @@ Hosted versions of the game are available here:
## Building and Testing
- Run `npm install` to install dependencies and build game to `build/`
- Run `npm start` to run the game on a local test server
\ No newline at end of file
- Run `npm start` to run the game on a local test server
\ No newline at end of file
{
"name": "stargazer",
"version": "0.1.0",
"version": "0.1.1",
"description": "",
"main": "index.js",
"scripts": {
......
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
......@@ -6,7 +6,6 @@ var Config = {
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,
......
......@@ -2,52 +2,74 @@ 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";
import Enemy from "./objects/Enemy.js";
import World from "./objects/World.js";
var gameLoop,
gameArea,
lastUpdate,
lastEnemySpawn,
options,
world,
enemies,
gazePoints;
function onTick() {
let now = Date.now(),
delta = now - lastUpdate,
adjustment = (Time.ONE_SECOND_IN_MS / options.frameRate) / delta;
updateEnemies(now, adjustment);
delta = now - lastUpdate;
updateGazePoints(now);
GameRenderer.render(getCurrentState(now));
updateEnemies(now, delta);
GameRenderer.render(getCurrentState(delta));
lastUpdate = Date.now();
}
function getCurrentState(now) {
function getCurrentState(delta) {
return {
delta: now - lastUpdate,
delta: delta,
frameRate: options.frameRate,
enemies: enemies,
objects: [...enemies, world],
gazePoints: gazePoints,
debug: options.debug,
};
}
function updateEnemies(now, adjustment) {
function updateGazePoints(now) {
for (let i = gazePoints.length - 1; i >= 0; i--) {
let age = now - gazePoints[i].createdAt;
if (age > Config.MAX_GAZE_POINT_AGE) {
gazePoints.splice(i, 1);
} else {
gazePoints[i].relativeAge = age / Config.MAX_GAZE_POINT_AGE;
}
}
}
function updateEnemies(now, delta) {
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));
enemies.push(Enemy.createEnemy(gameArea.width, -50, gameArea.center.x,
gameArea.center.y));
lastEnemySpawn = now;
}
}
for (let i = enemies.length - 1; i >= 0; i--) {
// Update enemy's position
enemies[i].update(gameArea.center, adjustment);
enemies[i].update(delta);
// Check if enemy has hit target
if (enemies[i].distanceTo(gameArea.center) < Config.TARGET_RADIUS) {
onEnemyHitsTarget(enemies[i].damage);
if (world.isHitBy(enemies[i])) {
Logger.log(`Enemy hit target for ${enemies[i].damage} dmg`, "Game");
// Reduce world health after enemy has hit
world.health -= enemies[i].damage;
if (world.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) {
......@@ -70,33 +92,13 @@ function updateEnemies(now, adjustment) {
}
}
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);
}
function onWorldDestroyed() {
Logger.log(`World destroyed [Current health ${world.health}]`, "Game");
}
class GameManager {
constructor() {
enemies = [];
gazePoints = [];
lastEnemySpawn = 0;
}
setOptions(gameOptions) {
init(gameOptions) {
options = gameOptions;
gameArea = {
width: options.width,
......@@ -106,9 +108,14 @@ class GameManager {
y: options.height / 2,
},
};
enemies = [];
gazePoints = [];
world = new World(gameArea.center.x, gameArea.center.y);
lastEnemySpawn = 0;
}
start() {
GameRenderer.init(options.canvas, options.width, options.height, options.version);
lastUpdate = Date.now();
gameLoop = setInterval(onTick, (Time.ONE_SECOND_IN_MS / options.frameRate));
......
......@@ -10,7 +10,7 @@ var canvas,
function draw(state) {
updateFPS(state.delta);
context.clearRect(0, 0, canvas.width, canvas.height);
drawEnemies(state.enemies);
drawObjects(state.objects);
drawGazePoints(state.gazePoints);
if (state.debug === true) {
drawDebugInfo();
......@@ -28,26 +28,12 @@ function updateFPS(delta) {
}
}
function drawEnemies(enemies) {
context.fillStyle = Config.DEFAULT_ENEMEY_COLOR;
for (let i = 0; i < enemies.length; i++) {
drawSingleEnemey(enemies[i]);
function drawObjects(objects) {
for (let i = 0; i < objects.length; i++) {
objects[i].draw(context);
}
}
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++) {
......@@ -56,9 +42,10 @@ function drawGazePoints(gazePoints) {
}
function drawSingleGazePoint(gazePoint) {
let radius = gazePoint.inversedAge * Config.DEFAULT_GAZE_POINT_RADIUS;
let inversedAge = 1 - gazePoint.relativeAge,
radius = inversedAge * Config.DEFAULT_GAZE_POINT_RADIUS;
context.save();
context.globalAlpha = gazePoint.inversedAge;
context.globalAlpha = inversedAge;
context.beginPath();
context.ellipse(gazePoint.targetX, gazePoint.targetY, radius, radius, Math.PI /
4, 0, 2 * Math.PI);
......
......@@ -6,7 +6,7 @@ var canvas, provider;
function init(options) {
Logger.log("Starting StarGazer game", "Game");
canvas = options.canvas;
GameManager.setOptions({
GameManager.init({
canvas: canvas,
frameRate: options.fps,
version: options.version,
......
import GameObject from "./GameObject.js";
const DEFAULT_VELOCITY = 1,
DEFAULT_HEALTH = 100,
DEFAULT_WIDTH = 50,
DEFAULT_HEIGHT = 50,
DEFAULT_DAMAGE = 10,
DEFAULT_HIT_BOX_RADIUS = 30,
DEFAULT_ENEMEY_COLOR = "#dd3939";
class Enemy extends GameObject {
constructor(x, y, direction) {
super(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS,
DEFAULT_VELOCITY, direction, DEFAULT_ENEMEY_COLOR);
this.health = DEFAULT_HEALTH;
this.damage = DEFAULT_DAMAGE;
}
draw(context) {
context.save();
context.fillStyle = this.color;
context.beginPath();
context.translate(this.x, this.y);
context.rotate(this.direction * Math.PI / 180);
context.moveTo(0, -this.height / 2);
context.lineTo(-(this.width / 2), this.height / 2);
context.lineTo(this.width / 2, this.height / 2);
context.closePath();
context.fill();
context.restore();
}
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);
}
draw(context) {}
}
export default GameObject;
\ No newline at end of file
import GameObject from "./GameObject.js";
const DEFAULT_HEALTH = 100,
DEFAULT_WIDTH = 200,
DEFAULT_HEIGHT = 200,
DEFAULT_HIT_BOX_RADIUS = 100,
DEFAULT_WORLD_COLOR = "#0d396f",
DEFAULT_WORLD_BORDER_COLOR = "#dafffe",
DEFAULT_WORLD_BORDER_WIDTH = "10";
class World extends GameObject {
constructor(x, y) {
super(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS,
0, 0, DEFAULT_WORLD_COLOR);
this.health = DEFAULT_HEALTH;
this.borderColor = DEFAULT_WORLD_BORDER_COLOR;
this.borderWidth = DEFAULT_WORLD_BORDER_WIDTH;
}
draw(context) {
context.save();
context.fillStyle = this.color;
context.strokeStyle = this.borderColor;
context.lineWidth = this.borderWidth;
context.beginPath();
context.ellipse(this.x, this.y, this.width / 2, this.height / 2, Math.PI /
4, 0, 2 * Math.PI);
context.closePath();
context.fill();
context.stroke();
context.restore();
}
}
export default World;
\ 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