• Advertisement

C++ ECS : same transformation component for both physic and graphic

Recommended Posts

I encapsulated Physics and Graphics with ECS successfully.

Here is a simplified version :-

Physics Rigid Body = Physic_MassAndInertia + Physic_Transform + Physic_Shape
Graphic Polygon Body = Graphic_Transform + Graphic_Mesh

I usually set their transform via :-

findService<Service_PhysicTransform>()->setPosition(physicEntity,somePos);
findService<Service_GraphicTransform>()->setPosition(graphicEntity,somePos);

It works so nice, and there is no problem in practice, because I always know its "type" (physic/graphic).  

However, I notice that Physic_Transform and Graphic_Transfrom are very similar and duplicate.

For the sake of good practice and maintainability, I consider to group them into Generic_Transform.

findService<Service_Transform>()->setPosition(anyEntity,somePos);  //cool

However, there is a little difficulty.  The physic transformation is quite complex for a child inside a compound body.

Assume that a physic body B is a child of a compound body C.   In this case, B's transformation component currently has no meaning (by design).

If I want to set the child transformation setTransformation(B,(x,y,45deg)), my current physic engine will not set the B's transformation directly - it will set C's transformation that make B's position match (x,y,45deg).

image.png.8d081eca5b014314aee226fc7564697d.png

Thus, it is not so practical to group them, except I code it like (ugly and worse performance):-

class Service_Transform{
    public: void setPosition(Entity B,Vec2 pos){
        bool checkIsPhysic = .... //check if B is physic
        if(checkIsPhysic){//physic
            Entity compound = .... //find C
            ComponentPtr<Transform> cCompound = compound.get<Transform>();
            cCompound->pos=pos*someValue; //calculate some transformation for C
        }else{//graphic
            ComponentPtr<Transform> cTransform=B.get<Transform>();
            cTransform->pos=pos;
        }
    }
}

Should I group them  into 1 type of component?  

I probably should not group them because its meaning and related implementation are very different, but I feel guilty ... I may miss something.

Advice about other things are also appreciated.  Thanks.

Edit: Hmm... I start to think that grouping component is OK, but I should still use 2 function (from different service/system).

Edit2: fix some code (pointed out by 0r0d, thank)

Edited by hyyou

Share this post


Link to post
Share on other sites
Advertisement

I don't think you should have two different transform components, no. You shouldn't implement the physics using the ECS-transform-component eigther. Physics is a vastly complex  system that requires (or at least should have) a separate implementation. So optimally, you'd just use the transform-component to communicate between the different system, which keeping a separate view of the physics-world:

struct PhysicsComponent
{
  RigidBody* pBody; // pointer to a body aquired from the physics-world upon initialization => could also be queried instead
};

struct PhysicsSystem
{
  
  void Update(double dt) // just to showcase...
  {
    world.Tick(dt);
    
    for(auto entity : EntitiesWithComponents<Physics, Transform>())
    {
      auto& physics = entity.GetComponent<Physics>();
      entity.GetComponent<Transform>().SetTransform(physics.pBody->QueryTransform());
    }
  }    
  
private:
  
  PhysicsWorld world;
};

(This is just some pseudo-code to get the idea across). So essentially Transform becomes a point for communication between different systems. What the physics-system wrote in, you can later read out ie. in the RenderSystem; also your gameplay-systems can just set the transform as well. Entities just register new rigidbodies to the physics-world, but the world doesn't know that an entity even exists, which keeps your physics separated & more flexibel. For example, its pretty easy in this system to exchange your physics with say bullet at some time, while what you originally do creates a sort of tight coupling between those independant modules.

As a general consensus, you should only implement the bare minimum required functionality inside the ECS, if possible. Do not use ECS as a basis for physics, rendering, HUD, ... but instead implemented those systems separately, and use the ECS as the high-level intercommunication-layer. At that, it really excells IMHO, but everything more will just result in many of the reasons why people despise ECS.

Hope that gives you a rough idea.

Share this post


Link to post
Share on other sites

I'm of the opinion that you should always have distinct, domain-specific representations of information.  Physics and rendering are two different domains, thus two transforms. You can split hairs over where these transforms live, and whether or not rendering/physics is part of the ECS, but regardless of where they are you should have one transform owned by "physics" and one transform owned by "rendering" (at least). Don't get distracted by the fact that they may appear similar or duplicate -- they are completely distinct conceptually. If you allow them to both own the same transform, you'll quickly find them fighting each other over even simple changes to functionality.

Don't think of data as a "point of communication". Communication is a behavior, and in the absence of code the only place behaviors exist are in assumptions and inferences. It may be fine for the rendering code to assume the transform mean one thing, and for the physics code to assume the transform mean something else, but when it comes to communicating that information across domain boundaries, you should't force the rendering code to make assumptions about what the transform means to the physics code and vice versa. This is exactly the type of behavioral coupling you'll come to regret

Edited by Zipster

Share this post


Link to post
Share on other sites
11 hours ago, hyyou said:

 


findService<Service_Transform>()->setPosition(anyEntity);  //cool

 

I'm not clear what the above code is supposed to do.

Share this post


Link to post
Share on other sites
15 hours ago, Juliean said:

... but instead implemented those systems separately

Thank Julien. 

In a more real case, I structure it like this :-

rocket = HP + physic_owner + graphic_owner + ....
Physic body = physic_ownee + bulletBodyAndShape_holder 
         + cache_physicAttr(pos,v,gravity,mass,mask) + ...
Graphic body = graphic_ownee 
         + ogreBody_holder (Ogre2.1 graphic body)
         + cache_renderAttribute(pos,blend_mode,layer) 
         + mesh_owner
mesh = mesh_ownee + filename 
        + ogreMeshPtr(point to vertex buffer)

The physic_owner / graphic_owner / physic_ownee / graphic_ownee  are just attach-point.  

If I want to create a rocket, I tend to create 1 rocket entity attach to 2-3 new physic body, 2-3 new graphic body.     The graphic body attach to a shared mesh.   Thus, for a single rocket, I create around 5-7 entities.

I copied the idea from https://gamedev.stackexchange.com/questions/31888/in-an-entity-component-system-engine-how-do-i-deal-with-groups-of-dependent-ent , not sure if it smells. ...  

For me, it works pretty good, but I may overlook something.

P.S. Your comment in another topic of mine boosted my engine's overall performance by 5%. Thank a lot.

7 hours ago, Zipster said:

Don't think of data as a "point of communication"

That is enlightening, thank.

@0r0d  You are right. I have edited the post :)

Edited by hyyou

Share this post


Link to post
Share on other sites
4 hours ago, hyyou said:

Thank Julien. 

In a more real case, I structure it like this :-


rocket = HP + physic_owner + graphic_owner + ....
Physic body = physic_ownee + bulletBodyAndShape_holder 
         + cache_physicAttr(pos,v,gravity,mass,mask) + ...
Graphic body = graphic_ownee 
         + ogreBody_holder (Ogre2.1 graphic body)
         + cache_renderAttribute(pos,blend_mode,layer) 
         + mesh_owner
mesh = mesh_ownee + filename 
        + ogreMeshPtr(point to vertex buffer)

The physic_owner / graphic_owner / physic_ownee / graphic_ownee  are just attach-point.  

If I want to create a rocket, I tend to create 1 rocket entity attach to 2-3 new physic body, 2-3 new graphic body.     The graphic body attach to a shared mesh.   Thus, for a single rocket, I create around 5-7 entities.

Are these all single components? phyiscs_owner, phyiscs_ownee, HP, ...? If so, it does sound needlessly complex, as I laid out in the other post there's really not much point of having stuff like "filename" as a component, but you should see for yourself ;) On the same note, having 5-7 entities for a single rocket sounds like huge overkill. Personally, I would have a rocket-mesh with a Mesh & Physics-component, and thats pretty much it; unless the rocket specifically needs sub-entities like a particle-emitter :D (Thats just what I came to agree on based on 4 years of working with an ECS across different projects; you might come to a different conclusion; though based on your workflow/toolchain, a certain few things like having many components & entities can make creating new content a nightmare).

12 hours ago, Zipster said:

Don't think of data as a "point of communication". Communication is a behavior, and in the absence of code the only place behaviors exist are in assumptions and inferences. It may be fine for the rendering code to assume the transform mean one thing, and for the physics code to assume the transform mean something else, but when it comes to communicating that information across domain boundaries, you should't force the rendering code to make assumptions about what the transform means to the physics code and vice versa. This is exactly the type of behavioral coupling you'll come to regret

I honestly don't agree to that statement. Whats wrong with using a Transform-component to communicate information between different systems, that all might have their own internal transform-structure? You're using data to transfer information anyways at some point, like sending information across a network, or storing and later loading a save-file. There's not code inside the save-file, its just data at that point, but because you agreed on a format, its save to store at one point and load at a later point. Thats how I see it with this example of the transform as well: You agree on a shared "format" for world-transform on a shared "Transform"-component, and systems can read/write to the transform while still being able to internally use their own data. You can still use buisness-logic to make the writing/reading of this transform-data more safe, but I don't see the benefit of code-based communication vs a data-"stream" of sorts.

Share this post


Link to post
Share on other sites
9 hours ago, Juliean said:

I honestly don't agree to that statement. Whats wrong with using a Transform-component to communicate information between different systems, that all might have their own internal transform-structure?

Actually it seems we do agree, at least on each system having its own internal structure. I just like to think in terms of domains as opposed to systems, since I find the conceptual delineation better for determining when different representations are actually needed. However if your systems are 1:1 with behaviors (physics, rendering, etc.), then it's essentially the same :)

I'm also in full agreement with having a separate component handle the transfer of data between systems, but in that case I don't see a need for any intermediary shared state between them. I'm not even sure what purpose it would have, or if it's feasible in the first place. How would you reconcile an OOBB or capsule used by physics, with a matrix transform used by rendering, with a sphere used by gameplay, all into a single representation that makes sense? And why would you even need it if each system already has sufficient internal state to function? The purpose of the transform component in this case would just be to copy the data from one system to another and perform any necessary conversion. Instead of each system assuming internal details about the other, you've inverted your dependencies, shifted that responsibility to a higher level, and made the data relationship explicit in code for all to see.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By nickyc95
      Hi.
      I'm kind of late to this party but I thought I would ask anyway as I haven't found a concrete answer.
       
      When creating a game engine, when should you choose one methodology over another (more specifically OOP and DOD)? Which areas benefit from DOD? Which areas benefit from OOP? Do people typically mix multiple methodologies throughout a project? I.e. certain sub-systems created in one, and others in the another?  
      DOD - Data Oriented Design
      OOP - Object Oriented Design
       
      Pretty simple
      Thanks
    • By Estra
      Memory Trees is a PC game and Life+Farming simulation game. Harvest Moon and Rune Factory , the game will be quite big. I believe that this will take a long time to finish
      Looking for
      Programmer
      1 experience using Unity/C++
      2 have a portfolio of Programmer
      3 like RPG game ( Rune rune factory / zelda series / FF series )
      4 Have responsibility + Time Management
      and friendly easy working with others Programmer willing to use Skype for communication with team please E-mail me if you're interested
      Split %: Revenue share. We can discuss. Fully Funded servers and contents
      and friendly easy working with others willing to use Skype for communication with team please E-mail me if you're interested
      we can talk more detail in Estherfanworld@gmail.com Don't comment here
      Thank you so much for reading
      More about our game
      Memory Trees : forget me not

      Thank you so much for reading
      Ps.Please make sure that you have unity skill and Have responsibility + Time Management,
      because If not it will waste time not one but both of us
       

    • By mike44
      typedef struct FontInfo_t* FontHandle;
      MichaelNS::FontMgr m;
      MichaelNS::FontHandle FontGeladen;
      const char *FontPtr;
      start:
      InFileFont.append("Ubuntu-B.ttf");
      FontPtr = InFileFont.c_str();
      FontGeladen = m.loadFont(FontPtr, NULL, NULL, 14, 0);
      stop:
          m.unloadFont(FontGeladen);
          delete FontPtr;
      delete causes a crash. How to unload the class correctly?
      Thanks
    • By Cat's machete
      I need to use global vars,
      So say i have main.cpp file where i #include "globalvars.h"
      In globalvars.h i define
      extern int ppp;
      In globalvars.cpp i put #include "globalvars.h"
      int ppp;
       
       
      Now in main cpp during compilation i get UNDEFINED REFERENCE TO ppp
    • By smit patil
      Can I use SFML to create a android app and is it good idea to do so ? I am so confuse right now to choose between SFML and SDL to learn . I liked SFML cause it is more Object oriented but Don't have android support(yet) on the other hand SDL have android support but is C based API.Some post on github tell that it is possible to use SFML in android but my question is that is it good Idea to do so? will app made in SFML C++ perform worse or equal to Java app on android ? I am really noob to Android and Game development.I can Only make some small Android apps using Java on Android and some some Calculator level apps using GTK+ on Linux, So I am pretty noob here. I don't know which to chose SDL or SFML but as My requirement for Android and Linux please also suggest me that should I choose SFML or SDL for learning ?
      Thanks for help in advance
  • Advertisement