Sign in to follow this  

Examples of component design in simple game such as pong

This topic is 2056 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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
[code]
class cEntity
{
cPosition* coords;
cMove* mover;
cDraw* drawer;
cCollision* collider;
}
[/code]

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.

[code]

// 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();
}
[/code]

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 :)

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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 [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]
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 [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

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 [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]



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 :)

Share this post


Link to post
Share on other sites
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 [b]object:message()[/b], 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 [i]too[/i] 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 [b]componentlist[/b] tables of the sub-systems to binary chunks; the state could be restored just as easily by loading the chunks back into [b]componentlist[/b].

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...

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
Love expects scripts to be placed in a folder. Save the script inside a folder and name the script [b]main.lua[/b], 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 [b]update()[/b] 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 [i]pointers[/i] to data structures, and the components are dynamically allocated and freed in the [b]createComponent()[/b] and [b]removeComponent()[/b] 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 [b]kill()[/b] and [b]message()[/b] 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 [b]removeComponent()[/b] method. Note that I do not include [b]createComponent()[/b] in the common interface, but rather implement it at the specific subsystem level. This is because the parameters passed to [b]createComponent()[/b] 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 [b]kill()[/b], [b]message()[/b] [b]addComponent()[/b], [b]removeComponent()[/b] and position.

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

framework_base.h

[spoiler]

[code]

#ifndef FRAMEWORK_BASE_H
#define FRAMEWORK_BASE_H
#include <sstream>
#include <vector>
#include <list>

class BaseSubsystem;
class BaseObject;
class BaseComponent
{
public:
BaseComponent(BaseSubsystem *subsystem, BaseObject *owner);
virtual ~BaseComponent();
virtual void message(std::string &msg)=0;
void kill();
BaseObject *getOwner();

private:
BaseSubsystem *subsystem_;
BaseObject *owner_;
};

class BaseObject
{
public:
BaseObject();
~BaseObject();

void kill();
void message(std::string &msg);
void addComponent(BaseComponent *c);
void removeComponent(BaseComponent *c);

float x_,y_;

private:
std::list<BaseComponent *> components_;
};

class BaseSubsystem
{
public:
BaseSubsystem(){};
virtual ~BaseSubsystem(){};
virtual void removeComponent(BaseComponent *c)=0;
};

#endif

[/code]

[/spoiler]

framework_base.cpp:

[spoiler]

[code]

#include "framework_base.h"
#include <iostream>

BaseComponent::BaseComponent(BaseSubsystem *subsystem, BaseObject *owner) : subsystem_(subsystem), owner_(owner)
{
}

BaseComponent::~BaseComponent()
{
}

BaseObject *BaseComponent::getOwner()
{
return owner_;
}

void BaseComponent::kill()
{
if(subsystem_)
{
subsystem_->removeComponent(this);
}
subsystem_=0;
}




BaseObject::BaseObject() : x_(0), y_(0){}
BaseObject::~BaseObject()
{
}

void BaseObject::kill()
{
std::list<BaseComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
(*iter)->kill();
}
components_.clear();
}

void BaseObject::message(std::string &msg)
{
std::list<BaseComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
(*iter)->message(msg);
}
}

void BaseObject::addComponent(BaseComponent *c)
{
components_.push_back(c);
}

void BaseObject::removeComponent(BaseComponent *c)
{
components_.remove(c);
}

[/code]

[/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]

[code]

#ifndef GRAPHICS_SUBSYSTEM_H
#define GRAPHICS_SUBSYSTEM_H
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include "framework_base.h"
#include <vector>

class GraphicsComponent : public BaseComponent
{
public:
GraphicsComponent(BaseSubsystem *subsystem, BaseObject *object, int width, int height) : BaseComponent(subsystem,object),
width_(width), height_(height){}
~GraphicsComponent(){}

void message(std::string &msg){}

float width_,height_;
};

class GraphicsSubsystem : public BaseSubsystem
{
public:
GraphicsSubsystem(sf::RenderWindow *window) : BaseSubsystem(), window_(window){}
~GraphicsSubsystem(){}
void removeComponent(BaseComponent *c);
void createComponent(BaseObject *object, float width, float height);

void draw();

private:
std::vector<GraphicsComponent *> components_;
sf::RenderWindow *window_;
};

#endif

[/code]

[/spoiler]

graphicssubsystem.cpp:

[spoiler]

[code]

#include "graphicssubsystem.h"

void GraphicsSubsystem::removeComponent(BaseComponent *c)
{
std::vector<GraphicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
if((*iter)==c)
{
delete (*iter);
components_.erase(iter);
return;
}
}
}
void GraphicsSubsystem::createComponent(BaseObject *object, float width, float height)
{
components_.push_back(new GraphicsComponent(this, object, width, height));
object->addComponent(components_[components_.size()-1]);
}
void GraphicsSubsystem::draw()
{
std::vector<GraphicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
BaseObject *owner=(*iter)->getOwner();
sf::RectangleShape rect;
rect.setSize(sf::Vector2f((*iter)->width_, (*iter)->height_));
rect.setPosition(owner->x_-(*iter)->width_/2, owner->y_-(*iter)->height_/2);
window_->draw(rect);
}
}

[/code]

[/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]

[code]

#ifndef COLLISION_SUBSYSTEM_H
#define COLLISION_SUBSYSTEM_H
#include "framework_base.h"
#include <vector>
#include <sstream>

enum EPlanes
{
Top,
Bottom,
Left,
Right
};

class CollisionComponent : public BaseComponent
{
public:
CollisionComponent(BaseSubsystem *subsystem, BaseObject *owner, float width, float height, bool solid) :
BaseComponent(subsystem,owner), width_(width), height_(height), solid_(solid){}
~CollisionComponent(){}

void message(std::string &msg){}

float width_, height_;
bool solid_;
};

class CollisionSubsystem : BaseSubsystem
{
public:
CollisionSubsystem() : BaseSubsystem(){}
~CollisionSubsystem(){}

void removeComponent(BaseComponent *c);
void createComponent(BaseObject *object, float width, float height, bool solid);
void update();

private:
std::vector<CollisionComponent *> components_;

bool testCollision(CollisionComponent *b1, CollisionComponent *b2, EPlanes &plane);
void buildBoxString(std::stringstream &s, CollisionComponent *c);
};

#endif

[/code]

[/spoiler]

collisionsubsystem.cpp:

[spoiler]

[code]

#include "collisionsubsystem.h"
#include <cmath>

void CollisionSubsystem::removeComponent(BaseComponent *c)
{
std::vector<CollisionComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
if((*iter)==c)
{
delete (*iter);
components_.erase(iter);
return;
}
}
}

void CollisionSubsystem::createComponent(BaseObject *object, float width, float height, bool solid)
{
components_.push_back(new CollisionComponent(this, object, width, height, solid));
object->addComponent(components_[components_.size()-1]);
}

void CollisionSubsystem::update()
{
std::vector<CollisionComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
std::vector<CollisionComponent *>::iterator test;
for(test=components_.begin(); test!=components_.end(); ++test)
{
if((*iter) != (*test))
{
EPlanes plane;
if(testCollision((*iter), (*test), plane))
{
std::stringstream msg;
msg << "collide " << (int)plane << " ";
buildBoxString(msg, (*iter));
buildBoxString(msg, (*test));
msg << " " << (*test)->solid_;
std::string s(msg.str());


(*iter)->getOwner()->message(s);
}
}
}
}
}

bool CollisionSubsystem::testCollision(CollisionComponent *b1, CollisionComponent *b2, EPlanes &plane)
{
BaseObject *o1=b1->getOwner();
BaseObject *o2=b2->getOwner();

float dx=o2->x_ - o1->x_;
float dy=o2->y_ - o1->y_;

if(fabs(dx) < (b1->width_/2+b2->width_/2) && fabs(dy) < (b1->height_/2+b2->height_/2))
{
// Intersection
// Calculate the plane that the line intersects
// For now, duplicate existing
float px=dx/(b2->width_/2);
float py=dy/(b2->height_/2);
if(fabs(py)>fabs(px))
{
if(dx<0)
{
plane=Top;
return true;
}
else
{
plane=Bottom;
return true;
}
}
else
{
if(dy<0)
{
plane=Left;
return true;
}
else
{
plane=Right;
return true;
}
}
}
return false;
}

