Skip to content


Why I switched from component-based game engine architecture to functional reactive programming

Components have become pretty popular these days and I’d like to share some issues we had with them in our game projects. I was experimenting with component-based game engine architectures for 2 years and eventually stumbled upon functional reactive programming (FRP) to solve some of the issues of game-object components. I hope to provide some arguments from an objectoriented programming (OOP) viewpoint to why I think FRP helps to write more reusable code.

The argument for component-based game-engines usually starts like this: Game-objects should be represented like objects in reality but as the project progresses however, class hierarchies become more and more complex and thus should be split into small, reusable components. Game developers like abstract and re-usable game-engines after all. The following diagram illustrates such a monolithic class hierarchy (adopted from the book Game Engine Architecture):

Game-object components address these issues by reducing game-objects to identifiable containers of components, where each component encapsulates some reusable functionality and automatically communicates with other components. A component could be something like: a position, player movement, 3D model, health-points and so on. For the communication of components in our engine we used messages, events or let them directly search for components implementing a specified interface, which is illustrated in the following diagram:

In component-based architecture we were mostly concerned about the component intercommunication, like the player movement for example: Should the Mover-component manipulate the position directly or send movement-messages? Or should the Position-component listen to movement-events? What if we want the player movement to behave just a little differently, like being a little random? Do we implement a new RandomMover-component, or do we need a new component,… again,… just to encapsulate the random function into a component? And how does this fit into the automatic Position-Mover-communication?

In the book Component-based Software Engineering (CBSE) they actually define a component as:

A software component is a software element that conforms to a component model and can be independently deployed and composed without modification according to a composition standard. [...] A component model defines specific interaction and composition standards. [...]

In plain English: “In order to allow a new component to be added to an existing environment of components in a game-object and automatically communicate with each other, the communication protocol between the components has to be predefined“. I think the component-based game engine literature mixes-up “combining existing functionality to define game-specific logic” (= reusing functionality) and “automatic communication within unknown game-objects” (= dynamic functionality). Automatic communication is restricted to the known components and new functionality always has to be game-specific (either hard-coded, or in scripts, or any other data-driven method). Even with components you define the player game-object specifically at some point: Player = PositionComponent + VisualComponent + InputComponent.

In FRP everything is based upon time, thus time should be abstracted away into “time dependent functions” (behaviors). A Mover-”component” should just produce a translation vector over time from user-input — nothing more! And if we want it to be a bit random, we just compose the output with the random function in the game-object definition. A pure mathematical function is the most isolated, self-existing and reusable component you can get, as it depends (and only depends!) on the direct input. There is no need the encapsulate a function into a component… which is then managed by a component container (game-object) and defines a lot of messages and events to get the data to the right point.

I’m arguing that game-objects are always game-specific but with time-dependent functions you just have to combine the existing functionality in the right way for every specific game-object. For example, let the movement of a game-object by calculated by: the initial position + some translation over time from user-input + a random factor + … and so on. You may then compose the movement behavior into more complex behaviors (or complete game-objects if you like) in every way you can imagine: A movable, static image game-object? MovingImage = translation function + draw image function. A movable, animation game-object? Certainly! MovingAnimation = same translation function + draw animation function. Or just a static animation perhaps? StaticAnimation = position + draw animation function.

I’m not going into more detail about FRP for now, you can find more information on my blog or on the internet. Component-based software engineering can still be applied in this environment on higher level, like the communication between subsystems or game-objects, but not on the level of game-object functionality level! If you still think you want to implement game-object components, ask yourself the following questions:

  • How can different functionality be defined in terms of relative complex, non-elementary components?
  • What are the most elementary components?
  • In which way are elementary components different from pure functions?
  • How can existing components automatically communicate with new messages from new components?
  • What is the point of ignoring a message that a component doesn’t know of?
  • What happened to the input-process-output model after all?

Tagged with , , , .


