Pārlūkot izejas kodu

Engine: simple level-entity system

SG 1 gadu atpakaļ
vecāks
revīzija
f8f1e9aea2
14 mainītis faili ar 546 papildinājumiem un 0 dzēšanām
  1. 49 0
      director.c
  2. 23 0
      director.h
  3. 20 0
      director_i.h
  4. 7 0
      engine.h
  5. 73 0
      entity.c
  6. 41 0
      entity.h
  7. 23 0
      entity_i.h
  8. 117 0
      level.c
  9. 30 0
      level.h
  10. 18 0
      level_i.h
  11. 62 0
      level_manager.c
  12. 18 0
      level_manager.h
  13. 18 0
      level_manager_i.h
  14. 47 0
      main.c

+ 49 - 0
director.c

@@ -0,0 +1,49 @@
+#include "director.h"
+#include "director_i.h"
+#include "level_manager.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct Director {
+    RunningGameEngine* engine;
+    LevelManager* level_manager;
+    InputState input;
+};
+
+Director* director_alloc() {
+    Director* director = malloc(sizeof(Director));
+    memset(director, 0, sizeof(Director));
+    return director;
+}
+
+void director_free(Director* director) {
+    free(director);
+}
+
+void director_input_set(Director* director, InputState input) {
+    director->input = input;
+}
+
+void director_engine_set(Director* director, RunningGameEngine* engine) {
+    director->engine = engine;
+}
+
+void director_level_manager_set(Director* director, LevelManager* level_manager) {
+    director->level_manager = level_manager;
+}
+
+InputState director_input_get(Director* director) {
+    return director->input;
+}
+
+RunningGameEngine* director_engine_get(Director* director) {
+    return director->engine;
+}
+
+LevelManager* director_level_manager_get(Director* director) {
+    return director->level_manager;
+}
+
+Level* director_level_get(Director* director) {
+    return level_manager_current_level_get(director->level_manager);
+}

+ 23 - 0
director.h

@@ -0,0 +1,23 @@
+#pragma once
+#include "game_engine.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Director Director;
+typedef struct RunningGameEngine RunningGameEngine;
+typedef struct LevelManager LevelManager;
+typedef struct Level Level;
+
+InputState director_input_get(Director* director);
+
+RunningGameEngine* director_engine_get(Director* director);
+
+LevelManager* director_level_manager_get(Director* director);
+
+Level* director_level_get(Director* director);
+
+#ifdef __cplusplus
+}
+#endif

+ 20 - 0
director_i.h

@@ -0,0 +1,20 @@
+#pragma once
+#include "director.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Director* director_alloc();
+
+void director_free(Director* director);
+
+void director_input_set(Director* director, InputState input);
+
+void director_engine_set(Director* director, RunningGameEngine* engine);
+
+void director_level_manager_set(Director* director, LevelManager* level_manager);
+
+#ifdef __cplusplus
+}
+#endif

+ 7 - 0
engine.h

@@ -0,0 +1,7 @@
+#pragma once
+#include <furi.h>
+#include "game_engine.h"
+#include "level.h"
+#include "level_manager.h"
+#include "director.h"
+#include "entity.h"

+ 73 - 0
entity.c

