You are following a tutorial that teach you the ECS implementation of LIBDRAGON.
ECS mean "Entity Component System".
It's a structure that link those three concepts into a scene.
How ?
We'll begin by creating a simple component. Here's a template :
static void component_destroy(void *data)
{
/*Free the component (data)*/
}
dg_component_t *cpt_/*name*/(/*Arguments that set the components*/)
{
void (*destroy)(void *) = &component_destroy;
/*initialize data*/
dg_component_t *component = NULL;
/*changes on data*/
component = dg_component_create(/*name*/, /*data*/, destroy);
return component;
}
Here's an example :
//This function destroy the component to avoid leaks
//the data of your component will be passed throught when the component will be destroyed
static void component_destroy(void *data)
{
sfVector2f *pos = (sfVector2f *)(data); //we cast the void* into the good type
free(pos); //we free the data
}
//This function is the component itself, here, a position,
//you can pass an x and a y to initialize the position.
//concretely, you just create a variable "dg_component_t *"
//and "specialize" it with the function
//by setting a specific name, data and destroyer.
dg_component_t *dg_cpt_pos(int x, int y)
{
void (*destroy)(void *) = &component_destroy; //set the destroy function pointer
sfVector2f *pos = 0; //initialize the position variable
dg_component_t *component = NULL; //init the component
pos = malloc(sizeof(sfVector2f)); //allocate the vector
pos->x = x;
pos->y = y;
component = dg_component_create("pos", pos, destroy); //create the component
return component;
}
Once done, you can add it to the entity. Alone, a component is often useless, maybe meaningless, but together they'll become powerfull.
Do not hesitate to use the prefab components and create your own !
Now that we have components, we need to group them with an entity, so let's create one :
dg_entity_t *ent_/*name*/(/*Arguments that set the entity*/)
{
dg_entity_t *entity = dg_entity_create(/*name*/);
dg_entity_add_component(entity, /*component*/);
dg_entity_add_component(entity, /*component*/);
dg_entity_add_component(entity, /*component*/);
/*...*/
return entity;
}
Here's an example :
//As for the component, this function is the entity itself
//here we create a player.
dg_entity_t *ent_player(sfVector2f scale)
{
dg_entity_t *player = dg_entity_create("player"); //create entity
dg_entity_add_component(player, dg_cpt_sprite((&scale)); //add a sprite component
dg_entity_add_component(player, dg_cpt_animator(&set_player_animator)); //add an animator component
return player;
}
Systems are the most complicated parts of the ECS.
They are what make your program more than a window and dynamics.
Here how to create one :
void sys_/*name*/(dg_entity_t *entity, dg_window_t *w,
dg_array_t **entities, sfTime dt)
{
/*init component's data variables*/
if (!dg_system_require(entity, /*nb of cpts*/, /*first cpt*/, /*second cpt*/, /*...*/))
return;
/*act on components*/
}
Here's an example :
//As before this function is a system.
//here, it manage the animator component
//Here are the parameter of an animator :
// entity : the entity the system is applyed on :
// The system will be called for EACH entities of the scene, one by one.
// w : the window
// entities : the linked_list of all the entities of the scene.
// dt : deltatime, or timelapse between two frames
void dg_sys_animator(dg_entity_t *entity, dg_window_t *w,
dg_array_t **entities, sfTime dt)
{
dg_animator_t *animator = (dg_animator_t *)(dg_entity_get_component(entity, "animator")); //get the animator
sfSprite *sprite = (sfSprite *)(dg_entity_get_component(entity, "sprite")); //get the sprite
//this function check if the entity contain the asked components;
//here we verify that it is the case.
//If not, then this system doesn't concern the actual entity.
if (!dg_system_require(entity, 2, "animator", "sprite")) //require animator and sprite
return;
dg_animator_update_sprite(animator, sprite, dt.microseconds); //launch the animator manager
}
Scenes are what warp everything together.
They just contains everything so far
Here how to create one :
dg_scene_t *sce_/*name*/(/*arguments*/)
{
dg_scene_t *scene = dg_scene_create(/*name*/);
dg_scene_add_ent(scene, /*entity*/);
dg_scene_add_ent(scene, /*entity*/);
dg_scene_add_ent(scene, /*entity*/);
/*...*/
dg_scene_add_sys(scene, dg_system_create(/*system*/));
dg_scene_add_sys(scene, dg_system_create(/*system*/));
dg_scene_add_sys(scene, dg_system_create(/*system*/));
/*...*/
return scene;
}
Here's an example :
dg_scene_t *sce_game(char *filepath)
{
dg_scene_t *scene_game = dg_scene_create("game"); //create a scene named "game"
dg_scene_add_ent(scene_game, entity_parallax_create()); //add parallax
dg_scene_add_ent(scene_game, entity_camera_create()); //add camera
load_map(scene_game, filepath); //this function set a map automatically
dg_scene_add_ent(scene_game, entity_player_create()); //add player
dg_scene_add_ent(scene_game, entity_music_create("res/ost.ogg")); //add music theme
dg_scene_add_sys(scene_game, dg_system_create(&system_parallax)); //add parallax system
dg_scene_add_sys(scene_game, dg_system_create(&system_player_control)); //add player control system
dg_scene_add_sys(scene_game, dg_system_create(&system_player_animator)); //add animator
dg_scene_add_sys(scene_game, dg_system_create(&system_camera_sp_render)); //add camera renderer
dg_scene_add_sys(scene_game, dg_system_create(&system_hud_sp_render)); //add hud renderer
dg_scene_add_sys(scene_game, dg_system_create(&system_display_text)); //add text renderer
return scene_game;
}
You see ? Easy, you just have to add everything you created before in the scene
Now that everything is set, one thing remain : you have to add the whole scene to your program.
No worries ! It's easy : you'll just have to create your scene in dg_init, update it in dg_loop and destroy it in dg_end
typedef struct data {
dg_scene_t *scene; //add scene to the variables of the window
} data_t;
void *dg_init(dg_window_t *w)
{
data_t *d = malloc(sizeof(data_t));
d->scene = scene_game_create("map.map"); //create the scene
return d;
}
int dg_loop(dg_window_t *w, void *data, sfTime dt)
{
data_t *d = ((data_t *)(data));
sfRenderWindow_clear(w->window, sfBlack);
dg_scene_update(d->scene, w, dt); // update the scene
return 0;
}
void dg_end(void *data, int id)
{
data_t *d = ((data_t *)(data));
dg_scene_destroy(d->scene); //destroy the scene
}
int main(int argc, char **argv)
{
dg_play(screen_size, "LibDragon", 0); // launch the window
return 0;
}
Here's more info about dg_scene_update :
the fisrt argument is your scene, the second is the dg_window and the last the timelapse (dt).
Now you just have to let the magic of ECS doing his job !