Jump to content

  • Log In with Google      Sign In   
  • Create Account


Examples of component design in simple game such as pong


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 24 April 2012 - 01:05 AM

Hi Guys n Girls,

First of all i am not asking for a detailed coded example i dont care about the specific language or graphics although if you do want to type semi c++ then i could probably understand it a little faster :)

I wouldnt say im dumb but i have been having a bit of trouble grasping the component based code and i think it would help me if you could show me how you would implement this in a simple game like pong or something else quite simple.

How i understand components is that say i have classes cMove, cDraw, cCollision ect i then have an cEntity object which takes a pointer to each component i need it to have.

so maybe
class cEntity
{
	 cPosition* coords;
	 cMove* mover;
	 cDraw* drawer;
	 cCollision* collider;
}

now whether that is right or wrong im not sure but after that i am stuck as to how to implement it do i simply make a container of components and fill it from each type of different cEntity i have so i could end up with.


// these are the walls names

componentArray[0] = topWall.cPosition;
componentArray[1] = bottomWall.cPosition;
componentArray[2] = leftWall.cPosition;
componentArray[3] = rightWall.cPosition;
// and then maybe

for (int i = 0; i < totalcomponents; i++)
{
	 componentArray[i]->whereIsMyPosition();
}

now please dont judge my code style i know it may be difficult to read but it is purley for an example im just wondering if i am on the right track or if i am not could you please show me with a similar example.

Many Thanks in advance :)

Sponsor:

#2 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 26 April 2012 - 02:27 AM

Hi Guys

i would like to re-phrase my question into an actual question.

#1 do you have any example of a simplest version of a component system in the context of an actual game?

i have looked on google and only really seem to find snippets which i dont fully follow.

#2 based on my first post am i in the right ball park in terms of how i percieve component based design?

#3 do you know any good books or web pages aimed at idiots like me who cannot grasp the concept fully or ones that implement them into a working program.

many thanks you help and wisdom is much appreciated :)

#3 kauna   Crossbones+   -  Reputation: 2154

Like
0Likes
Like

Posted 26 April 2012 - 06:43 AM

Hi,

I can't give you an answer which suits for every situation but how about making a abstract EntityComponent class from which all the components herite? You may store the components in a vector or list such as std::vector<EntityComponent *> Components; So this allows you to add and remove components easily to your entities.

Your entity should have some kind of messaging system for the components, in a way that any component can send messages to the entity itself or other components.

Is there a specific problem you are trying to solve currently?

Cheers!

#4 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 26 April 2012 - 08:41 AM

hi Kauna

thanks for your reply. im really just trying to understand how i would go about using a component system. the problem is i am struggling to find good examples and the ones i do find are a bit complex im sure i half understand it but not quite how to implement it and seeing as pong is a very simple game i figured it would only need a few components and it would be easy to see how it works.

so really its just how does component design work and how do i start to make one.

my above post isnt an example of my design rather a template to use to show me what component design really is.

thanks again for your help

#5 FLeBlanc   Crossbones+   -  Reputation: 3081

Like
6Likes
Like

Posted 26 April 2012 - 10:33 AM

The issue with your question as it stands is that Pong is such a simple game, that using a component system to implement it is really greatly over-engineering the problem.

That being said, there really isn't anything tricky or magical about component systems. They are just an instance of the object composition school of OOP design. A simple way of thinking about object composition is that object composition emphasizes the HAS-A relationships of the object to its parts, in contrast to the "traditional" polymorphic inheritance tree of IS-A relationships.

In a polymorphic system, for example, a FordMustang IS-A Car, a Car IS-A MotorVehicle, a MotorVehicle IS-A WheeledVehicle, etc... Edge cases like a FlyingFordMustang break this kind of brittle system, since you can't derive FlyingFordMusting from FordMustang, because a FordMustang IS-A WheeledVehicle, whereas FlyingFordMustang IS-A WingedVehicle.

By contrast, with an object composed of parts (HAS-A relationships), this sort of brittleness is not a problem. Rather than a FordMustang being a WheeledVehicle, instead a FordMustang object HAS-A BeefyEngine, HAS-A SetOfWheels, HAS-A ShinyFordMustangLogo, etc... and all these components together make a FordMustang. By contrast, a FlyingFordMustang HAS-A BeefyJetEngine, HAS-A SetOfWings, HAS-A ShinyFordMustangLogo, etc... The ShinyFordMustangLogo still identifies it as a FordMustang to any Chevy-hating purists, but it doesn't have to contain the cruft of a regular FordMustang (wheels, beefy engine). Objects are defined by their parts, by their attributes and behaviors, rather than what they supposedly are.