void CollisionSubsystem::buildBoxString(std::stringstream &s, CollisionComponent *c)
{
BaseObject *owner=c->getOwner();
s << owner->x_ << " " << owner->y_ << " " << c->width_ << " " << c->height_ << " ";
}

[/code]

[/spoiler]

Again, it's a direct port of the Lua version (with attendant problems). Of particular note, however, are the parts in [b]update()[/b] 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]

[code]

#ifndef BALL_PHYSICS_H
#define BALL_PHYSICS_H
#include "framework_base.h"
#include <SFML/System.hpp>
#include <vector>

class BallPhysicsComponent : public BaseComponent
{
public:
BallPhysicsComponent(BaseSubsystem *subsystem, BaseObject *owner, float vx, float vy) : BaseComponent(subsystem,owner), vx_(vx), vy_(vy){}
~BallPhysicsComponent(){}

void message(std::string &msg);

float vx_, vy_;
};

class BallPhysicsSubsystem : public BaseSubsystem
{
public:
BallPhysicsSubsystem() : BaseSubsystem(){}
~BallPhysicsSubsystem(){}

void removeComponent(BaseComponent *c);
void createComponent(BaseObject *object, float vx, float vy);
void update(sf::Time &elapsed);

private:
std::vector<BallPhysicsComponent *> components_;
};

#endif

[/code]

[/spoiler]

ballphysicssubsystem.cpp:

[spoiler]

[code]

#include "ballphysics.h"
#include "collisionsubsystem.h"
#include <cmath>
#include <sstream>

float sign(float v)
{
if(v<0) return -1.0f;
return 1.0f;
}

void BallPhysicsComponent::message(std::string &msg)
{
std::stringstream m;
m << msg;

// Extract message
std::string eventtype;
m >> eventtype;
if(eventtype==std::string("collide"))
{
// Extract plane
int plane;
m >> plane;

// Extract boxes
float bx1,by1,bw1,bh1;
float bx2,by2,bw2,bh2;

m >> bx1;
m >> by1;
m >> bw1;
m >> bh1;
m >> bx2;
m >> by2;
m >> bw2;
m >> bh2;

// Extract solid
bool solid;
m >> solid;

if(solid)
{
float dx=bx1-bx2;
float dy=by1-by2;
float diffx=(bw1/2+bw2/2) - fabs(dx);
float diffy=(bh1/2+bh2/2) - fabs(dy);
BaseObject *owner=getOwner();

if(diffx<0)
{
owner->x_ = owner->x_ - diffx*sign(dx);
}
if(diffy<0)
{
owner->y_ = owner->y_ - diffy*sign(dy);
}

if(plane==Top || plane==Bottom) vy_=vy_*-1.0f;
if(plane==Left || plane==Right) vx_=vx_*-1.0f;
}
}
}


void BallPhysicsSubsystem::removeComponent(BaseComponent *c)
{
std::vector<BallPhysicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
if((*iter)==c)
{
delete (*iter);
components_.erase(iter);
return;
}
}
}

void BallPhysicsSubsystem::createComponent(BaseObject *object, float vx, float vy)
{
components_.push_back(new BallPhysicsComponent(this, object, vx, vy));
object->addComponent(components_[components_.size()-1]);
}

void BallPhysicsSubsystem::update(sf::Time &elapsed)
{
std::vector<BallPhysicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
// Move the balls
BaseObject *owner=(*iter)->getOwner();
BallPhysicsComponent *comp=(*iter);

owner->x_=owner->x_+comp->vx_*elapsed.asSeconds();
owner->y_=owner->y_+comp->vy_*elapsed.asSeconds();
}
}

[/code]

[/spoiler]

playerpaddlephysicssubsystem:

[spoiler]

[code]

#ifndef PLAYER_PADDLE_SUBSYSTEM
#define PLAYER_PADDLE_SUBSYSTEM
#include <SFML/System.hpp>

#include "framework_base.h"

