UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

239 lines (184 loc) 5.42 kB
import { min2 } from "../../../core/math/min2.js"; import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js"; import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js"; import { System } from "../System.js"; import { Transform } from "../transform/Transform.js"; import { TransformAttachment, TransformAttachmentFlags } from "./TransformAttachment.js"; /** * @readonly * @type {number} */ const QUEUE_ITERATION_COUNT = 32; class UpdateContext { constructor() { /** * * @type {TransformAttachment|null} */ this.attachment = null; /** * * @type {Transform|null} */ this.transform = null; /** * * @type {number} */ this.entity = -1; /** * * @type {Transform|null} */ this.parent_transform = null; /** * * @type {EntityComponentDataset|null} */ this.ecd = null; } toString() { return `UpdateContext{ attachment:${this.attachment}, entity:${this.entity} }`; } update() { this.transform.multiplyTransforms( this.parent_transform, this.attachment.transform ); } /** * * @returns {boolean} */ bind_parent() { const parent_transform = this.ecd.getComponent(this.attachment.parent, Transform); if (parent_transform === undefined) { return false; } this.parent_transform = parent_transform; return true; } link() { const t_parent = this.parent_transform; t_parent.subscribe(this.update, this); const t_attachment = this.attachment.transform; t_attachment.subscribe(this.update, this); } unlink() { const transform = this.parent_transform; transform.unsubscribe(this.update, this); const t_attachment = this.attachment.transform; t_attachment.unsubscribe(this.update, this); } } export class TransformAttachmentSystem extends System { /** * * @type {UpdateContext[]} * @private */ __contexts = []; /** * * @type {UpdateContext[]} * @private */ __queue = []; __queue_size = 0; __queue_cursor = 0; constructor() { super(); this.dependencies = [TransformAttachment, Transform]; this.components_used = [ ResourceAccessSpecification.from(TransformAttachment, ResourceAccessKind.Read), ResourceAccessSpecification.from(Transform, ResourceAccessKind.Read | ResourceAccessKind.Write), ]; } /** * * @param {UpdateContext} ctx * @private */ __finalize_link(ctx) { ctx.link(); this.__contexts[ctx.entity] = ctx; if ((ctx.attachment.flags & TransformAttachmentFlags.Immediate) !== 0) { ctx.update(); } } /** * * @param {UpdateContext} ctx * @private */ __enqueue(ctx) { this.__queue[this.__queue_size++] = ctx; } /** * * @param {number} entity * @returns {boolean} * @private */ __dequeue_entity(entity) { for (let i = 0; i < this.__queue_size; i++) { const ctx = this.__queue[i]; if (ctx.entity === entity) { this.__queue.splice(i, 1); this.__queue_size--; return true; } } return false; } /** * * @param {TransformAttachment} attachment * @param {Transform} transform * @param {number} entity */ link(attachment, transform, entity) { const ctx = new UpdateContext(); ctx.attachment = attachment; ctx.transform = transform; ctx.entity = entity; ctx.ecd = this.entityManager.dataset; if (ctx.bind_parent()) { this.__finalize_link(ctx); } else { // failed to bind parent, queue up instead this.__enqueue(ctx); } } /** * * @param {TransformAttachment} attachment * @param {Transform} transform * @param {number} entity */ unlink(attachment, transform, entity) { const ctx = this.__contexts[entity]; if (ctx !== undefined) { delete this.__contexts[entity]; ctx.unlink(); } else { // no context found, check the queue this.__dequeue_entity(entity); } } update(timeDelta) { const step_count = min2(this.__queue_size, QUEUE_ITERATION_COUNT); for (let i = 0; i < step_count; i++) { const index = this.__queue_cursor % this.__queue_size; const ctx = this.__queue[index]; if (ctx.bind_parent()) { // parent obtained this.__finalize_link(ctx); this.__queue.splice(index, 1); this.__queue_size--; } else { this.__queue_cursor++; } } } }