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.
This commit is contained in:
Doyle Thai 2016-11-28 17:47:15 +11:00
parent 15b4ff59f7
commit aeeef1a60f
4 changed files with 139 additions and 51 deletions

View File

@ -191,7 +191,7 @@ INTERNAL b32 checkEdgeProjectionOverlap(v2 *vertexList, i32 listSize,
return result; return result;
} }
INTERNAL u32 moveEntity(World *world, MemoryArena_ *transientArena, INTERNAL u32 moveEntity(GameWorldState *world, MemoryArena_ *transientArena,
Entity *entity, i32 entityIndex, v2 ddP, f32 dt, Entity *entity, i32 entityIndex, v2 ddP, f32 dt,
f32 ddPSpeed) f32 ddPSpeed)
{ {
@ -301,7 +301,8 @@ typedef struct {
v2 dP; v2 dP;
} AsteroidSpec; } AsteroidSpec;
INTERNAL void addAsteroidWithSpec(World *world, enum AsteroidSize asteroidSize, INTERNAL void addAsteroidWithSpec(GameWorldState *world,
enum AsteroidSize asteroidSize,
AsteroidSpec *spec) AsteroidSpec *spec)
{ {
world->asteroidCounter++; world->asteroidCounter++;
@ -405,15 +406,15 @@ INTERNAL void addAsteroidWithSpec(World *world, enum AsteroidSize asteroidSize,
} }
asteroid->vertexPoints = vertexCache[cacheIndex]; 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); addAsteroidWithSpec(world, asteroidSize, NULL);
} }
INTERNAL void addBullet(World *world, Entity *shooter) INTERNAL void addBullet(GameWorldState *world, Entity *shooter)
{ {
Entity *bullet = &world->entityList[world->entityIndex++]; Entity *bullet = &world->entityList[world->entityIndex++];
bullet->id = world->entityIdCounter++; 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); 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) enum EntityType b, b32 rule)
{ {
ASSERT(a <= entitytype_count); ASSERT(a <= entitytype_count);
@ -451,7 +452,8 @@ INTERNAL void setCollisionRule(World *world, enum EntityType a,
world->collisionTable[b][a] = rule; world->collisionTable[b][a] = rule;
} }
INTERNAL AudioRenderer *getFreeAudioRenderer(World *world, AudioVorbis *vorbis, INTERNAL AudioRenderer *getFreeAudioRenderer(GameWorldState *world,
AudioVorbis *vorbis,
i32 maxSimultaneousPlayers) i32 maxSimultaneousPlayers)
{ {
i32 freeIndex = -1; i32 freeIndex = -1;
@ -480,7 +482,7 @@ INTERNAL AudioRenderer *getFreeAudioRenderer(World *world, AudioVorbis *vorbis,
return result; return result;
} }
INTERNAL void addPlayer(World *world) INTERNAL void addPlayer(GameWorldState *world)
{ {
Entity *ship = &world->entityList[world->entityIndex++]; Entity *ship = &world->entityList[world->entityIndex++];
ship->id = world->entityIdCounter++; ship->id = world->entityIdCounter++;
@ -507,7 +509,7 @@ INTERNAL void addPlayer(World *world)
ship->color = V4(1.0f, 0.5f, 0.5f, 1.0f); 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 > 0);
ASSERT(entityIndex < ARRAY_COUNT(world->entityList)); ASSERT(entityIndex < ARRAY_COUNT(world->entityList));
@ -520,9 +522,51 @@ INTERNAL void deleteEntity(World *world, i32 entityIndex)
world->entityList[--world->entityIndex] = emptyEntity; 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) 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) if (!world->init)
{ {
world->pixelsPerMeter = 70.0f; 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) INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt)
{ {
AssetManager *assetManager = &state->assetManager; AssetManager *assetManager = &state->assetManager;
@ -956,7 +997,8 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt)
Renderer *renderer = &state->renderer; Renderer *renderer = &state->renderer;
MemoryArena_ *transientArena = &state->transientArena; MemoryArena_ *transientArena = &state->transientArena;
UiState *uiState = &state->uiState; UiState *uiState = &state->uiState;
World *world = &state->world; StartMenuState *menuState =
GET_STATE_DATA(state, &state->persistentArena, StartMenuState);
Font *arial15 = asset_fontGetOrCreateOnDemand( Font *arial15 = asset_fontGetOrCreateOnDemand(
assetManager, &state->persistentArena, transientArena, "Arial", 15); assetManager, &state->persistentArena, transientArena, "Arial", 15);
@ -976,35 +1018,59 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt)
ui_beginState(uiState); ui_beginState(uiState);
flashingGameStartTimer -= dt; { // Draw blinking Start Game prompt
if (flashingGameStartTimer < 0) menuState->startMenuGameStartBlinkTimer -= dt;
{ if (menuState->startMenuGameStartBlinkTimer < 0.0f)
toggleShowGameStart = (toggleShowGameStart) ? FALSE : TRUE; {
flashingGameStartTimer = flashingGameStartTimerThreshold; 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"; const char *const optionPrompt = "Press [o] for options ";
v2 gameStartDim = asset_fontStringDimInPixels(arial25, gameStart); v2 dim = asset_fontStringDimInPixels(arial25, optionPrompt);
v2 halfGameStartDim = v2_scale(gameStartDim, 0.5f); v2 halfDim = v2_scale(dim, 0.5f);
v2 gameStartP = v2_add(screenCenter, V2(0, -40)); v2 p = v2_add(screenCenter, V2(0, -120));
gameStartP = v2_sub(gameStartP, halfGameStartDim); p = v2_sub(p, halfDim);
renderer_stringFixed(renderer, transientArena, arial25, optionPrompt, p,
renderer_stringFixed(renderer, transientArena, arial25, gameStart, V2(0, 0), 0, V4(1, 1, 0, 1), 0);
gameStartP, V2(0, 0), 0, V4(1, 1, 0, 1), 0);
} }
if (platform_queryKey(&inputBuffer->keys[keycode_enter], if (platform_queryKey(&inputBuffer->keys[keycode_enter],
readkeytype_one_shot, KEY_DELAY_NONE)) readkeytype_one_shot, KEY_DELAY_NONE))
{ {
state->appState = appstate_game; state->currState = appstate_GameWorldState;
addPlayer(&state->world);
GameWorldState *world =
GET_STATE_DATA(state, &state->persistentArena, GameWorldState);
addPlayer(world);
} }
ui_endState(uiState, inputBuffer); 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, void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
v2 windowSize, f32 dt) v2 windowSize, f32 dt)
{ {
@ -1028,21 +1094,21 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
Font *arial15 = asset_fontGet(&state->assetManager, "Arial", 15); Font *arial15 = asset_fontGet(&state->assetManager, "Arial", 15);
debug_init(&state->persistentArena, windowSize, *arial15); debug_init(&state->persistentArena, windowSize, *arial15);
state->appState = appstate_start_menu; state->currState = appstate_StartMenuState;
state->init = TRUE; state->init = TRUE;
} }
platform_inputBufferProcess(&state->input, dt); 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" // NOTE(doyle): Let menu overlay the game menu. We add player on "enter"
// So fall through to appstate_game is valid here! // So fall through to appstate_game is valid here!
startMenuUpdate(state, memory, dt); startMenuUpdate(state, memory, dt);
} }
case appstate_game: case appstate_GameWorldState:
{ {
gameUpdate(state, memory, dt); gameUpdate(state, memory, dt);
} }

