mike chambers | about

Understanding the 2D Coordinate System in Bevy

Saturday, October 29, 2022

I have started to play around with learning some game development using the Rust based Bevy game engine. One of the first things I wanted to make sure I understand is the coordinate system used by the engine. However, it was a little different than what I am used to (web and Flash), and took me a bit to figure out. This post gives a quick overview and summary of the 2D coordinate system used in Bevy.

If you come from a web or Flash background, then you should be familiar with an x, y coordinate system with 0,0 based in the top left of the screen (with all on screen coordinates being positive). However, Bevy uses a different coordinate system, with the origin (0,0) set at the center of the window. This means that absolute coordinates on the screen may have both positive and negative values, and that you may need to adjust coordinates based on window dimensions.

Here is a graphic which shows the Bevy 2D coordinate system, and how to calculate specific points.

Note that 0,0 is at the center of the screen with x and y increasing to the right and up respectively. In order to find the corners of the screen, you need to calculate them by using the window’s dimension properties.

Below is some example code which places squares at the center, and each corner of the window. Note that depending on the Anchor location you use for your sprite, you may need to adjust the position of the Sprite (to offset the origin). In the example below, I set the Anchor point to the TOP LEFT of the Sprite (since that is what I am used to).

use bevy::prelude::*;

const MARKER_SIDE_LENGTH:f32 = 10.0;

fn main() {

    App::new()
        //Insert a WindowDescriptor to set initial window size and to be
        //able to retrieve its value later on.
        //Note it has to be set before call to add_plugins(DefaultPlugins)
        .insert_resource(
            WindowDescriptor {
                width: 400.0,
                height: 400.0,
                title: "Coordinate Example".to_string(),
                ..default()
            }
        )
        .insert_resource(ClearColor(Color::ANTIQUE_WHITE))
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .run();
}

//Bundle to make it a bit easier to set and position markers on the screen
#[derive(Bundle)]
struct MarkerBundle {
    #[bundle]
    sprite_bundle:SpriteBundle
}

//takes a transform specifying its position, and color of the sprite / marker
impl MarkerBundle {
    fn new(transform:Transform, color:Color) -> Self {
        Self {
            sprite_bundle: SpriteBundle {
                sprite:Sprite { 
                    color,

                    //widht, height
                    custom_size:Some(
                        Vec2::new(MARKER_SIDE_LENGTH, MARKER_SIDE_LENGTH)
                    ), 
                    anchor: bevy::sprite::Anchor::TopLeft,
                    ..default()
                },
                transform,
                ..default()
            }
        }

    }
}

fn setup(mut commands:Commands, window:Res<WindowDescriptor>) {

    commands.spawn_bundle(Camera2dBundle::default());

    //spawn a bunch of sprites / markers in the center and corners of the window

    //CENTER
    commands.spawn_bundle(
        MarkerBundle::new(
            Transform::from_xyz(0.0, 0.0, 0.0),
            Color::BLUE
        )
    );


    //TOP LEFT
    commands.spawn_bundle(
        MarkerBundle::new(
            Transform::from_xyz(
                window.width / -2.0, 
                window.height / 2.0, 
                0.0
            ),
            Color::GREEN
        )
    );

    //BOTTOM LEFT
    commands.spawn_bundle(
        MarkerBundle::new(
            Transform::from_xyz(
                window.width / -2.0,
                window.height / -2.0 + MARKER_SIDE_LENGTH,
                0.0
            ),
            Color::RED
        )
    );

    //TOP RIGHT
    commands.spawn_bundle(
        MarkerBundle::new(
            Transform::from_xyz(
                window.width / 2.0 - MARKER_SIDE_LENGTH,
                window.height / 2.0,
                0.0
            ),
            Color::ORANGE
        )
    );

    //BOTTOM RIGHT
    commands.spawn_bundle(
        MarkerBundle::new(
            Transform::from_xyz(
                window.width / 2.0 - MARKER_SIDE_LENGTH,
                window.height / -2.0 + MARKER_SIDE_LENGTH,
                0.0
            ),
            Color::PURPLE
        )
    );
}

This outputs:

You can grab the code from here

Couple of things to keep in mind:

You can find some more discussion on the Bevy coordinate system in the Unofficial Bevy Cheat Book.

twitter github flickr behance