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