This is a silly, contrived example, of course. To get back to your Pong question:

In object-composition Pong, there is no concrete, discrete class called Paddle. There is no discrete class called Wall. No class called Ball. Instead, what you have are a set of components that define the attributes and behaviors that any entity can have, and a set of container classes that aggregate these attributes and behaviors into discrete entities.

So, for example, you have a CollisionBoxComponent that defines a collision box. You have a GraphicComponent that defines a visible graphic representation. You have a BallPhysics component that applies the physics constraints of a ball (ie, move in its current direction at a specified velocity until a collision occurs, at which point reflect the movement vector relative to the plane of collision) You have a GoalPhysicsComponent that notifies of win/loss conditions. And you have a PaddlePhysics component that reacts to player input (if a certain button is pressed, add velocity in this direction).

A wall, then, would be comprised of a CollisionBoxComponent and a GraphicComponent. The CollisionBoxComponent would be set to the bounding box of the wall piece, and the Graphic would be set to the white line graphic to draw a wall. That is all a Wall needs, since it just sits there.

A ball would be comprised of a CollisionBoxComponent and a GraphicComponent, with a BallPhysics component controlling its motion, the graphic set to the ball graphic. Likewise, a paddle would be simply comprised of a CollisionBoxComponent, GraphicComponent, and PaddlePhysics component.

A Goal would consist of a CollisionBoxComponent and a GoalPhysicsComponent. It doesn't need a GraphicComponent, since it only represents the goal area where a goal is to be detected. It would respond to collisions by passing a win/lose message to interested parties. An attribute of GoalPhysicsComponent would be the goal's owner, or which player owns the goal, so it knows who to notify of a win and who to notify of a loss.

Each game round or turn, the systems would be iterated and updated. The BallPhysics sub-system would iterate all balls and move them. The PaddlePhysics subsystem would iterate all paddles and check for user input, moving the paddles as ordered by the input. The GoalPhysics system, being purely reactive (ie, no movement) would just sit there, waiting. After physics updates, then the CollisionBox sub-system would iterate all contained collision boxes, testing for collisions. For each collision, a message would be generated and sent to the interested parties. In the case of a Wall which has no Physics component, this collision notification is silently ignored. A Wall doesn't care it got hit, a Wall is very Zen. A Wall just sits there. A Ball, however, has a Physics component that is capable of and interested in responding to collision notifications. In this case, it responds to the notification by reflecting its movement vector relative to the point and plane of collision as told to it by the CollisionBox sub-system, and correcting any collision penetration that may have occurred. Then it might play a ping sound to the sound system. A PaddlePhysics component will also silently ignore collision notifications. A GoalPhysics component will react to collisions by notifying the owner of the Goal of a win, and notifying the other player of a loss, notifying as well the score-keeping system of the point scored.

After physics and collisions are updated and resolved, the game loop passes on to the Graphics sub-system, telling it to go ahead and draw all objects.

In a system such as this, there is a bit of communication involved between components, handled via some sort of message system intended to reduce inter-component dependencies. Such dependencies should be kept to a minimum; however, it is nearly impossible to eliminate all dependencies. For example, CollsionBoxComponent, GraphicsComponent and PhysicsComponent(s) all require knowledge of shared state, ie the entity's Position, that must be communicated somehow.

As an example of the concept of shared state, consider that Position/Orientation/Scale (also called transformation, in 3D parlance) are the most commonly shared state data in game-related component systems. Some implementations might handle Transformation as a component of its own, that responds to queries (getPosition, etc...). Other implementations might put Transformation into the base Entity container, accessible by all components without having to go through the message system to query or modify them. Still other implementations might simply duplicate Transformation state across all interested components, so that for example the GraphicsComponent keeps its own local copy of the Ball's position, and so does the BallPhysics component. And so forth. These are decisions you have to make for yourself, and there really isn't any "right" way of handling them.

The backbone of such a system is the means of communication between components and between entities. As mentioned, many implementations use a message-passing system. These systems are common in programming (you can see examples of message-passing everywhere, especially among things like the Windows input queue). A message is like handing a note to someone. You don't have to know anything about that someone to hand them a note, and there is no requirement upon them to even care about your note. In the Pong example, the CollisionBox sub-system would iterate all of its contained box components, checking for collisions. For each collision, as I said, a message would be handed off to each involved party. The contents of this message would need to provide specific information for the interested parties. Where does the collision take place? What are the orientations of the colliding entities? What is the orientation of the "collision plane"? And so forth. All of this information would be bundled up and passed to both colliding entities. In the case of a Wall, this message is silently ignored. In the case of a Ball, this information is used to recalculate the Ball's trajectory based on its previous trajectory and the orientation of the collision plane.

