@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
144 lines (114 loc) • 3.57 kB
JavaScript
import Clock from "../../../Clock.js";
import { System } from "../../../ecs/System.js";
import { BehaviorStatus } from "../BehaviorStatus.js";
import { BehaviorComponent } from "./BehaviorComponent.js";
import { BehaviorComponentFlag } from "./BehaviorComponentFlag.js";
import { ClockChannelType } from "./ClockChannelType.js";
/**
*
* @param {Behavior} behavior
* @param {number} timeDelta
* @returns {BehaviorStatus}
*/
function updateBehavior(behavior, timeDelta) {
try {
return behavior.tick(timeDelta);
} catch (e) {
console.error('Behavior threw an exception', e);
// Transition behavior into failed state
return BehaviorStatus.Failed;
}
}
/**
* @extends System<BehaviorComponent>
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class BehaviorSystem extends System {
/**
*
* @param {Engine} engine
*/
constructor(engine) {
super();
this.dependencies = [BehaviorComponent];
this.systemClock = new Clock();
/**
*
* @type {Engine}
*/
this.engine = engine;
}
async startup(entityManager) {
this.systemClock.start();
this.entityManager = entityManager;
}
async shutdown(entityManager) {
this.systemClock.stop();
}
/**
*
* @param {BehaviorComponent} component
* @param {number} entity
*/
link(component, entity) {
const behavior = component.behavior;
component.clearFlag(BehaviorComponentFlag.Resolved);
if (behavior !== null) {
// initialize behavior
const dataset = this.entityManager.dataset;
try {
behavior.initialize({ entity, ecd: dataset, engine: this.engine });
} catch (e) {
console.warn('Behavior initialization failed:', e);
}
}
}
/**
*
* @param {BehaviorComponent} component
* @param {number} entity
*/
unlink(component, entity) {
const behavior = component.behavior;
if (behavior !== null) {
// finalize behavior
behavior.finalize();
}
}
update(timeDelta) {
const systemDelta = this.systemClock.getDelta();
const dataset = this.entityManager.dataset;
if (dataset === null) {
//no data
return;
}
/**
*
* @param {BehaviorComponent} b
* @param {number} entity
*/
function visitBehavior(b, entity) {
if (b.getFlag(BehaviorComponentFlag.Resolved)) {
return;
}
let td;
if (b.clock === ClockChannelType.Simulation) {
//use simulation time
td = timeDelta;
} else if (b.clock === ClockChannelType.System) {
//use system clock time delta
td = systemDelta;
}
const behavior = b.behavior;
if (behavior !== null) {
const state = updateBehavior(behavior, td);
if (state === BehaviorStatus.Failed || state === BehaviorStatus.Succeeded) {
b.setFlag(BehaviorComponentFlag.Resolved);
}
}
}
dataset.traverseComponents(BehaviorComponent, visitBehavior);
}
}