-
Notifications
You must be signed in to change notification settings - Fork 0
Guides Step Jump
A character struct with velocity, gravity, ground check, and a one-shot jump impulse. Position drives a SceneObject's transform; the rest of the rendering code never has to know it's a "character".
- Guides · Capsule — you have a capsule mesh.
-
Guides · Flying Camera —
dtand key polling are wired.
A character is just a state struct that gets ticked every frame and a transform that gets read every frame. Keep both small. The renderer doesn't care; the controller doesn't render. Two responsibilities, one dt.
#include "VCK.h"
using namespace VCK;
struct Character {
Vec3 position = {0, 1.2f, 0};
Vec3 velocity = {0, 0, 0};
float walkSpeed = 4.0f; // metres / sec
float jumpSpeed = 5.5f; // initial upward velocity
float gravity = 18.0f; // metres / sec^2 (snappier than 9.8)
float capsuleHalf = 0.8f; // matches BuildCapsule(...)
float capsuleR = 0.4f;
bool grounded = true;
};
Character me;grounded is a hint, not a contract. Real games use a swept collision query against the world; here we treat the floor as y = 0 and check the capsule's bottom against it.
void TickCharacter(GLFWwindow* w, Character& c, float dt, const FlyCam& cam)
{
// 1. Horizontal input -- WASD relative to the camera's yaw,
// flattened onto the XZ plane so jumping doesn't depend on look pitch.
Vec3 fwd = Forward(cam); fwd.y = 0; fwd = Normalize(fwd);
Vec3 right = Right(cam); right.y = 0; right = Normalize(right);
Vec3 wish = {0, 0, 0};
auto down = [w](int key) { return glfwGetKey(w, key) == GLFW_PRESS; };
if (down(GLFW_KEY_W)) wish = wish + fwd;
if (down(GLFW_KEY_S)) wish = wish - fwd;
if (down(GLFW_KEY_D)) wish = wish + right;
if (down(GLFW_KEY_A)) wish = wish - right;
if (!(wish.x == 0 && wish.z == 0))
wish = Normalize(wish);
c.velocity.x = wish.x * c.walkSpeed;
c.velocity.z = wish.z * c.walkSpeed;
// 2. Jump -- one-shot impulse, only when grounded.
if (c.grounded && down(GLFW_KEY_SPACE))
{
c.velocity.y = c.jumpSpeed;
c.grounded = false;
}
// 3. Gravity integrates vertical velocity.
c.velocity.y -= c.gravity * dt;
// 4. Integrate position.
c.position = c.position + c.velocity * dt;
// 5. Ground check (flat floor at y = 0; capsule sits on its lower hemisphere).
const float floorY = 0.0f;
const float feet = c.position.y - (c.capsuleHalf + c.capsuleR);
if (feet <= floorY)
{
c.position.y = floorY + (c.capsuleHalf + c.capsuleR);
c.velocity.y = 0;
c.grounded = true;
}
}Five steps: input → walk velocity → jump impulse → gravity → integrate → resolve ground. The order matters — gravity has to apply after the jump check so the impulse isn't immediately cancelled by the ground clamp.
capsuleHalf + capsuleR is the distance from the capsule's centre to the bottom of its lower hemisphere. With Translate(c.position) placing the capsule at its geometric centre, that's the offset for "feet on floor".
void DrawFrame()
{
if (window.IsMinimized()) return;
VCK::HandleLiveResize(window, device, swapchain, framebuffers, pipeline);
const float dt = ComputeDt(); // chrono delta from last frame
TickCharacter(rawWindow, me, dt, cam);
// The character's SceneObject is index 1 in this hypothetical scene.
scene[1].transform = Translate(me.position);
Frame& f = scheduler.BeginFrame();
// ... acquire, render pass, draw scene, end pass, submit, present ...
}Tick first, render second. The character's velocity / position update only happens once per frame, and the GPU sees a stable transform for the whole vkCmdDraw... chain.
Variable timestep is what you get with dt from chrono::steady_clock. Frame spikes can let dt get large enough that the integrator over-shoots a thin floor. Real physics engines run at a fixed timestep (60 / 120 Hz), accumulating the leftover. Add an accumulator if your character starts ghosting through walls.
No real collision. The ground check above is a hard plane, not a swept capsule. For real geometry, you want SAT against triangle meshes or — practically — a physics library like Jolt or PhysX. VCK's scope ends at rendering (rule 16); collision lives outside.
Air control is missing — wish overrides horizontal velocity even mid-jump. Real platformers blend toward wish while airborne with a damping factor. Add it once it bothers you.
- Home
- Quick Start
- Your First App
- Understanding cfg
- Build — Windows / Linux / macOS
Single source of truth for the full API surface is the doc block at the top of VCK.h.