class PlayerPaddlePhysicsComponent : public BaseComponent
{
public:
PlayerPaddlePhysicsComponent(BaseSubsystem *subsystem, BaseObject *owner, float low, float high, float speed) :
BaseComponent(subsystem,owner), low_(low), high_(high), speed_(speed){}
~PlayerPaddlePhysicsComponent(){}

void message(std::string &msg){};

float low_, high_, speed_;
};

class PlayerPaddlePhysicsSubsystem : public BaseSubsystem
{
public:
PlayerPaddlePhysicsSubsystem() : BaseSubsystem(){}
~PlayerPaddlePhysicsSubsystem(){}

void removeComponent(BaseComponent *c);
void createComponent(BaseObject *object, float low, float high, float speed);
void update(sf::Time &elapsed);

private:
std::vector<PlayerPaddlePhysicsComponent *> components_;
};

#endif

[/code]

[/spoiler]

playerpaddlephysicssubsystem.cpp

[spoiler]

[code]

#include "playerpaddlesubsystem.h"
#include <SFML/Window.hpp>

void PlayerPaddlePhysicsSubsystem::removeComponent(BaseComponent *c)
{
std::vector<PlayerPaddlePhysicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
if((*iter)==c)
{
delete (*iter);
components_.erase(iter);
return;
}
}
}

void PlayerPaddlePhysicsSubsystem::createComponent(BaseObject *object, float low, float high, float speed)
{
components_.push_back(new PlayerPaddlePhysicsComponent(this, object, low, high, speed));
object->addComponent(components_[components_.size()-1]);
}

void PlayerPaddlePhysicsSubsystem::update(sf::Time &elapsed)
{
std::vector<PlayerPaddlePhysicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
PlayerPaddlePhysicsComponent *comp=(*iter);
BaseObject *owner=comp->getOwner();
float vy=0;

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
{
vy=-comp->speed_*elapsed.asSeconds();
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Z))
{
vy=comp->speed_*elapsed.asSeconds();
}
owner->y_=owner->y_+vy;
if(owner->y_<comp->low_) owner->y_=comp->low_;
if(owner->y_>comp->high_) owner->y_=comp->high_;
}
}

[/code]

[/spoiler]

aipaddlephysicssubsystem.h:

[spoiler]

[code]

#ifndef AI_PADDLE_SUBSYSTEM
#define AI_PADDLE_SUBSYSTEM
#include "framework_base.h"
#include <SFML/System.hpp>

class AIPaddlePhysicsComponent : public BaseComponent
{
public:
AIPaddlePhysicsComponent(BaseSubsystem *subsystem, BaseObject *owner, float low, float high, float speed) :
BaseComponent(subsystem,owner), low_(low), high_(high), speed_(speed){}
~AIPaddlePhysicsComponent(){}

void message(std::string &msg){};

float low_, high_, speed_;
};

class AIPaddlePhysicsSubsystem : public BaseSubsystem
{
public:
AIPaddlePhysicsSubsystem() : BaseSubsystem(){}
~AIPaddlePhysicsSubsystem(){}

void removeComponent(BaseComponent *c);
void createComponent(BaseObject *object, float low, float high, float speed);
void update(sf::Time &elapsed);

private:
std::vector<AIPaddlePhysicsComponent *> components_;
};

#endif

[/code]

[/spoiler]

aipaddlephysicssubsystem.cpp:

[spoiler]

[code]

#include "aipaddlesubsystem.h"

void AIPaddlePhysicsSubsystem::removeComponent(BaseComponent *c)
{
std::vector<AIPaddlePhysicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
if((*iter)==c)
{
delete (*iter);
components_.erase(iter);
return;
}
}
}

void AIPaddlePhysicsSubsystem::createComponent(BaseObject *object, float low, float high, float speed)
{
components_.push_back(new AIPaddlePhysicsComponent(this, object, low, high, speed));
object->addComponent(components_[components_.size()-1]);
}

void AIPaddlePhysicsSubsystem::update(sf::Time &elapsed)
{
std::vector<AIPaddlePhysicsComponent *>::iterator iter;
for(iter=components_.begin(); iter!=components_.end(); ++iter)
{
AIPaddlePhysicsComponent *comp=(*iter);
BaseObject *owner=comp->getOwner();

owner->y_=owner->y_+comp->speed_*elapsed.asSeconds();
if(owner->y_<comp->low_)
{
owner->y_=comp->low_;
comp->speed_=comp->speed_*-1.0f;
}
if(owner->y_>comp->high_)
{
owner->y_=comp->high_;
comp->speed_=comp->speed_*-1.0f;
}
}
}

