Add entity component system in cocos2d-x

Ok. So the first step is to keep using the same logic. Don’t make it simpler. If that works, only then try to make it simpler.

Regarding beforeSimulation and afterSimulation I don’t think we need that… you are iterating all over the nodes twice. The iteration alone could be expensive when having many nodes. So I would try to avoid if possible.

Yes. Correct. If you use a flat hierarchy, then it should work with relative positioning… at least that is how PhysicsSprite work. But it is buggy. So, it is better to use World coordinates (also because that’s how PhysicsWorld works).

If so, then i have no idea what physics component will do. As you can see, now the component has less codes. And if using the old logic, then i don’t know what i can do with component codes.

We should synchronize nodes to physics before physics simulation and synchronize physics to nodes after simulation. The old codes just want to re-use Node::visit() iteration, that’s why it inserts codes into Node. I can re-use it too after the effect is correct. It is about optimization, but first, i should make the logic correct.

Yep, i just reviewed the codes of PhysicsWorld. It doesn’t think about anchor point and node tree relationship. It only works if the anchor point is center, and Node’s position is the same as world coordinate.

Now the problem is, when i get position, rotation from physics world, how can i set it to Node. Because the return value from physics is world coordinate, how can i translate to relative position and rotation and thinking about anchor point.

After looking into the codes in detail. I think it is reasonable to have a try. I will use component and physics manager instead with the same logic.

What I mean by that, is to use the “physics logic”. Your code is not using the same physics logic. As an example, your are using different math code.
So, the math should be the same, the algorithms should be the same.
But the workflow should be different.

If that works as expected, then feel free to simplify the math/algorithm.

the component, what should do is to use the same physics logic, but with a different workflow.
The component should be in charge doing the physics simulation, and not Node. But the the physics code should be the same.

What you are doing is 2 things at the same time:

  • change the workflow: from Node to Component (we need this)
  • Simplifying the physics code (we also need this).

But now you have some bugs… so we don’t know why… so the best thing to do, is to do one step at the time.

@ricardo
I changed the codes as you suggested in branch physics-component-use-old-logic.

As you can see, i used the same logic as before, just modify the logic of Node::updatePhysicsBodyTransform() and Node::updateTransformFromPhysics.

I am not sure if it is what you mean. But the result is the same. Also has the same bugs.

@zhangxm Ok. I’ll try that branch and I’ll let you know my findings.

@zhangxm

I couldn’t run your “physics-component-use-old-logic” branch. Whenever I run the “Node: Physics Component” test, it crashes… actually it enters into a recursive infinite loop. The function MarkSubtree enters into infinite recursion.

For the sake of clarity, what I meant is this:

  • There are some bugs in your branch.
  • But for me it is difficult to understand the cause of the bugs when the code has too many changes: a) new math code, and b) code moved from one file to another.
  • So in order to identify the bug, my proposal is to do the changes one step at the time: First move the physics logic from Node to ComponentPhysics2d.
  • And only after testing that, you should try to optimize / change/ simplify the physics/math code.

As someone who has used Cocos2d-x exclusively for the past couple of years and just started using Unity for prototyping, I do think the component model Unity uses makes a lot of sense, is simple, and integrates beautifully with their editor.

Of course there are all kinds of restraints, but if you guys manage to get anywhere near their implementation I would be ecstatic.

The way I can compose my own custom game objects by adding built-in components (majority of boilerplate design), then adding specific game behavior by attaching a script component is super easy. Like you guys have said, your equivalent scripting language wouldn’t be C#/JS but LUA/JS/etc.

Unity has black-boxed APIs but in your case that would just be an extendable C++ core? Everything could still be done in C++ programmatically as usual (including creating and setting properties on components and their parent objects), while unifying tons and making the CocosStudio editor much more capable with this new model.

I’m excited to see where this goes.

But the codes can not just be put from Node to component just copy&paste. As you know, the old codes will add physics body and remove physics body directly. Now we changed to use physics component, so we should comments the codes related with physics body directly.

As you can see, now the codes is almost the same as before, the work flow is the same, and only just modify Node::updatePhysicsBodyTransform() and Node::updateTransformFromPhysics. Because these codes is something related with physics body directly, so i can not just move the codes into physics component.

