Jump to content

  • Log In with Google      Sign In   
  • Create Account


Moonkis

Member Since 10 Apr 2011
Offline Last Active Aug 04 2014 02:44 PM

#5098228 [C++/SFML] ECS - How to deal with the entities?

Posted by Moonkis on 02 October 2013 - 01:45 AM

I'm currently trying to wrap my head around Entity, Component and Systems and I'm having a few problems with my design so far.
Basically Entities is a class that contains an index-value and an unique id associated with said index. I feel the way I create, store, recycle and iterate through the entities in the manger is quite unefficient and the code is filled with cluttery. I'm currently using a number of vectors that is somewhat hard to keep synced ( index-wise ) with each other.

 

#ifndef ENTITYMANAGER_HPP
#define ENTITYMANAGER_HPP

#include <vector>
#include "Entity.hpp"
#include "Component.hpp"
#include "System.hpp"

class EntityManager {
private:
	unsigned int indexCount;

	std::vector< Entity* > activeEntities; 
	std::vector< Entity* > deadEntities;
	std::vector< std::vector<Component*> > components;
	std::vector< System* > systems;
public:
	EntityManager();
	~EntityManager();

	Entity createEntity();

	void killEntity(Entity entity);
	void registerComponent(Entity entity, Component* component);
	void removeComponent(Entity entity, CID cid);
	
	Component* getComponent(Entity entity, CID cid);
	
	bool isAlive(Entity entity);

	void invokeSystems();
};

#endif
#include "EntityManager.hpp"
#include "DummySystem.hpp"

EntityManager::EntityManager():indexCount(0) {
	 deadEntities.reserve(100);
	 activeEntities.reserve(100);
	 // This will initialize 100, like reserve but for 2D vectors.
	 components.reserve(100);
	 components.resize(100, std::vector<Component*>(CID::UNIQUE_COUNT, nullptr) );
	 systems.push_back(new DummySystem(this));
}

EntityManager::~EntityManager() {
	for(Entity* e : activeEntities) delete e;
	for(Entity* e : deadEntities) delete e;
	for(System* s : systems) delete s;
	for(auto& v : components)
		for(Component* c : v) delete c;
}

Entity EntityManager::createEntity() {
	// Try to recycle a dead entity
	if ( !deadEntities.empty() ) {
		Entity* entity = deadEntities.back();
		deadEntities.pop_back();
		// Give it a new unique id to make sure it is treated as a new entity
		entity->setUID(entity->getUID()+1);
		activeEntities[entity->getIndex()] = entity;
		return (*entity); 
	}
	
	Entity* entity = new Entity(indexCount++,0);
	activeEntities.push_back(entity);
	components.push_back( std::vector<Component*>(CID::UNIQUE_COUNT) );
	return (*entity);
}

void EntityManager::killEntity(Entity entity) {
	if (!isAlive(entity)) return;
	for(int i = 0; i < CID::UNIQUE_COUNT; i++) {
		delete components[entity.getIndex()][i];
		components[entity.getIndex()][i] = nullptr;
	}
	deadEntities.push_back(activeEntities[entity.getIndex()]);
	activeEntities[entity.getIndex()] = nullptr;
}

Component* EntityManager::getComponent(Entity entity, CID cid) {
	if (!isAlive(entity)) return nullptr;
	return components[entity.getIndex()][cid];
}

bool EntityManager::isAlive(Entity entity) {
	if ( activeEntities.empty() ) return false;
	if ( activeEntities[entity.getIndex()] == nullptr ) return false;
	if ( activeEntities[entity.getIndex()]->getUID() != entity.getUID()) return false;
	return true;
}

void EntityManager::registerComponent(Entity entity, Component* component) {
	if (!isAlive(entity)) return;
	if ( component == nullptr ) return;

	if ( components[entity.getIndex()][component->getCID()] != nullptr )
		delete components[entity.getIndex()][component->getCID()];
	components[entity.getIndex()][component->getCID()] = component;
}