@@ -0,0 +1,73 @@
+#include "entity.h"
+#include "entity_i.h"
+#include <stdlib.h>
+#include <furi.h>
+
+struct Entity {
+    Vector position;
+    EntityBehaviour behaviour;
+    void* context;
+};
+
+Entity* entity_alloc() {
+    Entity* entity = malloc(sizeof(Entity));
+    entity->position = VECTOR_ZERO;
+    entity->behaviour = ENTITY_BEHAVIOUR_EMPTY;
+    entity->context = NULL;
+    FURI_LOG_D("Entity", "Allocated entity at %p", entity);
+    return entity;
+}
+
+void entity_free(Entity* entity) {
+    FURI_LOG_D("Entity", "Freeing entity at %p", entity);
+    free(entity);
+}
+
+void entity_behaviour_set(Entity* entity, EntityBehaviour behaviour) {
+    entity->behaviour = behaviour;
+}
+
+void entity_context_set(Entity* entity, void* context) {
+    entity->context = context;
+}
+
+Vector entity_pos_get(Entity* entity) {
+    return entity->position;
+}
+
+void entity_pos_set(Entity* entity, Vector position) {
+    entity->position = position;
+}
+
+void entity_pos_add(Entity* entity, Vector delta) {
+    entity->position.x += delta.x;
+    entity->position.y += delta.y;
+}
+
+void* entity_context_get(Entity* entity) {
+    return entity->context;
+}
+
+void entity_call_start(Entity* entity) {
+    if(entity->behaviour.start) {
+        entity->behaviour.start(entity);
+    }
+}
+
+void entity_call_stop(Entity* entity) {
+    if(entity->behaviour.stop) {
+        entity->behaviour.stop(entity->context);
+    }
+}
+
+void entity_call_update(Entity* entity, Director* director) {
+    if(entity->behaviour.update) {
+        entity->behaviour.update(entity, director, entity->context);
+    }
+}
+
+void entity_call_render(Entity* entity, Director* director, Canvas* canvas) {
+    if(entity->behaviour.render) {
+        entity->behaviour.render(entity, director, canvas, entity->context);
+    }
+}

+ 41 - 0
entity.h

@@ -0,0 +1,41 @@
+#pragma once
+#include "game_engine.h"
+#include "director.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Entity Entity;
+
+typedef struct {
+    float x;
+    float y;
+} Vector;
+
+#define VECTOR_ZERO ((Vector){0, 0})
+
+typedef struct {
+    void (*start)(Entity* entity);
+    void (*stop)(void* context);
+    void (*update)(Entity* entity, Director* director, void* context);
+    void (*render)(Entity* entity, Director* director, Canvas* canvas, void* context);
+} EntityBehaviour;
+
+#define ENTITY_BEHAVIOUR_EMPTY ((EntityBehaviour){NULL, NULL, NULL, NULL})
+
+void entity_behaviour_set(Entity* entity, EntityBehaviour behaviour);
+
+void entity_context_set(Entity* entity, void* context);
+
+Vector entity_pos_get(Entity* entity);
+
+void entity_pos_set(Entity* entity, Vector position);
+
+void entity_pos_add(Entity* entity, Vector delta);
+
+void* entity_context_get(Entity* entity);
+
+#ifdef __cplusplus
+}
+#endif

+ 23 - 0
entity_i.h

@@ -0,0 +1,23 @@
+#pragma once
+#include "entity.h"
+#include "director.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Entity* entity_alloc(void);
+
+void entity_free(Entity* entity);
+
+void entity_call_start(Entity* entity);
+
+void entity_call_stop(Entity* entity);
+
+void entity_call_update(Entity* entity, Director* director);
+
+void entity_call_render(Entity* entity, Director* director, Canvas* canvas);
+
+#ifdef __cplusplus
+}
+#endif

+ 117 - 0
level.c

