Add main menu
This commit is contained in:
@@ -28,8 +28,8 @@ add_executable(PixelDefense
|
|||||||
game/systems_entity.c
|
game/systems_entity.c
|
||||||
game/systems_input.c
|
game/systems_input.c
|
||||||
game/systems_ui.c
|
game/systems_ui.c
|
||||||
game/ui.c
|
game/ui_widgets.c
|
||||||
game/ui.h
|
game/ui_widgets.h
|
||||||
game/unit_actions.c
|
game/unit_actions.c
|
||||||
game/unit_actions.h
|
game/unit_actions.h
|
||||||
game/unit_ai.c
|
game/unit_ai.c
|
||||||
|
|||||||
@@ -4,7 +4,14 @@
|
|||||||
#include <breeze.h>
|
#include <breeze.h>
|
||||||
#include <flecs.h>
|
#include <flecs.h>
|
||||||
|
|
||||||
|
typedef enum GameScreen {
|
||||||
|
SCREEN_MAIN_MENU,
|
||||||
|
SCREEN_SETTINGS,
|
||||||
|
SCREEN_GAME
|
||||||
|
} GameScreen;
|
||||||
|
|
||||||
typedef struct Game {
|
typedef struct Game {
|
||||||
|
GameScreen screen;
|
||||||
Camera2D camera;
|
Camera2D camera;
|
||||||
BzTileset tileset;
|
BzTileset tileset;
|
||||||
BzTileMap map;
|
BzTileMap map;
|
||||||
|
|||||||
76
game/main.c
76
game/main.c
@@ -1,4 +1,5 @@
|
|||||||
#include <rlImGui.h>
|
#include <rlImGui.h>
|
||||||
|
#include <raygui.h>
|
||||||
|
|
||||||
#include "systems.h"
|
#include "systems.h"
|
||||||
#include "components.h"
|
#include "components.h"
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
#include "map_init.h"
|
#include "map_init.h"
|
||||||
#include "map_layers.h"
|
#include "map_layers.h"
|
||||||
#include "buildings.h"
|
#include "buildings.h"
|
||||||
|
#include "ui_widgets.h"
|
||||||
#include "unit_ai.h"
|
#include "unit_ai.h"
|
||||||
#include "unit_actions.h"
|
#include "unit_actions.h"
|
||||||
|
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
ECS_COMPONENT_DECLARE(Game);
|
ECS_COMPONENT_DECLARE(Game);
|
||||||
ECS_COMPONENT_DECLARE(InputState);
|
ECS_COMPONENT_DECLARE(InputState);
|
||||||
|
|
||||||
|
BzUI *UI = NULL;
|
||||||
ecs_world_t *ECS = NULL;
|
ecs_world_t *ECS = NULL;
|
||||||
|
|
||||||
static ecs_entity_t renderCollidersSystem;
|
static ecs_entity_t renderCollidersSystem;
|
||||||
@@ -45,6 +48,9 @@ bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) {
|
|||||||
appDesc->imguiRender = (BzAppRenderFunc) imguiRender;
|
appDesc->imguiRender = (BzAppRenderFunc) imguiRender;
|
||||||
|
|
||||||
appDesc->userData = NULL;
|
appDesc->userData = NULL;
|
||||||
|
|
||||||
|
//SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +88,8 @@ bool init(void *userData) {
|
|||||||
BZ_UNUSED(userData);
|
BZ_UNUSED(userData);
|
||||||
SetExitKey(0);
|
SetExitKey(0);
|
||||||
|
|
||||||
|
UI = bzUICreate();
|
||||||
|
|
||||||
ECS = ecs_init();
|
ECS = ecs_init();
|
||||||
|
|
||||||
initComponentIDs(ECS);
|
initComponentIDs(ECS);
|
||||||
@@ -93,6 +101,7 @@ bool init(void *userData) {
|
|||||||
ECS_COMPONENT_DEFINE(ECS, Game);
|
ECS_COMPONENT_DEFINE(ECS, Game);
|
||||||
ecs_singleton_set(ECS, Game, {});
|
ecs_singleton_set(ECS, Game, {});
|
||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
|
game->screen = SCREEN_MAIN_MENU;
|
||||||
|
|
||||||
ECS_COMPONENT_DEFINE(ECS, InputState);
|
ECS_COMPONENT_DEFINE(ECS, InputState);
|
||||||
ecs_singleton_set(ECS, InputState, {});
|
ecs_singleton_set(ECS, InputState, {});
|
||||||
@@ -225,6 +234,9 @@ void deinit(void *userData) {
|
|||||||
bzObjectPoolDestroy(gameCopy.pools.pathData);
|
bzObjectPoolDestroy(gameCopy.pools.pathData);
|
||||||
bzObjectPoolDestroy(gameCopy.pools.actions);
|
bzObjectPoolDestroy(gameCopy.pools.actions);
|
||||||
bzSpatialGridDestroy(gameCopy.entityGrid);
|
bzSpatialGridDestroy(gameCopy.entityGrid);
|
||||||
|
|
||||||
|
bzUIDestroy(UI);
|
||||||
|
UI = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -243,14 +255,70 @@ void update(float dt, void *userData) {
|
|||||||
|
|
||||||
updateInputState(input, game->camera, dt);
|
updateInputState(input, game->camera, dt);
|
||||||
|
|
||||||
|
switch (game->screen) {
|
||||||
|
case SCREEN_MAIN_MENU:
|
||||||
|
break;
|
||||||
|
case SCREEN_GAME:
|
||||||
updatePlayerInput();
|
updatePlayerInput();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renderMainMenu(Game *game, float dt) {
|
||||||
|
i32 width = GetScreenWidth();
|
||||||
|
i32 height = GetScreenHeight();
|
||||||
|
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
bzUIBegin(UI, width, height);
|
||||||
|
bzUISetParentLayout(UI, (BzUILayout) {
|
||||||
|
.type = BZ_UI_LAYOUT_FLEX_BOX,
|
||||||
|
.flags = BZ_UI_FLEX_DIR_COLUMN
|
||||||
|
});
|
||||||
|
|
||||||
|
uiPushDivParentPercentage(1.0f, 0.4f);
|
||||||
|
bzUISetParentLayout(UI, (BzUILayout) {
|
||||||
|
.type = BZ_UI_LAYOUT_FLEX_BOX,
|
||||||
|
.flags = BZ_UI_FLEX_DIR_COLUMN | BZ_UI_FLEX_JUSTIFY_CENTER | BZ_UI_FLEX_ALIGN_CENTER
|
||||||
|
});
|
||||||
|
uiLargeLabel("PixelDefense");
|
||||||
|
bzUIPopParent(UI);
|
||||||
|
|
||||||
|
uiPushDivParentPercentage(1.0f, 0.6f);
|
||||||
|
bzUISetParentLayout(UI, (BzUILayout) {
|
||||||
|
.type = BZ_UI_LAYOUT_FLEX_BOX,
|
||||||
|
.flags = BZ_UI_FLEX_DIR_COLUMN | BZ_UI_FLEX_ALIGN_CENTER
|
||||||
|
});
|
||||||
|
if (uiLargeTextButton("Play")) {
|
||||||
|
bzLogInfo("Play");
|
||||||
|
}
|
||||||
|
if (uiLargeTextButton("Settings")) {
|
||||||
|
bzLogInfo("Settings");
|
||||||
|
}
|
||||||
|
if (uiLargeTextButton("Exit")) {
|
||||||
|
bzLogInfo("Bye");
|
||||||
|
bzGameExit();
|
||||||
|
}
|
||||||
|
bzUIPopParent(UI);
|
||||||
|
bzUIEnd(UI);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renderSettings(Game *game, float dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(float dt, void *userData) {
|
void render(float dt, void *userData) {
|
||||||
BZ_UNUSED(userData);
|
BZ_UNUSED(userData);
|
||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
InputState *input = ecs_singleton_get_mut(ECS, InputState);
|
|
||||||
|
|
||||||
|
switch (game->screen) {
|
||||||
|
case SCREEN_MAIN_MENU:
|
||||||
|
renderMainMenu(game, dt);
|
||||||
|
break;
|
||||||
|
case SCREEN_SETTINGS:
|
||||||
|
renderSettings(game, dt);
|
||||||
|
break;
|
||||||
|
case SCREEN_GAME:
|
||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
BeginMode2D(game->camera);
|
BeginMode2D(game->camera);
|
||||||
|
|
||||||
@@ -269,6 +337,10 @@ void render(float dt, void *userData) {
|
|||||||
drawPlayerInputUI();
|
drawPlayerInputUI();
|
||||||
|
|
||||||
EndMode2D();
|
EndMode2D();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void imguiRender(float dt, void *userData) {
|
void imguiRender(float dt, void *userData) {
|
||||||
@@ -276,6 +348,8 @@ void imguiRender(float dt, void *userData) {
|
|||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
InputState *input = ecs_singleton_get_mut(ECS, InputState);
|
InputState *input = ecs_singleton_get_mut(ECS, InputState);
|
||||||
|
|
||||||
|
if (game->screen != SCREEN_GAME) return;
|
||||||
|
|
||||||
igSetNextWindowSize((ImVec2){300, 400}, ImGuiCond_FirstUseEver);
|
igSetNextWindowSize((ImVec2){300, 400}, ImGuiCond_FirstUseEver);
|
||||||
igBegin("Debug Menu", NULL, 0);
|
igBegin("Debug Menu", NULL, 0);
|
||||||
if (igSmallButton("Recruit worker [50 food]")) {
|
if (igSmallButton("Recruit worker [50 food]")) {
|
||||||
|
|||||||
@@ -1,152 +1,38 @@
|
|||||||
#include "ui.h"
|
#include "ui_widgets.h"
|
||||||
|
|
||||||
UIGrid uiGridFromRoot(i32 width, i32 height) {
|
void uiPushDivParentPercentage(f32 xPercent, f32 yPercent) {
|
||||||
return (UIGrid) {.bounds = {0, 0, width, height}};
|
bzUIPushDiv(UI, (BzUISize) {
|
||||||
}
|
.kind = BZ_UI_SIZE_PARENT_PERCENT,
|
||||||
UIGrid uiGridFromGrid(const UIGrid grid, i32 row, i32 col) {
|
.value = xPercent,
|
||||||
return uiGridFromGridSpan(grid, row, col, 1, 1);
|
}, (BzUISize) {
|
||||||
}
|
.kind = BZ_UI_SIZE_PARENT_PERCENT,
|
||||||
UIGrid uiGridFromGridSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan) {
|
.value = yPercent
|
||||||
Rectangle bounds = uiGridCellSpan(grid, row, col, rowSpan, colSpan);
|
});
|
||||||
return (UIGrid) {
|
|
||||||
.rowPadding = grid.rowPadding,
|
|
||||||
.cellPadding = grid.cellPadding,
|
|
||||||
.bounds = bounds
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle uiGridCell(const UIGrid grid, i32 row, i32 col) {
|
void uiLargeLabel(const char *txt) {
|
||||||
return uiGridCellSpan(grid, row, col, 1, 1);
|
bzUINodeMake(UI, bzUIKeyFromString(txt),
|
||||||
}
|
&(BzUINodeDesc) {
|
||||||
Rectangle uiGridCellSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan) {
|
.flags = BZ_UI_DRAW_TEXT | BZ_UI_ALIGN_CENTER,
|
||||||
BZ_ASSERT(grid.rows > 0 && grid.cols > 0);
|
.semanticSize[BZ_UI_AXIS_X] = {
|
||||||
BZ_ASSERT(rowSpan > 0 && colSpan > 0);
|
.kind = BZ_UI_SIZE_FIT,
|
||||||
BZ_ASSERT(row >= 0 && col >= 0);
|
},
|
||||||
BZ_ASSERT(row + rowSpan <= grid.rows);
|
.semanticSize[BZ_UI_AXIS_Y] = {
|
||||||
BZ_ASSERT(col + colSpan <= grid.cols);
|
.kind = BZ_UI_SIZE_FIT
|
||||||
|
},
|
||||||
f32 cellWidth = (grid.bounds.width - (grid.cols - 1) * grid.cellPadding) / grid.cols;
|
.string = txt,
|
||||||
f32 cellHeight = (grid.bounds.height - (grid.rows - 1) * grid.rowPadding) / grid.rows;
|
.padding = {5, 5, 5, 5},
|
||||||
|
.style = {
|
||||||
Rectangle cellBounds = {
|
.font = GetFontDefault(),
|
||||||
.width = cellWidth * colSpan,
|
.fontSize = 62,
|
||||||
.height = cellHeight * rowSpan,
|
.textColor = BLACK,
|
||||||
.x = (cellWidth + grid.cellPadding) * col,
|
.textHoverColor = BLACK,
|
||||||
.y = (cellHeight + grid.rowPadding) * row,
|
.textActiveColor = BLACK,
|
||||||
};
|
|
||||||
return cellBounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiGridDrawCells(const UIGrid grid) {
|
|
||||||
BZ_ASSERT(grid.rows > 0 && grid.cols > 0);
|
|
||||||
DrawRectangleLines(grid.bounds.x, grid.bounds.y, grid.bounds.width, grid.bounds.height, RED);
|
|
||||||
|
|
||||||
for (i32 rowIdx = 0; rowIdx < grid.rows; rowIdx++) {
|
|
||||||
for (i32 colIdx = 0; colIdx < grid.cols; colIdx++) {
|
|
||||||
Rectangle rec = uiGridCell(grid, rowIdx, colIdx);
|
|
||||||
DrawRectangleLines(rec.x, rec.y, rec.width, rec.height, BLUE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static f32 getX(const Rectangle rec) {
|
bool uiLargeTextButton(const char *txt) {
|
||||||
return rec.x;
|
return bzUIButton(UI, txt, NULL);
|
||||||
}
|
|
||||||
static f32 getY(const Rectangle rec) {
|
|
||||||
return rec.y;
|
|
||||||
}
|
|
||||||
static f32 getWidth(const Rectangle rec) {
|
|
||||||
return rec.width;
|
|
||||||
}
|
|
||||||
static f32 getHeight(const Rectangle rec) {
|
|
||||||
return rec.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef f32 (*FlexBoxGetFunc)(const Rectangle rec);
|
|
||||||
|
|
||||||
void uiFlexBoxCalc(const FlexBoxDesc *desc, i32 numElements, ...) {
|
|
||||||
if (numElements <= 0) return;
|
|
||||||
f32 totalWidth = 0;
|
|
||||||
f32 totalHeight = 0;
|
|
||||||
|
|
||||||
UIFlexBoxFlag flags = desc->flags;
|
|
||||||
|
|
||||||
// Calculate total size of elements in the main axis
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, numElements);
|
|
||||||
for (i32 i = 0; i < numElements; i++) {
|
|
||||||
const Rectangle *rec = va_arg(ap, Rectangle *);
|
|
||||||
totalWidth += rec->width;
|
|
||||||
totalHeight += rec->height;
|
|
||||||
}
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
// Default: DIR_ROW
|
|
||||||
FlexBoxGetFunc axisX = getX, axisY = getY, axisWidth = getWidth, axisHeight = getHeight;
|
|
||||||
FlexBoxGetFunc crossX = getY, crossY = getX, crossWidth = getHeight, crossHeight = getWidth;
|
|
||||||
f32 mainAxisSize = totalWidth;
|
|
||||||
if (flags & UI_FLEX_DIR_COLUMN) {
|
|
||||||
axisX = getY;
|
|
||||||
axisY = getX;
|
|
||||||
axisWidth = getHeight;
|
|
||||||
axisHeight = getWidth;
|
|
||||||
crossX = getX;
|
|
||||||
crossY = getY;
|
|
||||||
crossWidth = getWidth;
|
|
||||||
crossHeight = getHeight;
|
|
||||||
mainAxisSize = totalHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
f32 spacingWidth = 0.0f;
|
|
||||||
f32 spacingHeight = 0.0f;
|
|
||||||
|
|
||||||
f32 mainAxisTotalSpacing = getWidth(desc->window) - mainAxisSize;
|
|
||||||
f32 mainAxisPadding = desc->padding * (numElements - 1);
|
|
||||||
|
|
||||||
f32 mainAxisSpacing = mainAxisTotalSpacing - mainAxisPadding;
|
|
||||||
if (mainAxisSpacing < 0) mainAxisSpacing = 0;
|
|
||||||
|
|
||||||
// Default: JUSTIFY_START
|
|
||||||
f32 mainAxisOffset = 0;
|
|
||||||
if (flags & UI_FLEX_JUSTIFY_CENTER) {
|
|
||||||
mainAxisOffset = mainAxisSpacing * 0.5f;
|
|
||||||
} else if (flags & UI_FLEX_JUSTIFY_END) {
|
|
||||||
mainAxisOffset = mainAxisSpacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rowDir = flags & UI_FLEX_DIR_ROW;
|
|
||||||
|
|
||||||
f32 offsetX = desc->window.x;
|
|
||||||
f32 offsetY = desc->window.y;
|
|
||||||
if (rowDir) offsetX += mainAxisOffset;
|
|
||||||
else offsetY += mainAxisOffset;
|
|
||||||
|
|
||||||
va_start(ap, numElements);
|
|
||||||
for (i32 i = 0; i < numElements; i++) {
|
|
||||||
f32 x = offsetX;
|
|
||||||
f32 y = offsetY;
|
|
||||||
|
|
||||||
Rectangle *rec = va_arg(ap, Rectangle *);
|
|
||||||
f32 crossOffset = 0;
|
|
||||||
if (flags & UI_FLEX_ALIGN_CENTER) {
|
|
||||||
crossOffset = (getHeight(desc->window) - getHeight(*rec)) * 0.5f;
|
|
||||||
} else if (flags & UI_FLEX_ALIGN_END) {
|
|
||||||
crossOffset = (getHeight(desc->window) - getHeight(*rec));
|
|
||||||
}
|
|
||||||
if (rowDir) {
|
|
||||||
offsetX += rec->width + spacingWidth + desc->padding;
|
|
||||||
y += crossOffset;
|
|
||||||
} else {
|
|
||||||
offsetY += rec->height + spacingHeight + desc->padding;
|
|
||||||
x += crossOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
rec->x = x;
|
|
||||||
rec->y = y;
|
|
||||||
|
|
||||||
}
|
|
||||||
va_end(ap);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,15 @@
|
|||||||
#ifndef PIXELDEFENSE_UI_H
|
#ifndef PIXELDEFENSE_UI_WIDGETS_H
|
||||||
#define PIXELDEFENSE_UI_H
|
#define PIXELDEFENSE_UI_WIDGETS_H
|
||||||
|
|
||||||
#include <breeze.h>
|
#include <breeze.h>
|
||||||
#include <raygui.h>
|
#include <raygui.h>
|
||||||
|
|
||||||
typedef struct UIGrid {
|
extern BzUI *UI; // defined in main.c
|
||||||
i32 rows;
|
|
||||||
i32 cols;
|
|
||||||
f32 rowPadding;
|
|
||||||
f32 cellPadding;
|
|
||||||
Rectangle bounds;
|
|
||||||
} UIGrid;
|
|
||||||
|
|
||||||
UIGrid uiGridFromRoot(i32 width, i32 height);
|
void uiPushDivParentPercentage(f32 xPercent, f32 yPercent);
|
||||||
UIGrid uiGridFromGrid(const UIGrid grid, i32 row, i32 col);
|
|
||||||
UIGrid uiGridFromGridSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan);
|
|
||||||
|
|
||||||
Rectangle uiGridCell(const UIGrid grid, i32 row, i32 col);
|
void uiLargeLabel(const char *txt);
|
||||||
Rectangle uiGridCellSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan);
|
|
||||||
|
|
||||||
void uiGridDrawCells(const UIGrid grid);
|
bool uiLargeTextButton(const char *txt);
|
||||||
|
|
||||||
typedef enum UIFlexBoxFlag {
|
#endif //PIXELDEFENSE_UI_WIDGETS_H
|
||||||
UI_FLEX_NONE = 0,
|
|
||||||
UI_FLEX_DIR_ROW = 0b1,
|
|
||||||
UI_FLEX_DIR_COLUMN = 0b10,
|
|
||||||
UI_FLEX_ALIGN_START = 0b100,
|
|
||||||
UI_FLEX_ALIGN_CENTER = 0b1000,
|
|
||||||
UI_FLEX_ALIGN_END = 0b10000,
|
|
||||||
UI_FLEX_JUSTIFY_START = 0b100000,
|
|
||||||
UI_FLEX_JUSTIFY_CENTER = 0b1000000,
|
|
||||||
UI_FLEX_JUSTIFY_END = 0b10000000,
|
|
||||||
} UIFlexBoxFlag;
|
|
||||||
|
|
||||||
typedef struct FlexBoxDesc {
|
|
||||||
Rectangle window;
|
|
||||||
UIFlexBoxFlag flags;
|
|
||||||
f32 padding;
|
|
||||||
} FlexBoxDesc;
|
|
||||||
|
|
||||||
void uiFlexBoxCalc(const FlexBoxDesc *desc, i32 numElements, ...);
|
|
||||||
|
|
||||||
static f32 uiScale() {
|
|
||||||
return GetScreenHeight() / 720.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif //PIXELDEFENSE_UI_H
|
|
||||||
|
|||||||
Reference in New Issue
Block a user