abraker95 / tanks

2D arcade top-view shooting game
1 stars 0 forks source link

Entity Component System #38

Open ghost opened 9 years ago

ghost commented 9 years ago

This took a long time but I'm happy with the result. I added some components and systems to test if it works and I managed to get a moving tank.

I created a Readme which contains the change log and some explainations about the entity component system ( at the moment, it's just code samples ).

I hope you like it. The advantage with this system is that you can easily combine functionnalities with components, and creating new functionnalities is just creating a new system.

ghost commented 9 years ago

I took a glance at the component system and even though it's a bit weird to me, I like it. I will pull the code and check how everything works. Be ready to be flooded with question and document the system because this is new to me.

On Wed, Nov 26, 2014 at 7:38 PM, Sherushe notifications@github.com wrote:

This took a long time but I'm happy with the result. I added some components and systems to test if it works and I managed to get a moving tank.

I created a Readme which contains the change log and some explainations about the entity component system ( at the moment, it's just code samples ).

I hope you like it. The advantage with this system is that you can easily combine functionnalities with components, and creating new functionnalities is just creating a new system.

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38.

ghost commented 9 years ago

Restructure process - Classes added

  1. Application

Systems:

  1. InputSystem
  2. MovementSystem
  3. RenderSystem
  4. TextureManager

Components:

  1. BoundingCircle
  2. Gun
  3. Health
  4. RenderProperties
  5. TankControls
  6. TextureHandle
  7. Transform
  8. Velocity
ghost commented 9 years ago

It's awesome that you like it. Yea sure, you can ask me whatever questions you want.

Also I changed the naming conventions a bit, but I will correct it soon. Call every component class ...Component and every system class ...System is also a good thing I think.

ghost commented 9 years ago

So the entities are all generic. You spawn the entity based on the parameters you give it, not any object like Tank or Bullet?

ghost commented 9 years ago

Yeah right.

So in the entity component system, what we call "entity" is just an unsigned int ( the id ). To that id, we associate components, which is a struct of data.

Then they are the systems which are called once per frame.

The system retrieves all entities with certain components ( for example the movement system retrieves all entities with the tranform component and the velocity component ) and then the system does operations on these entities.

Note that we can also use components as flags, with no data.

ghost commented 9 years ago

What I could implement though is factories. For example a tank factory, so that every time we want to spawn a tank, we don't need to specify every component.

ghost commented 9 years ago

Yes that is what I was thinking. I was thinking having a factory header that just defines all types of objects we need in the game.

Also the component table/mask in the environment is a bit confusing to me. How does it work?

ghost commented 9 years ago

Actually every entity has also a mask. This mask tells which components the entity has. You probably noticed that the mask variable type is std::bitset. Every bit represents a component ( the bit position for a component is predefinied ).

The component table stores a list of pointers. Each pointer points to an array of components. For example the array of Transform component. I stored the same components contigously in the memory to have better performances.

ghost commented 9 years ago

For example I see the following line: struct BoundingCircle : public Component< BoundingCircle >

With the code: template< typename T > struct Component { static const unsigned bitpos; };

I may not followed the use of the type correctly, but what role does passing a type through the template parameter have? I mean, where is it being used?

Also I suggest commenting (possibly heavy documenting) the code in environment.h. Maybe it's because I'm not used to templates but I have a hard time following what is going on.

ghost commented 9 years ago

So I traced back to where to how the system is determining what is what, and came across this:

template<typename T, typename U, typename... Rest> ComponentMask getMask() { ComponentMask mask = getMask(); return mask | getMask<U, Rest...>(); }

Explain how this works (recursion is not one of my strong points).

ghost commented 9 years ago

Ok, I will try to comment a bit more once I get some spare time.

For the Component template, maybe wikipedia explains better than me. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern See under object counter.

The basic purpose of this is to assign a unique id to every component. And the id is automatically assigned.

Also

Component::bitpos != Component::bitpos

even though it's a static variable.

ghost commented 9 years ago

Component::bitpos != Component::bitpos

I meant

Component < Transform > ::bitpos != Component < Velocity > ::bitpos

ghost commented 9 years ago

Ok I'll study up on the design.

ghost commented 9 years ago

For the getMask, I'm using variadic templates

Once you understand those, I think it will be clear why I use recursion.

ghost commented 9 years ago

The "printf" example is worth a look in the link I sent you.

ghost commented 9 years ago

Also note that with the current design we can easily have multiple environments. One for the menu, one for the game, etc...

ghost commented 9 years ago

ok so getmask works similarly to a stack, where on each recurse function call, it "pops" the type from the template parameter list?

On Thu, Nov 27, 2014 at 5:43 AM, Sherushe notifications@github.com wrote:

The "printf" example is worth a look in the link I sent you.

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38#issuecomment-64774205.

ghost commented 9 years ago

Yea, basically.

ghost commented 9 years ago

Sorry I removed the vc++ related files. Maybe you will have to recreate a project.

ghost commented 9 years ago

Yea, I figured that when I tried to change to the experimental branch, but the solution was empty On Nov 27, 2014 2:58 PM, "Sherushe" notifications@github.com wrote:

Sorry I removed the vc++ related files. Maybe you will have to recreate a project.

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38#issuecomment-64826409.

ghost commented 9 years ago

I like the wiki page.

On the other note, I did a couple small modification on the input system and tank_controls. I also created a MouseControls component that will be used for the GUI. I am currently stuck trying to figure out why Visual Studio doesn't want to see the declaration of the update function.

On Thu, Nov 27, 2014 at 4:42 PM, A Braker abraker95@gmail.com wrote:

Yea, I figured that when I tried to change to the experimental branch, but the solution was empty On Nov 27, 2014 2:58 PM, "Sherushe" notifications@github.com wrote:

Sorry I removed the vc++ related files. Maybe you will have to recreate a project.

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38#issuecomment-64826409.

ghost commented 9 years ago

Just a note about the design. The components should only contain data, getters and setters. The logic must be done in the systems. The code will be more clear that way.

ghost commented 9 years ago

I will implement a system so that the collision information is not in a component. The collision informations is not associated to an entity. It's a more global state of the system.

A lot of ECS use the event system like this one. Another idea would be to have components arrays which are not associated to the entities but to the world.

ghost commented 9 years ago

I figured that the components just had values, so I followed that model when creating the MouseControler, but I think the update function has no harm in being there.

ghost commented 9 years ago

You mentioned that the max number of entities is 32. This would give problems when making the GUI and HUD since the number of entities will result a way bigger number than that. Consider on implementing a system that can handle a variable amount of entities.

On Fri, Nov 28, 2014 at 12:24 PM, A Braker abraker95@gmail.com wrote:

I figured that the components just had values, so I followed that model when creating the MouseControler, but I think the update function has no harm in being there.

ghost commented 9 years ago

It has harm being there sorry to tell it like that. If you do it that way that's oop. It couples the component with the system and the ecs is all about decoupling.

ghost commented 9 years ago

It can handle a fixed number of components, but the number can be easily configured ( via the constructor ). It simplifies the code and the performances are better. But the best would be to create another environment for the GUI.

ghost commented 9 years ago

Why can't it be a hybrid OOP and ESC?

On Fri, Nov 28, 2014 at 3:17 PM, Sherushe notifications@github.com wrote:

It can handle a fixed number of components, but the number can be easily configured ( via the constructor ). It simplifies the code and the performances are better. But the best would be to create another environment for the GUI.

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38#issuecomment-64925859.

ghost commented 9 years ago

Don't you want to have a clean code? We can do oop when we have to. If it is just to put code in a function we can also do it in the system class.

Did you read a little bit about ecs?

ghost commented 9 years ago

Alright. You might want to fix up the update functions then. I am just used to breaking up the code into smaller, better defined parts instead of having a function with a huge list of statements.

On Fri, Nov 28, 2014 at 4:30 PM, Sherushe notifications@github.com wrote:

Don't you want to have a clean code? We can do oop when we have to. If it is just to put code in a function we can also do it in the system class.

Did you read a little bit about ecs?

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38#issuecomment-64929646.

ghost commented 9 years ago

Please document any files you add. I had a link error which took me sometime to figure out I was missing files for projectile in my project to solve it.

ghost commented 9 years ago

The Component system is not optimal. I think I will a dynamic-type of system because it's easier and takes less memory.

ghost commented 9 years ago

What would that design be?

ghost commented 9 years ago

I need to think about it. Maybe I will do the mainstream ECS design where every entity is an object which contains a vector of components. Althought I would prefer components to be contiguous in the memory. Maybe there is a better design.

ghost commented 9 years ago

Well tell me if you come up with something. The ECS design is getting cluttered.

ghost commented 9 years ago

According to the ECS design, is every entity independent of the other? If so, then viewController depends on the Map and crashes without it.

ghost commented 9 years ago

Yes, but it was a bug. It sould not crash when a component is missing. Are you testing everything? I'm impressed you found this malfunction.

ghost commented 9 years ago

I found this when I trying to get the label drawn. The map overlayed the label, so I commented it out and that happened. Yes, I'm testing and still trying to get the label working. For some reason its blinking, and also doesn't want to accept the set font outside a local scope.

ghost commented 9 years ago

Also a layer management system would be nice. The map is overlaying the drawn label, which is another issue that needs resolving.

ghost commented 9 years ago

It's going to be a long while until I push. Getting the label drawn properly is difficult for reasons described above.

Also, commenting out every system update except the render system crashes the game.

ghost commented 9 years ago

Well...

Look at the draw order, if it's overlaying.

For the crash, did you pull the commit I just made? It's probably because of the map bug that it crashes.

ghost commented 9 years ago

Also for the project in general, tell me if's more a pain that fun. Because these past couple of days I was waiting for you to push something so I don't do too much but if it continues like that, it's better if we stop this. I don't really like slow projects. We should either stop for good or do something eaiser. Think about it.

ghost commented 9 years ago

With the final exams approaching, the time spent on the project decreases. I should be able to work on the project much more often starting about after next week. Sorry if I was halting your progress, you can push new commits. It will just take me some time to merge, but I don't think that would be a big issue unless we worked on the same thing.

ghost commented 9 years ago

Ok, if it doesn't bother you, I will push commits when I get things done. I guess it's unavoidable when our rythm is different.

ghost commented 9 years ago

I will try to push shortly after you so you won't need to deal with the merges.

ghost commented 9 years ago

I didn't do anything yet. Push when you want. I will work on it when I'm done with my studies ( in 3~4 hours ).

ghost commented 9 years ago

I will test if we can use some kind of factory design in our program. Resource loading or data initilialization should not be in systems, because it introduces data races.

ghost commented 9 years ago

I'm already using something similar to factories with the system constructors.

On Wed, Dec 10, 2014 at 4:44 PM, Sherushe notifications@github.com wrote:

I will test if we can use some kind of factory design in our program. Resource loading or data initilialization should not be in systems, because it introduces data races.

— Reply to this email directly or view it on GitHub https://github.com/Sherushe/tanks/issues/38#issuecomment-66529604.

ghost commented 9 years ago

I guess it's the UISystem one.

Yeah, I will do something similar for textures, maps, entities, etc. Althought doing it with the system constructor is good but it's only done once. That's a problem, buttons can be created in the middle of the execution for example. My first attempt was to do it inside the system update but it leads to system order of execution to be important. For example, the map can only be loaded after the tilesheet is loaded. That leads to weird dependancies so I thought the best would be to do it separatly.

I think it's the best option here unless you have something better.

ghost commented 9 years ago

Here is my tossed idea:

First make every entity identifiable by name by adding a Name component when creating any entities. unsigned createEntity(T*... t, string _name) in Environment.h

The components indicated by arrows are the ones that need parameters filled. The texture will be located by the name of the entity.

Tank would have these parameters: Tank(x, y, keys)

Tank: --> new Transform(400.f, 300.f, 0.f), new Velocity(0.f, 0.f), new TextureHandle("Tank_0.png"), --> new TankControls(p2_keys), new BoundingCircle(), new Gun(), new Sprite()

Factory System: template unsigned createObject(T*... t)

The factory system would be responsible for creating more entities as a bulk of components. To distinguish, I will call them objects. So it would contain a chain of if statements to pick which object fits the request.

struct Tank { Transform* trans, Velocity* vel, TextureHandle* tex, TankControls* ctrl, BoundingCircle* crcleBnd Gun* gun; Sprite *sprite;

Tank() { /^ construct /) ~Tank() {/ destroy */ } }

System::update() { if(create_entity) { Factory.createObject(new Tank(400.f, 300.f, p2_keys)); } }