From aeeef1a60f6d3564e40a31f784d09424eae3907b Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Mon, 28 Nov 2016 17:47:15 +1100 Subject: [PATCH] Add state data buffer to hold generic configs Add some system that allows us to automatically define and allocate different application states at runtime without having to manually make the entries into our game state structure. It's basically an array of pointers determined by the app states defined in the appstate enum. This is necessary since different modes, (start menu, options, game world) have different data and we'd like to be able to define them uniquely. --- src/Asteroid.c | 136 ++++++++++++++++++++++++--------- src/Debug.c | 16 ++-- src/dengine.c | 10 ++- src/include/Dengine/Asteroid.h | 28 +++++-- 4 files changed, 139 insertions(+), 51 deletions(-) diff --git a/src/Asteroid.c b/src/Asteroid.c index 3fa4637..d3ad93b 100644 --- a/src/Asteroid.c +++ b/src/Asteroid.c @@ -191,7 +191,7 @@ INTERNAL b32 checkEdgeProjectionOverlap(v2 *vertexList, i32 listSize, return result; } -INTERNAL u32 moveEntity(World *world, MemoryArena_ *transientArena, +INTERNAL u32 moveEntity(GameWorldState *world, MemoryArena_ *transientArena, Entity *entity, i32 entityIndex, v2 ddP, f32 dt, f32 ddPSpeed) { @@ -301,7 +301,8 @@ typedef struct { v2 dP; } AsteroidSpec; -INTERNAL void addAsteroidWithSpec(World *world, enum AsteroidSize asteroidSize, +INTERNAL void addAsteroidWithSpec(GameWorldState *world, + enum AsteroidSize asteroidSize, AsteroidSpec *spec) { world->asteroidCounter++; @@ -405,15 +406,15 @@ INTERNAL void addAsteroidWithSpec(World *world, enum AsteroidSize asteroidSize, } asteroid->vertexPoints = vertexCache[cacheIndex]; - asteroid->color = V4(0.0f, 0.5f, 0.5f, 1.0f); + asteroid->color = V4(1.0f, 1.0f, 1.0f, 1.0f); } -INTERNAL void addAsteroid(World *world, enum AsteroidSize asteroidSize) +INTERNAL void addAsteroid(GameWorldState *world, enum AsteroidSize asteroidSize) { addAsteroidWithSpec(world, asteroidSize, NULL); } -INTERNAL void addBullet(World *world, Entity *shooter) +INTERNAL void addBullet(GameWorldState *world, Entity *shooter) { Entity *bullet = &world->entityList[world->entityIndex++]; bullet->id = world->entityIdCounter++; @@ -442,7 +443,7 @@ INTERNAL void addBullet(World *world, Entity *shooter) bullet->color = V4(1.0f, 1.0f, 0, 1.0f); } -INTERNAL void setCollisionRule(World *world, enum EntityType a, +INTERNAL void setCollisionRule(GameWorldState *world, enum EntityType a, enum EntityType b, b32 rule) { ASSERT(a <= entitytype_count); @@ -451,7 +452,8 @@ INTERNAL void setCollisionRule(World *world, enum EntityType a, world->collisionTable[b][a] = rule; } -INTERNAL AudioRenderer *getFreeAudioRenderer(World *world, AudioVorbis *vorbis, +INTERNAL AudioRenderer *getFreeAudioRenderer(GameWorldState *world, + AudioVorbis *vorbis, i32 maxSimultaneousPlayers) { i32 freeIndex = -1; @@ -480,7 +482,7 @@ INTERNAL AudioRenderer *getFreeAudioRenderer(World *world, AudioVorbis *vorbis, return result; } -INTERNAL void addPlayer(World *world) +INTERNAL void addPlayer(GameWorldState *world) { Entity *ship = &world->entityList[world->entityIndex++]; ship->id = world->entityIdCounter++; @@ -507,7 +509,7 @@ INTERNAL void addPlayer(World *world) ship->color = V4(1.0f, 0.5f, 0.5f, 1.0f); } -INTERNAL void deleteEntity(World *world, i32 entityIndex) +INTERNAL void deleteEntity(GameWorldState *world, i32 entityIndex) { ASSERT(entityIndex > 0); ASSERT(entityIndex < ARRAY_COUNT(world->entityList)); @@ -520,9 +522,51 @@ INTERNAL void deleteEntity(World *world, i32 entityIndex) world->entityList[--world->entityIndex] = emptyEntity; } +#define GET_STATE_DATA(state, arena, type) \ + (type *)getStateData_(state, arena, appstate_##type) +INTERNAL void *getStateData_(GameState *state, MemoryArena_ *persistentArena, + enum AppState appState) +{ + void *result = NULL; + switch (appState) + { + case appstate_StartMenuState: + { + if (!state->appStateData[appState]) + { + state->appStateData[appState] = + MEMORY_PUSH_STRUCT(persistentArena, StartMenuState); + } + } + break; + + case appstate_GameWorldState: + { + if (!state->appStateData[appState]) + { + state->appStateData[appState] = + MEMORY_PUSH_STRUCT(persistentArena, GameWorldState); + } + } + break; + + default: + { + ASSERT(INVALID_CODE_PATH); + } + break; + } + + ASSERT(state->appStateData[appState]); + result = state->appStateData[appState]; + return result; +} + INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) { - World *world = &state->world; + GameWorldState *world = + GET_STATE_DATA(state, &state->persistentArena, GameWorldState); + if (!world->init) { world->pixelsPerMeter = 70.0f; @@ -946,9 +990,6 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) } } -LOCAL_PERSIST f32 flashingGameStartTimerThreshold = 1.0f; -LOCAL_PERSIST f32 flashingGameStartTimer = 1.0f; -LOCAL_PERSIST b32 toggleShowGameStart = TRUE; INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) { AssetManager *assetManager = &state->assetManager; @@ -956,7 +997,8 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) Renderer *renderer = &state->renderer; MemoryArena_ *transientArena = &state->transientArena; UiState *uiState = &state->uiState; - World *world = &state->world; + StartMenuState *menuState = + GET_STATE_DATA(state, &state->persistentArena, StartMenuState); Font *arial15 = asset_fontGetOrCreateOnDemand( assetManager, &state->persistentArena, transientArena, "Arial", 15); @@ -976,35 +1018,59 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) ui_beginState(uiState); - flashingGameStartTimer -= dt; - if (flashingGameStartTimer < 0) - { - toggleShowGameStart = (toggleShowGameStart) ? FALSE : TRUE; - flashingGameStartTimer = flashingGameStartTimerThreshold; + { // Draw blinking Start Game prompt + menuState->startMenuGameStartBlinkTimer -= dt; + if (menuState->startMenuGameStartBlinkTimer < 0.0f) + { + menuState->startMenuGameStartBlinkTimer = 1.0f; + menuState->startMenuToggleShow = + (menuState->startMenuToggleShow) ? FALSE : TRUE; + } + + if (menuState->startMenuToggleShow) + { + const char *const gameStart = "Press enter to start"; + v2 gameStartDim = asset_fontStringDimInPixels(arial25, gameStart); + v2 halfGameStartDim = v2_scale(gameStartDim, 0.5f); + v2 gameStartP = v2_add(screenCenter, V2(0, -40)); + gameStartP = v2_sub(gameStartP, halfGameStartDim); + + renderer_stringFixed(renderer, transientArena, arial25, gameStart, + gameStartP, V2(0, 0), 0, V4(1, 1, 0, 1), 0); + } } - if (toggleShowGameStart) { - const char *const gameStart = "Press enter to start"; - v2 gameStartDim = asset_fontStringDimInPixels(arial25, gameStart); - v2 halfGameStartDim = v2_scale(gameStartDim, 0.5f); - v2 gameStartP = v2_add(screenCenter, V2(0, -40)); - gameStartP = v2_sub(gameStartP, halfGameStartDim); - - renderer_stringFixed(renderer, transientArena, arial25, gameStart, - gameStartP, V2(0, 0), 0, V4(1, 1, 0, 1), 0); + const char *const optionPrompt = "Press [o] for options "; + v2 dim = asset_fontStringDimInPixels(arial25, optionPrompt); + v2 halfDim = v2_scale(dim, 0.5f); + v2 p = v2_add(screenCenter, V2(0, -120)); + p = v2_sub(p, halfDim); + renderer_stringFixed(renderer, transientArena, arial25, optionPrompt, p, + V2(0, 0), 0, V4(1, 1, 0, 1), 0); } if (platform_queryKey(&inputBuffer->keys[keycode_enter], readkeytype_one_shot, KEY_DELAY_NONE)) { - state->appState = appstate_game; - addPlayer(&state->world); + state->currState = appstate_GameWorldState; + + GameWorldState *world = + GET_STATE_DATA(state, &state->persistentArena, GameWorldState); + addPlayer(world); } ui_endState(uiState, inputBuffer); } +#define ASTEROID_GET_STATE_DATA(state, type) \ + (type *)asteroid_getStateData_(state, appstate_##type) +void *asteroid_getStateData_(GameState *state, enum AppState appState) +{ + void *result = state->appStateData[appState]; + return result; +} + void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, v2 windowSize, f32 dt) { @@ -1028,21 +1094,21 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, Font *arial15 = asset_fontGet(&state->assetManager, "Arial", 15); debug_init(&state->persistentArena, windowSize, *arial15); - state->appState = appstate_start_menu; - state->init = TRUE; + state->currState = appstate_StartMenuState; + state->init = TRUE; } platform_inputBufferProcess(&state->input, dt); - switch (state->appState) + switch (state->currState) { - case appstate_start_menu: + case appstate_StartMenuState: { // NOTE(doyle): Let menu overlay the game menu. We add player on "enter" // So fall through to appstate_game is valid here! startMenuUpdate(state, memory, dt); } - case appstate_game: + case appstate_GameWorldState: { gameUpdate(state, memory, dt); } diff --git a/src/Debug.c b/src/Debug.c index 5886d8e..565c542 100644 --- a/src/Debug.c +++ b/src/Debug.c @@ -278,12 +278,16 @@ void debug_drawUi(GameState *state, f32 dt) v2 persistentUsage = V2i(persistentUsedInKbs, persistentSizeInKbs); DEBUG_PUSH_VAR("Permanent Usage: %.0f/%.0f", persistentUsage, "v2"); - MemoryArena_ *entityArena = &state->world.entityArena; - i32 entitySizeInKbs = entityArena->size / 1024; - i32 entityUsedInKbs = entityArena->used / 1024; - v2 entityUsage = V2i(entityUsedInKbs, entitySizeInKbs); - DEBUG_PUSH_VAR("Entity Usage: %.0f/%.0f", entityUsage, "v2"); - DEBUG_PUSH_STRING("== =="); + GameWorldState *world = ASTEROID_GET_STATE_DATA(state, GameWorldState); + if (world) + { + MemoryArena_ *entityArena = &world->entityArena; + i32 entitySizeInKbs = entityArena->size / 1024; + i32 entityUsedInKbs = entityArena->used / 1024; + v2 entityUsage = V2i(entityUsedInKbs, entitySizeInKbs); + DEBUG_PUSH_VAR("Entity Usage: %.0f/%.0f", entityUsage, "v2"); + DEBUG_PUSH_STRING("== =="); + } } DEBUG_PUSH_VAR("Num Vertex: %d", diff --git a/src/dengine.c b/src/dengine.c index 7c5233e..c22a7d0 100644 --- a/src/dengine.c +++ b/src/dengine.c @@ -206,7 +206,7 @@ i32 main(void) glfwPollEvents(); /* Rendering commands here*/ - glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); asteroid_gameUpdateAndRender(gameState, &memory, windowSize, @@ -236,10 +236,16 @@ i32 main(void) f32 msPerFrame = secondsElapsed * 1000.0f; f32 framesPerSecond = 1.0f / secondsElapsed; + i32 entityCount = 0; + GameWorldState *world = + ASTEROID_GET_STATE_DATA(gameState, GameWorldState); + + if (world) entityCount = world->entityIndex; + char textBuffer[256]; snprintf(textBuffer, ARRAY_COUNT(textBuffer), "Dengine | %f ms/f | %f fps | Entity Count: %d", - msPerFrame, framesPerSecond, gameState->world.entityIndex); + msPerFrame, framesPerSecond, entityCount); glfwSetWindowTitle(window, textBuffer); titleUpdateFrequencyInSeconds = 0.5f; diff --git a/src/include/Dengine/Asteroid.h b/src/include/Dengine/Asteroid.h index 39dd041..94544eb 100644 --- a/src/include/Dengine/Asteroid.h +++ b/src/include/Dengine/Asteroid.h @@ -12,14 +12,15 @@ enum AppState { - appstate_start_menu, - appstate_game, + appstate_StartMenuState, + appstate_GameWorldState, appstate_count, }; -typedef struct World +typedef struct GameWorldState { b32 init; + MemoryArena_ entityArena; v2 *entityVertexListCache[entitytype_count]; @@ -49,12 +50,20 @@ typedef struct World // TODO(doyle): Ensure we change this if it gets too big b32 collisionTable[entitytype_count][entitytype_count]; -} World; +} GameWorldState; -typedef struct GameState { +typedef struct StartMenuState +{ + f32 startMenuGameStartBlinkTimer; + b32 startMenuToggleShow; +} StartMenuState; + +typedef struct GameState +{ b32 init; - - enum AppState appState; + + enum AppState currState; + void *appStateData[appstate_count]; MemoryArena_ transientArena; MemoryArena_ persistentArena; @@ -65,9 +74,12 @@ typedef struct GameState { Renderer renderer; UiState uiState; - World world; } GameState; +#define ASTEROID_GET_STATE_DATA(state, type) \ + (type *)asteroid_getStateData_(state, appstate_##type) +void *asteroid_getStateData_(GameState *state, enum AppState appState); + void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, v2 windowSize, f32 dt);