From 04992962e755161a86ad079e10cbb19756747704 Mon Sep 17 00:00:00 2001 From: youthfulIdealism Date: Sun, 27 Jun 2021 08:57:23 -0700 Subject: [PATCH] Added ActionRegistry pattern --- docs/Patterns.md | 115 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/docs/Patterns.md b/docs/Patterns.md index 4b898ec..aac3b0a 100644 --- a/docs/Patterns.md +++ b/docs/Patterns.md @@ -163,3 +163,118 @@ TODO ## ApeDestroy TODO + +## Actions + +Action systems allow entities to respond to events. For example, an entity could perform an action when the player presses a key, or could perform an action when the player steps on a button. + +Registering a seperate component and system for each action can be tedious. By using an action registry, the component and system can be generated by template code. + +```js +import { System, Component } from 'ape-ecs'; + +// the action registry +export class ActionRegistry { + constructor(world) { + this.world = world; + } + + register_action(action_config) { + let { action_name, action_setting_properties, on_system_config, on_entity_action } = action_config; + + // Generate a class and register it as a component for the action itself + let Action = class extends Component { + static get typeName() { return action_name; }//webpack decided not to work with static properties, so I get to do this stupid thing. + static get properties() { return { action_key: '' } }//webpack decided not to work with static properties, so I get to do this stupid thing. + } + this.world.registerComponent(Action); + + // Generate a class and register it as a component for any settings inherent to the action + let ActionSettings = class extends Component { + static get typeName() { return action_name + 'Settings'; }//webpack decided not to work with static properties, so I get to do this stupid thing. + static get properties() { return Object.assign(action_setting_properties, { action_key: '' }); }//webpack decided not to work with static properties, so I get to do this stupid thing. + } + this.world.registerComponent(ActionSettings); + + // Generate a class and register it as a system for the consequences of an action being performed + let ActionSystem = class ActionSystemClass extends System { + init() { + // set up the actors query + this.actors_query = this.createQuery().fromAll(action_name); + on_system_config(this);// set up any additional queries + } + update(currentTick) { + // fetch the actors and iterate through them + const actors = this.actors_query.refresh().execute(); + for (let actor of actors) { + let actions = actor.getComponents(action_name); + + // iterate through the instances of the action + for (let action of actions) { + // find any settings that match this action. Filter for the one with the appropriate key. + let settings = Array.from(actor.getComponents(action_name + 'Settings')); + let specific_setting = settings.find(ele => ele.action_key == action.action_key); + on_entity_action(this, actor, action, specific_setting); + } + + } + } + } + this.world.registerSystem('frame', ActionSystem); + + // Return the classes for later use if you need them. + return [ Action, ActionSettings ]; + } +} + +//create an action registry +let action_registry = new ActionRegistry(world); + +//register the fireProjectile action +let [FireProjectile, FireProjectileSettings] = action_registry.register_action({ + action_name: 'FireProjectile', + action_setting_properties: { + target: EntityRef, + image: './assets/stub.png' + }, + on_system_config: (system) => {// set up queries + console.log('no queries!'); + }, + on_entity_action: (system, entity, action, settings) => {// perform the action + if (!settings) { + console.log('no settings for FireProjectile.'); + entity.removeComponent(action); + return; + } + + console.log('fired') + + // originate the projectile at the entity that acted. + let location = entity.getOne('Location'); + let target = settings.target.getOne('Location'); + + // create the projectile. Assume location, image, and destination components. + world.createEntity({ + components: [ + { + type: 'Location', + x: location.x, + y: location.y, + }, + { + type: 'Image', + path: settings.image + }, + { + type: 'Destination', + x: target.x, + y: target.y + } + ] + }) + + // remove the FireProjectile component + entity.removeComponent(action); + } +}); +```