Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Framework Design Advice  (Read 9554 times)

0 Members and 1 Guest are viewing this topic.

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Framework Design Advice
« on: June 05, 2014, 08:51:25 am »
Hello everyone,

As some of you may already know, I spent some time designing a small game framework
in order to help me with my future projects. Unfortunately, because of the
university entry exams, I didn't really have a lot of time to spend on it.

Now that the exams are almost finished I thought I should start working seriously on
the actual design of the framework. I did make a prototype but C++ didn't prove that great for prototyping.
I am now making a prototype in Python and I will port it to C++ if the performance proves inadequate.

My goal is not to make a framework with incredible performance. What I want to achieve is a core which will allow me to easily implement features, without worrying about dependencies and coupling while also keeping the maintainable code to the minimum.

I have settled on a self-registration entity-component system where the entities register to the systems according to the components that they hold. All the registration functionality should remain hidden and the user should only have to provide a registration condition for each system.

Here is a small example in Python:

import veritas_framework as vf

class TestComponent:
    pass

class TestSystem(vf.System):
    def registration_condition(self, entity):
        return entity.has_component(TestComponent) # Take note that we use the type here
    def update(self, delta=0):
        for entity in self.entities:
            print "updated"

engine = vf.Engine()
test_system = TestSystem()
engine.add_to_systems(test_system) # Here we use the object
engine.add_to_pipeline(test_system)

entity = vf.Entity(engine) # The entity will signal the engine when it updates it's components

engine.update() # Nothing happens

test_comp = TestComponent()
entity.add_component(test_comp) # Passing an object again

engine.update() # Prints "updated"

entity.remove_component(TestComponent) # Here we use the type

engine.update() # Nothing happens

What I am having troubles with is small inconsistencies in the design.
We actually pass the objects when we add a system or a component but we use the types
to remove them or test if they exist.

This is because both the engine and the entity classes should only
keep 1 object per system or component type accordingly. I think this is not obvious with the current design.

One way to solve this is to actually use the type when we add a system/component and the function would return
the created object for further manipulation, however I am not sure whether this is acceptable .

Any help is much appreciated!

~Veritas
« Last Edit: June 05, 2014, 11:16:46 am by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

select_this

  • Full Member
  • ***
  • Posts: 130
  • Current mood: just ate a pinecone
    • View Profile
    • darrenferrie.com
Re: Framework Design Advice
« Reply #1 on: June 05, 2014, 09:56:12 am »
I agree that it doesn't really make sense as it stands. One question I'd like to ask is why you've got the limitation of one component type per system - I would guess that it's because a component encapsulates a specific piece of functionality as opposed to a traditional component of a larger object (which in most cases you would expect to be able to have multiple).

I would normally have some form of enumerated identifier to refer to components rather than referencing the type; that way, I don't have to care about the type once I've inserted it (encaspulated in a shared pointer). That way, if you ever decide you can have more than one of a particular component without breaking things, it's as simple as adding a new identifier rather than having a special case for a particular type.
Follow me on Twitter, why don'tcha? @select_this

Lolilolight

  • Hero Member
  • *****
  • Posts: 1232
    • View Profile
Re: Framework Design Advice
« Reply #2 on: June 05, 2014, 10:04:18 am »
Yes or you can use a base class for all your components types.
Or if you don't want to make inheritance, you can just use a template which the type erasure.

And you can also assign an id to each components to get them individually, enums works pretty wells, if you don't need to send components ids thought the networks, otherwise use std::string. (We can't send enums thought the networks. :/)

This is the reason why I have criticate the thor library system.
« Last Edit: June 05, 2014, 10:08:27 am by Lolilolight »

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Framework Design Advice
« Reply #3 on: June 05, 2014, 10:12:46 am »
I agree that it doesn't really make sense as it stands. One question I'd like to ask is why you've got the limitation of one component type per system - I would guess that it's because a component encapsulates a specific piece of functionality as opposed to a traditional component of a larger object (which in most cases you would expect to be able to have multiple).

Systems don't have a one component-type limitation.

An entity may have different components but not multiples of the same type. Same goes for the engine class. It can have multiple different systems but not multiple of the same type. This makes sense since you don't want two PlayerInputSystems for example or two GraphicsComponents.

~Veritas
« Last Edit: June 05, 2014, 12:32:33 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

select_this

  • Full Member
  • ***
  • Posts: 130
  • Current mood: just ate a pinecone
    • View Profile
    • darrenferrie.com
