Add entity component system in cocos2d-x

I added this function in ComponentLua to get another Node’s script component. The purpose is describe above.

Another issue is that, because ComponentLua depends on liblua and lua-binding codes. I am not sure where to put CCComponentLua.cpp/.h. And i think it is not suitable to be added to libcocos2d.

Ok, i will have a try.

After thinking more about it. There are two problems:

  • We should do physics simulation after all Node’s position and rotation are updated. Using scheduler can not make sure of it. Because developers may register scheduler to update Node’s position/rotation
  • And when doing physics simulation, the codes should work like this
for (auto physicsComponent : physicsComponents)
    beforeSimulation();

do physics simulation

for (auto physicsComponent : physicsComponents)
    afterSimulation();

Handler can not do the same logic.

So i think scheduler is a good idea. But can not register every Node to scheduler. There should be a class(that’s call it PhysicsManager) register to Scheduler and do all the work. The priority of PhysicsManager should be lowest which makes sure it can be invoked after all handlers are invoked.

Done, i updated my codes, use pure CBA for physics component. What i did now is:

  • add PhysicsManager, it is registered to Scheduler, and will be invoked last
  • PhysicsManager manages all physics components as described above
  • remove System and SystemManager and other codes
  • only Director is related with PhysicsManager, all other codes is independent with physics codes

The test case is in cpp-empty-test.

@ricardo
What’s your opinion of it?
I will continue to use physics component to write all physics test cases in cpp-tests if you agree with the solution.

Thanks.

IMO Physics workflow should be more clear. I (and many others) have problems dealing with physics in cocos2d-x … always have stuttering problems and I have to make a lot of hacks: see changing number of steps, trying to make a fixed timestep or apply a low pass filter, these are common issues seen in these forums.

Dealing with schedulers and asuming a priority for the physics update is also an issue. Almost always I end controlling manually the physics update with setAutoupdate(false) (and it should be an exception, not the norm!)

Also the control over the underlying chipmunk is very poor, we need access to more chipmunk functionalities: I have to manually tweak the underlying chipmunk because cocos2d-x does not support rogue bodies (or kinematic in 7.0, which is the version should be in current builds, I do not understand why it is still 6.2).

IMHO physics needs to be worked and all users would agree they do not care if its CBS / ECS / Flat big object.

3 Likes

@Dredok
Thanks for your feedback.

What i am doing now is just using component to refactor physics codes but not refactor or fix bugs of it. Yep, i know there are many issues in physics integration, i think we should fix them in another task after using physics component.

Yep, physics should have fixed update, schedule can not fix it. But as i said, we should fix it after refactoring.

View from developer, it is right. No matter what is implemented internally, developers need the result. But if the codes is hard to maintain, then it will slow down engine development.

Yes, @dredok has a point.

Probably we need to review the physics code before refactoring that.
Perhaps ask users if they find our physics code useful ? or not ? and what should we improve…

The goal, of course, is not to break compatibility in v3… but if we are going to make some changes for physics in v3, perhaps we can add other changes as well to make the our users’ lives easier.

As @dredok mentioned, having a clear understanding when the physics code gets called will be very useful. Before update ? after the update ? before the actions ? when?

One thing that I would like to have defined in cocos2d-x, is the order of the updates,signals that are emitted in cocos2d-x.
Something like this graphic from SpriteKit’s programmers guide:
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Introduction/Introduction.html

Using physics component will not break too much, it just put codes across cocos2d-x into one place. After doing it, it is very clear when physics codes are updated, and it is very easy to change it.

Using component will not change the effect of physics integration, it is just make logic more clear. Refactoring physics integration is another thing, it will take more time to understand issues and limitations. So i think we should refactoring it based on more clear logical codes.

yeah, I know that. But since we need to write new code that dispatch the updates messages to the physics component, it is a good time to fix and document the order in which those events are triggered.

Ok.
I will update the documentation after the codes are merged.

The design is that, physics update will be invoked last in Scheduler.

// PhysicsManager is registered with INT_MAX priority in Schedule in Director

_physicsManager = new (std::nothrow) PhysicsManager();
// PRIORITY_PHYSICS is INT_MAX
_scheduler->scheduleUpdate(_physicsManager, Scheduler::PRIORITY_PHYSICS, false);

Current scene processing is:

  1. Scheduler::update(): run all actions, update and registered call back
  2. EventDispatcher::dispatchEvent(): invoke all event handlers
  3. Scene::render(): invoke all Node's visit function
  4. EventDispatcher::dispatchEvent(): invoke event handler of after-visit event
  5. invoke notification node’s visit function
  6. draw a scene

I think the best point is between 2 and 3.

@Dredok
What’s your opinion?

@zhangxm
In your schema I think between 2 and 3 is a good fit, so yes. But in my opinion current physics needs more work, not only the scheduler.Right now, as I said previously, almost always I have to set the autoUpdate to false and manage all the physics myself, to avoid stuttering, performance issues and other artifacts.
See for example these forum posts by @Heyalda

http://discuss.cocos2d-x.org/t/does-the-built-in-physics-engine-physicsworld-update-need-a-low-pass-filter-for-dt/21534/6

There is also no support for the last chipmunk (7.0) and there is no support for things like rogue bodies and other stuff.

IMO these concerns are more important, I know we are talking about ECS here and not the physics code itself, should we open a new thread for this ?

