Add entity component system in cocos2d-x

yes… basically in a ECS your components are just “plain old data” organized in a contiguous array (no virtual functions). And the System just iterate all over it, making it super fast since there are no cache-misses.
There good articles out there explaining the benefits of data-locality:

Yes, if we count the init transfer as a miss, under the circumstance, there is no pre-fetching or whatever
A and B will have that init transfer miss.

Let me rephrase what I said: consecutive reads with data pre-loaded will have two misses, without pre-loading it will have 3. Regarding the pollution(and the 2 miss 1 hit with 4B interleaving/read), I referred to just one loop iteration(aka the init transfer). Sorry for the misunderstanding.

You were correct with the 3x misses, as I assume you were counting the miss on the init transfer.
Somehow I forgot to count that. I blame the high temperatures of 35°C :sunny:

Totally :smile:

It’s of course just the cache hit probability, which depends on the cache pollution/utilisation.

Sure. Developers can add their own physics system. It is better that cocos2d-x supports many physics system internally, but i am not sure if we have enough resource to do it.

Right now i just use component to implement physics integration, i am not refactoring the whole physics integration. It seems reasonable not limiting body numbers.

After discussion with @ricardo and @walzer, i will continue to work on script component. Anyone have any requirement of script component are appreciated. I will use script component to write some small games to do testing.

I am porting the simple game in cpp-tests/Classes/ExtensionsTest/CocoStudioComponentsTest using script component.

Current progress:

  • can show Player in correct position
  • can generate Enemy continuously, and these enemies can run action

Work left:

  • Player receive input message and generate a projectile
  • detect whether game is over, can calculate result
  • projectile collides with enemies

After discussion with @zhangxm, I’m thinking that we can have these modules as component:

  • script component, zhangxm has already did it. So we can reuse the logical codes in different game objects
  • physics 2d. It’s inherited from component already, what we need is decouple it from node, scene and director.
  • physics 3d, the same as physics 2d, they’re using the same pattern
  • audio. Cocos Studio currently can’t add a audio effect into the scene. Adding an audio component may resolve this problem

I’m not confident about splitting rendering to components. I’m afraid it will break the backward compatibility too much. Developers will complain cocos2d-x v4 just like they complained from v2 to v3.

The simple game is finished, it is in my entity-component branch. Now it only can run on Mac.

What i improved compared to previous script-component are:

  • pass onEnter, onExit events to lua script
  • lua script can use ComponentLua::getScriptObject() to get other Node’s script object, i will explain it in more detail following
  • add lua-binding for EventDispatcher::dispatchCustomEvent(), this function is skipped now, i don’t know why, i will explain it in more detail following
  • invoke Node::scheduleUpdate() in Node::addComponent(), i will explain why it is needed following

why add ComponentLua::getScriptObject()

It is convenient that script components can communicate with each other. For example, in the simple game, scene has a lua component, and the lua script records all enemies

// c++ codes

// create scene and attach a lua script component
auto scene = Scene::create();
auto sceneLuaComponent = ComponentLua::create("src/scene.lua");
scene->addComponent(sceneLuaComponent);

// scene.lua
local scene = {
    enemies = {},
    ...
}
return scene

And projectile wants to get these enemies information to do collision detection. The projectile is a sprite and has a lua component too,

// projectile is created in `player.lua`
local projectile = cc.Sprite:create("res/Projectile.png")
local projectileLuaComponent = cc.ComponentLua:create("src/projectile.lua")
projectile:addComponent(projectLuaComponent)

// projectile.lua
local projectile = {
    -- want to get enemies to do collision detection, so need to get the object of `scene` in `scene.lua`
}
return projectile

As you can see, lua components communication is needed.

why bind EventDispatcher::dispatchCustomEvent()

I don’t know why this function is skipped by default. In the simple game, it use it to communicated with lua codes and c++ codes. lua codes send an event to say game is over, and c++ codes receive the event and replace scene.

Of course i can do replacing scene in lua codes too, but i want to reuse existing c++ codes.

why invoke Node::scheduleUpdate() in Node::addComponent()

Because components wants to receive update events. But update event is not triggered until Node::scheduleUpdate() is invoked. And it is easy to forget to invoke it when adding a component. So i think it is reasonable to enable update event when a component is added.

some questions

After finishing this simple game, i have some questions:

  • I don’t know which part should be implemented in lua and which part to be implemented in c++. As mentioned above, i can replace scene both in c++ and in lua. If i implement all in lua, then what’s the difference between current lua-binding solution. As you can see in the simple game, there is little codes in c++, most are implemented in lua. And it is easy to implemented all in lua, and it is more easy.
  • What other games can we try to get more requirements about script components. Or is there any better way to collect requirements. It seems developers are not active about the script components because there is little discussion about it. Should i post another thread just for script component?