void EntityManager::removeComponent(Entity entity, CID cid) {
	if (!isAlive(entity)) return;
	if ( components[entity.getIndex()][cid] != nullptr )
		delete components[entity.getIndex()][cid];
	components[entity.getIndex()][cid] = nullptr;
}

void EntityManager::invokeSystems() {
	for(Entity* e : activeEntities)
		if (e != nullptr)
			for(System* sys : systems) sys->process(e);
}

Should I rather use another container? Like std::set or std::map and just use a unsigned int id as a key?
Should the EntitySystem handle both entities, components and systems? 




#5084348 What library to choose to write games in C + +

Posted by Moonkis on 09 August 2013 - 02:51 AM

 

I would recommend SFML for someone just beginning.

Seconded. I have used a tiny bit of SFML, and it is quite easy to get started with, stick some sprites on the screen and so on. Follow some tutorial to get over the initial hurdle of getting some app to compile, and you are set.

I'm planning to use SDL2 for a learning project precisely because it has less structure and hand-holding than SFML, and I would like to build my stuff from the ground up for once.

 

Thirded (....). I use SFML in almost all of my project and it's great! Easy to follow, easy to understand and very well designed with OOP in mind. I feel like enough people have voiced their opinion about using C++ as beginners ( Even though you didn't ask...4+ answers basically says the same thing ).




#5035180 Component based system, doing it right?

Posted by Moonkis on 21 February 2013 - 05:40 PM

So I'm trying to make an more sophisticated component system, most have been not as large, but this time around I'm aiming at highly flexible one so this is my first attempt at making anything decent.

I'v grasped the basic aspects of Components, and the only thing that ever has bothered me about it is communications between all of the components, especially in "larger" ( not 3xA large but larger than Pong or Tetris ) "games".

Now I'v came up with an design that I feel comfortable with ( In some aspects anyways ), It's inspired from Don't Starves Lua-Component system as well as a topic ( it can be found here: http://gameprogrammingpatterns.com/component.html#when-to-use-it ).

Basically the Entity ( Container Object ) have an array/vector/map of components that is indexed by an enum-table called "ComponentID", which is where all the components are defined as integer ids.

This helps to sort each component based on it's "priority" which in turn makes sure they are called in the right order as well as clarifies in code what it refers to. 

Entity has under 'protected' two methods called "hasComponent(ComponentID id)" & getComponent(ComponentID id), the later is a template'd function that dynamically casts the right class depending of the ID.

The Component Interface has access to these via friend-keyword seeing as those functions should be exposed but not publicly. Here is the syntax I'm currently targeting within my components, this is from the component "Movable" - which handles movement and coordinates.

 





/* Inside the Movable.cpp */
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Down ) )
{
	float ny = y + MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Up ) )
{
	float ny = y - MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Right ) )
{
	float nx = x + MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Left ) )
{
	float nx = x - MOVEMENT_SPEED * dt;
}

/* If an entity does have an Physics Component. */
if ( entity.hasComponent( ComponentID::PHYSICS ) )
{
	/* Request it as such. */
	Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS );
	/* Check if it's solid and there is no tile/terrain collision */
	if ( p->isSolid() && !p->terrainCollision( nx, ny, entity ) )
	{
		x = nx;
		y = ny;
	}
}
else /* Just move it as such. */
{
	x = nx;
	y = ny;
}

 

I know it's not exactly perfectly decoupled, but this is my first time around and I though it was an easy and flexible way of getting data from other components that said component needs, such as Graphic-Component needs coordinates where to draw the sprite, or in this case, if an Physics-Component exists then check if the object is solid and make sure it doesn't collide.

Now here is where you come in, I'm wondering if this is really a good, or acceptable way of handling communications/interactions between components?  Am I doing the component pattern completely wrong or am I on the right track?

Kind regards, Moonkis.

