Jump to content
  • entries
    46
  • comments
    207
  • views
    39,894

Component Based Engine design


AggrorJorn

8,305 views

 Share

Besides making tutorials I am also working on my own project. It is my biggest one yet, but I think I have enough knowledge to get at least really far. Right now I am building my own engine on top of Leadwerks 3. The entire project is written mainly in C++.

 

Component Based engine design.

Since Leadwerks is very flexible you can do a lot of different things with it. The Lua scripts in the editor are a good example of component based design. However if you switch to C++ you don't have this directly at your disposal for C++. That means you would have to implement it yourself. Of course there are tons of ways of doing this but this blog shows how I do it in my project.

Instead of Component Based Design you can also choose to go for 'Direct programming' like Josh describes in his blog here. Both approaches have their pros and cons. The reason I am going for Component Based Design is because I want to create code that is highly flexible and can be interchanged between projects.

 

The GameObject class

The current system that I have is a combination of direct (hierarchical) and component based design. The following image shows how it starts.

 

blogentry-45-0-95188700-1367573184.jpg

The first class is the GameObject. The gameObject is completely empty at first, meaning it doesn't contain any models or textures. The only thing that it contains are components. Every frame all the updates that are enabled are being updated and/or drawn. This is the core of the component based design. Lets say we create a Player Gameobject. We can add several components to it:

  • ModelComponent
  • WalkComponent
  • JumpComponent
  • FPSCameraComponent
  • HealthGUIComponent

Now lets say for instance that we want to have TPSCamera instead of a FPSCamera. All we need to do is remove the FPSCameraComponent and add a new TPSCameraComponent. Not only is this system very flexible, it allows us to create components that can be easily shared throughout your project and perhaps even other projects that also use this component based design.

 

The Component class

Every component is derived from the Component class. The component class itself is abstract and thus can not be instantiated.

blogentry-45-0-15439000-1367573191.jpg

 

Increasing complexity

When you look at the first 2 images, you will notice that some functions and variables are exactly the same. Most programmers will come to the conclusion that this is a bad design. So for that reason we enhance our design as follows:

 

blogentry-45-0-49489400-1367573798.jpg

 

This means that a GameObject is also a component. The complexity hereby increases but works a lot better when it is properly integrated. (Note that the images are toned down a bit to simplify the idea.)

It is debatable whether GameObject inherits from Component instead of the other way around. I decided to go for Component since it is called a Component Based Design and not a GameObject Based Design.

 

FSM and Components

At some point I came to the conclusion that it is not always necessary to create an entire component structure to get the desired hierarchy in a level. For instance in a menu it is easier to create a (finite) state machine rather then having to switch between components. Although components work perfectly well for menu's, I think it is a good principle to think about where to use FSM's and where to use Components.

 

The Scene class

The component based design is far from finished but the basics already work. The last thing that I show in this blog is the scene class. The scene class inherits from GameObject (and thus also Component) and contains a list of Components/GameObjects. Every gameObject that is created needs to be added to this list in order to be updated or drawn. I specifically want a scene to have a camera so that I allways know that when I load a new scene, a camera is being created.

 

blogentry-45-0-41299500-1367574840.jpg

 

 

Thanks for reading.

Jorn

  • Upvote 2
 Share

19 Comments


Recommended Comments

Good work aggror, nice to see your approach.

 

 

Just be aware, your current design will produce a lot of cpu cache misses. To avoid this you could store all your components in a componentcontroller (however you like to call) list/vector for each componenttype (vector<movecomp>, vector<guicomp>...).

  • Upvote 1
Link to comment

Thanks furbolg.

Are you referring to empty Draw() and Update() calls? If so, what I want to do is something like interface for these functions like: IDrawable and IUpdateable. That way only if they posses these interfaces, components will be drawn or updated.

Link to comment

 

When the processor needs to read or write a location in main memory, it first checks for a corresponding entry in the cache. The cache checks for the contents of the requested memory location in any cache lines that might contain that address. If the processor finds that the memory location is in the cache, a cache hit has occurred. However, if the processor does not find the memory location in the cache, a cache miss has occurred. In the case of:

  • a cache hit, the processor immediately reads or writes the data in the cache line.
  • a cache miss, the cache allocates a new entry, and copies in data from main memory. Then, the request is fulfilled from the contents of the cache.

 

IMO this can get pretty advanced and you can get pretty extreme with it and unless you are creating the next gen game isn't something you really have to worry about, but it allows you to get max performance which sounds great until you realize to really max this in your entire application it really changes the structure of it. Often to the point of making it confusing to understand and maintain if you aren't used to it.

  • Upvote 1
Link to comment

@Rick:

Sure but 20-40% more speed (tested with two dimensional loop where i switched the x/y loops) for very low cost (invested time) is worth it, in my opinion smile.png

(to be fair, it was a large 2d array... 1024 by 1024 i think)

 

@Aggror:

No, i mean you could store your components not directly within themselves (your pictures: list<component>components;). And store them in "a large array" so the cpu can walk straight through all components of type move / gui instead of breaking and process the next component and afterwards going to the next component and start with the next move component.

 

Its just an idea/opinion, go for your approach. It will definitly work and looks like clean code. Just wanted to give you the extra speed wink.png