Other implementations might communicate to components in a more direct fashion, by calling into the entity via some sort of requestComponent method, then calling directly into the component returned. My personal preference is to avoid this sort of thing, since it does introduce dependencies (ie, the calling entity needs to know something about the structure of the object being acted upon), but even a message-passing system introduces these dependencies, they're just sort of "hidden". That is, the CollisionBox system is making an assumption (a dependency, of a sort) that one of the interested parties is capable of acting on the collision message it sends. Whether or not either entity does respond to it is irrelevant; there is still a tenuous dependency. Completely eliminating cross-component dependencies is probably impossible, and at any rate probably unnecessary. The assumption that one of the interested parties wants to respond to a collision is probably a safe assumption to make.

Anyway, getting a bit far afield here. You might want to check out the usual suspects when it comes to entity and component systems.

http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/
http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/

#6 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 26 April 2012 - 02:28 PM

Hi FLeBlanc,

All i can say is wow!! or perhaps thank you very much :) i think i will need to re read your reply a few times but that is exactly what i needed.

the reason i said pong is because it is a simple game and i have made a version using inheritance but i have been intruiged by components and now i think im really beginning to grasp it. i could see the power of it before but not how to implement it. lots of phrases in your reply i will need to google but this is more than enough for me to get started on designing a component type game :)


well thanks again this has been an amazing response i appreciate your time and effort :)

#7 FLeBlanc   Crossbones+   -  Reputation: 3081

Like
5Likes
Like

Posted 26 April 2012 - 04:13 PM

Because I was bored and had some time to kill before work, I decided to put together a quick demonstration of component-based Pong. I didn't get to actually finish it (work calls, and I must away) but I did implement walls, a bouncing ball, a player controlled paddle and a really, really stupid AI controlled paddle. Some of the physics and collision are a bit wonky (it was a very rush effort) and non-robust, but it works well enough to demonstrate at least the basic idea of what I was talking about.

The demo is written for the Love library, a simple game library that uses the Lua language. Just download the Love binary, execute the script using the love executable and it should be golden. Here is the script:

Spoiler


The way it works is this:

There are 5 currently implemented sub-systems: Graphics, Collision, BallPhysics, PlayerPaddlePhysics and AIPaddlePhysics. Each sub-system has a corresponding component that is used to interface with the system. Objects are implemented as simple containers that hold a list of components, an (x,y) coordinate (held in the container for convenience, since just about every sub-system uses it) and some basic functionality to handle message-handling and to kill the object. Killing an object will kill and remove all of its components.

The lifetime of an object is as such:

1) Create an object, using the helper function createObject(). This creates a blank, empty entity
2) Add components using the createComponent() method of each sub-system. For example, to create a Graphics component of a rectangle sized 32x32, we call GraphicSubsystem:createComponent(object, 32, 32). A createComponent() call will create a new instance of the related component which holds local state for the given object, and stick it in a table held internally by the sub-system. It will also put a reference to that component inside the object itself.
3) Initialize position to place the object in the world

The basic rundown of the loop/application is as such:

1) In love.load, initialize all the sub-systems before doing the main loop. This is to initialize the internal component-list tables for each sub-system.

2) In love.load, create the playing field by setting up 4 walls bounding the screen, a ball, a player paddle and an AI paddle. See the function love.load() for the details.

3) In love.update, call the update() method of each sub-system. A sub-system update method will iterate the sub-system's internal list of components, and perform the basic update on each component in the sub-system. For example, in Collision update, we check for collisions between objects, and send messages (using the object.message() function) to an object if a collision with another object is detected; the message includes the plane of the collided-with object (top, bottom, left or right). In BallPhysics subsystem, the BallPhysics component implements a message handler that listens for these "collide" messages, and reverses the x or y velocity vector component depending on which plane the collision involves. In PlayerPaddlePhysics, the update method queries the keyboard and moves the paddle up or down based on 'q' and 'z' keypresses, bounding it between two Y values specified when the PlayerPaddlePhysicsComponent is created. And in AIPaddlePhysics, the update method causes the paddle to just reciprocate back and forth between the bounds.

4) in love.draw(), the draw() method of GraphicsSubsystem is called, which iterates all components in the internal list and draws the corresponding rectangles.

And voila, you have Pong (albeit Pong minus scoring, minus goals, plus some slightly wonky collision and physics, minus sound). It's very rough, but it demonstrates the idea, I think.

#8 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 27 April 2012 - 03:22 PM