21 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Jedd Haberstro says

    Would you mind giving a more concrete example or explanation of how you’re using FRP to replace a component-based design? :)

  2. Alex Schearer says

    This is a helpful post outlining the distinction between traditional game hierarchies and a component model as well as the motivation for moving from one to the other. I’m in the midst of implementing a game using a component model and have been wondering what a functional language could offer to resolve some of the complexity which creeps in when connecting components, especially when they aren’t owned by the same entity. I’d love to see a follow up post where you go into more detail about how functional programming worked and didn’t work. Of course without revealing your entire thesis!

  3. Johannes_von_Luck says

    This sort of layout is also possible from just having a heavy reliance on interfaces (say with an optional, but separate, default implementation class).

    The problem with the design above, where renderable object inherits from movable object that inherits from game object, is simply that functionality becomes top heavy. Consider an object added into this layout that is not movable but still requires the renderable part? You move the renderable up (or just reorganize). Now do this a few more times, say you need a different kind of pellet that is not renderable nor movable, etc., and you simply wind up with a top heavy class that is supposed to be “reused” and functionality not needed is “nulled out”.

    When you instead reference classes as members of inherited interfaces, then you simply talk through the interfaces. Doing so resembles quite closely the idea behind component based architecture.

    Why? Because game objects are more fit for a has-a relationship, not an is-a relationship.

  4. Gerold Meisinger says

    I wouldn’t say replace. I think the game literature understands components (in OOP!) as functionality that can be interconnected by forms of communication, but also mix-in the game-specific stuff (like a health component, which forces you into a specific scheme). In general CBSE components are just defined as something that communicates according to a *predefined protocol*, so new, unknown components can be used in an existing environment. But when you define game-specific(!) objects of their own, most of the time you just want to connect existing functionality, like “Player = Position+Translation+Animation, thus: let the position be changed by a translation suggestions and draw the animation at the resulting position”. That is what “signal functions” give you in Yampa with the usual benefits of FP (like combining new complex components from very, very basic ones). General CBSE can still be applied here, like when you want objects to communicate with each other. But maybe the communication isn’t needed at all. Collisions f.e. can be resolved by looking at the object states from a more global viewpoint and sending the game-objects collision events which need to be resolved by game-specific(!) rules. You may define a “Object which destroys itself on collision”, which is okay for most games, but more complex games have more specific rules.

  5. Den says

    Lots of jabber, no concrete samples: useless.

  6. Gerold Meisinger says

    Yap, I will eventually come to that. For now I just wanted to show the limits of components.

  7. Den says

    Fair enough. I am looking forward to it!

  8. Jesse Werner says

    Concrete examples please!!

  9. Nick Wiggill says

    To those who don’t get it:

    FRP is only a part of the whole. The most important aspect, pure functional composition, can be done with just straight FP and you can also achieve it with prototypal OO as in eg. JavaScript. If you’re not sold on that first, you’re not gonna be sold on FRP.

    FRP is really a whole layer on top of the concept of first-class functions as being the ideal form of composing functionality.

  10. imre says

    On one hand I understand what you say about the (possible) limitations of component systems, and I also see how important functional composition is. On the other hand I think I also know the basic principles behind FRP. But I fail to see the connection between these two.

    Most importantly:
    “How can existing components automatically communicate with new messages from new components?”
    This depends on how those components are designed, just like it would depend on how your functions are defined. In both cases, you need to keep functional composition in mind. Your example of Mover producing a translation based on time is not only possible in a component system, but actually to me it seems to be the straightforward version.

    If all you wanted to say is that in a good component system components have a single output (so they are really functions, making functional composition possible), then I agree.

  11. anon says

    Appears to me the OP doesn’t truly understand entity-component-systems, or perhaps I didn’t understand the explanation without concrete examples. Components should just hold data, for the example of random movement, that’s a simple random flag in the necessary component, and a change in the handling system to process the random. I’d like to see more examples… it’s very clear how OOP works and where it breaks down… if you see a fault in Component-Systems in favor of FRP, then illustrate how that works.

  12. Tom says

    Having each functional aspect of the game react to a time-based system is a bit clever (almost event-driven with respect to time). However, there are major down-sides to adopting this type of paradigm. In the aggregate, you run the risk of having your classes lose a lot of their self-maintainability and overall cleanliness; two fundamentally important concepts in OOP. Your classes will also lose some higher-levels of abstraction which is the whole point of OOP. This may be a viable solution to a procedural or object-based language (like JavaScript); However, in a low-level object-oriented language, FRP is not a conclusive enough solution for engine architecture.

    On another note, I do believe you are not approaching the concept of component-based architecture quite effectively. In fact, I believe that of many professional engineers within the gaming industry. Often times, I see many programmers demonstrate component-based objects as interfaces that registers to some base object handler which interprets some message and then executes some code. This is not how component-based objects work (or should work). Each component of the game should be encapsulated at a high-level and self-maintainable – meaning: each class takes care of its self and nothing more. Objects are like entities; entities have relationships. The relationship of these components are like siblings; they are children to the game object. Let me demonstrate:

    // Physics.h – An extremely contrived physics component of the main game object
    #ifndef _PHYSICS_H_
    #define _PHYSICS_H_

    class Physics
    {
    public:
    Physics() : G(0.0000000000667);
    virtual ~Physics();
    // Physical properties
    const float G;
    protected:
    // Don’t want any outer functions to toss around needless copies
    Physics(Physics &physical);
    };

    #endif

    // Game.h – An extremely contrived example of the main game object
    #ifndef _GAME_H_
    #define _GAME_H_

    #include “Physics.h”

    class Game
    {
    public:
    Game();
    virtual ~Game();
    protected:
    // Same deal here
    Game(Game &game);
    // TODO: List all instances of relevant component objects
    Physics physics;
    };

    #endif

    You see, each component is encapsulated and an instance (the only instance, hence the protected copy constructor) is giving to the main game object. That is what we call a HAS_A relationship; The game HAS_A physics component. Furthermore, the game does not manage the state of each component; rather it is responsible for the implementation of each component and nothing more. The game object must maintain its own state as well.

    I understand many programmers will find it hard to adopt – let alone understand – a new paradigm. A lot of the responses within the industry seem to be out of confusion. I do not wish to disrespect your ideals, nor the ideals of my fellows piers. However, In my experience, I believe a component-based architecture is most efficient in terms of OOP.

  13. Gerold Meisinger says

    @Nick Wiggil
    > pure functional composition, can be done with just straight FP

    Ah, you’re very right on that.

    @imre
    > “How can existing components automatically communicate with new messages from new components?” This depends on how those components are designed, just like it would depend on how your functions are defined.

    The emphasis is on NEW messages here. The argument in some implementations is, that you add a new component that broadcast messages to other components and magically everything works together. Which simply can’t work if they don’t use the same “protocol”.

    @anon
    > Components should just hold data, for the example of random movement, that’s a simple random flag in the necessary component

    What about “holding functionality”? And adding a random flag would make the component more powerful than an ordinary “straight-line” movement component would need. We could however add “apply-randomness” component to the movement component but that’s a lot of overhead for something I’d call “parametrization”.

    > it’s very clear how OOP works and where it breaks down
    Really? I haven’t read or heard of any OOP critism until I deliberately started looking for it :) And I’d glad to hear where you think “it breaks down”.

    @Tom
    > However, in a low-level object-oriented language, FRP is not a conclusive enough solution for engine architecture

    Well… most of them are written in Haskell, a non-object-oriented language ;)

    > This is not how component-based objects work (or should work).

    Yeah, a definition would be fine. I think a lot of confusion derives from using different terminology. What you call a component in your example, we used to call a subsystem(-component), wereas in my article I use game-object components.

    @Future me
    More concrete examples!

  14. Ywen says

    Just a question: why Yampa?

    Why an AFRP framework instead of an applicative and monad-based one (like reactive, or now sodium, reactive-banana or elerea)?

  15. Gerold Meisinger says

    Please note the date! I’d definitely would take a look at reactive-banana. My original choice was mainly:
    * more papers and examples available (FRP is hard enough if you’re not used to FP)
    * mature and though-through code (I still think that Arrows make a lot more sense in this context than monads)

  16. william says

    I have a few components I find very useful for defining game objects, like a “Bank” component (essentially a protected value kept between certain intervals, with methods to withdraw or deposit values) or a “Timer” element (which counts down until empty and then returns true instead of false).

    I suppose these aren’t components “per-se” as they don’t communicate automatically through (a)synchronous messaging, but they do demonstrate the usefulness of object-oriented programming over pure-functional!

    That said it’s always interesting to see new paradigms proposed: I’ll look into FRP with great interest :)

  17. Mikolaj Konarski says

    Hi! Your article has just been mentioned on

    http://www.reddit.com/r/haskellgamedev/comments/2fe3ox/im_working_on_a_haskell_game_engine/

    Would it possible to readd the diagrams mentioned in the text that are currently missing? Thank you!

  18. Dante says

    Highly descriptive post, I liked that bit.
    Will there be a part 2?

Continuing the Discussion

  1. Component system | BoxHacker linked to this post on 2011-07-02

    [...] Programming discusses these benefits in illustrated detail while Lambdor discusses some problems he [...]

  2. First Thoughts on XNA 4.0 Game Architecture – Small Thoughts linked to this post on 2011-09-09

    [...] is to have them abstract and the best way to do that is to normally make it so that your engine is Component base driven. Although that architecture lends itself to amazing data driven design, I wanted to just [...]



Some HTML is OK

or, reply to this post via trackback.