yes, sure. go ahead.

Yep, please create a new thread for it. It is very useful when fixing physics issues.

here you have
http://discuss.cocos2d-x.org/t/physics-issues-to-be-fixed/22713

I would suggest for this and any other manager/system to make it easy for the developer to change. I’m sure there are situations where you would want to run a scheduled update after all physics processing is done.

I changed it, let PhysicsManager be invoked as the same sequence as it is in v3.6. Because after Scheduler::update() is EventDispatcher::dispatchEvent(), we may modify Node’s properties(position, scale, rotation) in event listener.

And i also modified that, every Scene has a PhysicsManager as it is in v3.6. Because it is reasonable that, after a scene is active again by Director::runWithScene(), physics can take effect again.

Hi @ricardo,
I have some progress on physics component, but meet some issues. You are appreciated if you can give me a hand.

current progress

Currently, we implemented 6 test cases in tests/cpp-tests/Classes/PhysicsTest/PhysicsComponentTest.cpp using physics component.

how to test it

problems left

If you run Pyramid Stack test case, you will found these issues:

  • grossinis are jumped all the time
  • you can not select grossini but can for the ball(turn on Toggle debug to see it)
  • can not drag ball if there are grossinis around it

The codes to synchronize Node’s coordinate and physics body’s coordinate are in ComponentPhysics2d::beforeSimulation() and ComponentPhysics2d::afterSimulation(). The Corresponding file is cocos/physics/CCComponentPhysics2d.cpp. I think there are some issues about these two functions, but i haven’t found a method to resolved.

PS: i commented codes about physics integration in Node, Scene and some other places included in #if CC_USE_PHYSICS.

@zhangxm

The first thing that I noticed is that you changed the “logic”.

The previous algorithm was:

  • Scene -> updatePhysicsWorld()
  • PhysicsWorld -> Iterate all over the bodies and update it

Your current algorithm is:

  • Scene -> physicsManager();
  • For every component: Physics2d::afterSimulation(): you get transform, scale and rotation in world coordinates and updates the physics code
  • Simulate physics code
  • For every component: Physics2d::beforeSimulation(): get scale, rotation and transform and update from physics and update the nodes

My questions are:

  • Why do you need both afterSimulation and beforeSimulation ?
  • Why are you trying to get the world coordinates of the sprites ? Does the old PhysicsBody/PhysicsWorld do that ?
  • In any case, you are using a expensive 3d code to obtain the scale, rotation, transform. (eg: `Mat4::decompose() ). I would avoid using that function if possible… perhaps 2d version of it.

The fastest physics code is the one that is implemented in PhysicsSprite, in particular this function:

void PhysicsSprite::syncPhysicsTransform() const
{
    // Although scale is not used by physics engines, it is calculated just in case
	// the sprite is animated (scaled up/down) using actions.
	// For more info see: http://www.cocos2d-iphone.org/forum/topic/68990
    
	cpVect rot = (_ignoreBodyRotation ? cpvforangle(-CC_DEGREES_TO_RADIANS(_rotationX)) : _CPBody->rot);
	float x = _CPBody->p.x + rot.x * -_anchorPointInPoints.x * _scaleX - rot.y * -_anchorPointInPoints.y * _scaleY;
	float y = _CPBody->p.y + rot.y * -_anchorPointInPoints.x * _scaleX + rot.x * -_anchorPointInPoints.y * _scaleY;
    
	if (_ignoreAnchorPointForPosition)
    {
		x += _anchorPointInPoints.x;
		y += _anchorPointInPoints.y;
	}
    
    float mat[] = {  (float)rot.x * _scaleX, (float)rot.y * _scaleX, 0,  0,
        (float)-rot.y * _scaleY, (float)rot.x * _scaleY,  0,  0,
        0,  0,  1,  0,
        x,	y,  0,  1};
    
    _transform.set(mat);
}

As you can see, the code is not decomposing the matrix and trying to get the world coordinates. This could be seen as a bug… or feature.
But in any case, we should be backwards compatible. I haven’t reviewed the v3.0 Physics code in depth, but if you are trying to replace that code, then you must be 100% backwards compatible with it… so I would use the algorithms that are being used by it.

UPDATE:
So, I believe that the old “PhysicsWorld” code actually uses the world coordinates for the nodes, but I’m not 100% sure.
The physicsworld code is very difficult to understand, and we must be 100% backwards compatible with it.

So, I guess that the 1st step is to understand it: what is the its design ? How does it work ?
And after that implement that design in the component.

Old code also do the same thing. As you can see, what it does is

  • Scene -> updatePhysicsWorld
  • PhysicsWorld: Scene::updatePhysicsBodyTransform(): get scale, rotation and transform and update from nodes to physics this is the same effect as PhysicsManager::beforeSimulation
  • PhysicsWorld: do physics simulation
  • Scene::Visit() -> Scene::processParentFlags() -> Scene::updateTransformFromPhysics() -> Scene::draw(): before drawing, it synchronizes physics to nodes, it is the same as PhysicsManager::afterSimulation()

So i didn’t change the logic, just make things more simple.

Physics world only accepts world coordinate. So i needs to get Node’s world coordinate, then pass it to physics world.

Yep, i will void using it if there is an easy way to get Node’s world transform. I will take a look of PhysicsSprite.