thank you so much that was a very kind thing to do the first post was more than enough but the second one is truely amazing Posted Image
i think i understand exactly how this works now and will be studing this tread for a while but now i have the tools and the theory to move away from inheritance and focus on components Posted Image

i have looked on google for this kind of example one that is simple enough to understand but shows exactly what is going on i just hope it will help future noobs like me well i know it will.

thank you for helping me Posted Image



edit:
i have never used Lua and i have been reading a little to try to understand your code it seems like an awesome language i am going to test your code and then try to convert it to c++ but i think i may try to learn Lua one day :)

#9 FLeBlanc   Crossbones+   -  Reputation: 3081

Like
0Likes
Like

Posted 27 April 2012 - 05:13 PM

Lua, as a higher-level language than C++, offers some little tricks that make things like this easier. For one, the dynamic typing means that I can easily just pass a generic table as the argument to object:message(), and leave it up to the message sender and receiver as far as the actual layout of the table, without having to resort to any of the weird templating tricks or std::map trickery I might have to use to implement a similar structure in C++. Although it's actually not too hard to do. You can use an std::map<std::string, std::string> as a quick hack, and "cast" the second string to whatever you need it to be (number, letter, string, etc...). Another bonus of developing in Lua is the ad hoc nature of prototyping and data structuring. If I am prototyping a system, I can quickly iterate on table layouts to get the data and structure I need without recompiling. Quite often I will prototype even performance-critical systems in Lua, only translating them to C++ and writing a binding layer if it happens that I need the performance.

Lua's powers for data-description also mean that I have done away with all of the special-case data file formats I once-upon-a-time came up with; you know, all the little formats for describing an animated entity, for saving level data, etc... If I have a table full of data, I can just save or load it as a compiled Lua chunk. As an example, in the above Pong demo, you could very quickly save the state of the game at any time by dumping the componentlist tables of the sub-systems to binary chunks; the state could be restored just as easily by loading the chunks back into componentlist.

These days, I write all of my "upper level" logic in Lua, and use C++ only for the performance critical stuff: rendering scene structure, physics, etc...

#10 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 29 April 2012 - 03:39 PM

Cor i know one thing Lua is a bit complicated to understand ive read a few basic love2d tutorials and i still struggling to translate your code :)

also i tried to run your script but i must be doing something wrong as it says no code to run, any ideas?

Many Thanks again :)

#11 FLeBlanc   Crossbones+   -  Reputation: 3081

Like
2Likes
Like

Posted 30 April 2012 - 11:12 AM

Love expects scripts to be placed in a folder. Save the script inside a folder and name the script main.lua, then you can drag the entire folder and drop it on the Love executable to run the contained script(s). It's a bit odd, but it works.

At any rate, one of the reasons I prefer a higher level language like Lua for systems like this is that some things can get hairy under C++. Dynamic typing can smooth out a lot of issues. However, I went ahead and did an example implementation of the system I presented, using C++ and SFML, for demonstration purposes. Note that it is a direct port of the system; I didn't stop to build a better collision system, so the problems that exist in the Lua version (wonky collision, ball getting trapped inside moving paddles, sometimes sticking to walls, etc... like I said, it is not robust at all) still exist. This is meant to be demonstration code, not production code, and if I were making production code I would implement a better broadphase collision system, and implement the collision plane detection differently. Anyway...

Before I present the code, some disclaimers. I don't do higher level programming like this in C++ anymore. I just do low level stuff (scene graphs, etc...) So my C++ skills are very rusty. Also, I implemented a very quick hack of a generic message passing system that operates by encoding a message as a string. This way, I can implement a generic interface for components to handle messages, and leave it up to the components themselves (and any calling parties) to worry about the specific content of the message, without having to build some sort of weird templated class to hold messages or anything. It's probably not the optimal way of doing it, since it requires several string manipulation operations, but it works. Since I don't do this kind of thing in C++, I have no desire to build a generic message passing system in C++.

To begin with, I needed to create some base classes. As in the Lua example, the basic architecture is that any SubSystem (Physics, Collision, etc) implements a corresponding component type. So if you want your object to have a visual representation, you have to call the Graphics Subsystem to create a Graphics Component, and add the component to the object. Subsystems maintain internal vectors of the components that have been created, and their update() methods will simply iterate this internal vector and operate on the components. Doing it this way can have implications as far as cache coherency and data-orientedness are concerned; ie, you can implement your internal component list as a simple flat array of data which is processed sequentially, resulting in fewer cache misses and possibly significant increases in performance.

