diff --git a/src/app/direction.js b/src/app/direction.js new file mode 100644 index 0000000..65db384 --- /dev/null +++ b/src/app/direction.js @@ -0,0 +1,13 @@ +const Direction = Object.freeze({ + none: 0, + n: 1, + s: 2, + e: 4, + w: 8, + dirs: ['n', 's', 'e', 'w'], + dx: { e: 1, w: -1, n: 0, s: 0 }, + dy: { e: 0, w: 0, n: -1, s: 1 }, + opposite: { e: 8, w: 4, n: 2, s: 1 } +}) + +export { Direction as default } diff --git a/src/app/enemy.js b/src/app/enemy.js new file mode 100644 index 0000000..b779cba --- /dev/null +++ b/src/app/enemy.js @@ -0,0 +1,27 @@ +import BoundingRect from './bounding_rect' + +function Enemy(spec) { + const s = spec || {} + const graphics = s.graphics + const movementBehavior = s.movementBehavior + const frame = s.frame || BoundingRect(spec) + + const update = (delta) => { + movementBehavior.update(delta) + } + + const render = () => { + graphics.ctx.save() + graphics.ctx.fillStyle = 'red' + graphics.drawCircle(frame.x, frame.y, frame.width / 2) + graphics.ctx.restore() + } + + return { + frame: frame, + update: update, + render: render + } +} + +export { Enemy as default } diff --git a/src/app/hero.js b/src/app/hero.js index 2b4b13a..99b402a 100644 --- a/src/app/hero.js +++ b/src/app/hero.js @@ -6,7 +6,7 @@ function Hero(spec) { s.height = s.height || 25 const graphics = s.graphics const inputController = s.gameInputController - let frame = BoundingRect(spec) + let frame = s.rect || BoundingRect(spec) let speed = s.speed || 0.32 const update = (delta) => { diff --git a/src/app/main.js b/src/app/main.js index 8d528bc..15b0dd2 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -10,6 +10,9 @@ import MazeGenerator from './maze' import Quadtree from './quadtree' import { randomIntInRange } from './utils' import CollisionResolver from './collision_resolver' +import Enemy from './enemy' +import { RandomMovementBehavior } from './movement_behavior' +import BoundingRect from './bounding_rect' document.addEventListener('DOMContentLoaded', function() { const canvas = document.getElementById('canvas') @@ -39,6 +42,18 @@ document.addEventListener('DOMContentLoaded', function() { gameInputController: gameInputController }) + const enemyFrame = BoundingRect({ x: 40, y: 120, width: 25, height: 25 }) + const movementBehavior = RandomMovementBehavior({ + frame: enemyFrame, + targetFrame: hero.frame + }) + + const enemy = Enemy({ + graphics: graphics, + frame: enemyFrame, + movementBehavior: movementBehavior + }) + let treePadding = map.tileSize const quadtree = Quadtree({ x: -treePadding, @@ -56,12 +71,17 @@ document.addEventListener('DOMContentLoaded', function() { emitter.on('RunLoop:update', (delta) => { hero.update(delta) + enemy.update(delta) quadtree.insert(hero) + quadtree.insert(enemy) walls.forEach((wall) => { quadtree.insert(wall) }) let results = quadtree.query(hero.frame) collisionResolver.resolve(hero.frame, results) + + let enemyResults = quadtree.query(enemy.frame) + collisionResolver.resolve(enemy.frame, enemyResults) }) emitter.on('RunLoop:render', (interpolationPercentage) => { @@ -70,6 +90,7 @@ document.addEventListener('DOMContentLoaded', function() { camera.follow(hero.frame) map.render(camera.viewport) hero.render() + enemy.render() camera.end() }) diff --git a/src/app/map.js b/src/app/map.js index c419118..58e1cce 100644 --- a/src/app/map.js +++ b/src/app/map.js @@ -1,4 +1,4 @@ -import { Direction } from './maze' +import Direction from './direction' import BoundingRect from './bounding_rect' import { randomIntInRange } from './utils' diff --git a/src/app/maze.js b/src/app/maze.js index 5af6772..ac46699 100644 --- a/src/app/maze.js +++ b/src/app/maze.js @@ -1,4 +1,5 @@ import { shuffle } from './utils' +import Direction from './direction' function MazeGenerator() { const generate = (rows, cols, px, py) => { @@ -61,18 +62,4 @@ function MazeGenerator() { } } -const Direction = Object.freeze({ - n: 1, - s: 2, - e: 4, - w: 8, - dirs: ['n', 's', 'e', 'w'], - dx: { e: 1, w: -1, n: 0, s: 0 }, - dy: { e: 0, w: 0, n: -1, s: 1 }, - opposite: { e: 8, w: 4, n: 2, s: 1 } -}) - -export { - MazeGenerator as default, - Direction -} +export { MazeGenerator as default } diff --git a/src/app/movement_behavior.js b/src/app/movement_behavior.js new file mode 100644 index 0000000..b6020a6 --- /dev/null +++ b/src/app/movement_behavior.js @@ -0,0 +1,65 @@ +import Direction from './direction' +import { randomIntInRange } from './utils' + +function RandomMovementBehavior(spec) { + const s = spec || {} + const frame = s.frame + const targetFrame = s.targetFrame + let speed = s.speed || 0.31 + let lastPosition = { x: frame.x, y: frame.y } + let targetVector = { x: targetFrame.centerX(), y: targetFrame.centerY() } + let direction = s.direction || Direction.none + let randomness = s.randomness || 0.04 + let actionDistance = s.actionDistance || 250 + + const update = (delta) => { + targetVector.x = targetFrame.centerX() - frame.centerX() + targetVector.y = targetFrame.centerY() - frame.centerY() + const hypotenuse = Math.hypot(targetVector.x, targetVector.y) + if (hypotenuse < actionDistance) { + targetVector.x /= hypotenuse + targetVector.y /= hypotenuse + frame.x += targetVector.x * speed * delta + frame.y += targetVector.y * speed * delta + } else { + doRandomWalk(delta) + } + + if (frame.x === lastPosition.x && frame.y === lastPosition.y) { + direction = getRandomDirection() + } + lastPosition.x = frame.x + lastPosition.y = frame.y + } + + const doRandomWalk = (delta) => { + if (direction === Direction.none) { + direction = getRandomDirection() + } + + if ((direction & Direction.n) !== 0) { + frame.y -= delta * speed + } else if ((direction & Direction.s) !== 0) { + frame.y += delta * speed + } + if ((direction & Direction.e) !== 0) { + frame.x += delta * speed + } else if ((direction & Direction.w) !== 0) { + frame.x -= delta * speed + } + + if (Math.random() < randomness) { + direction |= getRandomDirection() + } + } + + const getRandomDirection = () => { + return Direction[Direction.dirs[randomIntInRange(0, Direction.dirs.length)]] + } + + return { + update: update + } +} + +export { RandomMovementBehavior }