From 6e825baf059ec4baa1adb34ac8b6be8fe644a523 Mon Sep 17 00:00:00 2001 From: Alexander Bazo Date: Thu, 10 Oct 2019 09:53:09 +0200 Subject: [PATCH] Add game objects and server side simulation --- .eslintrc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ .jsbeautifyrc | 26 ++++++++++++++++++++++++++ index.js | 43 ++++++++++++++++++++++++++----------------- lib/config/GameConfig.js | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/config/ServerConfig.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/game/Enemy.js | 27 ++++++++++++++++----------- lib/game/GameObject.js | 6 ++---- lib/game/GazePoint.js | 23 +++++++++++++++++++++++ lib/game/Planet.js | 23 +++++++++++++++++++++++ lib/game/StarGazerState.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- lib/utils/Logger.js | 37 +++++++++++++++++++++++++++++++++++++ 11 files changed, 449 insertions(+), 55 deletions(-) create mode 100755 .eslintrc create mode 100755 .jsbeautifyrc create mode 100644 lib/config/GameConfig.js create mode 100644 lib/config/ServerConfig.js create mode 100644 lib/game/GazePoint.js create mode 100644 lib/game/Planet.js create mode 100644 lib/utils/Logger.js diff --git a/.eslintrc b/.eslintrc new file mode 100755 index 0000000..45054ee --- /dev/null +++ b/.eslintrc @@ -0,0 +1,51 @@ +{ + "parserOptions": { + "sourceType": "module" + }, + "rules": + { + "block-scoped-var": "error", + "camelcase": "warn", + "comma-dangle": ["error", "always-multiline"], + "consistent-return": "error", + "curly": "error", + "default-case": "error", + "eqeqeq": "error", + "guard-for-in": "error", + "no-alert": "error", + "no-console": "warn", + "no-else-return": "error", + "no-empty-function": "error", + "no-eval": "error", + "no-eq-null": "error", + "no-extend-native": "warn", + "no-extra-bind": "error", + "no-loop-func": "error", + "no-magic-numbers": ["warn", {"ignore": [0, 1, -1], "ignoreArrayIndexes": true}], + "no-multiple-empty-lines": ["warn", {"max": 1}], + "no-multi-spaces": "warn", + "no-new": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-unused-expressions": "error", + "no-useless-concat": "error", + "no-undef-init": "error", + "no-underscore-dangle": "error", + "no-param-reassign": "error", + "no-self-compare": "error", + "no-void": "error", + "no-with": "error", + "one-var": "error", + "quotes": ["warn", "double"], + "semi": "error", + "strict": "error", + "vars-on-top": "error", + "yoda": "error" + }, + "env": + { + "es6": true, + "browser": true + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/.jsbeautifyrc b/.jsbeautifyrc new file mode 100755 index 0000000..c4e26c7 --- /dev/null +++ b/.jsbeautifyrc @@ -0,0 +1,26 @@ +{ +// Details: https://github.com/victorporof/Sublime-HTMLPrettify#using-your-own-jsbeautifyrc-options +"js": { + "allowed_file_extensions": ["js", "json", "jshintrc", "jsbeautifyrc"], + "brace_style": "collapse-preserve-inline", + "break_chained_methods": false, + "e4x": false, + "eol": "\n", + "end_with_newline": false, + "indent_char": " ", + "indent_level": 0, + "indent_size": 2, + "indent_with_tabs": false, + "jslint_happy": false, + "keep_array_indentation": false, + "keep_function_indentation": false, + "max_preserve_newlines": 0, + "preserve_newlines": true, + "space_after_anon_function": false, + "space_before_conditional": true, + "space_in_empty_paren": false, + "space_in_paren": false, + "unescape_strings": false, + "wrap_line_length": 80 + } +} \ No newline at end of file diff --git a/index.js b/index.js index 9d4d853..0bd7c56 100644 --- a/index.js +++ b/index.js @@ -1,26 +1,35 @@ -const http = require("http"); -const express = require("express"); -const cors = require("cors"); -const colyseus = require("colyseus"); -const monitor = require("@colyseus/monitor").monitor; -const socialRoutes = require("@colyseus/social/express").default; -const port = process.env.PORT || 2567; -const app = express(); -const StarGazerRoom = require("./lib/rooms/StarGazerRoom"); +/* eslint-env node */ + +const http = require("http"), + express = require("express"), + cors = require("cors"), + colyseus = require("colyseus"), + monitor = require("@colyseus/monitor").monitor, + socialRoutes = require("@colyseus/social/express").default, + StarGazerRoom = require("./lib/rooms/StarGazerRoom.js"), + Logger = require("./lib/utils/Logger.js"), + Config = require("./lib/config/ServerConfig.js"); + +var app = express(), + server = http.createServer(app), + gameServer; + +Logger.enable(); app.use(cors()); app.use(express.json()); -const server = http.createServer(app); -const gameServer = new colyseus.Server({ +gameServer = new colyseus.Server({ server: server, express: app }); +gameServer.define(Config.getGameRoomName(), StarGazerRoom); +// Must be set after game server is created +app.use(Config.getSocialRoute(), socialRoutes); +app.use(Config.getMonitorRoute(), monitor(gameServer)); +// Prrobably shoud be set after game server is created +app.use(Config.getGameRoute(), express.static(Config.getGamePath())); -gameServer.define("star_gazer_lab", StarGazerRoom); -app.use("/", socialRoutes); -app.use("/colyseus", monitor(gameServer)); - -gameServer.listen(port); -console.log(`Listening on ws://localhost:${ port }`) +gameServer.listen(Config.getPort()); +Logger.log(`Listening on ws://localhost:${ Config.getPort() }`, "Server"); \ No newline at end of file diff --git a/lib/config/GameConfig.js b/lib/config/GameConfig.js new file mode 100644 index 0000000..c0287c1 --- /dev/null +++ b/lib/config/GameConfig.js @@ -0,0 +1,114 @@ +/* eslint-disable no-magic-numbers */ + +var version, + fps, + mapWidth, + mapHeight, + maxGazePointAge, + gazePointRadius, + maxNumberOfEnemies, + enemySpawnDelay, + playerDamage; + +class GameConfiguration { + + constructor() {} + + reset() { + version = "$VERSION"; + fps = 60; + mapWidth = 1920, + mapHeight = 1080; + maxGazePointAge = 500; + gazePointRadius = 15; + maxNumberOfEnemies = 10; + enemySpawnDelay = 500; + playerDamage = 100; + } + + setVersion(value) { + version = value; + } + + getVersion() { + return version; + } + + setFPS(value) { + fps = value; + } + + getFPS() { + return fps; + } + + setMapWidth(value) { + mapWidth = value; + } + + getMapWidth() { + return mapWidth; + } + + setMapHeight(value) { + mapHeight = value; + } + + getMapHeight() { + return mapHeight; + } + + setMaxGazePointAge(value) { + maxGazePointAge = value; + } + + getMaxGazePointAge() { + return maxGazePointAge; + } + + setGazePointRadius(value) { + gazePointRadius = value; + } + + getGazePointRadius() { + return gazePointRadius; + } + + setGazePointColor(value) { + gazePointColor = value; + } + + getGazePointColor() { + return gazePointColor; + } + + setMaxNumberOfEnemies(value) { + maxNumberOfEnemies = value; + } + + getMaxNumberOfEnemies() { + return maxNumberOfEnemies; + } + + setEnemySpawnDelay(value) { + enemySpawnDelay = value; + } + + getEnemySpawnDelay() { + return enemySpawnDelay; + } + + setPlayerDamage(value) { + playerDamage = value; + } + + getPlayerDamage() { + return playerDamage; + } + +} + +const Config = new GameConfiguration; +Config.reset(); + +module.exports = Config; \ No newline at end of file diff --git a/lib/config/ServerConfig.js b/lib/config/ServerConfig.js new file mode 100644 index 0000000..f62a443 --- /dev/null +++ b/lib/config/ServerConfig.js @@ -0,0 +1,53 @@ +/* eslint-disable no-magic-numbers */ + +var port, +socialRoute, +monitorRoute, +gameRoute, +gamePath, +gameRoomName; + +class ServerConfig { + + constructor() {} + + reset() { + port = process.env.PORT || 2567; + socialRoute = "/"; + monitorRoute = "/colyseus"; + gameRoute = "/game"; + gamePath = "../StarGazerClient/build"; + gameRoomName = "star_gazer_lab"; + } + + getPort() { + return port; + } + + getSocialRoute() { + return socialRoute; + } + + getMonitorRoute() { + return monitorRoute; + } + + getGameRoute() { + return gameRoute; + } + + getGamePath() { + return gamePath; + } + + getGameRoomName() { + return gameRoomName; + } + +} + +const Config = new ServerConfig; +Config.reset(); + +module.exports = Config; + diff --git a/lib/game/Enemy.js b/lib/game/Enemy.js index 7416b95..9589f80 100644 --- a/lib/game/Enemy.js +++ b/lib/game/Enemy.js @@ -6,32 +6,37 @@ const DEFAULT_VELOCITY = 1, DEFAULT_WIDTH = 50, DEFAULT_HEIGHT = 50, DEFAULT_DAMAGE = 10, - DEFAULT_HIT_BOX_RADIUS = 30, - DEFAULT_ENEMY_COLOR = "#3f0d76", - DEFAULT_ENEMEY_TIP_COLOR = "#d2ccf3", - DEFAULT_ENEMY_HEALTH_COLOR = "#d2ccf3", - DEFAULT_ENEMY_HEALTH_WIDTH = 10, - DEFAULT_ENEMY_HEALTH_RADIUS = 50; + DEFAULT_HIT_BOX_RADIUS = 30; class Enemy extends GameObject { - constructor(x, y, width, height, hitBoxRadius, velocity, direction, color) { + constructor(x, y, width, height, hitBoxRadius, velocity, direction) { super(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS, - DEFAULT_VELOCITY, direction, DEFAULT_ENEMY_COLOR); - this.lp = 100; + DEFAULT_VELOCITY, direction); + this.lp = DEFAULT_HEALTH; + this.damage = DEFAULT_DAMAGE; + } + + 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); + return new Enemy(xPos, yPos, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS, DEFAULT_VELOCITY, direction); } } schema.defineTypes(Enemy, { - lp: "number" + lp: "number", + damage: "number" }); diff --git a/lib/game/GameObject.js b/lib/game/GameObject.js index 95d6ecd..d8846cd 100644 --- a/lib/game/GameObject.js +++ b/lib/game/GameObject.js @@ -3,7 +3,7 @@ const Schema = schema.Schema; class GameObject extends Schema { - constructor(x, y, width, height, hitBoxRadius, velocity, direction, color) { + constructor(x, y, width, height, hitBoxRadius, velocity, direction) { super(); this.x = x; this.y = y; @@ -12,7 +12,6 @@ class GameObject extends Schema { this.hitBoxRadius = hitBoxRadius; this.velocity = velocity; this.direction = direction; - this.color = color; } update(delta) { @@ -45,8 +44,7 @@ schema.defineTypes(GameObject, { width: "number", height: "number", velocity: "number", - direction: "number", - color: "string" + direction: "number" }); module.exports = GameObject; \ No newline at end of file diff --git a/lib/game/GazePoint.js b/lib/game/GazePoint.js new file mode 100644 index 0000000..a0b52c8 --- /dev/null +++ b/lib/game/GazePoint.js @@ -0,0 +1,23 @@ +const schema = require("@colyseus/schema"); +const Schema = schema.Schema; + +class GazePoint extends Schema { + + constructor(screenX, screenY) { + this.screenX = screenX; + this.screenY = screenY; + this.createdAt = Date.now(); + this.id = this.createdAt; + } + +} + +schema.defineTypes(GazePoint, { + screenX: "number", + screenY: "number", + createdAt: "number", + player: "text", + id: "number", +}); + +module.exports = GazePoint; \ No newline at end of file diff --git a/lib/game/Planet.js b/lib/game/Planet.js new file mode 100644 index 0000000..f9d2e8a --- /dev/null +++ b/lib/game/Planet.js @@ -0,0 +1,23 @@ +const schema = require("@colyseus/schema"), + GameObject = require("./GameObject.js"); + +const DEFAULT_HEALTH = 100, + DEFAULT_HIT_BOX_RADIUS = 270, + DEFAULT_WIDTH = 500, + DEFAULT_HEIGHT = 500; + +class Planet extends GameObject { + + constructor(x, y) { + super(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_HIT_BOX_RADIUS, + 0, 0); + this.lp = DEFAULT_HEALTH; + } + +} + +schema.defineTypes(Planet, { + lp: "number" +}); + +module.exports = Planet; \ No newline at end of file diff --git a/lib/game/StarGazerState.js b/lib/game/StarGazerState.js index 8d17dbf..4ba0024 100644 --- a/lib/game/StarGazerState.js +++ b/lib/game/StarGazerState.js @@ -1,46 +1,99 @@ const schema = require("@colyseus/schema"), + Logger = require("../utils/Logger.js"), + GazePoint = require("./GazePoint.js"), Enemy = require("./Enemy.js"), + Planet = require("./Planet.js"), Schema = schema.Schema, ArraySchema = schema.ArraySchema, - MAX_ENEMIES = 10, - ENEMY_SPAWN_DELAY = 1000; - -var lastEnemySpawn; - + GameConfig = require("../config/GameConfig.js"); class StarGazerState extends Schema { constructor() { - console.log("Initalizing game state"); + Logger.log("Initalizing game state", "Game"); super(); this.stateType = "idle"; - this.width = 1920; - this.height = 1080; + this.width = GameConfig.getMapWidth(); + this.height = GameConfig.getMapHeight(); + this.gazePoints = new ArraySchema(); + this.planet = new Planet(GameConfig.getMapWidth()/2, GameConfig.getMapHeight()); this.enemies = new ArraySchema(); + this.lastUpdate = Date.now(); + this.lastEnemySpawn = 0; } update(deltaTime) { - this.spawnEnemy(); + let now = Date.now(), + delta = now - this.lastUpdate; + this.updateGazePoints(now); + this.updateEnemies(now, delta); + this.lastUpdate = Date.now(); } - spawnEnemy() { - let now = Date.now(); - if (lastEnemySpawn === undefined) { - this.addEnemy(); - lastEnemySpawn = now; - return; + updateGazePoints(now) { + for (let i = this.gazePoints.length - 1; i >= 0; i--) { + let age = now - gazePoints[i].createdAt; + if (age > GameConfig.getMaxGazePointAge()) { + this.gazePoints.splice(i, 1); + } else { + this.gazePoints[i].relativeAge = age / GameConfig.getMaxGazePointAge(); + } } - if ((now - lastEnemySpawn) > ENEMY_SPAWN_DELAY) { - this.addEnemy(); - lastEnemySpawn = now; - } - } - addEnemy() { - if (this.enemies.length < MAX_ENEMIES) { - this.enemies.push(new Enemy()); + updateEnemies(now, delta) { + let currentGazePosition = this.gazePoints[this.gazePoints.length - 1]; + if ((now - this.lastEnemySpawn) >= GameConfig.getEnemySpawnDelay()) { + if (this.enemies.length < GameConfig.getMaxNumberOfEnemies()) { + Logger.log("Add new enemy", "Game"); + this.enemies.push(Enemy.createEnemy(GameConfig.getMapWidth(), 100, this.planet.x, this.planet.y)); + this.lastEnemySpawn = now; + } } + for (let i = this.enemies.length - 1; i >= 0; i--) { + // Remove focus from enemy + this.enemies[i].removeFocus(); + // Update enemy's position + this.enemies[i].update(delta); + // Check if enemy has hit target + if (this.planet.isHitBy(this.enemies[i])) { + Logger.log(`Enemy hit target for ${this.enemies[i].damage} dmg`, "Game"); + // Reduce planet health after enemy has hit + this.planet.health -= this.enemies[i].damage; + if (this.planet.health <= 0) { + this.onWorldDestroyed(); + } + // Remove enemy after it has hit target + this.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 (this.enemies[i].isHitBy({ + x: currentGazePosition.targetX, + y: currentGazePosition.targetY, + })) { + // Set focus on currently watched enemy + this.enemies[i].setFocus(); + // Update enemy's health + this.enemies[i].health -= GameConfig.getPlayerDamage() / (1000 / delta); + // Remove enemy if destroyed + if (this.enemies[i].health <= 0) { + Logger.log(`Enemy destroyed by player`, "Game"); + this.enemies.splice(i, 1); + // TODO: Add exposion at last position + } + } + } + } + + onWorldDestroyed() { + } } @@ -48,6 +101,8 @@ schema.defineTypes(StarGazerState, { stateType: "string", width: "number", height: "number", + planet: Planet, + gazePoints: [GazePoint], enemies: [Enemy], }); diff --git a/lib/utils/Logger.js b/lib/utils/Logger.js new file mode 100644 index 0000000..9f4d549 --- /dev/null +++ b/lib/utils/Logger.js @@ -0,0 +1,37 @@ +class Logger { + + constructor() { + this.enabled = false; + } + + enable() { + this.enabled = true; + } + + disable() { + 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(`[${label}]\t${hours}:${minutes}:${seconds} - ${msg}`); + } + +} + +module.exports = new Logger(); \ No newline at end of file -- libgit2 0.26.0