## 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))
.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:

• If the Window dimensions change, you may need to recalculate your coordinates especially corners. The example above handles that.
• This applies to 2D coordinates. If you are using UI, the 0,0 is at the bottom left of the screen (Im not yet sure how 3D coordinate work).
• When setting coordinates, you can use the z property in Vec3 to specific the z-index / layering (with higher numbers being on top)
• Keep in mind where the Anchor for your item is set, as it will impact where you need to place your items.

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