EDIT:
If anyone is wondering the function: "Entity::getComponent(ComponentID id)" will be using static_cast<> because I'v read that it performs WAY better than dynamic_cast<> and I am confident I'm trying to cast an correct type ( the map<ComponentID, IComponent*> looks like that ).
 




#4998399 C++ pointer strangeness

Posted by Moonkis on 07 November 2012 - 08:02 AM

You're passing the pointer by value. Assignments to the pointer inside the function won't be visible to the calling function. You can try using a reference to a pointer (char *&) instead.

This, or using a pointer to pointer has the same result: void function(Foo** foo) then to assign something new: *foo/(*foo) = <VALUE>


#4954902 Help with Exercises Guess my number

Posted by Moonkis on 02 July 2012 - 07:49 AM

Think of it as this, the computer guesses a random number ( using the rand() % 100 + 1 ) then you check if it's higher, lower or correct.
This can be done with some sort of loop.

For example a while loop:
Pseudo-code(ish):
int guess = rand() % 100 + 1 // Get the initial guess from the computer
while(true)
{
  if(guess < choosenNumber) {
  cout << "That is to low!" << endl;
  guess = rand() % 100 + choosenNumber;
  }
  else  if(guess > choosenNumber) {
  cout << "That is to high!" << endl;
  guess = rand() % choosenNumber + 1;
  }
  else
  {
	cout << "The correct number was guessed" << endl;
	return;
  }
}



#4941740 Handling Collisions Between Rectangles

Posted by Moonkis on 20 May 2012 - 03:59 PM

Now I'm no expert in collision-detection but wouldn't it suffice with only one box for the platform or is the platform in an irregular shape? I.E Not a rectangle type?
Also your problem could be solved by a simple X and Y coordinate check when you do your collision?

If the Players X is in between the platforms edges it's likely the player is colliding either the top or bottom hit-box ( If both top and bottom triggers ( Which they shouldn't according to your description, check if players Y is above or below the platform to determine if it was the hit-box of the bottom or top was triggered. ). If the player y coordinate is not between the edges it's most likely a collision of either edges.

Something like that?

Third problem:


I have a collision checker method in my game that effectively checks the collisions between two objects. (Two nested for loops check each of the player's boxes against each of object b's boxes.) After the check, I have the player respond to the collisions. The problem however, lies in his speed. Because in one frame he could be moving up to 30 pixels (his terminal velocity), he can pass through an object and never have it be detected. What happens more often is that he ends up half in half out of a platform, activating 3 of his collision boxes. I've tried telling the player to automatically move up to rest on the platform, but that results in the player constantly jumping between a point inside the platform, and the point that I want him to be at. Any suggestions to solve this?


I have never used this method before so I don't know if it works this way but couldn't you use a line collision check from point A where he starts and point B where he is after the movement, if the line collides you know you have a collision in the movement path move the player to that location and if you want check the collision as normal.


#4864578 [Remove]

Posted by Moonkis on 22 September 2011 - 02:24 AM

Never mind xD


#4836672 [XNA] Too Many Sprites?

Posted by Moonkis on 18 July 2011 - 12:59 AM

This might not solve your initial problem but, try re-using none active bullets ( bullets that is offscreen ).
That way you could check if their already is an inactive bullet before you allocate a NEW one :)


#4834363 How do people stand Java?

Posted by Moonkis on 12 July 2011 - 09:55 AM

But really, "C++ sucks" gives you about 2.3 million google hits (only a few thousand are on these forums). No need to hijack the thread.

You have really got to be kidding me! That's your argument? Google gives you 2.3 million hits, and then it's settled!? Well then, let's try "Al Qaeda rules" ... uhh, not good; 28.8 million hits *yiks*. Idiot.


First of all, that wasn't his main-argument it just shows that there are a lot of people actually thinking C++ is a bad language.
He is not settling the argument between you guys with THAT argument, he is finishing his posts because he don't want to hijack the entire thread.

Now stop calling people idiots, you sound like a C++ elitist/fanboy


PARTNERS