UNPKG

gibbon.js

Version:

Actor/Component system for use with pixi.js.

309 lines 8.34 kB
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