Link to comment

Yeah I've seen the extreme of what can be done to avoid memory cache misses and it can totally change how you have to think and design your code. I can't remember the name for that style now, but I find it a pain to read myself. This little example isn't the extreme but you can go far beyond this to get the speed. Where does it stop and what do you sacrifice?

 

I think some of the benefits of having the 1 list is the flexibility to it. There are always trade-offs between flexibility and speed. That might be more relevant in a more dynamic language than C++, but if he creates more components later, the maintenance is more involved as he'll have to create separate lists. This may seem small, but it can start adding up if he has 100 different components over time. It also adds in complexity.

 

That guys says one of the primary benefits is reducing cache misses, but I'd argue one of the primary benefits is also flexibility.

Link to comment

That sounds like a huge gain. For the game that I am creating this could make quite a difference. So basicly I would create gameObjects and place them in 1 of the following lists instead of just a components list:

 

The following lists come to mind:

  • InitedList (Components that are initialised but not updated or draw. Think about a static model, particle or texture)
  • UpdateList (Components that are updated. player, AI etc)
  • DrawList(Components that are drawn. GUI related components)
  • UpdateDrawnList(Components that are updating and drawing, dynamic GUI's)

Please correct me if I am wrong and thank you for the feedback. smile.png

Link to comment

I think he means store like components in their own lists like in his first post " list/vector for each componenttype (vector<movecomp>, vector<guicomp>...).".

 

When you process stuff you process a list at a time so that all/most of the memory that list refers to is in cached memory making it faster to deal with.

 

On a side note, the event code I posted on the asset store works great with this style of design because it lets components fire events and register to get events and helps with decoupling the classes. Your connection becomes hooking up events between components. I'm not sure if you thought about how components communicate together but this is a great way to do that and not have each component need to know about the other in terms of it's insides. The generic GameObject is where they know about the other components attached.

 

I was playing with this style in C# in LE2, but I stored it all in 1 list because my components were plugins (dll's) and loaded dynamically at run-time and added to generic game objects at design time. This is very dynamic and easy to extend functionality and didn't require creating extra containers for a new component.

 

 

This is a pretty good article that takes you through the thought process http://gameprogrammi.../component.html . However, I don't like the idea of having the specific components in the game object that they stay with. They do talk about another communication method of messages.

 

The funny thing is all of this is basically how Lua/Flowgraph works in LE3 smile.png

 

 

Here is the method that really takes this to the extreme, and should help explain why and how to do this.

http://gamesfromwith...oriented-design

 

 

Look at the first answer. I think that helps describe witih a small example the idea. It's for sure a different way to think but it produces faster code.

http://stackoverflow.com/questions/1641580/what-is-data-oriented-design

  • Upvote 1
Link to comment

@Rick:

 

Yes, you are right. Overoptimizing is a real bad thing and im normally for readability / usability / maintainability over pure speed (premature optimization is the root of all evil, you know ? wink.png).

 

But in my example the changes are small compared to a full data-oriented design and you can gain some extra speed.

 

Aggror's approach is not wrong as long it works for him and his game. It's always nice to see how other programmers solve a problem.

 

So we can share experience and help eachother, as i said to aggror already, this is the best feature of the leadwerks engine.

Link to comment

Thanks Rick and Furbolg. It is really interessting to read about it and it certainly applies for the game that I am creating.

The thing is: I am already glad that I got this far with the engine and console. I haven't even started on the game itself. This might be something for later when I have better skils with C++, engine design and design patterns. I think I am going to stick with what I have for now, but who knows, if my game would reach a 'completion within sights' range and performance is starting to go downhill, I will be forced to look in to this anyway.

Link to comment

Closed beta is on Monday. Beta is thursday. Release Mac and pc on wednesday. Thursday ios and android. Friday bugfixing, 1 free dlc release. Should be doable. :)

  • Upvote 1
Link to comment

I don't know if you are being serious or not, but iOS takes about 7 days before they'll get to looking at it. Then if they accept it it'll be ready for release on iOS devices. Android is about 2 hours lol

Link to comment

Actually, I was extremely sarcastic. I am not even making this project for ios and android. :) it will take a long time before it is even in beta.

Link to comment

Ironically we had a discussion about this is in class today: This problem can be solved uing the FlyWeight Pattern.

Link to comment

<p>Good work.

For speed it depends on your project, simple indie ones are good enought without too much speed needed. If you want some AAA style "Skyrim", yes optimize more.

Trine 2, TorchLight 2 style no need to go in incredible optimization.

Even playde some super indie game made with visual programming by a pure 3D artist in some popular indie 3D engine.

 

There is lot of optimisations and not in code, caus even with the better optimized code , if 3D art is not towards optimization, it will not work.

- At least 3 or 5 LOD steps

- MipMaps

- Shadows LOD

- shadows Z distance progressive display

- LOD on particles (more accurate maths as you come near)

- LOD on AI and NPC paths navigation (more precise calculation if they are visible)

- LOD on animations (far away NPC won't be animated each frame)

- Efficient culling system

- LOD for shaders (no shader at all at some distance)

- Efficient GUI system using one draw call only

etc ...

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...