← ziglang.inraylib-zigGitHub

custom frame control

★★★★by Ramon Santamaria (@raysan5)

There is no browser build of this example yet.

Is it the same as C?

//! raylib [core] example - custom frame control
//!
//! Zig-API translation of examples/core/core_custom_frame_control.c (raylib 6.2).
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2021-2025 Ramon Santamaria (@raysan5)
//!
//! NOTE: WARNING: This is an example for advanced users willing to have full control over
//! the frame processes. By default, endDrawing() calls the following processes:
//!     1. Draw remaining batch data: rl.gl.drawRenderBatchActive()
//!     2. swapScreenBuffer()
//!     3. Frame time control: waitTime()
//!     4. pollInputEvents()
//!
//! To avoid steps 2, 3 and 4, flag SUPPORT_CUSTOM_FRAME_CONTROL can be enabled in
//! config.h (it requires recompiling raylib). This way those steps are up to the user
//!
//! Note that enabling this flag invalidates some functions:
//!     - getFrameTime()
//!     - setTargetFPS()
//!     - getFPS()

const rl = @import("raylib");

pub fn main() void {
    // Initialization
    const screen_width = 800;
    const screen_height = 450;

    rl.initWindow(screen_width, screen_height, "raylib [core] example - custom frame control");
    defer rl.closeWindow(); // Close window and OpenGL context

    // Custom timming variables
    var previous_time = rl.getTime(); // Previous time measure
    var delta_time: f32 = 0.0; // Frame time (Update + Draw + Wait time)

    var time_counter: f32 = 0.0; // Accumulative time counter (seconds)
    var position: f32 = 0.0; // Circle position
    var pause = false; // Pause control flag

    var target_fps: i32 = 60; // Our initial target fps

    // Main game loop
    while (!rl.windowShouldClose()) { // Detect window close button or ESC key
        // Update
        // NOTE: On non web platforms the pollInputEvents just works before the inputs checks
        rl.pollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)

        if (rl.isKeyPressed(.space)) pause = !pause;

        if (rl.isKeyPressed(.up)) {
            target_fps += 20;
        } else if (rl.isKeyPressed(.down)) {
            target_fps -= 20;
        }

        if (target_fps < 0) target_fps = 0;

        if (!pause) {
            position += 200 * delta_time; // We move at 200 pixels per second
            if (position >= @as(f32, @floatFromInt(rl.getScreenWidth()))) position = 0;
            time_counter += delta_time; // We count time (seconds)
        }

        // Draw
        draw(position, time_counter, target_fps, delta_time);

        // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL,
        // Events polling, screen buffer swap and frame time control must be managed by the user

        rl.swapScreenBuffer(); // Flip the back buffer to screen (front buffer)

        var current_time = rl.getTime();
        const update_draw_time = current_time - previous_time; // Update + Draw time

        if (target_fps > 0) { // We want a fixed frame rate
            const wait_time = (1.0 / @as(f32, @floatFromInt(target_fps))) - update_draw_time;
            if (wait_time > 0.0) {
                rl.waitTime(@as(f32, @floatCast(wait_time)));
                current_time = rl.getTime();
                delta_time = @floatCast(current_time - previous_time);
            }
        } else delta_time = @floatCast(update_draw_time); // Framerate could be variable

        previous_time = current_time;
    }
}

fn draw(position: f32, time_counter: f32, target_fps: i32, delta_time: f32) void {
    rl.beginDrawing();
    defer rl.endDrawing();

    rl.clearBackground(.ray_white);

    for (0..@intCast(@divTrunc(rl.getScreenWidth(), 200))) |i| {
        rl.drawRectangle(200 * @as(i32, @intCast(i)), 0, 1, rl.getScreenHeight(), .sky_blue);
    }

    const x: i32 = @intFromFloat(position);
    const center_y = @divTrunc(rl.getScreenHeight(), 2);
    rl.drawCircle(x, center_y - 25, 50, .red);

    rl.drawText(rl.textFormat("{d:0>3.0} ms", .{time_counter * 1000.0}), x - 40, center_y - 100, 20, .maroon);
    rl.drawText(rl.textFormat("PosX: {d:0>3.0}", .{position}), x - 50, center_y + 40, 20, .black);

    rl.drawText("Circle is moving at a constant 200 pixels/sec,\nindependently of the frame rate.", 10, 10, 20, .dark_gray);
    rl.drawText("PRESS SPACE to PAUSE MOVEMENT", 10, rl.getScreenHeight() - 60, 20, .gray);
    rl.drawText("PRESS UP | DOWN to CHANGE TARGET FPS", 10, rl.getScreenHeight() - 30, 20, .gray);
    rl.drawText(rl.textFormat("TARGET FPS: {d}", .{target_fps}), rl.getScreenWidth() - 220, 10, 20, .lime);
    if (delta_time != 0) {
        const current_fps: i32 = @intFromFloat(1.0 / delta_time);
        rl.drawText(rl.textFormat("CURRENT FPS: {d}", .{current_fps}), rl.getScreenWidth() - 220, 40, 20, .green);
    }
}
/*******************************************************************************************
*
*   raylib [core] example - custom frame control
*
*   Example complexity rating: [★★★★] 4/4
*
*   NOTE: WARNING: This is an example for advanced users willing to have full control over
*   the frame processes. By default, EndDrawing() calls the following processes:
*       1. Draw remaining batch data: rlDrawRenderBatchActive()
*       2. SwapScreenBuffer()
*       3. Frame time control: WaitTime()
*       4. PollInputEvents()
*
*   To avoid steps 2, 3 and 4, flag SUPPORT_CUSTOM_FRAME_CONTROL can be enabled in
*   config.h (it requires recompiling raylib). This way those steps are up to the user
*
*   Note that enabling this flag invalidates some functions:
*       - GetFrameTime()
*       - SetTargetFPS()
*       - GetFPS()
*
*   Example originally created with raylib 4.0, last time updated with raylib 4.0
*
*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
*   BSD-like license that allows static linking with closed source software
*
*   Copyright (c) 2021-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/

#include "raylib.h"

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
    // Initialization
    //--------------------------------------------------------------------------------------
    const int screenWidth = 800;
    const int screenHeight = 450;

    InitWindow(screenWidth, screenHeight, "raylib [core] example - custom frame control");

    // Custom timming variables
    double previousTime = GetTime();    // Previous time measure
    double currentTime = 0.0;           // Current time measure
    double updateDrawTime = 0.0;        // Update + Draw time
    double waitTime = 0.0;              // Wait time (if target fps required)
    float deltaTime = 0.0f;             // Frame time (Update + Draw + Wait time)

    float timeCounter = 0.0f;           // Accumulative time counter (seconds)
    float position = 0.0f;              // Circle position
    bool pause = false;                 // Pause control flag

    int targetFPS = 60;                 // Our initial target fps
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())        // Detect window close button or ESC key
    {
        // Update
        //----------------------------------------------------------------------------------
        #ifndef PLATFORM_WEB            // NOTE: On non web platforms the PollInputEvents just works before the inputs checks
            PollInputEvents();          // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
        #endif

        if (IsKeyPressed(KEY_SPACE)) pause = !pause;

        if (IsKeyPressed(KEY_UP)) targetFPS += 20;
        else if (IsKeyPressed(KEY_DOWN)) targetFPS -= 20;

        if (targetFPS < 0) targetFPS = 0;

        if (!pause)
        {
            position += 200*deltaTime;  // We move at 200 pixels per second
            if (position >= GetScreenWidth()) position = 0;
            timeCounter += deltaTime;   // We count time (seconds)
        }

        #ifdef PLATFORM_WEB             // NOTE: On web platform for some reason the PollInputEvents only works after the inputs check, so just call it after check all your inputs (on web)
            PollInputEvents();          // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
        #endif
        //----------------------------------------------------------------------------------

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing();

            ClearBackground(RAYWHITE);

            for (int i = 0; i < GetScreenWidth()/200; i++) DrawRectangle(200*i, 0, 1, GetScreenHeight(), SKYBLUE);

            DrawCircle((int)position, GetScreenHeight()/2 - 25, 50, RED);

            DrawText(TextFormat("%03.0f ms", timeCounter*1000.0f), (int)position - 40, GetScreenHeight()/2 - 100, 20, MAROON);
            DrawText(TextFormat("PosX: %03.0f", position), (int)position - 50, GetScreenHeight()/2 + 40, 20, BLACK);

            DrawText("Circle is moving at a constant 200 pixels/sec,\nindependently of the frame rate.", 10, 10, 20, DARKGRAY);
            DrawText("PRESS SPACE to PAUSE MOVEMENT", 10, GetScreenHeight() - 60, 20, GRAY);
            DrawText("PRESS UP | DOWN to CHANGE TARGET FPS", 10, GetScreenHeight() - 30, 20, GRAY);
            DrawText(TextFormat("TARGET FPS: %i", targetFPS), GetScreenWidth() - 220, 10, 20, LIME);
            if (deltaTime != 0)
            {
                DrawText(TextFormat("CURRENT FPS: %i", (int)(1.0f/deltaTime)), GetScreenWidth() - 220, 40, 20, GREEN);
            }

        EndDrawing();

        // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL,
        // Events polling, screen buffer swap and frame time control must be managed by the user

        SwapScreenBuffer();         // Flip the back buffer to screen (front buffer)

        currentTime = GetTime();
        updateDrawTime = currentTime - previousTime;

        if (targetFPS > 0)          // We want a fixed frame rate
        {
            waitTime = (1.0f/(float)targetFPS) - updateDrawTime;
            if (waitTime > 0.0)
            {
                WaitTime((float)waitTime);
                currentTime = GetTime();
                deltaTime = (float)(currentTime - previousTime);
            }
        }
        else deltaTime = (float)updateDrawTime;    // Framerate could be variable

        previousTime = currentTime;
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();        // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
}

From raylib 6.2. The original on GitHub.