0
\$\begingroup\$

I am working on a small game engine for practicing purpose and also for putting some of my ideas into action. What I have so far is a couple of systems for rendering, sound etc. The next step is to put all these things together. I already looked into entity component systems, in particular the following article: http://www.gamedev.net/page/resources/_/technical/game-programming/implementing-component-entity-systems-r3382

I think that this approach would suit my needs to a certain level, but I also would like to implement the game logic itself inside scripts. I came up with a slightly different approach:

architecture visualization

The rendering, sound, physics, input and simulation systems perform actions on the data, which is basically a level object and a list of entities (for technical reasons I have to distinguish between entities and level geometry). The particular systems only perform actions on those entities which match their criteria (e.g. the physics system only handles entities with a bounding volume). In my approach the systems do not directly implement behavior. Instead they are either creating output (rendering, sound) or input (physics, input). The simulation system manages the scripts which may be assigned to entities. The script of an entity consists of two parts, first the initialization code which is executed when the entity is added to the scene, and second the code which is executed once per frame (or less, depending on whether the code contains a wait-statement). Additionally every entity may implement event handlers which are called whenever the input or physics system throws an event (e.g. a CollisionEvent, KeyDownEvent etc.). Entity scripts may define parameters, which can be modified inside the level editor.

My question is, whether this architecture has some severe weaknesses. Since I want to avoid threading, my scripts will be scheduled by the simulation system, which lead me to the restriction that an entity script is called once per frame (which means that loops should be avoided, since basically everything already is inside a big loop). Is this reasonable or is there any better way?

\$\endgroup\$
1
\$\begingroup\$

Input/Output

What's the idea in collecting input and outputs? Do you filter it somehow? Sort? That may be costly to do so for every frame. And disadvantage for it would be to have implement every system in two/three places:

  1. sorting/filtering inputs
  2. sorting/filtering outputs
  3. reading inputs and generating outputs (the system itself)

Implementation is scattered.

But I could misunderstand you. So the second thing I could think of is a hope to always have input/output. Systems are made to change state of entity components so it's all about modifying data. I'm not sure whether it would be easy for you to implement such output as DataChangeOutput (depends on programming language) but it would cost the CPU anyway.

Events

I'm not sure where do you handle your events. But seeing this statement

every entity may implement event handlers which are called whenever the input or physics system throws an event

looks dangerous. I believe that system should handle events in proper moment. Having events the way you described may lead into some incosistences due to order of call. It's best to collect events and handle them in proper time in the system queue. That way I don't see a reason of entities handling events.

Input, output - objects

This doesn't seem as a hard point but did you consider to implement Object Pools? Garbage collector may kill your performance very quickly in such architecture.

Scripts and parameters

Parameters in scripts - I feel that's approach taken from Unity3D. Some people believe it's good but the thing is that it's not ECS at all. Parameters are data and data should be put into Components (the C part). Parameters not fitting into components should be put somewhere else in my opinion, especially if those are static parameters.

But still, having a Script component is a pretty straightforward approach and still puts into ECS.

Custom implementation?

I know that's you may dislike it but I suggest you to try some ECS frameworks before implementing your own. It's a hard work and when you modify the whole ideas to your current likes, you may be in a place in the future when you have many incosistences. It's best to try already made things first to know how does it taste.

If you're asking for frameworks: artemis-odb or Ash. I prefer the first one but it's Java.

\$\endgroup\$
  • \$\begingroup\$ Input/Output: I think you misunderstood me, the input/output systems are not literally creating outputs. The rendering system writes the pixel data to the rendering plane once per render frame. The input system collects key and mouse input (in my case from SDL) and forwards them to the script engine / to the entities which registered for the particular event (e.g. a player catches key events for implementing movement). Parameters in the editor: I never used Unity, but I wanted the system to be as flexible as possible. \$\endgroup\$ – cpfr Nov 9 '14 at 17:38
0
\$\begingroup\$

In one word: eventloop.

In more than one word:

event loops allow you to have a complex runtime behaviour with multiple "simultaneous" events, timers, signals and much more without the use of more than one thread.

