Almost every game needs a camera. Comfy recognizes this and provides a reasonable default main camera implementation.
TL;DR: [0, 0]
is in the center of the screen, y
goes up, world units
independent of pixels, default zoom level is 30
.
This means that if your game draws a rectangular tile with size 1
, you should
fit roughly 30
tiles on the screen horizontally. While this is opinionated,
we found it to be a very useful default across our games.
If you're making a pixel art game with 16x16
sprites, you would use
draw_sprite
and specify size of 1
. This means your base sprite size is "one
world unit". A small side-benefit is that if your art style changes and you now
have 32x32
pixel sprites, all of your code still works. A sprite with size = 1
will render with the same world size independent of your texture's
dimensions.
This becomes especially useful for games with multiple resolutions, e.g. non-pixel art games that utilize a lot of asset packs with inconsistent resolution, where one does not have to worry about resizing things just to try out new textures/sprites.
The camera is controlled by a MainCamera
, and as a pragmatic limitation comfy
has one global main camera which can be accessed via the main_camera()
function (or main_camera_mut()
), which can be used to change where the camera
points to, or a zoom level, simply as follows:
let mut camera = main_camera_mut();
camera.position = vec2(5.0, 0.0);
camera.zoom = 50.0;
Allowing for a multi-camera setup is something that is on comfy's roadmap, but again it's not something we needed for our own games, and as such it hasn't been a priority.
If you want to move the camera to a new location and then move it back, you can
use .push_center
and .pop_center
which will store the current values on a
stack and later restore them. Since this uses a stack you can push as many
values you want.
Comfy also provides simple utility functions for converting between world and
screen space, namely world_to_screen
and screen_to_world
. These functions
are useful for things like mouse picking, or drawing UI elements without egui
.
As our camera is global, these functions are also global and can be called simply as
let mouse_world = screen_to_world(mouse_position());
let player_screen = world_to_screen(...);
Note that here we use another global function, mouse_position
, which returns
the screen-space mouse position. We also have a helper mouse_world()
which
already returns the world-space mouse position as would be converted by the
above code.
To get the current screen size in pixels or world units:
let screen_width_pixels = screen_width(); // for example: 1800
let screen_height_pixels = screen_height(); // for example: 920
let visible_world_size = main_camera().world_viewport(); // for example: 30.0 x 16.8