@@ -0,0 +1,117 @@
+#include "level.h"
+#include "level_i.h"
+#include "entity_i.h"
+#include <m-list.h>
+#include <furi.h>
+
+LIST_DEF(EntityList, Entity*, M_POD_OPLIST);
+
+struct Level {
+    EntityList_t entities;
+    EntityList_t entities_to_add;
+    EntityList_t entities_to_remove;
+    LevelBehaviour behaviour;
+    void* context;
+};
+
+static void level_process_add(Level* level) {
+    EntityList_it_t it;
+    EntityList_it(it, level->entities_to_add);
+    while(!EntityList_end_p(it)) {
+        EntityList_push_back(level->entities, *EntityList_cref(it));
+        entity_call_start(*EntityList_ref(it));
+        EntityList_next(it);
+    }
+    EntityList_clear(level->entities_to_add);
+}
+
+static void level_find_and_remove(Level* level, Entity* entity) {
+    EntityList_it_t it;
+    EntityList_it(it, level->entities);
+    while(!EntityList_end_p(it)) {
+        if(*EntityList_ref(it) == entity) {
+            entity_call_stop(*EntityList_ref(it));
+            EntityList_remove(level->entities, it);
+            entity_free(entity);
+            break;
+        }
+        EntityList_next(it);
+    }
+}
+
+static void level_process_remove(Level* level) {
+    EntityList_it_t it;
+    EntityList_it(it, level->entities_to_remove);
+    while(!EntityList_end_p(it)) {
+        level_find_and_remove(level, *EntityList_cref(it));
+        EntityList_next(it);
+    }
+    EntityList_clear(level->entities_to_remove);
+}
+
+Level* level_alloc(void) {
+    Level* level = malloc(sizeof(Level));
+    EntityList_init(level->entities);
+    EntityList_init(level->entities_to_add);
+    EntityList_init(level->entities_to_remove);
+    level->behaviour = LEVEL_BEHAVIOUR_EMPTY;
+    level->context = NULL;
+    FURI_LOG_D("Level", "Allocated level at %p", level);
+    return level;
+}
+
+void level_free(Level* level) {
+    level_process_add(level);
+    level_process_remove(level);
+
+    EntityList_it_t it;
+    EntityList_it(it, level->entities);
+    while(!EntityList_end_p(it)) {
+        entity_call_stop(*EntityList_ref(it));
+        entity_free(*EntityList_ref(it));
+        EntityList_next(it);
+    }
+
+    EntityList_clear(level->entities);
+    EntityList_clear(level->entities_to_add);
+    EntityList_clear(level->entities_to_remove);
+
+    FURI_LOG_D("Level", "Freeing level at %p", level);
+    free(level);
+}
+
+void level_behaviour_set(Level* level, LevelBehaviour behaviour, void* context) {
+    level->behaviour = behaviour;
+    level->context = context;
+}
+
+Entity* level_add_entity(Level* level) {
+    Entity* entity = entity_alloc();
+    EntityList_push_back(level->entities_to_add, entity);
+    return entity;
+}
+
+void level_remove_entity(Level* level, Entity* entity) {
+    EntityList_push_back(level->entities_to_remove, entity);
+}
+
+void level_update(Level* level, Director* director) {
+    level_process_add(level);
+    level_process_remove(level);
+
+    EntityList_it_t it;
+    EntityList_it(it, level->entities);
+    while(!EntityList_end_p(it)) {
+        entity_call_update(*EntityList_ref(it), director);
+        EntityList_next(it);
+    }
+}
+
+void level_render(Level* level, Director* director, Canvas* canvas) {
+    EntityList_it_t it;
+    EntityList_it(it, level->entities);
+    while(!EntityList_end_p(it)) {
+        entity_call_render(*EntityList_ref(it), director, canvas);
+        EntityList_next(it);
+    }
+}

+ 30 - 0
level.h

@@ -0,0 +1,30 @@
+#pragma once
+#include "entity.h"
+#include "director.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Level Level;
+
+typedef struct {
+    void (*start)(Level* level, void* context);
+    void (*stop)(Level* level, void* context);
+    void (*update_pre)(Level* level, Director* director, void* context);
+    void (*update_post)(Level* level, Director* director, void* context);
+    void (*render_pre)(Level* level, Director* director, Canvas* canvas, void* context);
+    void (*render_post)(Level* level, Director* director, Canvas* canvas, void* context);
+} LevelBehaviour;
+
+#define LEVEL_BEHAVIOUR_EMPTY ((LevelBehaviour){NULL, NULL, NULL, NULL, NULL, NULL})
+
+void level_behaviour_set(Level* level, LevelBehaviour behaviour, void* context);
+
+Entity* level_add_entity(Level* level);
+
+void level_remove_entity(Level* level, Entity* entity);
+
+#ifdef __cplusplus
+}
+#endif

+ 18 - 0
level_i.h

@@ -0,0 +1,18 @@
+#pragma once
+#include "level.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Level* level_alloc(void);
+
+void level_free(Level* level);
+
+void level_update(Level* level, Director* director);
+
+void level_render(Level* level, Director* director, Canvas* canvas);
+
+#ifdef __cplusplus
+}
+#endif

+ 62 - 0
level_manager.c

