gibbon.js
Version:
Actor/Component system for use with pixi.js.
309 lines • 8.34 kB
JavaScript
import { Actor } from "../..";
import { contains } from '../utils/array-utils';
import { EngineEvent } from '../events/engine-events';
/**
* If a clip is supplied to the Group, it will act as the parent
* of all Actor clips added to the group.
*/
export class Group {
get actor() {
return this._actor;
}
/**
* @property Optional clip associated with group.
*/
clip;
/**
* @property {string} name
*/
name;
/**
* @property {boolean} enabled
*/
get enabled() { return this._enabled; }
/**
* Subgroups of this group.
*/
subgroups = [];
/**
* Objects in group.
*/
objects = [];
/**
* Actor to hold group components.
*/
_actor;
_game;
/**
* Game group is added to, if any.
*/
get game() { return this._game; }
get parent() { return this._parent; }
/**
* Parent group, if any.
*/
_parent;
isDestroyed = false;
_enabled = false;
/**
* Whether group should enable when added to engine.
* Used to track enable state before actually added.
*/
_shouldEnable;
/**
*
* @param actor -actor to assign to group, or container to use as group container,
* or 'true' to create a group container.
* @param enabled
*/
constructor(actor, enabled = true) {
this._shouldEnable = enabled;
if (actor) {
this._actor = this.makeGroupActor(actor);
this.clip = this._actor.clip;
}
}
/**
* Ensure the group has its own group Actor.
*/
makeGroupActor(clip) {
let actor;
if (typeof clip === 'boolean') {
actor = new Actor();
}
else if (clip instanceof Actor) {
actor = clip;
}
else {
actor = new Actor(clip);
}
if (this._game) {
this._game.addActor(actor);
}
return actor;
}
enable() {
this._shouldEnable = true;
if (this._enabled === true)
return;
for (const g of this.subgroups) {
g.enable();
}
this._enabled = true;
}
disable() {
this._shouldEnable = false;
if (this._enabled === false)
return;
this._enabled = false;
for (const g of this.subgroups) {
g.disable();
}
}
/**
* Override in subclasses for notification of when
* group is added to game.
*/
onAdded() { }
/**
* Override in subclasses to be notified when group is removed.
*/
onRemoved() { }
/**
* Internal message of group being added to game.
* Do not call directly.
* Override onAdded() in subclasses for the event.
*/
_onAdded(game) {
if (this._game !== game) {
this._game = game;
if (this._actor && !this._actor.isAdded) {
/// add actor to group.
game.addActor(this._actor);
}
/// Add all objects in group.
for (const a of this.objects) {
game.addActor(a);
}
for (const s of this.subgroups) {
game.addGroup(s);
}
this.onAdded();
if (this._shouldEnable) {
this.enable();
}
}
}
/**
* Show all the objects in the group and subgroups.
*/
show() {
if (this.actor) {
this.actor.visible = true;
}
for (let i = this.subgroups.length - 1; i >= 0; i--) {
this.subgroups[i].show();
}
}
/**
* Hide all actors in this group and subgroups.
*/
hide() {
if (this.actor) {
this.actor.visible = false;
}
for (let i = this.subgroups.length - 1; i >= 0; i--) {
this.subgroups[i].hide();
}
}
/**
* Get group by class. Searches this group then
* recurses up the parent chain, and then to the
* current game, if Group has been added to game.
* @param type
* @returns
*/
getGroup(type) {
for (let i = this.subgroups.length - 1; i >= 0; i--) {
if (this.subgroups[i] instanceof type) {
return this.subgroups[i];
}
}
if (this._parent) {
return this._parent.getGroup(type);
}
else if (this._game) {
return this._game.getGroup(type);
}
}
/**
* Find subgroup of this group.
* @param gname
* @returns
*/
findGroup(gname) {
for (let i = this.subgroups.length - 1; i >= 0; i--) {
if (this.subgroups[i].name == gname)
return this.subgroups[i];
}
return undefined;
}
/**
* Return first subgroup found of type.
*/
get(kind) {
for (let i = this.subgroups.length - 1; i >= 0; i--) {
if (this.subgroups[i] instanceof kind) {
return this.subgroups[i];
}
}
return null;
}
/**
* Add subgroup to this group.
* @param {Group} sub
*/
addGroup(sub) {
if (!contains(this.subgroups, sub)) {
if (sub._parent) {
if (sub._parent === this)
return;
sub._parent.removeGroup(sub);
}
sub._parent = this;
this.subgroups.push(sub);
this.game?.addGroup(sub);
}
}
/**
* Remove Actor from group, but not Game or Engine.
* @param {Actor} obj
*/
remove(obj) {
const ind = this.objects.indexOf(obj);
if (ind < 0)
return;
this.objects.splice(ind, 1);
obj.off(EngineEvent.ActorDestroyed, this.remove, this);
obj.group = null;
}
/**
*
* @param {Actor} obj
* @returns {Actor} the object.
*/
add(obj) {
obj.group = this;
obj.on(EngineEvent.ActorDestroyed, this.remove, this);
this.objects.push(obj);
this._game?.engine.add(obj);
return obj;
}
/**
* Internal message of group being removed from game.
* Do not call directly.
* Override onRemoved() in subclasses for the event.
*/
_onRemoved() {
/// Save previous enabled state in case group is re-added.
const curEnable = this._enabled;
this.disable();
this._shouldEnable = curEnable;
const game = this._game;
if (game) {
this.onRemoved();
this._game = undefined;
for (const a of this.objects) {
game.engine.remove(a);
}
for (const s of this.subgroups) {
game.removeGroup(s);
}
}
}
/**
* Remove subgroup from this group.
* @param {Group} sub
*/
removeGroup(sub) {
if (sub._parent !== this) {
return;
}
sub._parent = undefined;
for (let i = this.subgroups.length - 1; i >= 0; i--) {
if (this.subgroups[i] == sub) {
this.subgroups.splice(i, 1);
break;
}
}
this.game?.removeGroup(sub);
}
destroy() {
this.isDestroyed = true;
for (let i = this.subgroups.length - 1; i >= 0; i--) {
this.subgroups[i].destroy();
}
for (let i = this.objects.length - 1; i >= 0; i--) {
// Don't listen to the remove event since we're already looping.
this.objects[i].off(EngineEvent.ActorDestroyed, this.remove, this);
this.objects[i].destroy();
}
this.objects.length = 0;
this.subgroups.length = 0;
/// workaround to ensure 'game' exists in the onDestroy()
/// function since _onRemoved() clears it.
const tempGame = this._game;
if (this._parent && !this._parent.isDestroyed) {
this._parent.removeGroup(this);
}
else {
this._game?.removeGroup(this);
}
this._game = tempGame;
this.onDestroy?.();
this._actor?.destroy();
this._parent = undefined;
this._game = undefined;
}
}
//# sourceMappingURL=group.js.map