diff --git a/CHANGELOG.md b/CHANGELOG.md index 4575ecbf..e415e62c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added new `ex.Scene.transfer(actor)` method for transferring actors between scenes, useful if you want to only have an actor in 1 scene at a time. - Added new `ex.Material` to add custom shaders per `ex.Actor`! * This feature cant be applied using the `ex.Actor.graphics.material = material` property or by setting the material property on the `ex.ExcaliburGraphicsContext.material = material` with `.save()/.restore()` * This feature opt out of batch rendering and issues a separate draw call diff --git a/src/engine/Scene.ts b/src/engine/Scene.ts index d804a1e7..112f2edb 100644 --- a/src/engine/Scene.ts +++ b/src/engine/Scene.ts @@ -431,6 +431,10 @@ export class Scene */ public add(actor: Actor): void; + /** + * Adds an [[Entity]] to the scene, once this is done the [[Actor]] will be drawn and updated. + * @param entity The entity to add to the current scene + */ public add(entity: Entity): void; /** @@ -450,6 +454,83 @@ export class Scene } } + + + /** + * Removes a [[Timer]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param timer The Timer to transfer to the current scene + */ + public transfer(timer: Timer): void; + + /** + * Removes a [[TileMap]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param tileMap The TileMap to transfer to the current scene + */ + public transfer(tileMap: TileMap): void; + + /** + * Removes a [[Trigger]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param trigger The Trigger to transfer to the current scene + */ + public transfer(trigger: Trigger): void; + + /** + * Removes an [[Actor]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param actor The Actor to transfer to the current scene + */ + public transfer(actor: Actor): void; + + /** + * Removes an [[Entity]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param entity The Entity to transfer to the current scene + */ + public transfer(entity: Entity): void; + + /** + * Removes a [[ScreenElement]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param screenElement The ScreenElement to transfer to the current scene + */ + public transfer(screenElement: ScreenElement): void; + + /** + * Removes an [[Entity]] (Actor, TileMap, Trigger, etc) or [[Timer]] from it's current scene + * and adds it to this scene. + * + * Useful if you want to have an object be present in only 1 scene at a time. + * @param entity + */ + public transfer(entity: any): void { + let scene: Scene; + if (entity instanceof Entity && entity.scene && entity.scene !== this) { + scene = entity.scene; + entity.scene.world.remove(entity, false); + } + if (entity instanceof Timer && entity.scene) { + scene = entity.scene; + entity.scene.removeTimer(entity); + } + scene?.emit('entityremoved', { target: entity } as any); + this.add(entity); + } + /** * Removes a [[Timer]] from the current scene, it will no longer be updated. * @param timer The timer to remove to the current scene. diff --git a/src/spec/SceneSpec.ts b/src/spec/SceneSpec.ts index d152b818..d826ad87 100644 --- a/src/spec/SceneSpec.ts +++ b/src/spec/SceneSpec.ts @@ -524,6 +524,64 @@ describe('A scene', () => { expect(scene.actors.length).toBe(1); }); + it('can have actors transferred to another scene', () => { + const scene1 = new ex.Scene(); + const scene2 = new ex.Scene(); + const actor = new ex.Actor(); + + const entityRemoved = jasmine.createSpy('entityremoved'); + const entityAdded = jasmine.createSpy('entityadded'); + + scene1.on('entityremoved', entityRemoved); + scene2.on('entityadded', entityAdded); + + scene1.add(actor); + + expect(actor.scene).toBe(scene1); + expect(scene1.contains(actor)).toBeTrue(); + expect(scene2.contains(actor)).toBeFalse(); + + scene2.transfer(actor); + + expect(actor.scene).toBe(scene2); + expect(scene1.contains(actor)).toBeFalse(); + expect(scene2.contains(actor)).toBeTrue(); + + expect(entityAdded).toHaveBeenCalledTimes(1); + expect(entityRemoved).toHaveBeenCalledTimes(1); + + }); + + it('can transfer timers', () => { + const scene1 = new ex.Scene(); + const scene2 = new ex.Scene(); + const timer = new ex.Timer({ + fcn: () => { /* pass */ }, + interval: 100 + }); + + const entityRemoved = jasmine.createSpy('entityremoved'); + const entityAdded = jasmine.createSpy('entityadded'); + + scene1.on('entityremoved', entityRemoved); + scene2.on('entityadded', entityAdded); + + scene1.add(timer); + + expect(timer.scene).toBe(scene1); + expect(scene1.timers.includes(timer)).toBeTrue(); + expect(scene2.timers.includes(timer)).toBeFalse(); + + scene2.transfer(timer); + + expect(timer.scene).toBe(scene2); + expect(scene1.timers.includes(timer)).toBeFalse(); + expect(scene2.timers.includes(timer)).toBeTrue(); + + expect(entityAdded).toHaveBeenCalledTimes(1); + expect(entityRemoved).toHaveBeenCalledTimes(1); + }); + it('can have timers added and retrieved', () => { const timer = new ex.Timer({ repeats: true,