fp: Fix Attackable reusing pre-existing bit, use move equations for attack kickback
This commit is contained in:
parent
543e759682
commit
4842bd32f9
198
feely_pona.cpp
198
feely_pona.cpp
@ -120,6 +120,102 @@ TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_A
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_V2 acceleration_meters_per_s)
|
||||||
|
{
|
||||||
|
// f"(t) = a
|
||||||
|
// f'(t) = at + v
|
||||||
|
// f (t) = 0.5f*a(t^2) + vt + p
|
||||||
|
|
||||||
|
FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle);
|
||||||
|
if (FP_Game_IsNilEntity(entity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP);
|
||||||
|
Dqn_f32 t_squared = DQN_SQUARED(t);
|
||||||
|
|
||||||
|
Dqn_f32 velocity_falloff_coefficient = 0.82f;
|
||||||
|
Dqn_f32 acceleration_feel_good_factor = 15'000.f;
|
||||||
|
Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game, acceleration_meters_per_s) * acceleration_feel_good_factor;
|
||||||
|
entity->velocity = (acceleration * t) + entity->velocity * velocity_falloff_coefficient;
|
||||||
|
|
||||||
|
// NOTE: Zero out velocity with epsilon
|
||||||
|
if (DQN_ABS(entity->velocity.x) < 5.f)
|
||||||
|
entity->velocity.x = 0.f;
|
||||||
|
if (DQN_ABS(entity->velocity.y) < 5.f)
|
||||||
|
entity->velocity.y = 0.f;
|
||||||
|
|
||||||
|
Dqn_V2 delta_pos = (acceleration * 0.5f * t_squared) + (entity->velocity * t);
|
||||||
|
Dqn_Rect entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
||||||
|
Dqn_V2 entity_pos = Dqn_Rect_Center(entity_world_hit_box);
|
||||||
|
Dqn_V2 entity_new_pos = entity_pos + delta_pos;
|
||||||
|
|
||||||
|
bool has_collision = false;
|
||||||
|
for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) {
|
||||||
|
FP_GameEntity *collider = collider_it.entity;
|
||||||
|
if (collider->handle == entity->handle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO(doyle): Calculate the list of collidables at the start of the frame
|
||||||
|
if ((collider->flags & FP_GameEntityFlag_NonTraversable) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// NOTE: Sweep collider with half the radius of the source entity
|
||||||
|
Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle);
|
||||||
|
Dqn_Rect swept_collider_world_hit_box = collider_world_hit_box;
|
||||||
|
swept_collider_world_hit_box.pos -= (entity_world_hit_box.size * .5f);
|
||||||
|
swept_collider_world_hit_box.size += entity_world_hit_box.size;
|
||||||
|
|
||||||
|
if (!Dqn_Rect_ContainsPoint(swept_collider_world_hit_box, entity_new_pos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Dqn_f32 collider_left_wall_x = swept_collider_world_hit_box.pos.x;
|
||||||
|
Dqn_f32 collider_right_wall_x = swept_collider_world_hit_box.pos.x + swept_collider_world_hit_box.size.w;
|
||||||
|
Dqn_f32 collider_top_wall_y = swept_collider_world_hit_box.pos.y;
|
||||||
|
Dqn_f32 collider_bottom_wall_y = swept_collider_world_hit_box.pos.y + swept_collider_world_hit_box.size.h;
|
||||||
|
|
||||||
|
Dqn_V2 o = entity_pos;
|
||||||
|
Dqn_V2 d = delta_pos;
|
||||||
|
|
||||||
|
// NOTE: Solve collision by determining the 't' value at which
|
||||||
|
// we hit one of the walls of the collider and move the entity
|
||||||
|
// at exactly that point.
|
||||||
|
// O + td = x
|
||||||
|
// td = x - O
|
||||||
|
// t = (x - O) / d
|
||||||
|
|
||||||
|
Dqn_f32 const SENTINEL_T = 999.f;
|
||||||
|
Dqn_f32 earliest_t = SENTINEL_T;
|
||||||
|
if (d.x != 0.f) {
|
||||||
|
Dqn_f32 left_t = (collider_left_wall_x - o.x) / d.x;
|
||||||
|
Dqn_f32 right_t = (collider_right_wall_x - o.x) / d.x;
|
||||||
|
if (left_t >= 0.f && left_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, left_t);
|
||||||
|
if (right_t >= 0.f && right_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, right_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.y != 0.f) {
|
||||||
|
Dqn_f32 top_t = (collider_top_wall_y - o.y) / d.y;
|
||||||
|
Dqn_f32 bottom_t = (collider_bottom_wall_y - o.y) / d.y;
|
||||||
|
if (top_t >= 0.f && top_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, top_t);
|
||||||
|
if (bottom_t >= 0.f && bottom_t <= 1.f)
|
||||||
|
earliest_t = DQN_MIN(earliest_t, bottom_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (earliest_t != SENTINEL_T) {
|
||||||
|
Dqn_V2 pos_just_before_collide = entity_pos + (d * earliest_t);
|
||||||
|
Dqn_V2 new_delta_p = pos_just_before_collide - entity_pos;
|
||||||
|
entity->local_pos += new_delta_p;
|
||||||
|
has_collision = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_collision) {
|
||||||
|
entity->local_pos += delta_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" __declspec(dllexport)
|
extern "C" __declspec(dllexport)
|
||||||
void TELY_DLL_Reload(void *user_data)
|
void TELY_DLL_Reload(void *user_data)
|
||||||
{
|
{
|
||||||
@ -394,7 +490,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entity_has_velocity /*&& !has_collision*/) {
|
if (!entity_has_velocity) {
|
||||||
action->next_state = FP_EntityTerryState_Idle;
|
action->next_state = FP_EntityTerryState_Idle;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -884,7 +980,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) {
|
if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4.f))) {
|
||||||
FP_SentinelListLink<FP_GameWaypoint> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
|
FP_SentinelListLink<FP_GameWaypoint> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
|
||||||
if (first_waypoint->data.entity != closest_terry->handle) {
|
if (first_waypoint->data.entity != closest_terry->handle) {
|
||||||
|
|
||||||
@ -992,95 +1088,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Core equations of motion ==========================================================
|
// NOTE: Core equations of motion ==========================================================
|
||||||
bool has_collision = false;
|
FP_Game_MoveEntity(game, entity->handle, acceleration_meters_per_s);
|
||||||
{
|
|
||||||
// f"(t) = a
|
|
||||||
// f'(t) = at + v
|
|
||||||
// f (t) = 0.5f*a(t^2) + vt + p
|
|
||||||
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP);
|
|
||||||
Dqn_f32 t_squared = DQN_SQUARED(t);
|
|
||||||
|
|
||||||
Dqn_f32 velocity_falloff_coefficient = 0.82f;
|
|
||||||
Dqn_f32 acceleration_feel_good_factor = 15'000.f;
|
|
||||||
Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game, acceleration_meters_per_s) * acceleration_feel_good_factor;
|
|
||||||
entity->velocity = (acceleration * t) + entity->velocity * velocity_falloff_coefficient;
|
|
||||||
|
|
||||||
// NOTE: Zero out velocity with epsilon
|
|
||||||
if (DQN_ABS(entity->velocity.x) < 5.f)
|
|
||||||
entity->velocity.x = 0.f;
|
|
||||||
if (DQN_ABS(entity->velocity.y) < 5.f)
|
|
||||||
entity->velocity.y = 0.f;
|
|
||||||
|
|
||||||
Dqn_V2 delta_pos = (acceleration * 0.5f * t_squared) + (entity->velocity * t);
|
|
||||||
Dqn_Rect entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
|
|
||||||
Dqn_V2 entity_pos = Dqn_Rect_Center(entity_world_hit_box);
|
|
||||||
Dqn_V2 entity_new_pos = entity_pos + delta_pos;
|
|
||||||
|
|
||||||
for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) {
|
|
||||||
FP_GameEntity *collider = collider_it.entity;
|
|
||||||
if (collider->handle == entity->handle)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// TODO(doyle): Calculate the list of collidables at the start of the frame
|
|
||||||
if ((collider->flags & FP_GameEntityFlag_NonTraversable) == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// NOTE: Sweep collider with half the radius of the source entity
|
|
||||||
Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle);
|
|
||||||
Dqn_Rect swept_collider_world_hit_box = collider_world_hit_box;
|
|
||||||
swept_collider_world_hit_box.pos -= (entity_world_hit_box.size * .5f);
|
|
||||||
swept_collider_world_hit_box.size += entity_world_hit_box.size;
|
|
||||||
|
|
||||||
if (!Dqn_Rect_ContainsPoint(swept_collider_world_hit_box, entity_new_pos))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Dqn_f32 collider_left_wall_x = swept_collider_world_hit_box.pos.x;
|
|
||||||
Dqn_f32 collider_right_wall_x = swept_collider_world_hit_box.pos.x + swept_collider_world_hit_box.size.w;
|
|
||||||
Dqn_f32 collider_top_wall_y = swept_collider_world_hit_box.pos.y;
|
|
||||||
Dqn_f32 collider_bottom_wall_y = swept_collider_world_hit_box.pos.y + swept_collider_world_hit_box.size.h;
|
|
||||||
|
|
||||||
Dqn_V2 o = entity_pos;
|
|
||||||
Dqn_V2 d = delta_pos;
|
|
||||||
|
|
||||||
// NOTE: Solve collision by determining the 't' value at which
|
|
||||||
// we hit one of the walls of the collider and move the entity
|
|
||||||
// at exactly that point.
|
|
||||||
// O + td = x
|
|
||||||
// td = x - O
|
|
||||||
// t = (x - O) / d
|
|
||||||
|
|
||||||
Dqn_f32 const SENTINEL_T = 999.f;
|
|
||||||
Dqn_f32 earliest_t = SENTINEL_T;
|
|
||||||
if (d.x != 0.f) {
|
|
||||||
Dqn_f32 left_t = (collider_left_wall_x - o.x) / d.x;
|
|
||||||
Dqn_f32 right_t = (collider_right_wall_x - o.x) / d.x;
|
|
||||||
if (left_t >= 0.f && left_t <= 1.f)
|
|
||||||
earliest_t = DQN_MIN(earliest_t, left_t);
|
|
||||||
if (right_t >= 0.f && right_t <= 1.f)
|
|
||||||
earliest_t = DQN_MIN(earliest_t, right_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d.y != 0.f) {
|
|
||||||
Dqn_f32 top_t = (collider_top_wall_y - o.y) / d.y;
|
|
||||||
Dqn_f32 bottom_t = (collider_bottom_wall_y - o.y) / d.y;
|
|
||||||
if (top_t >= 0.f && top_t <= 1.f)
|
|
||||||
earliest_t = DQN_MIN(earliest_t, top_t);
|
|
||||||
if (bottom_t >= 0.f && bottom_t <= 1.f)
|
|
||||||
earliest_t = DQN_MIN(earliest_t, bottom_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (earliest_t != SENTINEL_T) {
|
|
||||||
Dqn_V2 pos_just_before_collide = entity_pos + (d * earliest_t);
|
|
||||||
Dqn_V2 new_delta_p = pos_just_before_collide - entity_pos;
|
|
||||||
entity->local_pos += new_delta_p;
|
|
||||||
has_collision = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_collision) {
|
|
||||||
entity->local_pos += delta_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Tick the state machine
|
// NOTE: Tick the state machine
|
||||||
FP_EntityActionStateMachine(game, input, entity, dir_vector);
|
FP_EntityActionStateMachine(game, input, entity, dir_vector);
|
||||||
@ -1189,12 +1197,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
|||||||
else
|
else
|
||||||
attack_dir_vector.x = -1.f;
|
attack_dir_vector.x = -1.f;
|
||||||
|
|
||||||
Dqn_V2 acceleration = attack_dir_vector * 500'000.f;
|
Dqn_V2 attack_acceleration_meters_per_s = attack_dir_vector * 20.f;
|
||||||
Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP);
|
FP_Game_MoveEntity(game, defender->handle, attack_acceleration_meters_per_s);
|
||||||
Dqn_f32 t_squared = DQN_SQUARED(t);
|
|
||||||
|
|
||||||
Dqn_V2 delta_p = (acceleration * 0.5f * t_squared) + (defender->velocity * t);
|
|
||||||
defender->velocity = (acceleration * t) + defender->velocity * 2.0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_
|
|||||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters);
|
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters);
|
||||||
FP_Entity_AddDebugEditorFlags(game, result);
|
FP_Entity_AddDebugEditorFlags(game, result);
|
||||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||||
|
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ enum FP_GameEntityFlag
|
|||||||
FP_GameEntityFlag_MobSpawner = 1 << 7,
|
FP_GameEntityFlag_MobSpawner = 1 << 7,
|
||||||
FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 8,
|
FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 8,
|
||||||
FP_GameEntityFlag_AggrosWhenNearTerry = 1 << 9,
|
FP_GameEntityFlag_AggrosWhenNearTerry = 1 << 9,
|
||||||
FP_GameEntityFlag_Attackable = 1 << 9,
|
FP_GameEntityFlag_Attackable = 1 << 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FP_GameShapeType
|
enum FP_GameShapeType
|
||||||
|
Loading…
Reference in New Issue
Block a user