Add entity component system in cocos2d-x

I added some comment of Unreal on top of the thread.

Exams Are OVER Yaaaaaaaaaaaaaayy :stuck_out_tongue_winking_eye:
Time to attack visual studio :alien:

1 Like

@Packora
What did you mean?

@ricardo I have finished the lua component in my entity-component branch. The corresponding commit is here.

I modified lua-empty-test, and it only works on Mac.

As you can see, it is easy to invoke a lua function. But if you want to do more thing in the lua function, then you need to do more binding codes. And i think it is another topic.

Node::update() will invoke Component::update() if it is enabled by Node::scheduleUpdate().

@zhangxm ok thanks.
Tomorrow (wednesday) I’ll take a look at it.

@zhangxm
I took a look at your code. I think is a good staring point, but we need a much more complete example.
The idea is that you can associate components to Nodes (either from C++ or Lua).

The use case that I want to solve is more or less this:

  • you have a Sprite in C++ (probably created by Cocos Studio reader, but that is another case)
  • you extend it with a Lua (or JS) component.
  • the Lua/JS component should override onEnter, update(), and others

So, instead of just calling a “Lua function ‘update’” from C++, it should call the Component method ‘update’.

Does it make sense what I’m saying?
thanks.

Additional use case:

  • the Sprite is created in Lua instead of C++ (although I don’t know if this use case is ver common in scripting).

@zhangxm

Let’s do this:

For v3.x

  • Re-use existing Component code created by CS team
  • Fix, polish, extend the Component code when needed
  • Add tests, examples, examples, examples and more examples and documentation about it. A complete game using components… that’s they way to make sure it works.
  • Users should be able to extend Nodes from C++, Lua and JS
  • Rapid-prototyping / fast iteration is the priority.
  • Provide onEnter, onExit, onUpdate events… what else? onCollision?, … how to propagates the input events? Should we have an input object and the user should poll it ?
  • Backward compatibility

Users who want high-performing ECS in v3.x should use Entity-X. v.3x won’t try to solve the problems that ECS like Entity-X solves.

For v4.x:

  • Gather requirements from v3.x users… are they happy with v3.x approach? do they want more performance?
  • We cannot define the v4.x roadmap yet, but including a complete ECS like Entity-X is one option.
2 Likes

Component’s update is invoked, and it invoked corresponding lua function.

Current lua component is nothing about current lua binding. So it can not create a Sprite in lua. Did you mean lua component work together with current lua binding codes?

Yep. I need more test cases. Any one who can provide a test case is appreciated.

It is the goal. But as said above, i need test cases.

Again, i need test cases.

So the first thing is to collect test cases, then we can discuss and implement every test case. If test cases are complete, then it can cover most requirements needed in developing a game.

I am trying to implement lua component like this:

// c++ codes
auto luacomponent = LuaComponent::create("PositionComponent.lua");
auto sprite = Sprite::create("mypicture.png");
sprite->addComponent(luacomponent);
// PositionComponent.lua
PositionComponent = {
    -- the update function will be invoked when sprite's update function is invoked
    function update(dt)
        -- self:getOwner() will invoke LuaComponent::getOwner()
        local owner = self:getOwner()
        local x, y = owner:getPosition()
        owner:setPosition(x+10, y+10)
    end
}

I think it is possible to implement it, i have to be familiar with binding codes first. Because the binding codes will binding all LuaComponent’s functions to lua, and i will make it invokable in lua.

The usage is different from current lua binding mechanism. In current lua binding model, c++ object should be created in lua side. But in our use case, we create c++ object in c++ side, and we should create a lua object to associated with the c++ object.

yeah, that :smile:

Another thing to take into account is that what ever we do in v3, must work in v4… even if we decide to implement a complete ECS in v4… I think I know more or less how to do it. But for the moment let’s focus on v3.

There must be a way to do it… it shouldn’t be that complex.
I mean, Node::onEnter propagates the “onEnter” event to JS nodes… so somehow, C++ Node has a reference to the JS Node.
And also, the JS bindings create JS object that contains the C++ object… so my guess is that we don’t need to create new bindings or anything like that… we just need to call the correct functions… we are already using them in the bindings… we just need to read the bindings code again.

Yep, i have to be familiar with the binding codes to retrieve needed information.

A big progress that lua script works as expected. The codes is here. You can run lua-empty-test on Mac.

The lua script is

https://github.com/minggo/cocos2d-x/blob/entity-component/tests/lua-empty-test/src/hello.lua#L2

As you see, it will change its owner’s position.

The limitation is that, cc.ComponentLua is a global table. If there are two components define cc.ComponentLua.update, then only one modification take effects, and two components share the same function.