[/code]

[/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 [b]load()[/b], [b]update()[/b] and [b]draw()[/b] function. In main, an SFML window is created then execution is handed off to the [b]kernel()[/b] which calls [b]load()[/b], then enters a loop until the window is closed.

main.cpp:

[spoiler]

[code]
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>
#include "testsubsystem.h"

#include "graphicssubsystem.h"
#include "collisionsubsystem.h"
#include "ballphysics.h"
#include "playerpaddlesubsystem.h"
#include "aipaddlesubsystem.h"

sf::RenderWindow *window;
GraphicsSubsystem *graphics;
CollisionSubsystem *collision;
BallPhysicsSubsystem *ballphysics;
PlayerPaddlePhysicsSubsystem *playerpaddle;
AIPaddlePhysicsSubsystem *aipaddle;

BaseObject topwall,bottomwall,leftwall,rightwall, ball,ppaddle,apaddle;

void draw()
{
window->clear();

graphics->draw();

window->display();
}

void update(sf::Time &dt)
{
playerpaddle->update(dt);
aipaddle->update(dt);
ballphysics->update(dt);
collision->update();
}

void load()
{
graphics=new GraphicsSubsystem(window);
collision=new CollisionSubsystem();
ballphysics=new BallPhysicsSubsystem();
playerpaddle=new PlayerPaddlePhysicsSubsystem();
aipaddle=new AIPaddlePhysicsSubsystem();

// Build walls
graphics->createComponent(&topwall,800,32);
collision->createComponent(&topwall,800,32,true);
topwall.x_=400;
topwall.y_=16;

graphics->createComponent(&bottomwall,800,32);
collision->createComponent(&bottomwall,800,32,true);
bottomwall.x_=400;
bottomwall.y_=600-16;

graphics->createComponent(&leftwall,32,600);
collision->createComponent(&leftwall,32,600,true);
leftwall.x_=16;
leftwall.y_=300;

graphics->createComponent(&rightwall,32,600);
collision->createComponent(&rightwall,32,600,true);
rightwall.x_=800-16;
rightwall.y_=300;

graphics->createComponent(&ball, 8,8);
collision->createComponent(&ball, 8,8,true);
ballphysics->createComponent(&ball,128,128);
ball.x_=400;
ball.y_=300;

// create player paddle
graphics->createComponent(&ppaddle, 16,64);
collision->createComponent(&ppaddle, 16, 64, true);
playerpaddle->createComponent(&ppaddle, 100, 500, 256);
ppaddle.x_=150;
ppaddle.y_=300;

// Create ai paddle
graphics->createComponent(&apaddle, 16,64);
collision->createComponent(&apaddle, 16, 64, true);
aipaddle->createComponent(&apaddle,100,500,256);
apaddle.x_=800-150;
apaddle.y_=300;

}

void shutdown()
{
topwall.kill();
bottomwall.kill();
leftwall.kill();
rightwall.kill();
ball.kill();
ppaddle.kill();
apaddle.kill();

delete graphics;
delete collision;
delete ballphysics;
delete playerpaddle;
delete aipaddle;
}

void kernel()
{
load();
sf::Clock clock;
sf::Time elapsed;
while(window->isOpen())
{
sf::Time thistime=clock.getElapsedTime();
sf::Event event;
while(window->pollEvent(event))
{
if(event.type == sf::Event::Closed)
{
window->close();
return;
}
}
update(elapsed);
draw();
sf::Time endtime=clock.getElapsedTime();
elapsed=endtime-thistime;
}
shutdown();
window->close();
}

int main(int argc, char **argv)
{
window=new sf::RenderWindow(sf::VideoMode(800, 600), "SFML window");
window->setFramerateLimit(60);
kernel();
delete window;
return 0;
}

[/code]

[/spoiler]
See the [b]load()[/b] 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

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

This topic is 2056 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this