0
\$\begingroup\$

I'm reading that Components in ECS are just only data and have no logic. I'm trying to follow this and I got that working:

class Sprite : public Component
{
public:
    Sprite(const std::string &textureid = "");

    std::string getTextureId() const;
    void setTextureId(const std::string& textureid);

    void setColor(const Color& color);
    Color getColor() const;
private:
    std::string m_textureid;
    Color m_color;
};

Now in my SpriteRenderingSystem I have to create every frame for every Entity that has a Sprite component a SFML Sprite object (I'm using it right now), set the parameters of that object, render it and destroy it. I know it's not optimized and I don't like it.

I think that I can use my component to hold a pointer to the SFML::Sprite object and then I only have to render it every frame without having to create/set/render/destroy it as you can see there:

class Sprite : public Component
{
public:
    Sprite(const std::string &textureid = "");

    std::string getTextureId() const;
    void setTextureId(const std::string& textureid);

    void setColor(const Color& color);
    Color getColor() const;

private:
    friend class ::SpriteRenderSystem;
    std::shared_ptr<sf::Sprite> m_sfspr;
    std::string m_textureid;
};

Am I breaking the "only data in a component" rule here? If so, what other alternatives are there?

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Why does the rule matter if it still works as intended? \$\endgroup\$
    – Sturlen
    Nov 29, 2015 at 12:51
  • \$\begingroup\$ Of course it works, I'm just asking how can I improve it. I'm out of ideas right now :). Just for testing out new things. \$\endgroup\$ Nov 29, 2015 at 12:54
  • 1
    \$\begingroup\$ If you're looking for critique on your code I'd recommend asking this on Code review. \$\endgroup\$
    – Sturlen
    Nov 29, 2015 at 12:58
  • 2
    \$\begingroup\$ @ZEKE purists claim that proper ECS will result in clearer code and increased performance. I've yet to see any data that proves it. Just lots of beginners fighting with non-issues. :) \$\endgroup\$
    – snake5
    Nov 29, 2015 at 13:02
  • \$\begingroup\$ I think the goal of keeping components as plain data is to discourage the pattern foreach(component) {component.DoStuff();} in favour of system.DoStuff(batchOfComponents); so that you're less likely to write code that's inefficient for batch processing. The example you've shown doesn't really violate this goal, so I wouldn't worry about it. \$\endgroup\$
    – DMGregory
    Dec 11, 2015 at 17:13

1 Answer 1

2
\$\begingroup\$

The way to make your components "only contain raw data" is to write them so they "only contain raw data."

What specifically "only contains raw data" means is up to you; there are several ways you could interpret it, and whether or not those ways make sense depends on the implementation you're building.

The two most common interpretations of "only containing raw data" I've seen are as follows:

  • The component is literally a POD/standard layout/trivial structure. This distinction is important when your implementation will make use of copying via memcpy to move component data around and doesn't want to deal with the potential overhead of copy constructors, non-standard-layout bits in the data (such as the virtual function table pointer), complex ownership, or any of the other things you don't have to deal with if your types conform to one of those three concepts.

  • The component has no non-trivial behavior, it just consists of fields of data and possibly some trivial bits of utility behavior (functions to access the data or mutate it in a simple form while maintaining invariants). This distinction tends to be important if your implementation is focused on centralizing complex logical behavior outside of the components themselves (typically within "systems"), either for maintainability or to allow that behavior to exist as a synthesis of data from multiple, possibly-heterogeneous sets of components per entity.

(There are of course other ways to interpret the phrase, but these are the two most common in my experience.)

Both sprite implementations you posted "only contains raw data" in the second sense. There's absolutely nothing wrong with this, and no need for you to fervently excise all member functions or non-POD fields from your component types if this approach is working for you. If the second one is more straightforward or more optimal (as it sounds, from your description), then use it. It's fine.

Don't let dogmatic purism get in the way of actually making your game work and work well.

\$\endgroup\$
4
  • \$\begingroup\$ Your case #1 seems to be about making data public. Standard layout rules don't require public members; only that they are all at the same access level. Also, standard layout is for layout compatibility between objects (and between C representations thereof). Trivial copyability is what deals with bitwise copying. And you can have both standard layout and trivial copyability while still hiding your data and maintaining invariants. Ownership of resources is usually what defeats trivial copyability. \$\endgroup\$ Dec 11, 2015 at 16:54
  • \$\begingroup\$ Good points, although I don't think the first approach is really about making the data public -- I think it's mainly about being one of POD/standard/trivial. Whichever is wanted by the rest of the system. In practice every implementation I've seen using this approach does make everything public, but I feel like that wasn't the point. It's not an approach I tend to write myself. \$\endgroup\$
    – user1430
    Dec 11, 2015 at 17:01
  • \$\begingroup\$ Thank you :) . One thing I don't like is the friendship with the system to not give public acces to the internal data std::shared_ptr<sf::Sprite> m_sfspr; but I think this will be harder to hide it without the friendship. \$\endgroup\$ Dec 14, 2015 at 17:20
  • \$\begingroup\$ @FrameBuffer I wouldn't be afraid of "friend;" it's true that it can be misused easily, but if used sparingly and in a way that prevents API abuse or otherwise makes it easier to use an API correctly, it's not a bad thing. \$\endgroup\$
    – user1430
    Dec 14, 2015 at 17:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .