Fixed timestep for gameplay and physics

main
Cameron Murphy Reikes 2 years ago
parent 11b1c0964e
commit d028d96786

186
main.c

@ -735,6 +735,8 @@ typedef struct GameState {
Entity entities[MAX_ENTITIES];
} GameState;
GameState gs = {0};
double unprocessed_fixed_timestep_time = 0.0;
#define FIXED_TIMESTEP (1.0/60.0)
EntityRef frome(Entity *e)
{
@ -2569,44 +2571,20 @@ void frame(void)
assert(player != NULL);
#ifdef DEVTOOLS
dbgsquare(screen_to_world(mouse_pos));
// tile coord
if(show_devtools)
// fixed timestep loop
Entity *interacting_with = 0; // used by rendering to figure out who to draw dialog box on
int num_timestep_loops = 0;
{
TileCoord hovering = world_to_tilecoord(screen_to_world(mouse_pos));
Vec2 points[4] ={0};
AABB q = tile_aabb(hovering);
dbgrect(q);
draw_text((TextParams){false, false, tprint("%d", get_tile(&level_level0, hovering).kind), world_to_screen(tilecoord_to_world(hovering)), BLACK, 1.0f});
}
// debug draw font image
unprocessed_fixed_timestep_time += dt;
while(unprocessed_fixed_timestep_time > FIXED_TIMESTEP)
{
draw_quad((DrawParams){true, quad_centered(V2(0.0, 0.0), V2(250.0, 250.0)), image_font,full_region(image_font), WHITE});
}
// statistics
if(show_devtools)
PROFILE_SCOPE("statistics")
{
Vec2 pos = V2(0.0, screen_size().Y);
int num_entities = 0;
ENTITIES_ITER(gs.entities) num_entities++;
char *stats = tprint("Frametime: %.1f ms\nProcessing: %.1f ms\nEntities: %d\nDraw calls: %d\nProfiling: %s\n", dt*1000.0, last_frame_processing_time*1000.0, num_entities, num_draw_calls, profiling ? "yes" : "no");
AABB bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
pos.Y -= bounds.upper_left.Y - screen_size().Y;
bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
// background panel
colorquad(false, quad_aabb(bounds), (Color){1.0, 1.0, 1.0, 0.3f});
draw_text((TextParams){false, false, stats, pos, BLACK, 1.0f});
num_draw_calls = 0;
}
#endif // devtools
num_timestep_loops++;
unprocessed_fixed_timestep_time -= FIXED_TIMESTEP;
float dt = (float)FIXED_TIMESTEP;
// process gs.entities
PROFILE_SCOPE("entity processing")
{
ENTITIES_ITER(gs.entities)
{
assert(!(it->exists && it->generation == 0));
@ -2865,6 +2843,7 @@ void frame(void)
assert(false);
}
}
}
PROFILE_SCOPE("Destroy gs.entities")
{
@ -2879,13 +2858,10 @@ void frame(void)
}
}
PROFILE_SCOPE("process player and render player character")
PROFILE_SCOPE("process player")
{
Vec2 character_sprite_pos = AddV2(player->pos, V2(0.0, 20.0f));
// do dialog
Entity *closest_interact_with = NULL;
Entity *closest_interact_with = 0;
{
// find closest to talk to
{
@ -2919,23 +2895,13 @@ void frame(void)
}
Entity *interacting_with = closest_interact_with;
interacting_with = closest_interact_with;
if(player->state == CHARACTER_TALKING)
{
interacting_with = gete(player->talking_to);
assert(interacting_with);
}
// if somebody, show their dialog panel
if(interacting_with)
{
// interaction circle
draw_quad((DrawParams){true, quad_centered(interacting_with->pos, V2(TILE_SIZE, TILE_SIZE)), image_dialog_circle, full_region(image_dialog_circle), WHITE});
if(interacting_with->is_npc)
{
draw_dialog_panel(interacting_with);
}
}
// process dialog and display dialog box when talking to NPC
if(player->state == CHARACTER_TALKING)
@ -2948,6 +2914,7 @@ void frame(void)
}
}
}
// roll input management, sometimes means talk to the npc
if(player->state != CHARACTER_TALKING && roll_just_pressed && closest_interact_with)
{
@ -2994,8 +2961,6 @@ void frame(void)
BUFF_CLEAR(&player->done_damage_to_this_swing);
player->swing_progress = 0.0;
}
// roll processing
{
if(player->state != CHARACTER_IDLE && player->state != CHARACTER_WALKING)
@ -3015,7 +2980,6 @@ void frame(void)
if(!player->is_rolling) player->time_not_rolling += dt;
}
Vec2 target_vel = {0};
float speed = 0.0f;
@ -3033,17 +2997,6 @@ void frame(void)
{
speed *= 1.0f + ((float)player->boots_modifier * 0.1f);
}
if(player->is_rolling)
{
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
}
else
{
draw_animated_sprite(&knight_running, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
}
if(LenV2(movement) == 0.0)
{
player->state = CHARACTER_IDLE;
@ -3054,14 +3007,6 @@ void frame(void)
}
else if(player->state == CHARACTER_IDLE)
{
if(player->is_rolling)
{
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
}
else
{
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
}
if(LenV2(movement) > 0.01) player->state = CHARACTER_WALKING;
}
else if(player->state == CHARACTER_ATTACK)
@ -3073,9 +3018,7 @@ void frame(void)
{
request_do_damage(*it, player->pos, 0.2f);
}
player->swing_progress += dt;
draw_animated_sprite(&knight_attack, player->swing_progress, player->facing_left, character_sprite_pos, WHITE);
if(player->swing_progress > anim_sprite_duration(&knight_attack))
{
player->state = CHARACTER_IDLE;
@ -3083,11 +3026,10 @@ void frame(void)
}
else if(player->state == CHARACTER_TALKING)
{
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
}
else
{
assert(false); // unknown character state? not defined how to draw
assert(false); // unknown character state? not defined how to process
}
// velocity processing
@ -3096,14 +3038,66 @@ void frame(void)
player->vel = LerpV2(player->vel, dt * 15.0f, target_vel);
player->pos = move_and_slide((MoveSlideParams){player, player->pos, player->vel});
}
// health
if(player->damage >= 1.0)
{
reset_level();
}
}
} // while loop
}
PROFILE_SCOPE("render player")
{
Vec2 character_sprite_pos = AddV2(player->pos, V2(0.0, 20.0f));
// if somebody, show their dialog panel
if(interacting_with)
{
// interaction circle
draw_quad((DrawParams){true, quad_centered(interacting_with->pos, V2(TILE_SIZE, TILE_SIZE)), image_dialog_circle, full_region(image_dialog_circle), WHITE});
if(interacting_with->is_npc)
{
draw_dialog_panel(interacting_with);
}
}
if(player->state == CHARACTER_WALKING)
{
if(player->is_rolling)
{
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
}
else
{
draw_animated_sprite(&knight_running, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
}
}
else if(player->state == CHARACTER_IDLE)
{
if(player->is_rolling)
{
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
}
else
{
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
}
}
else if(player->state == CHARACTER_ATTACK)
{
draw_animated_sprite(&knight_attack, player->swing_progress, player->facing_left, character_sprite_pos, WHITE);
}
else if(player->state == CHARACTER_TALKING)
{
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
}
else
{
assert(false); // unknown character state? not defined how to draw
}
// hurt vignette
if(player->damage > 0.0)
{
draw_quad((DrawParams){false, (Quad){.ul=V2(0.0f, screen_size().Y), .ur = screen_size(), .lr = V2(screen_size().X, 0.0f)}, image_hurt_vignette, full_region(image_hurt_vignette), (Color){1.0f, 1.0f, 1.0f, player->damage}});
}
@ -3287,7 +3281,6 @@ void frame(void)
draw_all_translucent();
// ui
#define HELPER_SIZE 250.0f
if(!mobile_controls)
{
@ -3312,6 +3305,43 @@ void frame(void)
draw_quad((DrawParams){false, quad_centered(attack_button_pos(), V2(mobile_button_size(), mobile_button_size())), IMG(image_mobile_button), WHITE, .y_coord_sorting = 1.0f});
}
#ifdef DEVTOOLS
dbgsquare(screen_to_world(mouse_pos));
// tile coord
if(show_devtools)
{
TileCoord hovering = world_to_tilecoord(screen_to_world(mouse_pos));
Vec2 points[4] ={0};
AABB q = tile_aabb(hovering);
dbgrect(q);
draw_text((TextParams){false, false, tprint("%d", get_tile(&level_level0, hovering).kind), world_to_screen(tilecoord_to_world(hovering)), BLACK, 1.0f});
}
// debug draw font image
{
draw_quad((DrawParams){true, quad_centered(V2(0.0, 0.0), V2(250.0, 250.0)), image_font,full_region(image_font), WHITE});
}
// statistics
if(show_devtools)
PROFILE_SCOPE("statistics")
{
Vec2 pos = V2(0.0, screen_size().Y);
int num_entities = 0;
ENTITIES_ITER(gs.entities) num_entities++;
char *stats = tprint("Frametime: %.1f ms\nProcessing: %.1f ms\nEntities: %d\nDraw calls: %d\nProfiling: %s\nNumber fixed timestep loops: %d\n", dt*1000.0, last_frame_processing_time*1000.0, num_entities, num_draw_calls, profiling ? "yes" : "no", num_timestep_loops);
AABB bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
pos.Y -= bounds.upper_left.Y - screen_size().Y;
bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
// background panel
colorquad(false, quad_aabb(bounds), (Color){1.0, 1.0, 1.0, 0.3f});
draw_text((TextParams){false, false, stats, pos, BLACK, 1.0f});
num_draw_calls = 0;
}
#endif // devtools
// update camera position
{
Vec2 target = MulV2F(player->pos, -1.0f * cam.scale);

@ -1,7 +1,6 @@
Happening by END OF STREAM:
- Payment working
- Respond to cancel with stripe, redirect to ?cancelled=true that clears day pass ticket cookie and ui value
- Fixed timesep the gameplay (which means separate player rendering)
DONE - Payment working
DONE - Fixed timesep the gameplay (which means separate player rendering)
- Make action between stars much more technical sounding so AI doesn't hallucinate responses
- Help you fight and fight you actions
- New characters/items from fate
@ -21,6 +20,7 @@ SHIP BETA
- Make ANOTHER tiktok if you can. Por favor
Later:
- Respond to cancel with stripe, redirect to ?cancelled=true that clears day pass ticket cookie and ui value
- Put playgpt.io in twitch title
- Portal at end, exit level
- Keep people away from skeletons, make aggroed/chase state where you can't talk to people

Loading…
Cancel
Save