I am trying to make a toy Entity Component System to improve on my C++ skills. To do so, I have an ECS_Manager Class, which uses templates to have multiple storage classes for each data type I'd like to use as a component.
Each component is just a struct with an int and whatever else data I'd like. In the example, it contains a Vector2D object. Nothing special.
As a starting point, internally the storage class for each component type has its own vector. I can store new data just fine (it eventually prints out if I do so). However, when I try to save new data to the element in the vector using a pointer dereference (see Physics_System
below), the new values aren't saved. I cannot figure out why.
#include <iostream>
#include <vector>
#include <map>
#include <typeinfo>
#include <memory>
#include <array>
#include "Vector2D.hpp"
#include "./ECS/components/Position_comp.hpp"
class VComponentStorage {
public:
virtual ~VComponentStorage() = default;
};
template <typename T>
class ComponentStorage : public VComponentStorage{
public:
ComponentStorage(){
this->storage_container_index = 0;
}
void add_component(T component){
this->storage_container.push_back(component);
this->storage_container_index++;
}
T *get_component(int entity_id){
T* start_ptr = this->storage_container.data();
return &(start_ptr[entity_id]);
}
size_t get_component_count(){
return 1;
}
private:
std::vector<T> storage_container;
size_t storage_container_index;
};
class ECS_Mananger{
public:
ECS_Mananger(){
}
template<typename T> void register_component(){
const char *type_name = typeid(T).name();
ComponentStorage<T> *comp_storage_ptr = new ComponentStorage<T>;
this->T_to_comp_storage_Map.insert({type_name, comp_storage_ptr});
}
template<typename T>
void add_component(T component){
const char *type_name = typeid(T).name();
std::cout << type_name << std::endl;
ComponentStorage<T>* my_ptr = static_cast<ComponentStorage<T>*>(this->T_to_comp_storage_Map[type_name]);
my_ptr->add_component(component);
}
template<typename T>
T *get_component(int entity_id){
const char *type_name = typeid(T).name();
ComponentStorage<T>* my_ptr = static_cast<ComponentStorage<T>*>(this->T_to_comp_storage_Map[type_name]);
return my_ptr->get_component(entity_id);
}
private:
std::map<std::string, VComponentStorage*> T_to_comp_storage_Map;
};
void Physics_System(ECS_Mananger &world){
Position_Component* my_ptr = world.get_component<Position_Component>(0);
*my_ptr = { 0, Vector2D(5.0, 2.0)};
};
int main() {
//Create Arrays
ECS_Mananger my_world;
my_world.register_component<Position_Component>();
Position_Component init_pos_val = {0, Vector2D(1.0, 2.0)};
my_world.add_component<Position_Component>(init_pos_val);
// Main game loop
while (1) // Detect window close button or ESC key
{
// Update
Physics_System(my_world);
my_world.get_component<Position_Component>(0)->position.print();
}
return 0;
}
In the Physics_System
function, I would expect the value to be updated. Any reason why it isn't? My C++ is a little rusty. At first, I was doing this: 'world.get_component<Position_Component>(0)->position = Vector2D(4.4, 3.3);' to change the value. It wasn't working either, so I thought maybe it had something to do with right/left-hand issues with the language. I suspect that even though I am using pointers, somewhere something is getting copied. I just can't tell where.
Edit:
Position_Component and the Vector2D specification:
#include "Vector2D.hpp"
struct Position_Component {
int entity_id;
Vector2D position;
};
class Vector2D {
public:
double x;
double y;
Vector2D() {
this->x = 0.0;
this->y = 0.0;
}
Vector2D(double x, double y){
this->x = x;
this->y = y;
}
void print(){ std::cout << "(" << this->x << "," << this->y << ")" << std::endl; };
Vector2D operator+(const Vector2D& vec){
Vector2D result;
result.x = this->x + vec.x;
result.y = this->y + vec.y;
return result;
}
Vector2D operator-(const Vector2D& vec){
Vector2D result;
result.x = this->x - vec.x;
result.y = this->y - vec.y;
return result;
}
Vector2D operator=(const Vector2D& vec){
Vector2D result;
result.x = vec.x;
result.y = vec.y;
return result;
}
};
Vector2D operator*(const double& s, Vector2D vec){
Vector2D result;
result.x = s*vec.x;
result.y = s*vec.y;
return result;
}
The problem is your Vector2D
assignment operator:
Vector2D operator=(const Vector2D& vec){
Vector2D result;
result.x = vec.x;
result.y = vec.y;
return result;
}
The assignment operator is supposed to assign to this
object, not create a new object. It should also return a reference to *this
(to allow chained assignments).
Because of that problem, the assignment
*my_ptr = { 0, Vector2D(5.0, 2.0)};
is simply thrown away, you never actually modify *my_ptr
.
To solve it change to e.g.:
Vector2D& operator=(const Vector2D& vec){
x = vec.x;
y = vec.y;
return *this;
}
I recommend spending some time reading this canonical implementation reference for overloading operators.