Skip to content

Commit

Permalink
feat: [#2856] Emit actionstart and actioncomplete events on actors (#…
Browse files Browse the repository at this point in the history
…2857)


Closes #2856

## Changes:

- Added `actionstart` and `actioncomplete` events to the Actor that are fired when an action starts and completes
  • Loading branch information
mattjennings authored Dec 29, 2023
1 parent a77658c commit 3e82bb0
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 5 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added

-
- Added `actionstart` and `actioncomplete` events to the Actor that are fired when an action starts and completes


### Fixed
Expand Down
8 changes: 7 additions & 1 deletion src/engine/Actions/ActionQueue.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ActionCompleteEvent, ActionStartEvent } from '../Events';
import { Entity } from '../EntityComponentSystem/Entity';
import { Action } from './Action';

Expand Down Expand Up @@ -90,10 +91,15 @@ export class ActionQueue {
*/
public update(elapsedMs: number) {
if (this._actions.length > 0) {
this._currentAction = this._actions[0];
if (this._currentAction !== this._actions[0]) {
this._currentAction = this._actions[0];
this._entity.emit('actionstart', new ActionStartEvent(this._currentAction, this._entity));
}

this._currentAction.update(elapsedMs);

if (this._currentAction.isComplete(this._entity)) {
this._entity.emit('actioncomplete', new ActionCompleteEvent(this._currentAction, this._entity));
this._completedActions.push(this._actions.shift());
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/engine/Actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import {
PreDrawEvent,
PostDrawEvent,
PreDebugDrawEvent,
PostDebugDrawEvent
PostDebugDrawEvent,
ActionStartEvent,
ActionCompleteEvent
} from './Events';
import { Engine } from './Engine';
import { Color } from './Color';
Expand Down Expand Up @@ -166,6 +168,8 @@ export type ActorEvents = EntityEvents & {
pointerdragmove: PointerEvent;
enterviewport: EnterViewPortEvent;
exitviewport: ExitViewPortEvent;
actionstart: ActionStartEvent;
actioncomplete: ActionCompleteEvent;
}

export const ActorEvents = {
Expand Down Expand Up @@ -193,7 +197,9 @@ export const ActorEvents = {
PointerDragLeave: 'pointerdragleave',
PointerDragMove: 'pointerdragmove',
EnterViewPort: 'enterviewport',
ExitViewPort: 'exitviewport'
ExitViewPort: 'exitviewport',
ActionStart: 'actionstart',
ActionComplete: 'actioncomplete'
};

/**
Expand Down
24 changes: 23 additions & 1 deletion src/engine/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { OnInitialize, OnPreUpdate, OnPostUpdate, SceneActivationContext } from
import { BodyComponent } from './Collision/BodyComponent';
import { ExcaliburGraphicsContext } from './Graphics';
import { Axes, Buttons, Gamepad } from './Input/Gamepad';
import { Action } from './Actions/Action';

export enum EventTypes {
Kill = 'kill',
Expand Down Expand Up @@ -80,7 +81,10 @@ export enum EventTypes {
PointerDragEnd = 'pointerdragend',
PointerDragEnter = 'pointerdragenter',
PointerDragLeave = 'pointerdragleave',
PointerDragMove = 'pointerdragmove'
PointerDragMove = 'pointerdragmove',

ActionStart = 'actionstart',
ActionComplete = 'actioncomplete'
}

/* istanbul ignore next */
Expand Down Expand Up @@ -545,3 +549,21 @@ export class ExitTriggerEvent extends GameEvent<Actor> {
super();
}
}

/**
* Event thrown on an [[Actor]] when an action starts.
*/
export class ActionStartEvent extends GameEvent<Entity> {
constructor(public action: Action, public target: Entity) {
super();
}
}

/**
* Event thrown on an [[Actor]] when an action completes.
*/
export class ActionCompleteEvent extends GameEvent<Entity> {
constructor(public action: Action, public target: Entity) {
super();
}
}
47 changes: 47 additions & 0 deletions src/spec/ActionSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1244,4 +1244,51 @@ describe('Action', () => {
expect(actor.graphics.opacity).toBe(0);
});
});

describe('events', () => {
it('emits actionstart event', () => {
const spy = jasmine.createSpy();
actor.actions.moveTo(20, 0, 20);
actor.on('actionstart', spy);
scene.update(engine, 500);
scene.update(engine, 500);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.MoveTo) }));
});

it('emits actioncomplete event', () => {
const spy = jasmine.createSpy();
actor.actions.moveTo(20, 0, 20);
actor.on('actioncomplete', spy);
for (let i = 0; i < 10; i++) {
scene.update(engine, 200);
}
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.MoveTo) }));
});

it('emits actionstart and actioncomplete events for each action in a repeat', () => {
const startSpy = jasmine.createSpy();
const completeSpy = jasmine.createSpy();
actor.actions.repeat((ctx) => ctx.moveTo(20, 0, 20).moveTo(0, 0, 20), 1);
actor.on('actionstart', startSpy);
actor.on('actioncomplete', completeSpy);

for (let i = 0; i < 10; i++) {
scene.update(engine, 500);
}

const startCalls = startSpy.calls.all();
expect(startSpy).toHaveBeenCalledTimes(3);
expect(startCalls[0].args[0]).toEqual(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.Repeat) }));
expect(startCalls[1].args[0]).toEqual(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.MoveTo) }));
expect(startCalls[2].args[0]).toEqual(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.MoveTo) }));

const completeCalls = completeSpy.calls.all();
expect(completeSpy).toHaveBeenCalledTimes(3);
expect(completeCalls[0].args[0]).toEqual(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.MoveTo) }));
expect(completeCalls[1].args[0]).toEqual(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.MoveTo) }));
expect(completeCalls[2].args[0]).toEqual(jasmine.objectContaining({ target: actor, action: jasmine.any(ex.Repeat) }));
});
});
});

0 comments on commit 3e82bb0

Please sign in to comment.