commit 2a172e5f13f6c512a989033eadf080139e738ab2
parent 0c1d771d0aac601d906647dee11e80b5c0c5b9d4
Author: amrfti <andrew@kloet.net>
Date: Sun, 28 Dec 2025 15:38:50 -0500
fix micro sliding on slopes
Diffstat:
3 files changed, 41 insertions(+), 22 deletions(-)
diff --git a/config.h b/config.h
@@ -29,10 +29,10 @@
// camera
#define BASE_FOV 70.0f
-#define SPRINT_FOV 80.0f
+#define SPRINT_FOV 85.0f
#define MAX_PITCH 1.5f
#define FOV_LERP_SPEED 30.0f
-#define ROLL_FACTOR 5.0f
+#define ROLL_FACTOR 6.0f
#define ROLL_LERP_SPEED 20.0f
#define MOUSE_SENSITIVITY 0.003f
#define SPRINT_FOV_LERP_SPEED 10.0f
diff --git a/main.c b/main.c
@@ -111,8 +111,9 @@ int main(void) {
targetVel.z = wishDir.z * speed;
}
- velocity.x = targetVel.x;
- velocity.z = targetVel.z;
+ float accel = grounded ? ACCELERATION : AIR_ACCELERATION;
+ velocity.x = Lerp(velocity.x, targetVel.x, accel * dt);
+ velocity.z = Lerp(velocity.z, targetVel.z, accel * dt);
if (grounded && IsKeyPressed(KEY_SPACE))
velocity.y = JUMP_FORCE;
diff --git a/physics.c b/physics.c
@@ -5,6 +5,9 @@
#define SQR(x) ((x) * (x))
+/*
+ * Check for if point p is within the bounds of triangle t with radius r
+ */
static inline bool TriangleLikelyContains(Vector3 p, float r,
const Triangle *t) {
float r2 = r + SKIN;
@@ -68,7 +71,7 @@ bool SphereCastTriangle(Vector3 pos, Vector3 dir, float radius, float maxDist,
const Triangle *tri, HitInfo *hit) {
Vector3 n = tri->normal;
- // 1. Intersect with the infinite plane
+ // intersect with the infinite plane
float denom = Vector3DotProduct(n, dir);
if (denom >= -EPSILON)
@@ -80,16 +83,16 @@ bool SphereCastTriangle(Vector3 pos, Vector3 dir, float radius, float maxDist,
if (tPlane > maxDist)
return false;
- // 2. Determine where the sphere touches the plane
+ // determine where the sphere touches the plane
Vector3 planeIntersectionPoint = Vector3Add(pos, Vector3Scale(dir, tPlane));
Vector3 centerOnPlane =
Vector3Subtract(planeIntersectionPoint, Vector3Scale(n, radius));
- // 3. Find closest point on the actual triangle
+ // find closest point on the actual triangle
Vector3 closestPt =
ClosestPointOnTriangle(centerOnPlane, tri->a, tri->b, tri->c);
- // 4. Check distance
+ // check distance
float distSq = Vector3DistanceSqr(centerOnPlane, closestPt);
// CASE A: Hit the FACE
@@ -160,6 +163,11 @@ void ResolveOverlaps(Vector3 *pos, float radius, Triangle *tris, int triCount,
for (int i = 0; i < triCount; i++) {
Triangle *t = &tris[i];
+ /*
+ * Broad phase AABB check. If the position isn't even
+ * within the bounding box of triangle t then skip it
+ * TODO: replace with O(1) BVH
+ */
if (!TriangleLikelyContains(*pos, radius, t))
continue;
@@ -167,22 +175,27 @@ void ResolveOverlaps(Vector3 *pos, float radius, Triangle *tris, int triCount,
Vector3 pushVec = Vector3Subtract(*pos, closest);
float d2 = Vector3LengthSqr(pushVec);
- // allow for floating point error at the exact surface boundary
- if (d2 > 0 && d2 < rSq + EPSILON) {
- float d = sqrtf(d2);
- Vector3 n = Vector3Scale(pushVec, 1.0f / d);
+ if (d2 <= 0 || d2 >= rSq + EPSILON)
+ continue;
- // push out
- float penetration = radius - d + SKIN;
+ float d = sqrtf(d2);
+ Vector3 n = Vector3Scale(pushVec, 1.0f / d);
+
+ float penetration = radius - d + SKIN;
+
+ /*
+ * If it is ground, only push UP.
+ * We convert the normal penetration into vertical penetration:
+ * dist_vertical = dist_normal / cos(theta)
+ * n.y is actually cos(theta) between Up and Normal.
+ */
+ if (n.y > GROUND_NORMAL_Y) {
+ pos->y += penetration / n.y;
+ *hitFloor = true;
+ } else
*pos = Vector3Add(*pos, Vector3Scale(n, penetration));
- if (n.y > GROUND_NORMAL_Y)
- *hitFloor = true;
- else if (t->normal.y > GROUND_NORMAL_Y && n.y > EPSILON)
- *hitFloor = true;
-
- collisionFound = true;
- }
+ collisionFound = true;
}
if (!collisionFound)
@@ -190,6 +203,10 @@ void ResolveOverlaps(Vector3 *pos, float radius, Triangle *tris, int triCount,
}
}
+/*
+ * Snaps the player to the ground, this is useful so we don't get
+ * micro air time while running down slopes or climbing over the top.
+ */
void SnapToGround(Vector3 *pos, float radius, Triangle *tris, int triCount,
bool *touchingGround) {
HitInfo hit;
@@ -222,8 +239,9 @@ void Physics_MoveCharacter(Vector3 *pos, Vector3 *velocity, float dt,
if (effectivelyGrounded && velocity->y <= 0) {
SnapToGround(pos, radius, tris, triCount, &touchingGround);
velocity->y = 0;
- } else
+ } else {
velocity->y -= GRAVITY * dt;
+ }
if (touchingGround)
groundedTimer = COYOTE_TIME;