View File

@ -278,12 +278,16 @@ void debug_drawUi(GameState *state, f32 dt)
v2 persistentUsage = V2i(persistentUsedInKbs, persistentSizeInKbs); v2 persistentUsage = V2i(persistentUsedInKbs, persistentSizeInKbs);
DEBUG_PUSH_VAR("Permanent Usage: %.0f/%.0f", persistentUsage, "v2"); DEBUG_PUSH_VAR("Permanent Usage: %.0f/%.0f", persistentUsage, "v2");
MemoryArena_ *entityArena = &state->world.entityArena; GameWorldState *world = ASTEROID_GET_STATE_DATA(state, GameWorldState);
i32 entitySizeInKbs = entityArena->size / 1024; if (world)
i32 entityUsedInKbs = entityArena->used / 1024; {
v2 entityUsage = V2i(entityUsedInKbs, entitySizeInKbs); MemoryArena_ *entityArena = &world->entityArena;
DEBUG_PUSH_VAR("Entity Usage: %.0f/%.0f", entityUsage, "v2"); i32 entitySizeInKbs = entityArena->size / 1024;
DEBUG_PUSH_STRING("== =="); 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", DEBUG_PUSH_VAR("Num Vertex: %d",

View File

@ -206,7 +206,7 @@ i32 main(void)
glfwPollEvents(); glfwPollEvents();
/* Rendering commands here*/ /* 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); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
asteroid_gameUpdateAndRender(gameState, &memory, windowSize, asteroid_gameUpdateAndRender(gameState, &memory, windowSize,
@ -236,10 +236,16 @@ i32 main(void)
f32 msPerFrame = secondsElapsed * 1000.0f; f32 msPerFrame = secondsElapsed * 1000.0f;
f32 framesPerSecond = 1.0f / secondsElapsed; f32 framesPerSecond = 1.0f / secondsElapsed;
i32 entityCount = 0;
GameWorldState *world =
ASTEROID_GET_STATE_DATA(gameState, GameWorldState);
if (world) entityCount = world->entityIndex;
char textBuffer[256]; char textBuffer[256];
snprintf(textBuffer, ARRAY_COUNT(textBuffer), snprintf(textBuffer, ARRAY_COUNT(textBuffer),
"Dengine | %f ms/f | %f fps | Entity Count: %d", "Dengine | %f ms/f | %f fps | Entity Count: %d",
msPerFrame, framesPerSecond, gameState->world.entityIndex); msPerFrame, framesPerSecond, entityCount);
glfwSetWindowTitle(window, textBuffer); glfwSetWindowTitle(window, textBuffer);
titleUpdateFrequencyInSeconds = 0.5f; titleUpdateFrequencyInSeconds = 0.5f;

View File

@ -12,14 +12,15 @@
enum AppState enum AppState
{ {
appstate_start_menu, appstate_StartMenuState,
appstate_game, appstate_GameWorldState,
appstate_count, appstate_count,
}; };
typedef struct World typedef struct GameWorldState
{ {
b32 init; b32 init;
MemoryArena_ entityArena; MemoryArena_ entityArena;
v2 *entityVertexListCache[entitytype_count]; v2 *entityVertexListCache[entitytype_count];
@ -49,12 +50,20 @@ typedef struct World
// TODO(doyle): Ensure we change this if it gets too big // TODO(doyle): Ensure we change this if it gets too big
b32 collisionTable[entitytype_count][entitytype_count]; b32 collisionTable[entitytype_count][entitytype_count];
} World; } GameWorldState;
typedef struct GameState { typedef struct StartMenuState
{
f32 startMenuGameStartBlinkTimer;
b32 startMenuToggleShow;
} StartMenuState;
typedef struct GameState
{
b32 init; b32 init;
enum AppState appState; enum AppState currState;
void *appStateData[appstate_count];
MemoryArena_ transientArena; MemoryArena_ transientArena;
MemoryArena_ persistentArena; MemoryArena_ persistentArena;
@ -65,9 +74,12 @@ typedef struct GameState {
Renderer renderer; Renderer renderer;
UiState uiState; UiState uiState;
World world;
} GameState; } 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, void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
v2 windowSize, f32 dt); v2 windowSize, f32 dt);