DEV Community

Sébastien Belzile
Sébastien Belzile

Posted on • Updated on

Making Games in Rust - Part 2 - Drawing Stuff and Cameras

Drawing a Square

Our game will be composed of sprites. All our world will be built with squares of equal width and height.

This means that the first thing we want to do is draw a square on the screen.

Let's create a new system called spawn_player:

// Player component
struct Player;

// spawn player system
fn spawn_player(mut commands: Commands, mut materials: ResMut<Assets<ColorMaterial>>) {
    commands
        .spawn_bundle(SpriteBundle {
            material: materials.add(Color::rgb(0.7, 0.7, 0.7).into()),
            sprite: Sprite::new(Vec2::new(10.0, 10.0)),
            ..Default::default()
        })
        .insert(Player);
}
Enter fullscreen mode Exit fullscreen mode

and register this system with our Bevy application:

App::build()
        .insert_resource(WindowDescriptor {
            title: "Platformer!".to_string(),
            width: 640.0,
            height: 400.0,
            vsync: true,
            ..Default::default()
        })
        .insert_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04)))
        .add_startup_stage("player_setup", SystemStage::single(spawn_player.system())) // line to add
        .add_plugins(DefaultPlugins)
        .run();
Enter fullscreen mode Exit fullscreen mode

Now let's run cargo run. The window will open and we should see: well, nothing.

We drew a square in our Bevy scene, but it is not yet visible since we did not add a camera to the scene...

Adding a Camera

To add a camera, we will create a new setup system.

fn setup(mut commands: Commands) {
    commands.spawn_bundle(OrthographicCameraBundle::new_2d());
}
Enter fullscreen mode Exit fullscreen mode

and register it with our application:

// ...
+ .add_startup_system(setup.system())
.add_startup_stage("player_setup", SystemStage::single(spawn_player.system()))
// ...
Enter fullscreen mode Exit fullscreen mode

This new system adds an orthographic camera to the scene. There are 2 main types of camera that you need to know about when making computer games: orthographic and perspective.

Perspective camera are mostly used in 3D games, when we need to simulate a real world view. This type of camera is more representative of the way we see things in real life.

Orthographic cameras are used when we don't need a concept of depth. An orthographic camera does not modify the size of our objects as they are moved further from our point of view (camera). In our case, we are building a 2D platformer, so this is what we are going to use.

Let's run cargo run:

🥳🥳🥳
Image description
🥳🥳🥳

Camera Adjustments

If the window is resized, the visible area gets bigger... In a usual platformer, the width of the visible area rarely changes. The camera will follow our player, but the visible width of our screen should remain constant.

Looking at the code of OrthographicCameraBundle::new_2d() (the source, not our code), we can see that it adds a resource called OrthographicProjection which has a parameter called scaling_mode. This parameter is an enum that can take 4 different values. The one we are interested in is the FixedHorizontal option.

Let's try it:

// src/camera.rs
use bevy::{prelude::OrthographicCameraBundle, render::camera::{OrthographicProjection, DepthCalculation, ScalingMode}, math::Vec3};

pub fn new_camera_2d() -> OrthographicCameraBundle {
  let far = 1000.0;
  let mut camera = OrthographicCameraBundle::new_2d();
  camera.orthographic_projection = OrthographicProjection {
    far,
    depth_calculation: DepthCalculation::ZDifference,
    scaling_mode: ScalingMode::FixedHorizontal,
    ..Default::default()
  };
  camera.transform.scale = Vec3::new(10., 10., 1.);
  return camera;
}
Enter fullscreen mode Exit fullscreen mode

The line camera.transform.scale = Vec3::new(10., 10., 1.); is to scale up our camera, otherwise, the visible area goes from -1 to +1 on the y and x axis in our world's units.

// src/main.rs
mod camera;
pub use camera::*;
// ...
+ commands.spawn_bundle(new_camera_2d()); 
- commands.spawn_bundle(OrthographicCameraBundle::new_2d());
Enter fullscreen mode Exit fullscreen mode

Image description

It works, but our player is way too huge. Let's scale it down:

// src/main.rs spawn_player method
.spawn_bundle(SpriteBundle {
    material: materials.add(Color::rgb(0.7, 0.7, 0.7).into()),
    sprite: Sprite::new(Vec2::new(1.0, 1.0)),
    ..Default::default()
})
Enter fullscreen mode Exit fullscreen mode

🥳🥳🥳
Image description
🥳🥳🥳

It's all for today, in the next part, we will discuss physics.

All code available here.

Part 1:

Discussion (0)