Beginners guide to Rust Raylib - raylib-rs/raylib-rs GitHub Wiki

C version

int main(void) {
    const int screenWidth = 800;
    const int screenHeight = 450;
    InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera");

    Rectangle player = { 400, 280, 40, 40 };

    Camera2D camera = { 0 };
    camera.target = (Vector2){ player.x + 20.0f, player.y + 20.0f };
    camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
    camera.zoom = 1.0f;

    SetTargetFPS(60);
    while (!WindowShouldClose())
    {
        // Update
        if (IsKeyDown(KEY_RIGHT)) player.x += 2;
        else if (IsKeyDown(KEY_LEFT)) player.x -= 2;

        // Camera target follows player
        camera.target = (Vector2){ player.x + 20, player.y + 20 };
        camera.zoom = expf(logf(camera.zoom) + ((float)GetMouseWheelMove()*0.1f));

        BeginDrawing(); // (1) START DRAWING
            ClearBackground(RAYWHITE);
                BeginMode2D(camera); //(2) DRAW STUFF INTO CAMERA SPACE START
                    DrawRectangle(-6000, 320, 13000, 8000, DARKGRAY);
                    DrawRectangleGradientH(0, 0, scerenWidth, ScreenHeight, RED, BLUE)
                    DrawRectangleRec(player, RED);
                    DrawLine((int)camera.target.x, -screenHeight*10, (int)camera.target.x, screenHeight*10, GREEN);
                    DrawLine(-screenWidth*10, (int)camera.target.y, screenWidth*10, (int)camera.target.y, GREEN);
                EndMode2D(); //(3) DRAW STUFF INTO CAMERA SPACE END

            // DRAW STUFF IN SCREEN SPACE
            DrawText("SCREEN AREA", 640, 10, 20, RED);
            DrawRectangleLines( 10, 10, 250, 113, RED);

            DrawRectangle( 10, 10, 250, 113, Fade(SKYBLUE, 0.5f));
            DrawRectangleLines( 10, 10, 250, 113, BLUE);
x
        EndDrawing(); // (4) END DRAWING
    }
    CloseWindow();

    return 0;
}

Closure version

#![allow(non_snake_case)]
use raylib::prelude::*;
#[rustfmt::skip]
fn main() {
    let screen_width = 800;
    let screen_height = 450;
    use raylib::consts::KeyboardKey::*;
    let (mut rl, thread) = raylib::init()
        .size(screen_width, screen_height)
        .title("camera example")
        .resizable()
        .vsync()
        .build();

    let mut player = Rectangle::new(400.0, 280.0, 40.0, 40.0);

    let mut camera = Camera2D {
        target: Vector2::new(player.x + 20.0, player.y + 20.0),
        offset: Vector2::new(player.x, player.y),
        rotation: 0.0,
        zoom: 1.0,
    };

    while !rl.window_should_close() {
        if rl.is_key_down(KEY_RIGHT) {
            player.x += 2.0;
        } else if rl.is_key_down(KEY_LEFT) {
            player.x -= 2.0;
        }

        // Camera follows player
        camera.target = Vector2::new(player.x + 20.0, player.y + 20.0);

        rl.draw(&thread, |mut d| { // (1) BeginDrawing()
            d.clear_background(Color::RAYWHITE);

            d.draw_mode2D(camera, |mut d2| { //(2) BeginMode2D()
                d2.draw_rectangle(-6000, 320, 13000, 8000, Color::DARKGRAY);

                d2.draw_rectangle_gradient_h(0,0, screen_width, screen_height,Color::RED,Color::BLUE,);
                d2.draw_rectangle_rec(player, Color::RED);

                d2.draw_line(camera.target.x as i32,-screen_height * 10,camera.target.x as i32,screen_height * 10, Color::GREEN);
                d2.draw_line(-screen_width * 10,camera.target.y as i32,screen_width * 10,camera.target.y as i32,Color::GREEN);
                //(3) EndMode2D()
            });

            // DRAW STUFF IN SCREEN SPACE
            d.draw_text("SCREEN AREA", 640, 10, 20, Color::RED);
            d.draw_rectangle_lines(0, 0, screen_width, screen_height, Color::RED);

            d.draw_rectangle(10, 10, 250, 113, Color::SKYBLUE.alpha(0.5));
            d.draw_rectangle_lines(10, 10, 250, 113, Color::BLUE);
            // EndDrawing()
        });
    }
}

Handle version

    while !rl.window_should_close() {
        if rl.is_key_down(KEY_RIGHT) {
            player.x += 2.0;
        } else if rl.is_key_down(KEY_LEFT) {
            player.x -= 2.0;
        }

        // Camera follows player
        camera.target = Vector2::new(player.x + 20.0, player.y + 20.0);

        let mut d = rl.begin_drawing(&thread); 
        { // (1) BeginDrawing()
            d.clear_background(Color::RAYWHITE);
            { 
                //(2) BeginMode2D()
                let mut d = d.begin_mode2D(camera); // SHADOW d in this scope
                d.draw_rectangle(-6000, 320, 13000, 8000, Color::DARKGRAY);

                d.draw_rectangle_gradient_h(0,0, screen_width, screen_height,Color::RED,Color::BLUE,);
                d.draw_rectangle_rec(player, Color::RED);

                d.draw_line(camera.target.x as i32,-screen_height * 10,camera.target.x as i32,screen_height * 10, Color::GREEN);
                d.draw_line(-screen_width * 10,camera.target.y as i32,screen_width * 10,camera.target.y as i32,Color::GREEN);
                //(3) EndMode2D()
            };

            // DRAW STUFF IN SCREEN SPACE
            d.draw_text("SCREEN AREA", 640, 10, 20, Color::RED);
            d.draw_rectangle_lines(0, 0, screen_width, screen_height, Color::RED);

            d.draw_rectangle(10, 10, 250, 113, Color::SKYBLUE.alpha(0.5));
            d.draw_rectangle_lines(10, 10, 250, 113, Color::BLUE);
            // EndDrawing()
        };
    }

Explanation:

  • Map the C equiv to Rust
  • Notice that we don't call EndDrawing(), EndMode2D() and various End() functions. This is because of the Drop trait in rust which the safe bindings implicitly call at the end of the scope

why is there a closure version and a handle version?

  • The handles and closure version do the exact same thing, so why are both included?
  • Slightly better debugger support, e.g so structs in the closure are mystruct.__foo
  • Borrowing issues with the closure holding a mutable reference to things too long. See samples/imgui for the example