From 39f1d191810b9fbb9a6a922dd59e32a578f06618 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Wed, 25 Jan 2023 22:13:13 -0700 Subject: [PATCH] fix(pointer): correctly track objects when scene/tileEngine camera is moved (#340) --- src/scene.js | 31 +++++++++++++++++++----- src/tileEngine.js | 2 ++ test/integration/scene.spec.js | 31 ++++++++++++++++++++++++ test/integration/tileEngine.spec.js | 37 +++++++++++++++++++++++++++++ test/unit/scene.spec.js | 30 +++++++++++++++++++++++ test/unit/tileEngine.spec.js | 11 +++++++++ 6 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/scene.js b/src/scene.js index d1aff28f..30394411 100644 --- a/src/scene.js +++ b/src/scene.js @@ -1,5 +1,5 @@ import { getContext } from './core.js'; -import GameObject from './gameObject.js'; +import { GameObjectClass } from './gameObject.js'; import { on, off } from './events.js'; import { srOnlyStyle, addToDom, removeFromArray } from './utils.js'; import { collides } from './helpers.js'; @@ -156,8 +156,6 @@ class Scene { section.id = id; section.setAttribute('aria-label', name); - this.add(objects); - /** * The camera object which is used as the focal point for the scene. Defaults to to the size of the canvas with a focal point at its center. The scene will not render objects that are outside the bounds of the camera. * @@ -165,12 +163,31 @@ class Scene { * @memberof Scene * @property {GameObject} camera */ - this.camera = GameObject({ + let _this = this; + class Camera extends GameObjectClass { + set x(value) { + _this.sx = value - this.centerX; + super.x = value; + } + get x() { + return super.x; + } + set y(value) { + _this.sy = value - this.centerY; + super.y = value; + } + get y() { + return super.y; + } + } + this.camera = new Camera({ context, anchor: { x: 0.5, y: 0.5 }, render: this._rf.bind(this) }); + this.add(objects); + // i = init this._i = () => { this.context ??= getContext(); @@ -179,12 +196,12 @@ class Scene { let x = width / 2; let y = height / 2; Object.assign(this.camera, { + centerX: x, + centerY: y, x, y, width, height, - centerX: x, - centerY: y, }); if (!this._dn.isConnected) { @@ -218,6 +235,7 @@ class Scene { add(...objects) { objects.flat().map(object => { this._o.push(object); + object.parent = this; // move all objects to be in the scenes DOM node so we can // hide and show the DOM node and thus hide and show all the @@ -238,6 +256,7 @@ class Scene { remove(...objects) { objects.flat().map(object => { removeFromArray(this._o, object); + object.parent = null; getAllNodes(object).map(node => { addToDom(node, this.context); diff --git a/src/tileEngine.js b/src/tileEngine.js index e1512850..7b634e45 100644 --- a/src/tileEngine.js +++ b/src/tileEngine.js @@ -277,6 +277,7 @@ class TileEngine { add(...objects) { objects.flat().map(object => { this._o.push(object); + object.parent = this; }); } @@ -290,6 +291,7 @@ class TileEngine { remove(...objects) { objects.flat().map(object => { removeFromArray(this._o, object); + object.parent = null; }); } // @endif diff --git a/test/integration/scene.spec.js b/test/integration/scene.spec.js index 963fdd37..74e2b691 100644 --- a/test/integration/scene.spec.js +++ b/test/integration/scene.spec.js @@ -3,6 +3,9 @@ import Sprite from '../../src/sprite.js'; import TileEngine from '../../src/tileEngine.js'; import { init, getCanvas } from '../../src/core.js'; import { depthSort } from '../../src/helpers.js'; +import * as pointer from '../../src/pointer.js'; +import { noop } from '../../src/utils.js'; +import { emit } from '../../src/events.js'; describe('scene integration', () => { before(() => { @@ -64,4 +67,32 @@ describe('scene integration', () => { expect(spies[2].calledBefore(spies[1])).to.be.true; expect(spies[1].calledBefore(spies[0])).to.be.true; }); + + it('should correctly track objects with pointer when camera is moved', () => { + let pntr = pointer.initPointer({ radius: 1 }); + let object = { + x: 100, + y: 50, + width: 10, + height: 20, + render: noop + }; + let scene = Scene({ + id: 'myId', + objects: [object] + }); + pointer.track(object); + object.render(); + emit('tick'); + + pntr.x = 105; + pntr.y = 55; + expect(pointer.pointerOver(object)).to.equal(true); + + scene.camera.x += 100; + expect(pointer.pointerOver(object)).to.equal(false); + + pntr.x = 5; + expect(pointer.pointerOver(object)).to.equal(true); + }); }); diff --git a/test/integration/tileEngine.spec.js b/test/integration/tileEngine.spec.js index ca1188ce..d6d34f02 100644 --- a/test/integration/tileEngine.spec.js +++ b/test/integration/tileEngine.spec.js @@ -5,6 +5,9 @@ import { load, _reset } from '../../src/assets.js'; +import * as pointer from '../../src/pointer.js'; +import { noop } from '../../src/utils.js'; +import { emit } from '../../src/events.js'; import { init, getCanvas } from '../../src/core.js'; describe('tileEngine integration', () => { @@ -145,4 +148,38 @@ describe('tileEngine integration', () => { expect(func).to.throw(); }); + + it('should correctly track objects with pointer when camera is moved', done => { + load('/data/tileset/tileset.json', '/data/tileset/bullet.png') + .then(assets => { + let tileEngine = TileEngine(assets[0]); + let pntr = pointer.initPointer({ radius: 1 }); + let object = { + x: 100, + y: 50, + width: 10, + height: 20, + render: noop + }; + + tileEngine.add(object); + pointer.track(object); + object.render(); + emit('tick'); + + tileEngine.mapwidth = 900; + + pntr.x = 105; + pntr.y = 55; + expect(pointer.pointerOver(object)).to.equal(true); + + tileEngine.sx = 100; + expect(pointer.pointerOver(object)).to.equal(false); + + pntr.x = 5; + expect(pointer.pointerOver(object)).to.equal(true); + done(); + }) + .catch(done); + }); }); diff --git a/test/unit/scene.spec.js b/test/unit/scene.spec.js index c69dacf5..7eca15a0 100644 --- a/test/unit/scene.spec.js +++ b/test/unit/scene.spec.js @@ -289,6 +289,12 @@ describe('scene', () => { expect(scene.objects.length).to.equal(2); }); + it('should set the objects parent to the scene', () => { + let object = {}; + scene.add(object); + expect(object.parent).to.equal(scene); + }); + it('should add any objects with DOM nodes to the scenes DOM node', () => { let object = { _dn: document.createElement('div') @@ -373,6 +379,13 @@ describe('scene', () => { expect(scene.objects.length).to.equal(0); }); + it('should remove the objects parent', () => { + let object = {}; + scene.add(object); + scene.remove(object); + expect(object.parent).to.equal(null); + }); + it('should remove any objects with DOM nodes', () => { let object = { _dn: document.createElement('div') @@ -432,6 +445,15 @@ describe('scene', () => { expect(scene._dn.contains(node2)).to.be.false; expect(node1.contains(node2)).to.be.true; }); + + it('moving the camera should set the scenes sx and sy properties', () => { + let canvas = scene.context.canvas; + scene.camera.x = 10; + scene.camera.y = 20; + + expect(scene.sx).to.equal(10 - canvas.width / 2); + expect(scene.sy).to.equal(20 - canvas.height / 2); + }); }); // -------------------------------------------------- @@ -542,6 +564,14 @@ describe('scene', () => { expect(scene.camera.x).to.equal(10); expect(scene.camera.y).to.equal(10); }); + + it('should set the scenes sx and sy properties', () => { + let canvas = scene.context.canvas; + scene.lookAt({ x: 10, y: 20 }); + + expect(scene.sx).to.equal(10 - canvas.width / 2); + expect(scene.sy).to.equal(20 - canvas.height / 2); + }); }); // -------------------------------------------------- diff --git a/test/unit/tileEngine.spec.js b/test/unit/tileEngine.spec.js index e7c02c71..54d09ac6 100644 --- a/test/unit/tileEngine.spec.js +++ b/test/unit/tileEngine.spec.js @@ -988,6 +988,11 @@ describe( tileEngine.add([obj, {}]); expect(tileEngine.objects.length).to.equal(2); }); + + it('should set the objects parent to the tileEngine', () => { + tileEngine.add(obj); + expect(obj.parent).to.equal(tileEngine); + }); } else { it('should not exist', () => { expect(tileEngine.add).to.not.exist; @@ -1044,6 +1049,12 @@ describe( expect(tileEngine.objects.length).to.equal(0); }); + it('should remove the objects parent', () => { + tileEngine.add(obj); + tileEngine.remove(obj); + expect(obj.parent).to.equal(null); + }); + it('should not error if the object was not added before', () => { function fn() { tileEngine.remove(obj);