Skip to main content
    Courses/C++/Game Development

    Lesson 20 • Advanced

    Game Development in C++

    By the end of this lesson you'll understand the heartbeat of every game — the input/update/render loop — and why movement must be scaled by delta time. You'll write a fixed-timestep loop, a small 2D vector struct, and a tiny entity-component world, all as plain console simulations you can run right here.

    What You'll Learn

    • Run the game loop: input → update → render, every frame
    • Scale all movement by delta time so it is frame-rate independent
    • Implement a fixed timestep with an accumulator for stable physics
    • Write a small 2D vector (Vec2) struct for positions and velocities
    • Model entities with the entity-component pattern and simple systems
    • Pick a starting engine or library: Unreal, SDL2, SFML, or raylib

    💡 Real-World Analogy

    Think of a game like an animated flip-book. Each page is a frame. To draw the next page you do three things in order: glance at what the player pressed (input), nudge every character a little (update), then ink the page (render). Flip the pages fast enough and still images become motion. The catch: if one artist flips pages twice as fast as another, the characters would race ahead — unless each nudge is sized by how much time the page took. That time-per-page is delta time, and multiplying movement by it is what keeps the game running the same on a slow laptop and a fast desktop.

    1. The Game Loop & Delta Time

    A game loop is a loop that runs over and over while the game is alive. Each pass — a frame — does three jobs in the same order: input (what did the player press?), update (move everything forward a little), and render (draw it). Because there is no graphics window here, "render" just prints to the console — but the structure is exactly what Unreal and every other engine uses. Read this worked example and run it.

    Worked example: one game-loop tick

    Five frames of input → update → render, with movement scaled by dt.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        // A game loop does the SAME 3 jobs every frame:
        //   1) input   2) update (move things)   3) render (draw)
        // We "simulate" it on the console — no graphics library needed.
    
        double playerX = 0.0;      // start at the left edge
        double speed   = 50.0;     // 50 PIXELS PER SECOND (not per frame!)
    
        // dt = "delta time" = seconds since the last frame.
        // Pretend each frame takes 1/10 of a second (10 frames per second).
    ...

    The single most important habit in game code: multiply movement by delta time. delta time (dt) is the number of seconds the last frame took. If you instead move by a flat amount per frame, a faster computer renders more frames per second and everything sprints ahead. position += speed * dt turns "pixels per frame" into "pixels per second" so the game behaves identically everywhere. Run this and watch two very different frame rates land on the same answer.

    Worked example: why delta time matters

    Same real speed on a 60 FPS and a 10 FPS machine — both reach x=50.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    // Two players move at the SAME real speed (50 px/s) but on
    // machines with different frame rates. With dt, they end up together.
    void run(const string& label, double dt, int frames) {
        double x = 0.0;
        double speed = 50.0;            // pixels PER SECOND
        for (int f = 0; f < frames; f++) {
            x += speed * dt;            // multiply by dt -> time-based, not frame-based
        }
        cout << label << ": " << frames << " frames, dt=" << dt
         
    ...

    Your turn. The program below makes a falling object accelerate under gravity. Fill in the two blanks marked ___ using the // 👉 hints, then run it and check your output against the expected lines.

    🎯 Your turn: scale gravity by delta time

    Fill in the ___ blanks so the object falls the same on any frame rate.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        // 🎯 YOUR TURN — replace each ___ then press "Try it Yourself".
    
        double y      = 100.0;   // start height
        double gravity = 200.0;  // pixels per second, downward
    
        // 1) Each frame lasts a quarter of a second
        double dt = ___;         // 👉 0.25
    
        cout << "Frame  Y" << endl;
        for (int frame = 1; frame <= 4; frame++) {
            // 2) Move down by gravity, scaled by dt (frame-rate proof!)
            y = y + gravity * ___; 
    ...

    2. Fixed Timestep for Stable Physics

    Delta time keeps movement consistent, but raw dt wobbles frame to frame — and wobbly physics means jumps and collisions that behave slightly differently every time. The fix is a fixed timestep: always advance the physics by the same step (commonly 1.0 / 60.0 seconds). An accumulator banks each frame's real time and you run as many fixed steps as fit. A long, stuttery frame simply runs more steps to catch up, so the simulation never drifts.

    Worked example: accumulator + fixed steps

    Uneven frame times, but physics always advances by a constant 1/60 s step.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        // FIXED TIMESTEP: physics always advances by the SAME step (1/60 s),
        // no matter how long a frame really took. An "accumulator" banks the
        // leftover time so nothing is lost.
        const double STEP = 1.0 / 60.0;   // 0.016666... seconds per physics step
        double accumulator = 0.0;
        int totalSteps = 0;
    
        // Pretend 3 frames arrive with uneven lengths (a stutter mid-game):
        double frameTimes[3] = { 0.020, 0.050, 0.010 }
    ...

    3. A Tiny 2D Vector Struct

    Almost everything in a 2D game is an (x, y) pair: a position, a velocity, a direction. Bundling those two numbers into a small Vec2 struct — with + to add and * to scale — makes movement read like the maths you'd write on paper: pos = pos + velocity * dt. The length() (magnitude) of a velocity vector is the object's speed.

    Worked example: a Vec2 struct

    Add and scale 2D vectors, and measure length with the 3-4-5 triangle.

    Try it Yourself »
    C++
    #include <iostream>
    #include <cmath>
    using namespace std;
    
    // A 2D vector = an (x, y) pair. It is the workhorse of game math:
    // positions, velocities, and directions are all Vec2 values.
    struct Vec2 {
        double x = 0, y = 0;
    
        Vec2 operator+(const Vec2& o) const { return { x + o.x, y + o.y }; }   // add
        Vec2 operator*(double s)      const { return { x * s,   y * s   }; }   // scale
        double length() const { return sqrt(x * x + y * y); }                  // magnitude
    };
    
    int main() {
     
    ...

    4. Entities & the Entity-Component Pattern

    An entity is "a thing in the world" — a hero, a slime, a coin. Rather than one enormous class that does everything, the entity-component pattern gives each entity a bag of small components (Position, Velocity, Health), and writes systems — plain functions — that loop over every entity and act on the data they care about. One movement system moves all entities; one render system draws them all. It keeps data together and lets you build behaviour by mixing components instead of deep inheritance.

    Worked example: entities, components & systems

    A movement system and a render system act on a vector of entities.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    // Entity-component pattern (the simple version):
    // an Entity is a bundle of small DATA components. A "system" is just a
    // function that loops over every entity and acts on the data it needs.
    struct Entity {
        string name;
        double x = 0, y = 0;     // Position component
        double vx = 0, vy = 0;   // Velocity component
        int hp = 100;            // Health component
    };
    
    // MOVEMENT SYSTEM: update every entity'
    ...

    Now you try. Finish the update step of a side-scrolling ship's game loop — the only blank is the delta-time scaling you learned in Section 1:

    🎯 Your turn: finish the game-loop tick

    Fill in the ___ so the ship moves at a steady 100 px/s.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        // 🎯 YOUR TURN — build one game-loop tick for a side-scrolling ship.
    
        double shipX = 0.0;
        double speed = 100.0;   // pixels per second
        double dt    = 0.2;     // each frame is 0.2 seconds
    
        cout << "Frame  ShipX" << endl;
        for (int frame = 1; frame <= 3; frame++) {
            // --- UPDATE --- move the ship right, scaled by dt
            shipX = shipX + speed * ___;   // 👉 dt
    
            // --- RENDER --- print the frame and p
    ...

    🔎 Deep Dive: update rate vs render rate

    Real engines separate how often physics updates from how often the screen draws. Physics runs on the fixed timestep (say 60 updates a second); rendering runs as fast as the monitor refreshes. When the two don't line up, you interpolate: draw the object a fraction alpha of the way between its last and next physics positions, so motion looks buttery even if physics ticked slightly before the frame was drawn.

    while (running) {
        processInput();
        accumulator += frameTime;
        while (accumulator >= STEP) {   // fixed-step physics
            update(STEP);
            accumulator -= STEP;
        }
        double alpha = accumulator / STEP;   // 0..1 between steps
        render(alpha);                       // interpolate for smoothness
    }

    Pro Tips

    • 💡 Always scale by dt: every speed, every acceleration. If a value moves something, it is "per second" and must be multiplied by dt.
    • 💡 Clamp huge dt: after a pause or a breakpoint, one frame may report a giant dt. Cap it (e.g. if (dt > 0.1) dt = 0.1;) so objects don't teleport.
    • 💡 Keep components tiny: one piece of data each — Position, not PositionVelocityHealth. Small components compose better.
    • 💡 Learn the loop before the engine: once the plain-C++ loop here makes sense, raylib or SFML is mostly "the same loop, but draw a sprite instead of cout".

    Common Errors (and the fix)

    • Frame-rate-dependent movement (no delta time): writing x += speed; moves the object once per frame, so it races on fast PCs and crawls on slow ones. Always scale by time: x += speed * dt;.
    • Treating speed as "per frame": a value like 50 is meaningless without a unit. Decide it's pixels per second, then * dt converts it to this frame's movement.
    • Integer division in dt: double dt = 1 / 60; gives 0 (integer division), so nothing moves. Write 1.0 / 60.0 so the maths is done in double.
    • Spiral of death: if each fixed step takes longer than STEP real seconds, the while (accumulator >= STEP) loop never drains and the game freezes. Cap the steps per frame, or clamp the incoming frame time.
    • Forgetting to subtract from the accumulator: leaving out accumulator -= STEP; inside the loop makes it spin forever. Each fixed step must spend one STEP of banked time.

    📋 Quick Reference: Engines & Libraries

    ToolLevelBest for
    raylibLibrary (high-level)Absolute beginners — tiny API, a window and shapes in minutes
    SFMLLibrary (mid-level)Clean C++ classes for 2D graphics, audio, input, networking
    SDL2Library (low-level)Full control over windows, input, and the render loop
    Unreal EngineFull engine (AAA)3D/AAA titles — editor, renderer, physics, and tooling built in

    All four are C++ at heart. The loop you wrote above is the same one they run — they just replace cout with drawing a sprite to a window.

    Frequently Asked Questions

    Q: What is the game loop and why does every game need one?

    A game loop is a loop that never stops while the game runs. Each pass — called a frame — it does three jobs in order: read input, update the world a tiny bit, then draw. Without it, the screen would freeze the instant your code stopped running. Every engine from Unreal to a homemade Pong has one beating at its core.

    Q: What is delta time and why must I multiply movement by it?

    Delta time (dt) is how many seconds passed since the last frame. If you move an object by a flat amount each frame, it goes faster on a fast computer and slower on a slow one. Multiplying speed by dt — position += speed * dt — turns 'pixels per frame' into 'pixels per second', so movement looks identical on every machine.

    Q: What is a fixed timestep and when do I need one?

    A fixed timestep updates the physics with the same dt every time (for example exactly 1/60 of a second), using an accumulator to bank leftover time. You need it whenever consistent, repeatable physics matters — jumps, collisions, networked play — because variable dt can make the same input produce different results on different hardware.

    Q: What is the entity-component pattern in one sentence?

    Instead of one giant Player class that does everything, you give an entity a bag of small data pieces — a Position component, a Velocity component, a Health component — and write systems that act on every entity that has the right pieces. It keeps related data together and lets you mix and match behaviour by composition rather than deep inheritance.

    Q: Which engine or library should a beginner start with?

    For learning the fundamentals from scratch, raylib or SFML are the gentlest — small APIs, instant window-and-shape on screen. SDL2 is lower-level and great once you want full control. Unreal Engine is a full AAA toolkit: powerful, but a lot to swallow on day one. Learn the loop in plain C++ first (as in this lesson), then a small library, then a big engine.

    Mini-Challenge: Bouncing Ball

    No blanks this time — just a brief and an outline to keep you on track. Build a game loop where a ball climbs, hits a ceiling, and falls back down by flipping its velocity. Run it and check your output against the example in the comments.

    🎯 Mini-Challenge: bouncing ball loop

    Write the update logic yourself: move by velocity * dt, then flip at the edges.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        // 🎯 MINI-CHALLENGE: a bouncing ball loop
        // 1. Make doubles: y = 0, velocity = 10 (px per second), dt = 1.0.
        // 2. Loop 6 frames. Each frame:
        //      - UPDATE:  y = y + velocity * dt;
        //      - if y reaches 30 or more, flip direction: velocity = -velocity;
        //      - if y drops to 0 or below,  flip direction: velocity = -velocity;
        //      - RENDER:  print  "Frame N: y=..."
        // 3. Watch the ball climb to ~30, t
    ...

    🎉 Lesson Complete

    • ✅ The game loop runs input → update → render once per frame, forever
    • Delta time: position += speed * dt makes movement frame-rate independent
    • ✅ A fixed timestep + accumulator keeps physics stable on uneven frames
    • ✅ A small Vec2 struct powers positions, velocities, and directions
    • ✅ The entity-component pattern: entities hold components; systems act on them
    • ✅ Start with raylib or SFML, grow into SDL2, then a full engine like Unreal
    • Next lesson: C++ Libraries — pulling in and using external code

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service