Ver Fonte

Engine: game callback

SG há 1 ano atrás
pai
commit
8cd9d6512a
2 ficheiros alterados com 92 adições e 34 exclusões
  1. 48 29
      game_engine.c
  2. 44 5
      game_engine.h

+ 48 - 29
game_engine.c

@@ -10,6 +10,8 @@ GameEngineSettings game_engine_settings_init() {
     GameEngineSettings settings;
     GameEngineSettings settings;
     settings.fps = 60.0f;
     settings.fps = 60.0f;
     settings.show_fps = false;
     settings.show_fps = false;
+    settings.callback = NULL;
+    settings.context = NULL;
     return settings;
     return settings;
 }
 }
 
 
@@ -29,6 +31,8 @@ typedef enum {
 #define GameThreadFlagMask (GameThreadFlagUpdate | GameThreadFlagStop)
 #define GameThreadFlagMask (GameThreadFlagUpdate | GameThreadFlagStop)
 
 
 GameEngine* game_engine_alloc(GameEngineSettings settings) {
 GameEngine* game_engine_alloc(GameEngineSettings settings) {
+    furi_check(settings.callback != NULL);
+
     GameEngine* engine = malloc(sizeof(GameEngine));
     GameEngine* engine = malloc(sizeof(GameEngine));
     engine->gui = furi_record_open(RECORD_GUI);
     engine->gui = furi_record_open(RECORD_GUI);
     engine->input_pubsub = furi_record_open(RECORD_INPUT_EVENTS);
     engine->input_pubsub = furi_record_open(RECORD_INPUT_EVENTS);
@@ -62,23 +66,35 @@ static void clock_timer_callback(void* context) {
     furi_thread_flags_set(engine->thread_id, GameThreadFlagUpdate);
     furi_thread_flags_set(engine->thread_id, GameThreadFlagUpdate);
 }
 }
 
 
+static const GameKey keys[] = {
+    [InputKeyUp] = GameKeyUp,
+    [InputKeyDown] = GameKeyDown,
+    [InputKeyRight] = GameKeyRight,
+    [InputKeyLeft] = GameKeyLeft,
+    [InputKeyOk] = GameKeyOk,
+    [InputKeyBack] = GameKeyBack,
+};
+
+static const size_t keys_count = sizeof(keys) / sizeof(keys[0]);
+
 static void input_events_callback(const void* value, void* context) {
 static void input_events_callback(const void* value, void* context) {
     AtomicUint32* input_state = context;
     AtomicUint32* input_state = context;
-
     const InputEvent* event = value;
     const InputEvent* event = value;
-    if(event->type == InputTypePress) {
-        *input_state |= (1 << event->key);
-    } else if(event->type == InputTypeRelease) {
-        *input_state &= ~(1 << event->key);
+
+    if(event->key < keys_count) {
+        switch(event->type) {
+        case InputTypePress:
+            *input_state |= (keys[event->key]);
+            break;
+        case InputTypeRelease:
+            *input_state &= ~(keys[event->key]);
+            break;
+        default:
+            break;
+        }
     }
     }
 }
 }
 
 
-typedef struct {
-    uint32_t current;
-    uint32_t pressed;
-    uint32_t released;
-} InputState;
-
 void game_engine_run(GameEngine* engine) {
 void game_engine_run(GameEngine* engine) {
     engine->running = true;
     engine->running = true;
 
 
@@ -105,45 +121,48 @@ void game_engine_run(GameEngine* engine) {
         furi_check((flags & FuriFlagError) == 0);
         furi_check((flags & FuriFlagError) == 0);
 
 
         if(flags & GameThreadFlagUpdate) {
         if(flags & GameThreadFlagUpdate) {
+            // update fps counter
+            uint32_t time_end = DWT->CYCCNT;
+            uint32_t time_delta = time_end - time_start;
+            time_start = time_end;
+
+            // update input state
             uint32_t input_current_state = input_state;
             uint32_t input_current_state = input_state;
             InputState input = {
             InputState input = {
-                .current = input_current_state,
+                .held = input_current_state,
                 .pressed = input_current_state & ~input_prev_state,
                 .pressed = input_current_state & ~input_prev_state,
                 .released = ~input_current_state & input_prev_state,
                 .released = ~input_current_state & input_prev_state,
             };
             };
             input_prev_state = input_current_state;
             input_prev_state = input_current_state;
 
 
+            // clear screen
             canvas_reset(canvas);
             canvas_reset(canvas);
-            uint32_t time_end = DWT->CYCCNT;
-            uint32_t time_delta = time_end - time_start;
-            time_start = time_end;
 
 
+            // do the work
+            float fps = 1.0f / (time_delta / (float)SystemCoreClock);
+            float delta = engine->settings.fps / fps;
+
+            engine->settings.callback(engine, canvas, input, delta, engine->settings.context);
+
+            // show fps if needed
             if(engine->settings.show_fps) {
             if(engine->settings.show_fps) {
-                float fps = 1.0f / (time_delta / (float)SystemCoreClock);
                 canvas_set_color(canvas, ColorXOR);
                 canvas_set_color(canvas, ColorXOR);
                 canvas_printf(canvas, 0, 7, "%u", (uint32_t)roundf(fps));
                 canvas_printf(canvas, 0, 7, "%u", (uint32_t)roundf(fps));
             }
             }
 
 
-            if(input.pressed != 0) {
-                FURI_LOG_I("input", "pressed: %lu", input.pressed);
-
-                if(input.pressed & (1 << InputKeyBack)) {
-                    game_engine_stop(engine);
-                }
-            }
-
-            if(input.released != 0) {
-                FURI_LOG_I("input", "released: %lu", input.released);
-            }
-
+            // and output screen buffer
             canvas_commit(canvas);
             canvas_commit(canvas);
-        } else if(flags & GameThreadFlagStop) {
+        }
+
+        if(flags & GameThreadFlagStop) {
             break;
             break;
         }
         }
     }
     }
 
 
+    // stop timer
     clock_timer_stop();
     clock_timer_stop();
 
 
+    // release gui canvas and unsubscribe from input events
     gui_direct_draw_release(engine->gui);
     gui_direct_draw_release(engine->gui);
     furi_pubsub_unsubscribe(engine->input_pubsub, input_subscription);
     furi_pubsub_unsubscribe(engine->input_pubsub, input_subscription);
 
 

+ 44 - 5
game_engine.h

@@ -1,25 +1,64 @@
 #pragma once
 #pragma once
 #include <stdbool.h>
 #include <stdbool.h>
+#include <gui/canvas.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-typedef struct {
-    float fps;
-    bool show_fps;
-} GameEngineSettings;
+typedef enum {
+    GameKeyUp = 1 << 0,
+    GameKeyDown = 1 << 1,
+    GameKeyRight = 1 << 2,
+    GameKeyLeft = 1 << 3,
+    GameKeyOk = 1 << 4,
+    GameKeyBack = 1 << 5,
+} GameKey;
 
 
-GameEngineSettings game_engine_settings_init();
+typedef struct {
+    uint32_t held; // mask of GameKey held in current frame
+    uint32_t pressed; // mask of GameKey pressed in current frame
+    uint32_t released; // mask of GameKey released in current frame
+} InputState;
 
 
 typedef struct GameEngine GameEngine;
 typedef struct GameEngine GameEngine;
 
 
+typedef void (*GameEngineCallback)(
+    GameEngine* engine,
+    Canvas* canvas,
+    InputState input,
+    float delta_time,
+    void* context);
+
+typedef struct {
+    float fps; // target fps
+    bool show_fps; // show fps counter
+    GameEngineCallback callback; // game logic and rendering callback, called at target fps
+    void* context; // user context passed to callback
+} GameEngineSettings;
+
+/** Default settings initializer */
+GameEngineSettings game_engine_settings_init(void);
+
+/** Game Engine allocator
+ * @param settings engine settings
+ * @return GameEngine*  engine instance
+ */
 GameEngine* game_engine_alloc(GameEngineSettings settings);
 GameEngine* game_engine_alloc(GameEngineSettings settings);
 
 
+/** Run the Game Engine. Blocks until game_engine_stop() is called.
+ * @param engine engine instance
+ */
 void game_engine_run(GameEngine* engine);
 void game_engine_run(GameEngine* engine);
 
 
+/** Stop the Game Engine. Will not block execution.
+ * @param engine engine instance
+ */
 void game_engine_stop(GameEngine* engine);
 void game_engine_stop(GameEngine* engine);
 
 
+/** Free the Game Engine
+ * @param engine engine instance
+ */
 void game_engine_free(GameEngine* engine);
 void game_engine_free(GameEngine* engine);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus