Mugen87/yuka

Flyweight pattern of Behaviours lost when reloading JSON

Closed this issue · 5 comments

Typically, entities of similar types often share similar behavior instances within their own steering managers to save on memory usage, duplication of data or simply managing certain shared contexts. A caveat with reloading back a scene of entities via JSON is the lack of referencing to track potentially shared behaviors. Perhaps, behaviours could be factored out to a different location in the JSON outside of entity scope, with a Map<Instance, ID> lookup for json references.

I'm afraid it is not possible to implement this feature request with the current architecture. The idea is that you can serialized/deserialize an instance of SteeringManager in an isolated way. There is currently no concept of shared data which makes it impossible to share steering behaviors across multiple managers.

The actual intended usage of the API is that each vehicle has its own steering manager with its own/independent set of steering behaviors. So sharing steering behaviors is actually not fully supported although there are use cases where this setup makes sense.

Let's consider this issue as a feature request and consider it in future development/design decisions.

I'd say for future development there could be some improvements in customising the architectural output of the engine and what features to include vs exclude to determine build size. (eg. I noticed with the serialization feature, all behaviors must be imported in...rather than selected ones...). Even though i would say the minified output is pretty small as it stands now, it may come with a lot of features that remain unused for a given project/example.

Note that certain issues with webpack module export (with minification) cases might cause constructor.name to become mangled (and theserialization depends on this). But this isn't so much of a problem with Yuka but more of certain bulid configuration must be done to ensure constructor.name is kept consistent.

I'd be nice to consider different game architecture approaches with YUKA, even if it means having to come up with different versions/mods of Entity managers based on the type of approach.

The 4 common approaches to game architecture:

  1. Entity
  2. Entity/.Component
  3. System/<-Entity/.Component
  4. System/<-Component/(Entity)

Legend:

  • / - Looks up (on right hand side) from left hand side (for any possible reasons).
  • /. - Left Hand side typically stores right hand side, or to put it in another way: right hand side is typically looked up entirely based on left hand side.
  • () - May or may not look up/exist, etc. depending on the situation.
  • <- - Based on the nature of supplied instance on right hand side or/and what it contains, gets registered to the relevant left hand side instance(s).

Currently, YUKA examples are using Approach 1. Though, Approach 2 is easy to set up as well by simply extending GameEntity with your own EntityWithComponents entity holding an array of components that you call updateFor(this) in a specific curated sequence.... Vehicle steering management can be factored out to an individual state-based controller component rather than extending from Vehicle itself. Plus, you still get the advantages of using renderComponent callbacks at the end of EntityManager's updateEntity process.

Changing architecture from Entity (1,2) to System (3,4) based though, can be tricky and likely that will mean requiring different custom roll-up conditional compiling builds or an entirely different repo branch, since the nature of components typically becomes pure data only for consumption by systems. Different architectures mean likely different setup of the GameEntity-based classes. (or perhaps none at all..). Eg. If using a systems-based architecture, update() functions per entity and per component would definitely no longer be needed. The thing about Systems architecture in either (3,4), is with regards to the Entity itself, whether an Entity should already have basic 2D/3D GameObject transform properties and act either as "the Entity" , or it's 2D/3D/transform aspects only act a Component in itself alongside a bare entity bag instance/or no entity bag instance exists at all, but only exist as some abstract index number.

Thanks for your valuable feedback! Yes, we were aware of the different levels of complexity you can use when creating such an engine. The idea was to keep Yuka simple even if this means that it can't benefit of advantages resulting from more advanced architectures. Similar to three.js, we will definitely stick to a straightforward style.

Sorry, when I've mentioned "future development/design", I did not meant to change the basic direction Yuka is heading. Your considerations might be more interesting for the case somebody is starting a project from scratch.

Even without an architecture shift, I think it will be doable to implement the actual feature request of this issue.

Agreed, given the dynamic nature of javascript, it's easy to override the prototype or instances of EntityManager to take into account existing entity-List queries and such (during addEntity/removeEntity calls), so that external systems can check them out/in. I actually did up
draft of a potential simple utility to set up or lazily initialize "component" identifiers into a registry (via matches with: .propName !=null, .propName!=null: true/false, .propName !=null: TypeCheck, .symbol()PropName != null, instanceof, constructor, or generic obj struct {key: TypeCheck} matches) which can be used to filter any persistent array list of objects that meet all the "necessary" conditions for specific systems. Then, rather than coding extended entity classes (with very specific internalised update functions and dependencies per entity), one can easily begin coding systems that iterate through specific entities to perform more variety of focused tasks minimising code duplication/re-implementation as a result of OOP. (This would allow one to hook in other add-on features like physics/collision-resolution systems and such without being forced to code/rewrite every variant of logic within an entity's update() function,, before actually re-integrating the final velocities into actual positions which can then update spatial hash or any other broad-phased data structure ( via another systems implementation ) for specific entities. Also, it's not necessary one or the other...many games can use a combination of both whenever the need arises.

After some consideration I'm afraid we won't touch the serialization/deserialization logic in order to support this feature request. It's important to me to keep the code as simple as possible even if it means we only have a 80% solution and thus discard certain use cases (like shared steering behaviors).