GM Version: GMS1, GMS2
Target Platform: All
Download: https://github.com/YAL-GameMaker/pixel-perfect-smooth-camera
Links: https://yal.cc/gamemaker-smooth-pixel-perfect-camera/
Summary:
This is a mini-tutorial and an explanation of an approach that allows you to have fluid sub-pixel movements with pixel-perfect cameras in GameMaker!
Target Platform: All
Download: https://github.com/YAL-GameMaker/pixel-perfect-smooth-camera
Links: https://yal.cc/gamemaker-smooth-pixel-perfect-camera/
Summary:
This is a mini-tutorial and an explanation of an approach that allows you to have fluid sub-pixel movements with pixel-perfect cameras in GameMaker!
For this we'll be rendering a view into a surface.
Although it is possible to draw the application_surface directly, adjusting its size can have side effects on aspect ratio and other calculations, so it is easier not to.
Create
Since application_surface will not be visible anyway, we might as well disable it. This is also where we adjust the view dimensions to include one extra pixel.
End Step
The view itself will be kept at integer coordinates to prevent entities with fractional coordinates from "wobbling" as the view moves along.
This is also where we make sure that the view surface exists and is bound to the view.
(camera object marks the view's top-left corner here)
Draw GUI Begin
We draw a screen-sized portion of the surface based on fractions of the view coordinates:
the earlier call to display_set_gui_size ensures that it fits the game window.
Cleanup
Finally, we remove the surface once we're done using it.
In GMS1, you'd want to use Destroy and Room End events instead.
Although it is possible to draw the application_surface directly, adjusting its size can have side effects on aspect ratio and other calculations, so it is easier not to.
Create
Since application_surface will not be visible anyway, we might as well disable it. This is also where we adjust the view dimensions to include one extra pixel.
GML:
application_surface_enable(false);
// game_width, game_height are your base resolution (ideally constants)
game_width = camera_get_view_width(view_camera[0]);
game_height = camera_get_view_height(view_camera[0]);
// in GMS1, set view_wview and view_hview instead
camera_set_view_size(view_camera[0], game_width + 1, game_height + 1);
display_set_gui_size(game_width, game_height);
view_surf = -1;
The view itself will be kept at integer coordinates to prevent entities with fractional coordinates from "wobbling" as the view moves along.
This is also where we make sure that the view surface exists and is bound to the view.
GML:
// in GMS1, set view_xview and view_yview instead
camera_set_view_pos(view_camera[0], floor(x), floor(y));
if (!surface_exists(view_surf)) {
view_surf = surface_create(game_width + 1, game_height + 1);
}
view_surface_id[0] = view_surf;
Draw GUI Begin
We draw a screen-sized portion of the surface based on fractions of the view coordinates:
GML:
if (surface_exists(view_surf)) {
draw_surface_part(view_surf, frac(x), frac(y), game_width, game_height, 0, 0);
// or draw_surface(view_surf, -frac(x), -frac(y));
}
Cleanup
Finally, we remove the surface once we're done using it.
GML:
if (surface_exists(view_surf)) {
surface_free(view_surf);
view_surf = -1;
}