Re: Framework Design Advice
« Reply #4 on: June 05, 2014, 10:16:53 am »
Yeah, that's what I meant, sorry (typed it in a hurry).
Follow me on Twitter, why don'tcha? @select_this

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Framework Design Advice
« Reply #5 on: June 05, 2014, 10:55:11 am »
I am not sure what you mean. Systems don't have a one component type limitation. If you want them to register entities with multiple components just put the requirements in the registration_condition function.

~Veritas
« Last Edit: June 05, 2014, 12:32:44 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Framework Design Advice
« Reply #6 on: June 05, 2014, 11:07:20 am »
Since components possibly take parameters themselves, I don't think it's meaningful to only pass a type to the entity. Passing a function object that is used to create the component (i.e. a factory) is a possibility, but I wouldn't use this indirection unless it brings actual advantages. Points to consider are rather whether multiple entities might share components, and whether components can be used outside entities.

I don't see the big inconsistency here. It may seem so because of Python's dynamic typing, but in C++, the methods would be quite symmetric:
class Entity
{
public:
    template <typename Component>
    void AddComponent(Component c);

    template <typename Component>
    void RemoveComponent();
};
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

select_this

  • Full Member
  • ***
  • Posts: 130
  • Current mood: just ate a pinecone
    • View Profile
    • darrenferrie.com
Re: Framework Design Advice
« Reply #7 on: June 05, 2014, 11:08:19 am »
I apologise for not explaining myself clearly enough.

I meant that if you ever wanted two (or more) of the same type attached, in case you ever come across a situation where it would actually make sense (e.g. I can imagine two player input systems if you separated out local multiplayer controls in that manner - though of course that's another design decision altogether) then you'll only have to add another enumerated identifier to the list of acceptable identifiers rather than allow multiples of a particular type of component to be attached based on the type.

I hope that makes more sense now - if not, I'll give it another go :).

EDIT: Added some stuff to try to clarify things further.
« Last Edit: June 05, 2014, 11:10:09 am by select_this »
Follow me on Twitter, why don'tcha? @select_this

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Framework Design Advice
« Reply #8 on: June 05, 2014, 06:20:22 pm »
Components are used to represent an object. Sharing those between different objects does not seem logical to me even though some of their components' attributes may be shared. Of course copying components should be possible. The systems are supposed to take some components and modify them the same for every object depending on the data that they hold. If you want to implement a two players systems you can either create a different system for each player or implement the key bindings in the input component. In any case allowing for systems of the same type doesn't make any sense.

Since components possibly take parameters themselves, I don't think it's meaningful to only pass a type to the entity. Passing a function object that is used to create the component (i.e. a factory) is a possibility, but I wouldn't use this indirection unless it brings actual advantages. Points to consider are rather whether multiple entities might share components, and whether components can be used outside entities.

I don't see the big inconsistency here. It may seem so because of Python's dynamic typing, but in C++, the methods would be quite symmetric:
class Entity
{
public:
    template <typename Component>
    void AddComponent(Component c);

    template <typename Component>
    void RemoveComponent();
};

Another option is to allow to specify the constructor arguments as function parameters but I wouldn't call it an elegant solution. Like you suggested, it may actually be that I am using python.

@Lolilolight It's like you are typing random c++ concepts, I have no idea what you are saying.

~Veritas
« Last Edit: June 05, 2014, 06:21:56 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

select_this

  • Full Member
  • ***
  • Posts: 130
  • Current mood: just ate a pinecone
    • View Profile
    • darrenferrie.com
Re: Framework Design Advice
« Reply #9 on: June 05, 2014, 06:38:13 pm »
It was only a theoretical situation, and of course could be handled in other ways (after all, who's to say that a user of your framework won't create a component of which there can be multiple attached to a single object - assuming that you release your framework, of course :)). I'm just always wary of people going down the 'one and only one' road in any way as more often than not it doesn't stand up to scrutiny.

Personally I would still use an identifier rather than the type itself - that way I only care about the type when it is created, and I can then send that information to some form of monitor if I want meaningful diagnostics without RTTI, for example.

EDIT: fixed formatting
Follow me on Twitter, why don'tcha? @select_this

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Framework Design Advice
« Reply #10 on: June 05, 2014, 07:00:45 pm »
To be honest I haven't really prevented sharing a component, I just don't think it makes sense.
As for the enums, I am not using them because of maintenance reasons. I don't think it's necessary to maintain an enum for each component just to use them as identifiers. Would you mind to elaborate on only caring about the component type when created? Systems still have to check for them and retrieve them so maybe I am misunderstanding something.