Yep, the first case has some problem. I will try to fix it.

There are some codes in PhysicsShapePolygon::updateScale(), it is invoked when a shape is scaled, the codes are:

I have some questions about these codes:

  • when a shape is scaled, its normal should be changed?
  • what’s the meaning of cpSplittingPlane.d?

After i changed the codes to

for (int i = 0; i < count; ++i)
    {
//        cpVect n = cpvnormalize(cpvperp(cpvsub(vects[i], vects[(i + 1) % count])));
//
//        planes[i].n = n;
//        planes[i].d = cpvdot(n, vects[i]);
        
        planes[i].d = cpvdot(planes[i].n, vects[i]);
    }

Many things work correctly:

  • grossines will not jump
  • can select grossines

You can use my physics-component branch to have a test.

there are some bugs regarding to scaling in physics2d. Might it be related ?
This one is schedulled for 3.8:
https://github.com/cocos2d/cocos2d-x/issues/11955

Quick fix: It doesn’t compile:

/Users/riq/progs/cocos2d-x/cocos/physics/CCPhysicsBody.h:309:18: Inline function 'cocos2d::PhysicsBody::getNode' is not defined

but if you remove inline from here, it will compile Ok:

    /** get the sprite the body set to. */
    inline Node* getNode() const;

The other thing, is that I can still see the bouncing grossinis.

Perhaps I’m missing something, but why copying & pasting doesn’t work ?

A component, they way I see is, should be able to transform the Node in any possible way.

So, if Node has some hardcoded physics logic in it, it should be possible to copy & paste it and put it in a component… just copy & paste the logic and all the physics ivars that are needed.

If you post an example with code regarding why you can’t copy & paste the logic, I think I will be able to understand why it is not possible to reuse the same logic.

Thanks.

UPDATE:

So, what I think is needed in order to copy&paste the original code, is to add one more callback to Component… I think you need to broadcast visit. eg:

uint32_t Node::visit(const Mat4& parentTransform, uint32_t parentFlags)
{
    uint32_t flags = parentFlags;
    for(auto component: _components)
    {
        flags |= component->visit(parentTransform, parentFlags);
    }
...
}

and in ComponentPhysics2d::visit you need to do something similar to updateTransformFromPhysics(parentTransform, parentFlags); .
At least that is how I would do it in order to be as compatible as possible with the previous code.
And also it gives more flexibility to other components. On the other hand, this could slow down the visit if not done correctly. Perhaps when you register a component, you pass a Hint flag saying which functions are overriden…

@ricardo
Sorry, i forgot to push my codes to remote. I think it works now.

Node::updatePhysicsBodyTransform() depends on some codes done in Node::setPhysicsBody(). Node::setPhysicsBody() and Node::addComponent() are two different ways to connect Node and physics world. And there are many codes depends on Node::setPhysicsBody().

Node has many member variables, such as _physicsBody, _physicsScaleStartX, _physicsScaleStartY and so on. These variables makes Node and physics body tightly coupled together. If we make physics component did the same way, then physics component will be tightly coupled with Node as current.

I worked out why can not drag ball in PhysicsComponentDemoPyramidStack test case. The reason are:

  • when Node is scaled, corresponding physics body’s mass is not re-calculated again
  • then grossini’s mass is more bigger than ball(grossini is scaled by 0.13, ball is scaled by 3)

Why are old codes work?

  • When you scaled a Node, you should scaled its corresponding physics body’s scale by yourself

As you can see

  • if you scaled Node before setting physics body, you should scaled it when creating the physics body
  • if you scaled Node after setting physics body, you should create a new physics body with scaled factor and replace old one

physics body doesn’t support scale property. But our physics integration was designed to support it(PhysicsBody::setScale()), and it doesn’t support it well.

@zhangxm so you have it working now? yeah!

Yep, it fixed the problem in PhysicsDemoPyramidStack.
But there are other problems met in other test cases. I will continue to fix them.

Just wana say,

Keep up the good work guys! :smiley:

I’m always following the discussion! :smiley: