Meep, a Game Engine

In the past, I have released small demos of the game engine I’ve been working on:



and a few others. I have been working on this engine for maybe 6 years. It’s an ECS (Entity Component System) engine, which makes it quite different from most of the engines on the market that follow composition paradigm instead. That doesn’t mean those engines are bad though, just different.

I have used this engine to build my game “Might is Right”, so I am confident that this is not a “toy” engine that can demonstrate a thing or two but crumbles in real world. It’s been battle tested, so to speak.

There are some over-polished parts of the engine, some parts are quite raw, documentation is mostly in code at the moment. There are some 400 tests or so.

The main things that this engine has an advantage over other JS engines are:

  • threading system
  • compact binary serialization
  • soft particles
  • trails
  • robust scene abstraction, to package your game in self-contained logical chunks
  • engine for massive terrain
  • AI library with MCTS, problem optimization, A* and Behavior trees

there’s a ton more in there, of course, but these are things that I haven’t seen elsewhere in JS engines.

Why is this being posted here on three.js? Well, the engine has a tight integration with three.js for trails, particles and meshes. You can totally build your own rendering system on top of the engine if you don’t want to use three.js though. The engine is completely modular, there are no critical systems, you can boot it up with 0 systems if you wanted to. Entire engine is just over 150k lines of code at the time of release.

Project is released under MIT license.

Finally, here’s the link to the project:

12 Likes

Amazing stuff, thanks for sharing!

Any reason why you made an ECS-based engine? From what I’ve read, there doesn’t seem to be any advantage in terms of performance for DOD in JS since arrays can behave as objects, which is not guaranteed to give good CPU-cache locality. Am I missing something in terms of performance or perhaps there’s another motivation behind it (e.g. syntactic, organizational, architectural)?

Hey Droopie,

there are actually still advantages in terms of locality. You don’t have a guarantee that the data will be local in most cases, but you do have a guarantee that the code will be local, more or less, which means fewer cache misses on the code. Beyond this, it gives you a very strong architectural decoupling, a component need only know about it’s own data, even systems only need a small amount of information about the outside world.

Beyond locality, some systems actually work with their own “threads”. The terrain system for example works with a Worker to build visible terrain chunks on demand.

In my game, I use a generator to create randomized enemies, they are obviously not completely random and there are a lot of rules that need to be respected when generating one such enemy. It’s pretty expensive and I have a lot of these enemies. So the system uses consumer “Thread” and a queue to generate those enemies in the background, using fixed amount of run-time share.

There’s another small system that’s responsible for snapping objects onto terrain, when an object moves - it gets added to the update queue, and every update cycle I pull up-to fixed number of objects from the queue. Eventually everything will be updated, but I can control the performance impact of the system that way.

There’s a really good presentation by blizzard at GDC on their ECS engine for Overwatch, the guy there goes into some detail from their perspective.

In reality, the engine itself doesn’t really work in a “traditional” ECS way, it does offer you an easy mechanism out of the box to loop over all of your components inside the “update” method, but thanks to event buses you can write both, a looping update or an even-driven update where only stuff that changes gets updated.

I think there are a ton of ways to game engines, no right or wrong. I have worked myself as an architect in software engineering industry at the time of writing the engine - so I was leaning more towards architecturally pleasing solution :slight_smile:

If I was starting to write the engine today - it could turn out very differently, but I’m still convinced that it was the right choice, the decoupling that it brings is just so amazing for managing a complex network of systems that typically go into a production-ready engine that this way reduces amount of code and possible bugs based on my experience.

For example, thanks to the fact that components are completely isolated - I have a universal serialization system that just works, because you only need to think about “how do I serialize/deserialize this one component”, that’s was quite powerful to me. You can have amazing decoupling without ECS - but ECS forces you to have that decoupling and doesn’t offer much in a way of advantages for stronger coupling which in turn makes you write better code.

1 Like

Ahh, that makes a lot of sense. Threading and serialization do seem like it would be way easier in ECS

Superinteresting to crawl through your project. I think I will need quite some time to understand the complete setup, but from what you wrote, this sounds like a very well thought through engine!

Did you consider typescript instead of javascript? And would you change anything from the structure/setup if you would again start from scratch?

Thanks for sharing the engine!

Hey Johannes,

The engine is, I would say, half-well thought out. It’s in a state that I’m quite proud of, but not because I had an incredible fore-sight, but because I have re-written large parts of it several times over the years.

I happen to have surprisingly unrelated background in software from big data, binary codecs to simulation and operating system - somehow I found a lot of relevant knowledge in my past when trying to solve certain problems for the engine.

If I was starting over - I would probably put more effort on an editor, I found that improvements to my editor provided most value when developing a game. But if we’re talking architecture - I would de-couple systems from components, allowing multiple system per arbitrary set of component types, the basics are there, it’s just something that would take a day or so of re-factoring. I didn’t do that yet because it would mean a lot more work for my actual game.

About TypeScript - I think I could go with it, I do like the static typing, as you can probably guess by the fact that most of my code is type-annotated. TypeScript is this weird beast that is a better version of javascript, but is not javascript. Last part being the awkward part, requiring additional compilation step. I don’t like language barriers.

Another reason is arguably irrational: I want to know how many function calls I have, I want to know when I create a field on an object etc. To tune the performance of a piece of code - the fewer transformations there between your code and the CPU instructions - the easier it is. I mean, there is a lot of transformation going on already with just the javascript :slight_smile: So, it’s basically about having control. But I think that it’s possible to write fast TypeScript, though I have a very limited amount of experience with it personally.

Update: Added license terms to the project. It is released under MIT license :balance_scale: , in spirit of three.js licensing terms.

have fun :slight_smile:

6 Likes

Added an NPM module to make it easier to use the engine:

2 Likes

Added a minimap system to the engine, this is a fully-fledged and battle-tested implementation. The usage is simple - you add a MinimapMarker component to your entity with desired marker size and sprite image and it just works. Following features are provided:

  • serialization (json as well as binary)
  • camera visualization
  • camera interaction (drag/click to focus on a given part of the world)
  • supports FogOfWar
  • two view implementation:
    • GL implementation using THREE.Points
    • HTML implementation

here’s an example from my game

4 Likes