Gameplay Pillars
- >Deterministic simulation steps
- >Debuggability first, visuals second
- >Extensible architecture for gameplay experiments
Engine & Gameplay Programmer
I developed this engine to deepen my low-level gameplay and simulation programming skills. The project covers the full loop from broadphase collision culling to contact resolution and frame-stable integration.
Duration
6 months
Team
Solo project
Engine
Custom C++ Engine
Platforms
Windows
Simulation runs on a fixed step independent from rendering framerate.
Impact: Eliminated jitter and made movement tests reproducible across machines.
Separating axis tests produce robust contact normals and penetration depths.
Impact: Reduced tunneling and stabilized stack behavior in stress tests.
ImGui panels expose runtime tuning for drag, friction, and impulse clamping.
Impact: Accelerated balancing passes and simplified issue reproduction.
Screenshots and clips from gameplay, debug tools, and iteration passes.

Collider wireframes with normal vectors and penetration depth readouts.

Runtime impulse diagnostics used to tune stack stability.
Stack stress test with alternating mass ratios and restitution values.
Focused clips showing implementation outcomes, tuning passes, and polished gameplay moments.
Step-by-step playback of broadphase, narrowphase, and impulse resolution.
Practical samples from gameplay systems and runtime tools used in production.
RigidBody.cpp | cpp
Stable integration step used for velocity and position updates.
void RigidBody::Integrate(float dt)
{
if (isStatic)
{
return;
}
linearVelocity += (forceAccum * inverseMass) * dt;
angularVelocity += torqueAccum * inverseInertia * dt;
position += linearVelocity * dt;
orientation += angularVelocity * dt;
orientation.Normalize();
forceAccum = Vec3::Zero();
torqueAccum = Vec3::Zero();
}ContactSolver.cpp | cpp
Computes normal impulse and applies equal/opposite responses.
void ContactSolver::ResolveVelocity(Contact& contact)
{
const Vec3 relativeVelocity =
contact.bodyB->linearVelocity - contact.bodyA->linearVelocity;
const float separatingVelocity = Dot(relativeVelocity, contact.normal);
if (separatingVelocity > 0.0f)
{
return;
}
const float newSepVelocity = -separatingVelocity * contact.restitution;
const float deltaVelocity = newSepVelocity - separatingVelocity;
const float totalInverseMass =
contact.bodyA->inverseMass + contact.bodyB->inverseMass;
if (totalInverseMass <= 0.0f)
{
return;
}
const float impulseMagnitude = deltaVelocity / totalInverseMass;
const Vec3 impulse = contact.normal * impulseMagnitude;
contact.bodyA->linearVelocity -= impulse * contact.bodyA->inverseMass;
contact.bodyB->linearVelocity += impulse * contact.bodyB->inverseMass;
}