I use lua-empty-test project to do the work. So you should choose it to run on Mac.

I have not tried Lua yet. I would like to do it but I do not know if it is easy enough to add Lua scripts to an existing C++ game. I would like to code almost everything in C++ and use Lua to rapid prototyping / adjustements.
What would be the quickest way to achieve this? What I have found suggest to create a lua project, that is not what I want. I want to add Lua capabilities to an existent C++ project without having to do a lot of witchery.

You need the binding-codes to do useful things. For example, you add a lua script to a Sprite, and want to invoke Sprite::setPosition(), then you need the binding-codes to achieve it.

Can I use the binding-codes in an existent C++ cocos2d-x project? if so, is there some test / example / tutorial showing me how to do it?

You can refer to my branch: https://github.com/minggo/cocos2d-x/tree/entity-component.

I am not sure if it meets your need. But feel free to add your comment.

I have discussed with studio team. They said they planned to implement all things in lua. For example,

  • editor create a sprite(the sprite will be created in lua)
  • change values of sprite directly in lua
  • receive messages directly in lua

They said they have some ideas. And i invited them to join this discussion.

ok.

This should only be scheduled if the component needs update, otherwise it will make the game slower.

Yes, components need to talk to each other. But I couldn’t understand your use case.
Usually it should be something like this:

# I'm not familiar with the Lua syntax, so I'm writing it in kind of python
class MyScriptComponent:
    def update(self):
         owner = self.getOwner()
         # returns a list of all the components
         components = owner.getComponents()

         # or I can get a component by name
         physicsComponent = owner.getComponent("physics")

         # So, I can get the component of any node:
         someNode = owner.enumerateChildren("/enemies", func)
   
  def func(self, node):
        physics = node.getComponent("physics)

Absolutely. If we write everything in Lua, then there is no gain in having components.
The idea is to write the basic things in C++:

  • initialization
  • creating the scene
  • creating the sprites
  • creating the components
  • assigning the components to the sprites

And the logic of the game in Lua using components.
So, all the components in Lua, and the rest in C++. And the logic of the game should be put in one component.

Does it make sense?

1 Like

I’ve just reviewed @zhangxm 's physics code. I think is good. But I have the feeling that it is neither a CBA nor a ECS. So, if we are going to use systems, then the “component” should be pure data.

eg:

  • PhysicsComponent2D should only be a struct with no logic: beforeSimulation, afterSimulation, etc. shouldn’t be there
  • Use data locality features: have an an allocator for the PhysicsComponent2D so that it could be in contiguous memory.

Does it makes sense?

It is that, if we use script = node.getComponent("script");, it will return the c++ object, not the script.

Sometimes we need to create sprite in lua, for example, there is a lua object receives input message, then it wants to create a projectile and move to some position.

I can move beforeSimulation and afterSimulation to physics system. But it is also not pure data in PhysicsComponent2D. As you can see, it is just a wrapper of PhysicsBody. If we want to use pure data as you said, we should re-write all physics codes.

Yep, it is better to do like that. But i found i can not invoke Node::scheduleUpdate() in lua, and sometimes i want to create a sprite in lua.

If we want to implement pure ECS, and don’t consider compatibility, then we can refactor physics to include just data.

But if we want to break as less compatibility, then put physics into a component, then we can include logical codes in component.

I create system to hold:

  • all physics components
  • physics world
  • driven physics simulation

If i don’t use system, then physics world is tight with Scene.

ok. So, what kind of communication you were talking about?
What do you have in mind?

Yes. My point is, if we want to go the ECS route, then, there is no point in creating the Physics System since it adds complexity to the code, and we gain nothing from it since it is not optimized for data-locality.
If that is the case, putting everything in a big PhysicsComponent makes more sense.

Could you please view my entity component branch? What i want is:

  • invoke other script component’s function

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

  • get data installed in other script component

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

The benefit of physics system without data-locality optimization are:

  • move codes that drives physics2d, physics3d and other components into one place, and make it decouple with Scene. And schedule can be managed by System.
  • Now Scene holds a reference of physics world which makes codes tight with Scene.

You can achieve the same result without having a system. Then only thing that is needed is that “node” should register itself to the scheduler when a new component is added.

I don’t think it a good practice to add systems to cocos2d-x without the benefits of systems.

Could you be more specific? Do you want to add new methods ?
If so, to which class?

thanks.