@@ -0,0 +1,62 @@
+#include "level_manager.h"
+#include "level_i.h"
+#include <furi.h>
+#include <m-list.h>
+
+LIST_DEF(LevelList, Level*, M_POD_OPLIST);
+
+struct LevelManager {
+    LevelList_t levels;
+    Level* current_level;
+    Level* next_level;
+};
+
+LevelManager* level_manager_alloc() {
+    LevelManager* lm = malloc(sizeof(LevelManager));
+    lm->current_level = NULL;
+    lm->next_level = NULL;
+    LevelList_init(lm->levels);
+    return lm;
+}
+
+void level_manager_free(LevelManager* lm) {
+    LevelList_it_t it;
+    LevelList_it(it, lm->levels);
+    while(!LevelList_end_p(it)) {
+        level_free(*LevelList_cref(it));
+        LevelList_next(it);
+    }
+
+    LevelList_clear(lm->levels);
+    free(lm);
+}
+
+Level* level_manager_add_level(LevelManager* manager) {
+    UNUSED(manager);
+    Level* level = level_alloc();
+    LevelList_push_back(manager->levels, level);
+    if(!manager->current_level) {
+        manager->current_level = level;
+    }
+    return level;
+}
+
+void level_manager_next_level_set(LevelManager* lm, Level* next_level) {
+    lm->next_level = next_level;
+}
+
+Level* level_manager_current_level_get(LevelManager* lm) {
+    return lm->current_level;
+}
+
+void level_manager_update(LevelManager* lm, Director* director) {
+    if(lm->next_level) {
+        lm->current_level = lm->next_level;
+        lm->next_level = NULL;
+    }
+    level_update(lm->current_level, director);
+}
+
+void level_manager_render(LevelManager* lm, Director* director, Canvas* canvas) {
+    level_render(lm->current_level, director, canvas);
+}

+ 18 - 0
level_manager.h

@@ -0,0 +1,18 @@
+#pragma once
+#include "level.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct LevelManager LevelManager;
+
+Level* level_manager_add_level(LevelManager* manager);
+
+void level_manager_next_level_set(LevelManager* manager, Level* level);
+
+Level* level_manager_current_level_get(LevelManager* manager);
+
+#ifdef __cplusplus
+}
+#endif

+ 18 - 0
level_manager_i.h

@@ -0,0 +1,18 @@
+#pragma once
+#include "level_manager.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LevelManager* level_manager_alloc(void);
+
+void level_manager_free(LevelManager* manager);
+
+void level_manager_update(LevelManager* level, Director* director);
+
+void level_manager_render(LevelManager* level, Director* director, Canvas* canvas);
+
+#ifdef __cplusplus
+}
+#endif

+ 47 - 0
main.c

@@ -0,0 +1,47 @@
+#include <furi.h>
+#include "fgeo/game_engine.h"
+#include "fgeo/director_i.h"
+#include "fgeo/level_manager_i.h"
+#include "fgeo/level_i.h"
+
+void game_setup(Level* level);
+void game_destroy(void);
+
+static void frame_cb(RunningGameEngine* engine, Canvas* canvas, InputState input, void* context) {
+    Director* director = context;
+    director_input_set(director, input);
+    director_engine_set(director, engine);
+
+    LevelManager* level_manager = director_level_manager_get(director);
+    level_manager_update(level_manager, director);
+    level_manager_render(level_manager, director, canvas);
+}
+
+int32_t game_app(void* p) {
+    UNUSED(p);
+
+    Director* director = director_alloc();
+    LevelManager* level_manager = level_manager_alloc();
+    director_level_manager_set(director, level_manager);
+    Level* level = level_manager_add_level(level_manager);
+
+    game_setup(level);
+
+    GameEngineSettings settings = game_engine_settings_init();
+    settings.fps = 60.0f;
+    settings.show_fps = true;
+    settings.always_backlight = true;
+    settings.frame_callback = frame_cb;
+    settings.context = director;
+
+    GameEngine* engine = game_engine_alloc(settings);
+    game_engine_run(engine);
+    game_engine_free(engine);
+
+    game_destroy();
+
+    level_manager_free(level_manager);
+    director_free(director);
+
+    return 0;
+}