I planned to create an instance for each component, but the problem is that, i can not get the instance in c++ side, for example if i wrote the script like this

MoveComponent = {
    update = function()
        ...
    end
}

MoveComponent is a global table, but i can not get it from c++ side, because c++ doesn’t know a table named MoveComponent is created. The alternative method is that, the script should return a local table to c++ like this

local MoveComponent = {
    update = function()
        ...
    end
}

-- this local table will be accessed in c++ to get the `update` function
return MoveComponent

Done.

Now the script is

local Movement = {

    update = function(self)
        local owner = self:getOwner()
        local x, y = owner:getPosition()
        owner:setPosition(self:getPos(x, y))
    end,

    getPos = function(self, x, y)
        return x+1, y+1
    end
}

return Movement

As you see, self can invoke ComponentLua::getOwner() and Movement.getPos().

Because ComponentLua needs to invoke lua functions, so should link lua lib and other binding codes. That’s why i put ComponentLua.cpp/.h in lua-empty-test.

Following i will continue to send more events to lua, such as touch event.

1 Like

As discussed with @Walzer, entity component doesn’t have to be finished in v3.x. And he though physics2d integration is not good, it added two many physics related codes in Node.h/.cpp.

So i started to refactor physics integration with component.

1 Like

@zhangxm @walzer
For v3.x, we should use something like what you did.

I think what you did is ready for v3.x since it is 100% backward compatible, and works Ok, and people can extended nodes without subclassing, and if people want to extend Node with “data” and not “code”, then can do it using Lua and JS.
So, I would add it perhaps for v3.8… but we need more tests, tests, and tests: Lua tests, JS tests, C++ tests… Perhaps code a little game using it.

For v4.x, but we must re-think the whole ECS again.
We really need to modernize cocos2d-x… we must support multi-cores, be faster, much faster… and the way to achieve that is by having “systems”: particle system as a system, the transforms as a system, physics as a system… etc.
And v3.x components must work in v4.x too… and I think I know how to do it.

4 Likes

@ricardo
What i did is 100% backward compatible until now.
What i did for script component is all about researching lua and lua bindings. And i have to do research about js and js-bindings too if we continue the work. It is no too related with ECS.

If we want to continue the work, then i think we should think clear what it want to achieve. What problems it wants to resolve.

And i already add systems when refactoring physics component. I will upload my codes when it finishes. It can be done in one or two days. Then you can have a look no matter if we need it or not. But it may be a good research for v4.x.

About which way to go, let’s discuss it through weekly meeting :slight_smile:

sure. let’s discuss it tomorrow.
Just to give you more info about what I think we should do in v4 is decouple “data” from “code” from the scene graph (basically we need “systems” and “components”). We need a complete ECS for v4.

Not only it will allow users to extend nodes without subclassing (this is what we are doing right now for v3), but it will allow us to use multiple cores (one per system perhaps), or even use SIMD instructions for the systems, or perhaps use multiple threads to execute the systems…
Basically it will allow us to have very efficient engine for modern mobile devices :smile:

2 Likes

Why bother with it in 3.8 if you are going to re-do it in 4? Seems like it would just make it harder in 4.0 to have to maintain compatibility with the 3.8 line, why not just push any ECS additions to 4.0 so you can have the flexibility. What’s the pressing need for earlier support?

Just curious from a project planning point of view.

Rick

1 Like

@Rick_S
Good question.

Actually we have components in v3… although they are not finished.
Finishing what was started is rather easy… we just need to add a few callbacks to let people extend Nodes from JS / Lua. That is what @minggo did.

And I don’t want to change that in v4… In fact having the possibility to extend Nodes in JS and Lua is a good thing. So, I don’t want to change that interface.

But we need a complete ECS, not because it is needed to extend Nodes (we will have that in v3.8), but because we need a modern architecture to improve the performance.

An also it is a question of “time-to-market”: Doing the v3.8 approach is quick, easy, with little or no risks.
Implementing a complete ECS inside cocos2d-x will take time and it is very complex. We have to do it carefully.

Basically I want to refactor the scene graph and remove all the “logic / code” from it, and put it in “systems”.
For example, CCParticleSystem has the logic of the “system” in the Node… that doesn’t scale well.
If you have 200 particles, you will have to execute 200 systems (you have to visit 200 nodes)… and that is not efficient.
If we move away the Particle System code from the Node into a stand-alone “Particle System”, then a lot of optimizations could be done like:

  • data locality, to avoid CPU cache misses
  • parallelize the execution of the system… perhaps 2 threads, or more can execute it… or the Particle System could be run in parallel together with another System.

So, that is what I want to do for v4: Have more Systems, and less “code” in the Scene graph.