They are used among other things in microcontrollers.

The open-source GUI library called Qt5 is based on eventloops. In fact, I am pondering why not more games use Qt5 for that very reason (and the fact that it has excellent high-performance, industry tested, cross-platform support for OpenGL/OpenGL-ES, networking, threads, input, media playback, ....

\$\endgroup\$
  • \$\begingroup\$ This seems reasonable, but I don't think that every entity behavior should be modeled by just using events. For a player entity it might work that it only moves on key press events, but if I have a pickup entity which is supposed to rotate continuously, I need to execute the rotation code once per frame. Otherwise I would have to use timer events. Is this what you suggest? \$\endgroup\$ – cpfr Nov 9 '14 at 17:45
  • \$\begingroup\$ i think yes - we use qt in one of the incarnations of our open source engine and there have events for kb input and such but also an update event that's called for every frame. can use timers too. \$\endgroup\$ – antont Nov 10 '14 at 18:26
  • \$\begingroup\$ It seems like a bit of an overhead to send update events each frame for calling update code which would have been called each frame anyway \$\endgroup\$ – cpfr Nov 11 '14 at 10:39
  • \$\begingroup\$ Actually, events are implemented by default as a simple function/method call as long as both sender and receiver is in same thread. I am not saying you should use events every frame, but having access to them might make it easier to find a good architecture. \$\endgroup\$ – Lennart Rolland Nov 11 '14 at 13:29
0
\$\begingroup\$

Why not invert the relationship with the simulation system and allow it to be a hookable option in your entity design?

For example, you have a LogicComponent that essentially allows you to associate a specific script (or even a list of scripts) to a given entity. During the initialization of your logic component, it runs an init method from your script and during various update phases, calls the appropriate script function.

Additionally, other components in your framework could have a place where you can either place scripting code snippets that are to be executed for various conditions or allow your components to reference other scripts that can be fired at specific points. These scripts can be inspected during load and appropriate handlers linked to the event loop based on some set API.

For example, your PhysicsComponent may allow you to associate a script that has an event handler for collision. Inside this handler which gets called when a collision is detected, you can implement whatever custom behavior is needed.

While you are arriving at a similar place as your proposed design, the beauty here is that you've now decoupled your script logic into smaller snippets enabling you to reuse as well as modify and affect larger volumes of entities across the board when a change is needed.

\$\endgroup\$
  • \$\begingroup\$ i find his original idea of having a simulation system actually better. i usually don't want to scatter app logic into little scripts attached to different entities, but have it in one normal program that can then manipulate all the entities it needs. \$\endgroup\$ – antont Nov 10 '14 at 18:25
  • \$\begingroup\$ My intention was to define a script for each entity type (e.g. a script for an enemy) which can be assigned to entities in the level editor. That way, a designer could load different scripts for different kinds of entities. The rendering and sound systems then provide an API to the scripting interface to e.g. add a blur filter to the rendered image or play a sound when an enemy attacks. The script is also able to listen for events which are sent by input or collision detection \$\endgroup\$ – cpfr Nov 11 '14 at 10:45
0
\$\begingroup\$

I use coroutines. Coroutines are just functions which, instead of returning and exiting, yield while maintaining their state. For example, here is a python coroutine which counts up from 1 to N:

def count(num):
   for i in range(1, num):
       print i
       yield RUNNING

   yield SUCCESS

Now, to put a coroutine into your game loop, you simply have to do something like this:

def tickCoroutines():
    for routine in coroutines:
        status = routine.next()

I've also had a lot of success at combining coroutines with behavior trees. Coroutines give you several advantages over simple tick or update functions:

  • Allow you to loop without blocking
  • Allow you to store local state and garuntee it will still be there when you come back
  • Allow you to run several of them in parallel
  • Allow you to cancel, resume, or restart depending on the return value

They also have advantages over threads:

  • Generally lighter-weight
  • Require no locking
  • Deterministic update order

However, depending on your language, implementing a coroutine might be slightly more difficult. Python supports coroutines through generators. C# and C++ support them through iterators. Other languages might require some glue to get it to work.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.