The implementation I present here, however, does not present the optimal method. Rather than a flat array of data structures, I implement the internal component lists as arrays of pointers to data structures, and the components are dynamically allocated and freed in the createComponent() and removeComponent() methods. Since objects maintain internal pointers to their components for message-passing, it was necessary that the pointers never change; if the subsystem component list is implemented as a dynamic array, re-allocations will break the object's internal component pointers, causing segfaults. Ways around this might include using smart pointers or other more complex techniques. I was not interested in solving this problem here because, again, I don't spend much time in C++. There are many, many different ways of achieving the same kind of architecture.

All Components must inherit from the BaseComponent class, which presents the common interface all components share. This interface is simple, consisting of the kill() and message() methods.

All Subsystems inherit from the BaseSubsystem class, which presents the common interface all subsystems share. This interface is also very simple, consisting only of the removeComponent() method. Note that I do not include createComponent() in the common interface, but rather implement it at the specific subsystem level. This is because the parameters passed to createComponent() vary depending on component type, and this method is not called in any of the base classes so it is not necessary it be known at the interface level.

All objects will be of the type BaseObject. This is not a class that is inherited from; all objects in the world will be instances of this class itself. It implements the basic interface of an object, including kill(), message() addComponent(), removeComponent() and position.

To see what I'm talking about, here is the base framework:

framework_base.h

Spoiler


framework_base.cpp:

Spoiler


The BaseComponent and BaseSubsystem classes are pure virtuals, so they must be derived from. However, there does not need to be any deep hierarchies; a single level of derivation is all that is necessary. We just need a common interface for these classes.

With these base systems in place, we can start constructing the subsystems. Here is the graphics subsystem and associated component:

graphicssubsystem.h:

Spoiler


graphicssubsystem.cpp:

Spoiler


This system works pretty much exactly as in the Lua example, though it uses SFML under the hood. The GraphicsComponent is a simple rectangle of size (width,height), and the owner object is queried for position when drawing. Components are iterated and drawn as simple white rectangles.

Here is the Collision subsystem:

collisionsubsystem.h

Spoiler


collisionsubsystem.cpp:

Spoiler


Again, it's a direct port of the Lua version (with attendant problems). Of particular note, however, are the parts in update() where a message is constructed and passed. The message is built as a string using stringstream to serialize the required data and send it along. This is a hack, but it at least eliminates any kind of linkage dependency between the components. There is a meta-dependency, in that both the message passer and receiver need to know the data layout of the message, but this dependency is separate from any compile or link-time dependencies.

The other components as in the original Lua example:

ballphysicssubsystem.h:

Spoiler


ballphysicssubsystem.cpp:

Spoiler


playerpaddlephysicssubsystem:

Spoiler


playerpaddlephysicssubsystem.cpp

Spoiler


aipaddlephysicssubsystem.h:

Spoiler


aipaddlephysicssubsystem.cpp:

Spoiler


Of course, you need a kernel or game loop. I implemented a quickie that sets up the systems, creates the walls, ball and paddle objects, then executes a loop similar to what Love provides. There is a load(), update() and draw() function. In main, an SFML window is created then execution is handed off to the kernel() which calls load(), then enters a loop until the window is closed.

main.cpp:

Spoiler

See the load() function for how the objects are created. As in the Lua demo, it creates 4 walls, a ball and 2 paddles.

And that's it. It works (I tested it), but it is not an exhaustively tested setup, so I wouldn't trust it without further testing. Still, it demonstrates the same concepts as the Lua example. Hopefully it helps.

Edited by FLeBlanc, 30 April 2012 - 11:20 AM.


#12 BeerNutts   Crossbones+   -  Reputation: 2550

Like
0Likes
Like

Posted 30 April 2012 - 04:13 PM

You are welcome to take a look at my Journal (link in my Sig). I detail my process of converting one of my games to a Component Based Entity System. You can see how I handle adding the components to entities, and the communication between them.

Good Luck!
My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#13 thestien   Members   -  Reputation: 102

Like
0Likes
Like

Posted 02 May 2012 - 01:44 AM

Hi FLeBlanc

i wish i was your boss because i would give you a promotion and a payrise thank you for doing that for me you have gone well above the call of duty :)

now its in c++ i can understand your code fully and also use it as a base for future programs i make. i also think im begining to understand the benefits of learning this way over oo. and now i have the fun of learning and expanding on your example im sure it will open up a whole new rabbit hole for me to fall into but at least now i have a map and a candle to find my way out :) perhaps ill need to learn mapreading also lol

Hi Beernutts that looks very cool i will have a good read of it soon it looks very interesting at the moment i think its quite alot of info in one project to wrap my head around but once i finish teething on this examples then i can have a good chew at yours :)

i cant thank you enough and im sure i will be back to call on your knowledge soon




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS