Patchies - JS Behaviors API

A behavior is essentially a system in the ECS (entity-component-system) model. It first queries the relevant objects, then does something with it. See the Patchies - Example Behaviors notes for more examples of how behaviors look like.

You can define a behavior as a class in JavaScript. A behavior receives the BehaviorContext as the constructor, and provides a setup and cleanup method. It calls setup when the canvas is ready, and cleanup when the canvas is going to be destroyed.

interface BehaviorContext<ObjectData, Options = any> {
  options: Options
  getObjects(): BaseObject<ObjectData>[]
}
 
interface Behavior<Context extends BehaviorContext> {
  constructor(context: Context)
  setup(): void
  cleanup(): void
}

Here is a simple "fixed cycle" tick behavior that sends an 'update' message to the recipients.

type FixedCycleContext = BehaviorContext<
	{ update: () => void },
	{ tickInterval?: number }
>
 
class FixedCycleBehavior implements Behavior<FixedCycleContext> {
	context: FixedCycleContext
 
	constructor(context: FixedCycleContext) {
		this.context = context
		this.timer = null
		this.tickInterval = context.options.tickInterval ?? (1000 / 60)
	}
	
	private update() {
	   // getObjects return the object's class instances.
		for (const obj of this.context.getObjects()) {
			obj?.update()
		}
	}
 
	setup() {
		this.timer = setInterval(this.update.bind(this), tickInterval)
	}
	
	cleanup() {
		clearInterval(this.timer)
	}
}
 
export default FixedCycleBehavior

Here's how one might implement a behavior for objects to reply to messages immediately:

type OnMessageContext = BehaviorContext<
	{ tags?: string[] },
	{ tickInterval?: number }
>
 
class OnMessageBehavior implements Behavior<OnMessageContext> {
   // message queues
	mq: Map<string, Message[]> = new Map()
 
	constructor(context: FixedCycleContext) {
		this.context = context
		this.timer = null
		
		// operations per second
		this.controlRate = 1000
	}
	
	setup() {
	   // on ready, we grab list of producers to tick first
		const producers = this.context.getObjects()
			.filter(o => o.tags?.includes('producer'))
			
		producers.update()
	}
	
	cleanup() {
	}
}
 
export default FixedCycleBehavior

#patchies