~Veritas
"Lasciate ogni speranza, voi ch'entrate"

lezebulon

  • Full Member
  • ***
  • Posts: 235
    • View Profile
Re: Framework Design Advice
« Reply #11 on: June 05, 2014, 07:51:56 pm »
A bit off-topic, butI keep reading all over the web about how entity system is so great and useful, but tbh it seems like total BS to me. All you are basically  doing is creating a giant matrix of tickable boxes, then constantly poll aller over the place to see what is ticked. Also they never explain how the logic to actually exécuté action is supposed to work at all. To me it seems l'île this concept was popularized by people who are awful at programming and who think they can solve every issue by using a big 2d table and checking all the combinations...

select_this

  • Full Member
  • ***
  • Posts: 130
  • Current mood: just ate a pinecone
    • View Profile
    • darrenferrie.com
Re: Framework Design Advice
« Reply #12 on: June 05, 2014, 08:01:25 pm »
A bit off-topic...

Just a bit! The only entity framework I've ever used was a royal pain, but a more mature implementation would most likely have its uses (choose the right tool for the problem, rather than choosing a tool and then finding a problem for it)

Veritas - what I described doesn't apply to Python (which I'd totally forgotten about and only answered in a C++ context) so feel free to ignore it. I would only use enums if they had meaning e.g. were used in a translation table, so if you don't need that sort of thing then I agree, it's wasted effort.
Follow me on Twitter, why don'tcha? @select_this

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Framework Design Advice
« Reply #13 on: June 05, 2014, 08:29:37 pm »
A bit off-topic, butI keep reading all over the web about how entity system is so great and useful, but tbh it seems like total BS to me. All you are basically  doing is creating a giant matrix of tickable boxes, then constantly poll aller over the place to see what is ticked. Also they never explain how the logic to actually exécuté action is supposed to work at all. To me it seems l'île this concept was popularized by people who are awful at programming and who think they can solve every issue by using a big 2d table and checking all the combinations...

I also had troubles with finding info on how actual actions are implemented on an entity-component system. There is very little info on this specific area and the only examples deal with straightforward stuff that work great on a particle system but are pretty worthless when it comes to actual game logic. What I have done in some testing prototypes is implement all the logic in the systems and keep the data that implements the state in components.

Also keep in mind that what defines the entity-component system is not really keeping matrices but avoiding class hierarchies and keeping things decoupled. This can prove very helpful in larger games but for small games I also have my doubts.

By the way, what I also found very important is implementing a messaging system that runs independently from the main pipeline. This can prove very useful when implementing achievements or even if you don't want to continuously keep track of the components for stuff like GUIs. Not everything should be a component or a system.

~Veritas
« Last Edit: June 05, 2014, 08:37:50 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Framework Design Advice
« Reply #14 on: June 05, 2014, 11:04:55 pm »
Another option is to allow to specify the constructor arguments as function parameters but I wouldn't call it an elegant solution.
Yes, this is the emplacement idiom. Usually it is used to construct objects in-place because they cannot (or only expensively) be moved. You can also use it elsewhere, but it has the slight disadvantage of making the function a template, and thereby moving it to the header and abstracting the types (you get no IDE support for constructor parameters and you can't track where the constructor was called from (symbol references), plus compile errors get an additional level of indirection).

Personally I would still use an identifier rather than the type itself - that way I only care about the type when it is created, and I can then send that information to some form of monitor if I want meaningful diagnostics without RTTI, for example.
It's a possibility to have another form of component identifiers, at the latest when serialization is a concern. But I would not overenginner things from the beginning.

A bit off-topic, but I keep reading all over the web about how entity system is so great and useful, but tbh it seems like total BS to me.
Yes, it's one of the things that's widely hyped, but nobody seems to know how to implement it concretely. The very most websites show only the trivial and superficial things, while all the interesting (i.e. challenging) parts are omitted. That's also something that annoys me... you have to look at frameworks (usually a bad idea for C++) or think about it yourself (definitely a nice way, but takes time and some experience) or dig through forums.

Anyway, component entity systems are definitely useful because of their scalability. I assume with the 2D matrix you're referring to the setting where entities are columns and components are rows? Well, you needn't poll through all the entries and check whether they're there. An entity can hold a list of its components, so you only iterate through those that actually exist.
« Last Edit: June 05, 2014, 11:07:35 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development: