From 29e8317e17cccd9b39d3dbd02a7f3239c1c87ba2 Mon Sep 17 00:00:00 2001 From: Yorick van Klinken Date: Thu, 29 Aug 2024 14:55:42 +0200 Subject: [PATCH] Feat: [#3157] return target on trigger (#3176) Closes #3157 ## Changes: - The action callback of Trigger now returns the entity that triggered it. - General cleanup of Trigger class - `target` is now 100% separate from `filter`, because that's what the functionality described in the documentation implied (to me). - Converted all the different "default value fallbacks" to a single setup. - Trigger used `Entity` and `Actor` interchangeably. Rewrote it to be 100% Entity (Although Entity doesn't have any collision mechanics build in, so an argument could be made to go for 100% Actor) --- CHANGELOG.md | 4 ++ sandbox/tests/trigger/trigger.ts | 10 +++- src/engine/Events.ts | 8 +-- src/engine/Trigger.ts | 97 +++++++++++--------------------- 4 files changed, 50 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba1ff86db..5e3c928e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Breaking Changes +- `Trigger` API has been slightly changed: + - `action` now returns the triggering entity: `(entity: Entity) => void` + - `target` now works in conjunction with `filter` instead of overwriting it. + - `EnterTriggerEvent` and `ExitTriggerEvent` now contain a `entity: Entity` property instead of `actor: Actor` - `ex.Vector.normalize()` return zero-vector (`(0,0)`) instead of `(0,1)` when normalizing a vector with a magnitude of 0 - `ex.Gif` transparent color constructor arg is removed in favor of the built in Gif file mechanism - Remove core-js dependency, it is no longer necessary in modern browsers. Technically a breaking change for older browsers diff --git a/sandbox/tests/trigger/trigger.ts b/sandbox/tests/trigger/trigger.ts index 4f9d35b1e..576218ff5 100644 --- a/sandbox/tests/trigger/trigger.ts +++ b/sandbox/tests/trigger/trigger.ts @@ -17,12 +17,18 @@ var trigger = new ex.Trigger({ }); trigger.on('collisionstart', (evt: ex.EnterTriggerEvent) => { - evt.actor.color = ex.Color.Green; + if (evt.entity instanceof ex.Actor) { + evt.entity.color = ex.Color.Green; + } + console.log(evt); }); trigger.on('collisionend', (evt: ex.ExitTriggerEvent) => { - evt.actor.color = ex.Color.Red; + if (evt.entity instanceof ex.Actor) { + evt.entity.color = ex.Color.Red; + } + console.log(evt); }); diff --git a/src/engine/Events.ts b/src/engine/Events.ts index b945b2d58..6b9754401 100644 --- a/src/engine/Events.ts +++ b/src/engine/Events.ts @@ -665,19 +665,19 @@ export class EnterViewPortEvent extends GameEvent { } } -export class EnterTriggerEvent extends GameEvent { +export class EnterTriggerEvent extends GameEvent { constructor( public target: Trigger, - public actor: Actor + public entity: Entity ) { super(); } } -export class ExitTriggerEvent extends GameEvent { +export class ExitTriggerEvent extends GameEvent { constructor( public target: Trigger, - public actor: Actor + public entity: Entity ) { super(); } diff --git a/src/engine/Trigger.ts b/src/engine/Trigger.ts index 04fbd93d6..ae1d6f668 100644 --- a/src/engine/Trigger.ts +++ b/src/engine/Trigger.ts @@ -1,4 +1,3 @@ -import { Engine } from './Engine'; import { Vector } from './Math/vector'; import { ExitTriggerEvent, EnterTriggerEvent, CollisionEndEvent, CollisionStartEvent } from './Events'; import { CollisionType } from './Collision/CollisionType'; @@ -29,27 +28,15 @@ export interface TriggerOptions { // whether the trigger is visible or not visible: boolean; // action to take when triggered - action: () => void; - // if specified the trigger will only fire on a specific actor and overrides any filter + action: (entity: Entity) => void; + // if specified the trigger will only fire on a specific entity and overrides any filter target: Entity; - // Returns true if the triggers should fire on the collided actor - filter: (actor: Entity) => boolean; + // Returns true if the triggers should fire on the collided entity + filter: (entity: Entity) => boolean; // -1 if it should repeat forever repeat: number; } -const triggerDefaults: Partial = { - pos: Vector.Zero, - width: 10, - height: 10, - visible: false, - action: () => { - return; - }, - filter: () => true, - repeat: -1 -}; - /** * Triggers are a method of firing arbitrary code on collision. These are useful * as 'buttons', 'switches', or to trigger effects in a game. By default triggers @@ -57,78 +44,62 @@ const triggerDefaults: Partial = { */ export class Trigger extends Actor { public events = new EventEmitter(); - private _target: Entity; + public target?: Entity; /** * Action to fire when triggered by collision */ - public action: () => void = () => { - return; - }; + public action: (entity: Entity) => void; /** * Filter to add additional granularity to action dispatch, if a filter is specified the action will only fire when - * filter return true for the collided actor. + * filter return true for the collided entity. */ - public filter: (actor: Entity) => boolean = () => true; + public filter: (entity: Entity) => boolean; /** * Number of times to repeat before killing the trigger, */ - public repeat: number = -1; + public repeat: number; /** - * - * @param opts Trigger options + * @param options Trigger options */ - constructor(opts: Partial) { - super({ x: opts.pos.x, y: opts.pos.y, width: opts.width, height: opts.height }); - opts = { - ...triggerDefaults, - ...opts - }; + constructor(options: Partial) { + super({ x: options.pos?.x, y: options.pos?.y, width: options.width, height: options.height }); - this.filter = opts.filter || this.filter; - this.repeat = opts.repeat || this.repeat; - this.action = opts.action || this.action; - if (opts.target) { - this.target = opts.target; - } + this.filter = options.filter ?? (() => true); + this.repeat = options.repeat ?? -1; + this.action = options.action ?? (() => undefined); + this.target = options.target; - this.graphics.visible = opts.visible; + this.graphics.visible = options.visible ?? false; this.body.collisionType = CollisionType.Passive; - this.events.on('collisionstart', (evt: CollisionStartEvent) => { - if (this.filter(evt.other)) { - this.events.emit('enter', new EnterTriggerEvent(this, evt.other)); - this._dispatchAction(); - // remove trigger if its done, -1 repeat forever - if (this.repeat === 0) { - this.kill(); - } + this.events.on('collisionstart', ({ other: collider }: CollisionStartEvent) => { + if (!this._matchesTarget(collider)) { + return; } - }); - this.events.on('collisionend', (evt: CollisionEndEvent) => { - if (this.filter(evt.other)) { - this.events.emit('exit', new ExitTriggerEvent(this, evt.other)); + this.events.emit('enter', new EnterTriggerEvent(this, collider)); + this._dispatchAction(collider); + // remove trigger if its done, -1 repeat forever + if (this.repeat === 0) { + this.kill(); } }); - } - public set target(target: Entity) { - this._target = target; - this.filter = (actor: Entity) => actor === target; - } - - public get target() { - return this._target; + this.events.on('collisionend', ({ other: collider }: CollisionEndEvent) => { + if (this._matchesTarget(collider)) { + this.events.emit('exit', new ExitTriggerEvent(this, collider)); + } + }); } - public _initialize(engine: Engine) { - super._initialize(engine); + private _matchesTarget(entity: Entity): boolean { + return this.filter(entity) && (this.target === undefined || this.target === entity); } - private _dispatchAction() { + private _dispatchAction(target: Entity) { if (this.repeat !== 0) { - this.action.call(this); + this.action.call(this, target); this.repeat--; } }