fp: Add mob spawner and profiling metrics

This commit is contained in:
doyle 2023-09-23 16:42:22 +10:00
parent f431be1b29
commit 38410073cf
6 changed files with 165 additions and 96 deletions

2
External/tely vendored

@ -1 +1 @@
Subproject commit 2e846b010ab9c32e3792375aa5db23796201b3ee
Subproject commit 5a56d52e71c4b66a9c6fa795a5a851f0dd4206cb

View File

@ -478,8 +478,8 @@ void TELY_DLL_Init(void *user_data)
// NOTE: Hero
{
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Hero");
entity->local_pos = Dqn_V2_InitNx2(100.f, 100.f);
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry");
entity->local_pos = Dqn_V2_InitNx2(1334, 396);
entity->size_scale = Dqn_V2_InitNx1(4);
entity->sprite_sheet = &game->hero_sprite_sheet;
entity->sprite_anims = game->hero_sprite_anims;
@ -488,27 +488,15 @@ void TELY_DLL_Init(void *user_data)
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->facing_left = true;
game->clicked_entity = entity->handle;
}
// NOTE: Enemy
{
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Enemy");
entity->local_pos = Dqn_V2_InitNx2(300.f, 300.f);
entity->size_scale = Dqn_V2_InitNx1(4);
entity->sprite_sheet = &game->hero_sprite_sheet;
entity->sprite_anims = game->hero_sprite_anims;
entity->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size);
entity->flags |= FP_GameEntityFlag_Clickable;
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_NonTraversable;
game->player = entity->handle;
}
game->tile_size = 37;
Dqn_V2I max_tile = platform->core.window_size / game->tile_size;
// NOTE: Wall
// NOTE: Wall ==================================================================================
{
Dqn_V2I vert_wall_tile_size = Dqn_V2I_InitNx2(1, 12);
Dqn_V2I right_wall_tile_pos = Dqn_V2I_InitNx2(max_tile.x - vert_wall_tile_size.x - 0, (max_tile.y / 2.f) - (vert_wall_tile_size.y * .5f));
@ -551,6 +539,16 @@ void TELY_DLL_Init(void *user_data)
}
}
// NOTE: Mob spawner
{
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob spawner");
entity->local_pos = Dqn_V2_InitNx2(0, platform->core.window_size.y * .5f);
entity->flags |= FP_GameEntityFlag_Clickable;
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_MobSpawner;
}
uint16_t font_size = 18;
game->camera.scale = Dqn_V2_InitNx1(1);
game->inter_regular_font = platform->func_load_font(assets, DQN_STRING8("Inter (Regular)"), DQN_STRING8("Data/Inter-Regular.otf"), font_size);
@ -561,6 +559,8 @@ void TELY_DLL_Init(void *user_data)
void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
{
Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate);
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
game->clicked_entity = game->prev_active_entity;
@ -586,6 +586,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
game->camera.world_pos += dir_vector * 5.f;
}
Dqn_ProfilerZone update_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: Entity loop"), FP_ProfileZone_FPUpdate_EntityLoop);
for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) {
FP_GameEntity *entity = it.entity;
entity->alive_time_s += input->delta_s;
@ -602,15 +603,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
}
// NOTE: Stalk entity ======================================================================
#if 1
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
if (entity->name == DQN_STRING8("Enemy")) {
if (entity->handle != game->clicked_entity && entity->stalk_entity != game->clicked_entity) {
entity->stalk_entity = game->clicked_entity;
}
{
Dqn_Profiler_ZoneScopeWithIndex("FP_Update: Path finding", FP_ProfileZone_FPUpdate_PathFinding);
FP_GameEntity *stalk_entity = FP_Game_GetEntity(game, entity->stalk_entity);
if (stalk_entity) {
if (stalk_entity->handle.id) {
Dqn_V2 stalk_world_pos = FP_Game_CalcEntityWorldPos(game, stalk_entity->handle);
Dqn_V2I stalk_tile = Dqn_V2I_InitNx2(stalk_world_pos.x / game->tile_size, stalk_world_pos.y / game->tile_size);
if (entity->stalk_entity_last_known_tile != stalk_tile) {
@ -636,30 +633,29 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
}
}
}
}
// NOTE: Render the waypoints
for (FP_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; waypoint = waypoint->next) {
Dqn_V2 circle_pos = Dqn_V2_InitNx2(waypoint->pos.x * game->tile_size + game->tile_size * .5f, waypoint->pos.y * game->tile_size + game->tile_size * .5f);
TELY_Render_CircleColourV4(renderer, circle_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4);
}
// NOTE: Render the waypoints
for (FP_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; waypoint = waypoint->next) {
Dqn_V2 circle_pos = Dqn_V2_InitNx2(waypoint->pos.x * game->tile_size + game->tile_size * .5f, waypoint->pos.y * game->tile_size + game->tile_size * .5f);
TELY_Render_CircleColourV4(renderer, circle_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4);
}
if (entity->waypoints->next != entity->waypoints) {
FP_GameWaypoint *waypoint = entity->waypoints->next;
Dqn_V2I target_tile = entity->waypoints->next->pos;
Dqn_V2 target_pos = Dqn_V2_InitNx2(target_tile.x * game->tile_size + game->tile_size *.5f, target_tile.y * game->tile_size + game->tile_size * .5f);
Dqn_V2 entity_to_target_pos = target_pos - entity_world_pos;
if (entity->waypoints->next != entity->waypoints) {
FP_GameWaypoint *waypoint = entity->waypoints->next;
Dqn_V2I target_tile = entity->waypoints->next->pos;
Dqn_V2 target_pos = Dqn_V2_InitNx2(target_tile.x * game->tile_size + game->tile_size *.5f, target_tile.y * game->tile_size + game->tile_size * .5f);
Dqn_V2 entity_to_target_pos = target_pos - entity_world_pos;
if (Dqn_V2_LengthSq(entity_to_target_pos) < DQN_SQUARED(entity->local_hit_box_size.x * .5f)) {
waypoint->next->prev = waypoint->prev;
waypoint->prev->next = waypoint->next;
TELY_ChunkPool_Dealloc(game->chunk_pool, waypoint);
} else {
Dqn_V2 entity_to_target_pos_norm = Dqn_V2_Normalise(entity_to_target_pos);
entity->local_pos += entity_to_target_pos_norm * (entity->local_hit_box_size.x * .05f);
if (Dqn_V2_LengthSq(entity_to_target_pos) < DQN_SQUARED(entity->local_hit_box_size.x * .5f)) {
waypoint->next->prev = waypoint->prev;
waypoint->prev->next = waypoint->next;
TELY_ChunkPool_Dealloc(game->chunk_pool, waypoint);
} else {
Dqn_V2 entity_to_target_pos_norm = Dqn_V2_Normalise(entity_to_target_pos);
entity->local_pos += entity_to_target_pos_norm * (entity->local_hit_box_size.x * .05f);
}
}
}
#endif
// NOTE: Core equations of motion ==========================================================
{
@ -895,9 +891,17 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} else {
entity->attack_box_size = {};
}
// NOTE: Mob spawner =======================================================================
if ((entity->flags & FP_GameEntityFlag_MobSpawner) && input->timer_s >= entity->next_spawn_timestamp_s) {
entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f);
FP_Game_EntityAddMob(game, entity_world_pos);
}
}
Dqn_Profiler_EndZone(update_zone);
// NOTE: Do attacks ============================================================================
auto attack_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: Attacks"), FP_ProfileZone_FPUpdate_Attacks);
for (FP_GameEntityIterator attacker_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &attacker_it, game->root_entity); ) {
FP_GameEntity *attacker = attacker_it.entity;
@ -932,10 +936,12 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
defender->velocity = (acceleration * t) + defender->velocity * 2.0f;
}
}
Dqn_Profiler_EndZone(attack_zone);
}
void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
{
Dqn_Profiler_ZoneScopeWithIndex("FP_Render", FP_ProfileZone_FPRender);
TELY_PlatformInput *input = &platform->input;
Dqn_M2x3 model_view = FP_Game_CameraModelViewM2x3(game->camera, platform);
Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(model_view, input->mouse_p);
@ -1117,63 +1123,85 @@ void TELY_DLL_FrameUpdate(void *user_data)
FP_Update(platform, game, renderer, input);
FP_Render(game, platform, renderer);
// NOTE: UI ====================================================================================
{
TELY_Render_PushTransform(renderer, Dqn_M2x3_Identity());
DQN_DEFER { TELY_Render_PopTransform(renderer); };
TELY_RFuiResult info_bar = TELY_RFui_Row(rfui, DQN_STRING8("Info Bar"));
info_bar.widget->semantic_position[TELY_RFuiAxis_X].kind = TELY_RFuiPositionKind_Absolute;
info_bar.widget->semantic_position[TELY_RFuiAxis_X].value = 10.f;
info_bar.widget->semantic_position[TELY_RFuiAxis_Y].kind = TELY_RFuiPositionKind_Absolute;
info_bar.widget->semantic_position[TELY_RFuiAxis_Y].value = 10.f;
// NOTE: Info bar ==========================================================================
{
TELY_RFuiResult info_bar = TELY_RFui_Row(rfui, DQN_STRING8("Info Bar"));
info_bar.widget->semantic_position[TELY_RFuiAxis_X].kind = TELY_RFuiPositionKind_Absolute;
info_bar.widget->semantic_position[TELY_RFuiAxis_X].value = 10.f;
info_bar.widget->semantic_position[TELY_RFuiAxis_Y].kind = TELY_RFuiPositionKind_Absolute;
info_bar.widget->semantic_position[TELY_RFuiAxis_Y].value = 10.f;
TELY_RFui_PushParent(rfui, info_bar.widget);
DQN_DEFER { TELY_RFui_PopParent(rfui); };
TELY_RFui_PushParent(rfui, info_bar.widget);
DQN_DEFER { TELY_RFui_PopParent(rfui); };
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8Builder builder = {};
builder.allocator = scratch.allocator;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
TELY_RFui_TextF(rfui, "TELY");
if (Dqn_String8_IsValid(platform->core.os_name)) {
TELY_RFui_TextF(rfui, " | %.*s", DQN_STRING_FMT(platform->core.os_name));
}
TELY_RFui_TextF(rfui, "TELY");
Dqn_String8Builder_AppendF(&builder, "TELY");
if (Dqn_String8_IsValid(platform->core.os_name)) {
TELY_RFui_TextF(rfui, " | %.*s", DQN_STRING_FMT(platform->core.os_name));
Dqn_String8Builder_AppendF(&builder, " | %.*s", DQN_STRING_FMT(platform->core.os_name));
TELY_RFui_TextF(rfui,
" | %dx%d %.1fHz | TSC %.1f GHz",
platform->core.display.size.w,
platform->core.display.size.h,
platform->core.display.refresh_rate,
platform->core.tsc_per_second / 1'000'000'000.0);
if (platform->core.ram_mb)
TELY_RFui_TextF(rfui, " | RAM %.1fGB", platform->core.ram_mb / 1024.0);
TELY_RFui_TextF(rfui,
" | Work %04.1fms/f (%04.1f%%) | %05.1f FPS | Frame %'I64u | Timer %.1fs",
input->work_ms,
input->work_ms * 100.0 / input->delta_ms,
1000.0 / input->delta_ms,
input->frame_counter,
input->timer_s);
}
Dqn_String8Builder_AppendF(&builder,
" | %dx%d %.1fHz | TSC %.1f GHz",
platform->core.display.size.w,
platform->core.display.size.h,
platform->core.display.refresh_rate,
platform->core.tsc_per_second / 1'000'000'000.0);
// NOTE: Profiler
{
TELY_RFuiResult profiler_layout = TELY_RFui_Column(rfui, DQN_STRING8("Profiler Bar"));
profiler_layout.widget->semantic_position[TELY_RFuiAxis_X].kind = TELY_RFuiPositionKind_Absolute;
profiler_layout.widget->semantic_position[TELY_RFuiAxis_X].value = 10.f;
profiler_layout.widget->semantic_position[TELY_RFuiAxis_Y].kind = TELY_RFuiPositionKind_Absolute;
profiler_layout.widget->semantic_position[TELY_RFuiAxis_Y].value = TELY_Asset_GetFont(assets, TELY_RFui_ActiveFont(rfui))->pixel_height * 1.5f;
TELY_RFui_TextF(rfui,
" | %dx%d %.1fHz | TSC %.1f GHz",
platform->core.display.size.w,
platform->core.display.size.h,
platform->core.display.refresh_rate,
platform->core.tsc_per_second / 1'000'000'000.0);
TELY_RFui_PushParent(rfui, profiler_layout.widget);
DQN_DEFER { TELY_RFui_PopParent(rfui); };
if (platform->core.ram_mb) {
Dqn_String8Builder_AppendF(&builder, " | RAM %.1fGB", platform->core.ram_mb / 1024.0);
TELY_RFui_TextF(rfui, " | RAM %.1fGB", platform->core.ram_mb / 1024.0);
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
for (size_t anchor_index = 1; anchor_index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; anchor_index++) {
Dqn_ProfilerAnchor const *anchor = anchors + anchor_index;
if (!anchor->hit_count)
continue;
uint64_t tsc_exclusive = anchor->tsc_exclusive;
uint64_t tsc_inclusive = anchor->tsc_inclusive;
Dqn_f64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DQN_CAST(Dqn_f64)platform->core.tsc_per_second;
if (tsc_exclusive == tsc_inclusive) {
TELY_RFui_TextF(rfui,
"%.*s[%u]: %.1fms",
DQN_STRING_FMT(anchor->name),
anchor->hit_count,
tsc_exclusive_milliseconds);
} else {
Dqn_f64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DQN_CAST(Dqn_f64)platform->core.tsc_per_second;
TELY_RFui_TextF(rfui,
"%.*s[%u]: %.1f/%.1fms",
DQN_STRING_FMT(anchor->name),
anchor->hit_count,
tsc_exclusive_milliseconds,
tsc_inclusive_milliseconds);
}
}
}
Dqn_String8Builder_AppendF(&builder,
" | Work %04.1fms/f (%04.1f%%) | %05.1f FPS | Frame %'I64u | Timer %.1fs",
input->work_ms,
input->work_ms * 100.0 / input->delta_ms,
1000.0 / input->delta_ms,
input->frame_counter,
input->timer_s);
TELY_RFui_TextF(rfui,
" | Work %04.1fms/f (%04.1f%%) | %05.1f FPS | Frame %'I64u | Timer %.1fs",
input->work_ms,
input->work_ms * 100.0 / input->delta_ms,
1000.0 / input->delta_ms,
input->frame_counter,
input->timer_s);
}
TELY_RFui_Flush(rfui, renderer, input, assets);

14
feely_pona.h Normal file
View File

@ -0,0 +1,14 @@
#if defined(__clang__)
#pragma once
#include "feely_pona_unity.h"
#endif
enum FP_ProfileZone
{
FP_ProfileZone_FPUpdate = TELY_ProfileZone_Count,
FP_ProfileZone_FPUpdate_EntityLoop,
FP_ProfileZone_FPUpdate_PathFinding,
FP_ProfileZone_FPUpdate_Attacks,
FP_ProfileZone_FPRender,
};

View File

@ -472,8 +472,6 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
DQN_DEFER { Dqn_DSMap_Deinit(&astar_info); };
// NOTE: Enumerate the entities that are collidable ============================================
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
Dqn_List<FP_GameEntity const *> colliders = Dqn_List_Init<FP_GameEntity const *>(scratch.arena, 128);
for (FP_GameEntityIterator it = {}; FP_Game_DFSPreOrderWalkEntityTree(game, &it, game->root_entity); ) {
FP_GameEntity const *walk_entity = it.entity;
if (entity == walk_entity->handle)
@ -606,6 +604,7 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
return result;
}
// NOTE: Spawning code =============================================================================
static FP_GameEntityHandle FP_Game_EntityAddWallAtTile(FP_Game *game, Dqn_String8 name, Dqn_V2I tile_pos, Dqn_V2I size_in_tiles)
{
Dqn_V2 size = Dqn_V2_InitV2I(size_in_tiles * DQN_CAST(int32_t) game->tile_size);
@ -628,3 +627,21 @@ static FP_GameEntityHandle FP_Game_EntityAddWallAtTile(FP_Game *game, Dqn_String
FP_GameEntityHandle result = entity->handle;
return result;
}
static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos)
{
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob");
entity->local_pos = pos;
entity->size_scale = Dqn_V2_InitNx1(4);
entity->sprite_sheet = &game->hero_sprite_sheet;
entity->sprite_anims = game->hero_sprite_anims;
entity->local_hit_box_size = Dqn_V2_InitV2I(game->hero_sprite_sheet.sprite_size);
entity->flags |= FP_GameEntityFlag_Clickable;
entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->stalk_entity = game->player;
FP_GameEntityHandle result = entity->handle;
return result;
}

View File

@ -11,6 +11,7 @@ enum FP_GameEntityFlag
FP_GameEntityFlag_DrawHitBox = 1 << 3,
FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 4,
FP_GameEntityFlag_NonTraversable = 1 << 5,
FP_GameEntityFlag_MobSpawner = 1 << 6,
};
enum FP_GameShapeType
@ -79,6 +80,12 @@ struct FP_GameEntityAction
struct FP_GameEntity
{
FP_GameEntity *next;
FP_GameEntity *prev;
FP_GameEntity *first_child;
FP_GameEntity *last_child;
FP_GameEntity *parent;
Dqn_String8 name;
FP_GameEntityHandle handle;
TELY_AssetSpriteSheet *sprite_sheet;
@ -98,16 +105,13 @@ struct FP_GameEntity
Dqn_V2 attack_box_size;
Dqn_V2 attack_box_offset;
uint64_t next_spawn_timestamp_s;
uint64_t flags;
bool facing_left;
Dqn_V2 local_pos;
Dqn_f64 alive_time_s;
Dqn_FArray<FP_GameShape, 4> shapes;
FP_GameEntity *next;
FP_GameEntity *prev;
FP_GameEntity *first_child;
FP_GameEntity *last_child;
FP_GameEntity *parent;
};
struct FP_GameEntityIterator
@ -142,14 +146,19 @@ struct FP_Game
TELY_AssetSpriteSheet hero_sprite_sheet;
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
Dqn_VArray<FP_GameEntity> entities;
FP_GameEntity *root_entity;
FP_GameEntity *entity_free_list;
FP_GameEntityHandle player;
FP_GameEntityHandle clicked_entity;
FP_GameEntityHandle hot_entity;
FP_GameEntityHandle active_entity;
FP_GameEntityHandle prev_clicked_entity;
FP_GameEntityHandle prev_hot_entity;
FP_GameEntityHandle prev_active_entity;
FP_GameCamera camera;
TELY_RFui rfui;
};

View File

@ -62,6 +62,7 @@ DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with inte
// NOTE: feely_pona ================================================================================
#include "feely_pona_game.h"
#include "feely_pona.h"
#include "feely_pona_game.cpp"
#include "feely_pona.cpp"