Add feedback UI for selecting and placing units
This commit is contained in:
@@ -31,6 +31,7 @@ typedef struct InputState {
|
|||||||
TileSize buildingSize;
|
TileSize buildingSize;
|
||||||
// SELECTED_UNITS
|
// SELECTED_UNITS
|
||||||
ecs_entity_t *entities;
|
ecs_entity_t *entities;
|
||||||
|
Position *unitPositions;
|
||||||
// SELECTED_OBJECT
|
// SELECTED_OBJECT
|
||||||
// SELECTED_BUILDING
|
// SELECTED_BUILDING
|
||||||
} InputState;
|
} InputState;
|
||||||
|
|||||||
113
game/main.c
113
game/main.c
@@ -72,6 +72,7 @@ bool init(void *userData) {
|
|||||||
input->CLICK_LIMIT = 0.2f;
|
input->CLICK_LIMIT = 0.2f;
|
||||||
|
|
||||||
input->entities = bzArrayCreate(ecs_entity_t, 16);
|
input->entities = bzArrayCreate(ecs_entity_t, 16);
|
||||||
|
input->unitPositions = bzArrayCreate(Position, 16);
|
||||||
|
|
||||||
|
|
||||||
// init pools
|
// init pools
|
||||||
@@ -177,6 +178,7 @@ void deinit(void *userData) {
|
|||||||
ECS = NULL;
|
ECS = NULL;
|
||||||
|
|
||||||
bzArrayDestroy(inputCopy.entities);
|
bzArrayDestroy(inputCopy.entities);
|
||||||
|
bzArrayDestroy(inputCopy.unitPositions);
|
||||||
bzObjectPoolDestroy(gameCopy.pools.pathData);
|
bzObjectPoolDestroy(gameCopy.pools.pathData);
|
||||||
bzSpatialGridDestroy(gameCopy.entityGrid);
|
bzSpatialGridDestroy(gameCopy.entityGrid);
|
||||||
}
|
}
|
||||||
@@ -202,116 +204,7 @@ void render(float dt, void *userData) {
|
|||||||
|
|
||||||
bzTileMapDraw(&game->map);
|
bzTileMapDraw(&game->map);
|
||||||
|
|
||||||
Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera);
|
drawPlayerInputUI(NULL);
|
||||||
switch (input->state) {
|
|
||||||
case INPUT_NONE:
|
|
||||||
if (IsMouseButtonDown(input->LMB) && input->mouseDownElapsed > input->CLICK_LIMIT) {
|
|
||||||
Vector2 start = input->mouseDownWorld;
|
|
||||||
Vector2 end = worldPos;
|
|
||||||
if (start.x > end.x) {
|
|
||||||
f32 tmp = start.x;
|
|
||||||
start.x = end.x;
|
|
||||||
end.x = tmp;
|
|
||||||
}
|
|
||||||
if (start.y > end.y) {
|
|
||||||
f32 tmp = start.y;
|
|
||||||
start.y = end.y;
|
|
||||||
end.y = tmp;
|
|
||||||
}
|
|
||||||
Rectangle area = {start.x, start.y, end.x - start.x, end.y - start.y};
|
|
||||||
DrawRectangleLines(area.x, area.y, area.width, area.height, RED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INPUT_BUILDING: {
|
|
||||||
Color placeColor = input->buildingCanPlace ?
|
|
||||||
(Color) {0, 255, 0, 200} :
|
|
||||||
(Color) {255, 0, 0, 200};
|
|
||||||
|
|
||||||
BzTile width = game->map.tileWidth;
|
|
||||||
BzTile height = game->map.tileHeight;
|
|
||||||
DrawRectangleLines(input->buildingPos.x * width,
|
|
||||||
input->buildingPos.y * height,
|
|
||||||
input->buildingSize.sizeX * width,
|
|
||||||
input->buildingSize.sizeY * height, placeColor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case INPUT_SELECTED_UNITS:
|
|
||||||
break;
|
|
||||||
case INPUT_SELECTED_OBJECT:
|
|
||||||
break;
|
|
||||||
case INPUT_SELECTED_BUILDING:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera);
|
|
||||||
int tileX = (int) worldPos.x / 16;
|
|
||||||
int tileY = (int) worldPos.y / 16;
|
|
||||||
|
|
||||||
i32 numUnits = 240;
|
|
||||||
static Vector2 *units = NULL;
|
|
||||||
if (!units) units = bzArrayCreate(Vector2, 3);
|
|
||||||
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && game->input.mouseDownElapsed > 0.1) {
|
|
||||||
Vector2 start = game->input.mouseDownWorld;
|
|
||||||
Vector2 end = worldPos;
|
|
||||||
//bzArrayClear(units);
|
|
||||||
placeUnits(numUnits, 3.5f, start, end, &game->map, &units);
|
|
||||||
if (start.x > end.x) {
|
|
||||||
f32 tmp = start.x;
|
|
||||||
start.x = end.x;
|
|
||||||
end.x = tmp;
|
|
||||||
}
|
|
||||||
if (start.y > end.y) {
|
|
||||||
f32 tmp = start.y;
|
|
||||||
start.y = end.y;
|
|
||||||
end.y = tmp;
|
|
||||||
}
|
|
||||||
Vector2 size = Vector2Subtract(end, start);
|
|
||||||
|
|
||||||
DrawRectangleLines(start.x, start.y, size.x, size.y, RED);
|
|
||||||
|
|
||||||
BzSpatialGridIter it = bzSpatialGridIter(game->entityGrid, start.x, start.y, size.x, size.y);
|
|
||||||
i32 count = 0;
|
|
||||||
while (bzSpatialGridQueryNext(&it)) {
|
|
||||||
ecs_entity_t *e = it.data;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
bzLogInfo("%d", count);
|
|
||||||
|
|
||||||
//bzArrayClear(units);
|
|
||||||
//placeUnits(numUnits, 3.5f, start, end, &game->map, &units);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PathNode *heap = NULL;
|
|
||||||
if (!heap)
|
|
||||||
heap = bzHeapCreate(PathNode, game->map.width * game->map.height);
|
|
||||||
if (!ecs_has(ECS, game->entity, Path) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
|
||||||
Path path = {};
|
|
||||||
clock_t begin = clock();
|
|
||||||
const Position *start = ecs_get(ECS, game->entity, Position);
|
|
||||||
findPath(&(PathfindingDesc) {
|
|
||||||
.start=(TilePosition) {
|
|
||||||
(int) (start->x / game->map.tileWidth),
|
|
||||||
(int) (start->y / game->map.tileHeight)
|
|
||||||
},
|
|
||||||
.target=(TilePosition) {tileX, tileY},
|
|
||||||
.map=&game->map,
|
|
||||||
.openSet=heap,
|
|
||||||
.outPath=&path,
|
|
||||||
.pool=game->pools.pathData
|
|
||||||
});
|
|
||||||
if (path.paths) {
|
|
||||||
ecs_set_ptr(ECS, game->entity, Path, &path);
|
|
||||||
}
|
|
||||||
clock_t end = clock();
|
|
||||||
double timeSpent = (double) (end - begin) / CLOCKS_PER_SEC;
|
|
||||||
timeSpent *= 1000;
|
|
||||||
bzLogInfo("A* took: %.3fms", timeSpent);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ecs_progress(ECS, dt);
|
ecs_progress(ECS, dt);
|
||||||
ecs_enable(ECS, renderDebugPathSystem, game->debugDraw.path);
|
ecs_enable(ECS, renderDebugPathSystem, game->debugDraw.path);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void renderDebugPath(ecs_iter_t *it);
|
|||||||
|
|
||||||
|
|
||||||
/**********************************
|
/**********************************
|
||||||
* Entity Systems
|
* Input systems
|
||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -83,4 +83,15 @@ void renderDebugPath(ecs_iter_t *it);
|
|||||||
*/
|
*/
|
||||||
void updatePlayerInput(ecs_iter_t *it);
|
void updatePlayerInput(ecs_iter_t *it);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Task:
|
||||||
|
* 0: Game (singleton)
|
||||||
|
* 0: InputState (singleton)
|
||||||
|
*/
|
||||||
|
void drawPlayerInputUI(ecs_iter_t *it);
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* UI systems
|
||||||
|
**********************************/
|
||||||
|
|
||||||
#endif //PIXELDEFENSE_SYSTEMS_H
|
#endif //PIXELDEFENSE_SYSTEMS_H
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <rlImGui.h>
|
#include <rlImGui.h>
|
||||||
#include <raymath.h>
|
#include <raymath.h>
|
||||||
|
|
||||||
|
bool getEntityBounds(ecs_entity_t entity, Position *outPos, Size *outSize, Rectangle *outBounds);
|
||||||
void pickEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t **outEntities);
|
void pickEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t **outEntities);
|
||||||
void pickEntities(BzSpatialGrid *entityGrid, Rectangle area, ecs_entity_t **outEntities);
|
void pickEntities(BzSpatialGrid *entityGrid, Rectangle area, ecs_entity_t **outEntities);
|
||||||
|
|
||||||
@@ -116,14 +117,14 @@ void updatePlayerInput(ecs_iter_t *it) {
|
|||||||
// TODO: For click it should just move them
|
// TODO: For click it should just move them
|
||||||
i32 numUnits = bzArraySize(input->entities);
|
i32 numUnits = bzArraySize(input->entities);
|
||||||
f32 unitSpacing = 3.5f;
|
f32 unitSpacing = 3.5f;
|
||||||
Vector2 *unitPositions = bzArrayCreate(Position, numUnits);
|
bzArrayClear(input->unitPositions);
|
||||||
placeUnits(numUnits, unitSpacing, input->mouseDownWorld, worldPos,
|
placeUnits(numUnits, unitSpacing, input->mouseDownWorld, worldPos,
|
||||||
map, &unitPositions);
|
map, &input->unitPositions);
|
||||||
|
|
||||||
BZ_ASSERT(bzArraySize(unitPositions) == numUnits);
|
BZ_ASSERT(bzArraySize(input->unitPositions) == numUnits);
|
||||||
|
|
||||||
bzArrayFor(unitPositions, i) {
|
bzArrayFor(input->unitPositions, i) {
|
||||||
Position target = bzArrayGet(unitPositions, i);
|
Position target = bzArrayGet(input->unitPositions, i);
|
||||||
ecs_entity_t entity = bzArrayGet(input->entities, i);
|
ecs_entity_t entity = bzArrayGet(input->entities, i);
|
||||||
|
|
||||||
const Position *start = ecs_get(ECS, entity, Position);
|
const Position *start = ecs_get(ECS, entity, Position);
|
||||||
@@ -139,9 +140,6 @@ void updatePlayerInput(ecs_iter_t *it) {
|
|||||||
ecs_set_ptr(ECS, entity, Path, &path);
|
ecs_set_ptr(ECS, entity, Path, &path);
|
||||||
input->state = INPUT_NONE;
|
input->state = INPUT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bzArrayDestroy(unitPositions);
|
|
||||||
|
|
||||||
} else if (wasInputClicked(input)) {
|
} else if (wasInputClicked(input)) {
|
||||||
bzArrayFor(input->entities, i) {
|
bzArrayFor(input->entities, i) {
|
||||||
ecs_entity_t entity = bzArrayGet(input->entities, i);
|
ecs_entity_t entity = bzArrayGet(input->entities, i);
|
||||||
@@ -170,8 +168,70 @@ void updatePlayerInput(ecs_iter_t *it) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool getEntityBounds(ecs_entity_t entity, Position *outPos, Size *outSize,
|
void drawPlayerInputUI(ecs_iter_t *it) {
|
||||||
Rectangle *outBounds) {
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
|
InputState *input = ecs_singleton_get_mut(ECS, InputState);
|
||||||
|
Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera);
|
||||||
|
|
||||||
|
switch (input->state) {
|
||||||
|
case INPUT_NONE:
|
||||||
|
if (IsMouseButtonDown(input->LMB) && input->mouseDownElapsed > input->CLICK_LIMIT) {
|
||||||
|
Vector2 start = input->mouseDownWorld;
|
||||||
|
Vector2 end = worldPos;
|
||||||
|
if (start.x > end.x) {
|
||||||
|
f32 tmp = start.x;
|
||||||
|
start.x = end.x;
|
||||||
|
end.x = tmp;
|
||||||
|
}
|
||||||
|
if (start.y > end.y) {
|
||||||
|
f32 tmp = start.y;
|
||||||
|
start.y = end.y;
|
||||||
|
end.y = tmp;
|
||||||
|
}
|
||||||
|
Rectangle area = {start.x, start.y, end.x - start.x, end.y - start.y};
|
||||||
|
DrawRectangleLines(area.x, area.y, area.width, area.height, RED);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INPUT_BUILDING: {
|
||||||
|
Color placeColor = input->buildingCanPlace ?
|
||||||
|
(Color) {0, 255, 0, 200} :
|
||||||
|
(Color) {255, 0, 0, 200};
|
||||||
|
|
||||||
|
BzTile width = game->map.tileWidth;
|
||||||
|
BzTile height = game->map.tileHeight;
|
||||||
|
DrawRectangleLines(input->buildingPos.x * width,
|
||||||
|
input->buildingPos.y * height,
|
||||||
|
input->buildingSize.sizeX * width,
|
||||||
|
input->buildingSize.sizeY * height, placeColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INPUT_SELECTED_UNITS: {
|
||||||
|
bzArrayFor(input->entities, i) {
|
||||||
|
ecs_entity_t entity = bzArrayGet(input->entities, i);
|
||||||
|
Position pos = {0};
|
||||||
|
if (!getEntityBounds(entity, &pos, NULL, NULL))
|
||||||
|
continue;
|
||||||
|
DrawCircleLines(pos.x, pos.y, 4.5f, GREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INPUT_SELECTED_OBJECT:
|
||||||
|
break;
|
||||||
|
case INPUT_SELECTED_BUILDING:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bzArrayFor(input->unitPositions, i) {
|
||||||
|
Position pos = bzArrayGet(input->unitPositions, i);
|
||||||
|
DrawCircle(pos.x, pos.y, 2.0f, ORANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getEntityBounds(ecs_entity_t entity, Position *outPos, Size *outSize, Rectangle *outBounds) {
|
||||||
if (!ecs_is_alive(ECS, entity))
|
if (!ecs_is_alive(ECS, entity))
|
||||||
return false;
|
return false;
|
||||||
const Position *pos = ecs_get(ECS, entity, Position);
|
const Position *pos = ecs_get(ECS, entity, Position);
|
||||||
|
|||||||
Reference in New Issue
Block a user