<\/path><\/svg><\/span>class<\/span> <\/span>Entity<\/span> {<\/span><\/span>\n <\/span>addComponent<\/span>( <\/span>key<\/span>, <\/span>component<\/span> ) {}<\/span><\/span>\n <\/span>removeComponent<\/span>( <\/span>key<\/span> ) {}<\/span><\/span>\n}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\nIn hindsight, this was not great … why should a component have a key? but we keep going. This is what a component looks like:<\/p>\n\n\n\n
<\/circle><\/circle><\/circle><\/g><\/svg><\/span><\/path><\/path><\/svg><\/span>const<\/span> <\/span>BodyComponent<\/span> <\/span>=<\/span> <\/span>function<\/span>( <\/span>width<\/span>, <\/span>height<\/span> ) {<\/span><\/span>\n\t<\/span>this<\/span>.width <\/span>=<\/span> width<\/span><\/span>\n\t<\/span>this<\/span>.height <\/span>=<\/span> height<\/span><\/span>\n};<\/span><\/span>\n<\/span>\nexport<\/span> <\/span>default<\/span> BodyComponent;<\/span><\/span>\n<\/span><\/code><\/pre><\/div>\n\n\n\nTo add body to an entity we would:<\/p>\n\n\n\n
<\/circle><\/circle><\/circle><\/g><\/svg><\/span><\/path><\/path><\/svg><\/span>const<\/span> <\/span>exampleEntity<\/span> <\/span>=<\/span> <\/span>new<\/span> <\/span>Entity<\/span>().<\/span>addComponent<\/span>( <\/span>'body'<\/span>, <\/span>new<\/span> <\/span>BodyComponent<\/span>( <\/span>16<\/span>, <\/span>16<\/span> ) );<\/span><\/span><\/code><\/pre><\/div>\n\n\n\nThere is an entity manager and a component manager that handle creation and querying of data. So a system would do:<\/p>\n\n\n\n
<\/circle><\/circle><\/circle><\/g><\/svg><\/span><\/path><\/path><\/svg><\/span>class<\/span> <\/span>MyPhysicsSystem<\/span> <\/span>extends<\/span> <\/span>System<\/span> {<\/span><\/span>\n <\/span>update<\/span>( <\/span>delta<\/span> ) {<\/span><\/span>\n <\/span>this<\/span>.componentManager.<\/span>query<\/span>( <\/span>e<\/span> <\/span>=><\/span> e.components.<\/span>has<\/span>( <\/span>'body'<\/span> ) ).<\/span>forEach<\/span>( <\/span>entity<\/span> <\/span>=><\/span> {<\/span><\/span>\n <\/span>let<\/span> body <\/span>=<\/span> entity.components.<\/span>get<\/span>( <\/span>'body'<\/span> );<\/span><\/span>\n <\/span>\/\/ do something with the body component<\/span><\/span>\n } );<\/span><\/span>\n }<\/span><\/span>\n}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\nThe whole thing works, and thanks to modern computing, it works fast, but I took a bunch of shortcuts that I’m not too proud of:<\/p>\n\n\n\n
\nEntities as well as Components MUST<\/em> be pooled so the garbage collection doesn’t kill you, I didn’t.<\/li>\n\n\n\nQueries are run on every iteration even if the results haven’t changed. This is highly inefficient, there should be some kind of query caching or pre-work done so we don’t have to iterate every time. That said, this was querying 75.000 entities at 60 fps. Not incredible, but more than enough for my 13k game. At most I had 30 thousand entities …. so, not a problem.<\/li>\n\n\n\n I already said it, but adding keys to components is just silly.<\/li>\n<\/ul>\n\n\n\nIt is small. It worked. That’s all I needed.<\/p>\n\n\n\n
I cannot stress this enough: calling ECS the holy grail, doesn’t mean there aren’t other ways … in fact, I think object oriented would have been better in this case! but I wanted to give ECS a shot. It is a completely different mindset though. It was strange. It was complicated.<\/p>\n\n\n\n
On the bright side, having systems completely isolated from everything else was really nice! each system does one thing, and because of that, they are super simple to debug.<\/p>\n\n\n\n
Behavior Trees<\/h2>\n\n\n\n Making NPCs act interestingly can be done in a couple of ways. The more common approach is FSMs (Finite State Machine), for simple behaviors, and for more complex ones Behavior Trees (right more here<\/a>). There are other ways of course, but I find these two easy to understand.<\/p>\n\n\n\nAt first I was going to use BehaviorTree.js<\/a> which I had used before. I had done some simple trees and was cruising on the testing. When I built the project I realized that while great, it was just doing too much, and the exports were not being eliminated by tree shaking. In other words, footprint was too big, can’t fit on 13kb. So I ended up rewriting the whole thing using a similar API. Even though I didn’t have enough time to actually make the units have interesting behaviors I am very<\/em> proud of this particular btree implementation. It is short and sweet, and works like a charm. Exactly what you need for this jam. I will probably release this as a stand-alone. The whole thing can be found here<\/a>.<\/p>\n\n\n\nOne thing that I should mention, and in the spirit of being self-criticizing, the implementation of the Cooldown decorator is rather lazy:<\/p>\n\n\n\n
<\/circle><\/circle><\/circle><\/g><\/svg><\/span><\/path><\/path><\/svg><\/span>export<\/span> <\/span>class<\/span> <\/span>CooldownDecorator<\/span> <\/span>extends<\/span> <\/span>Decorator<\/span> {<\/span><\/span>\n\t<\/span>constructor<\/span>( <\/span>args<\/span> ) {<\/span><\/span>\n\t\t<\/span>super<\/span>( args );<\/span><\/span>\n