• Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

Discussion [GUIDE] Meet the New Camera System

MUHAHA! I WILL TAKE OVER THE FORUM WITH ALL MY GUIDES!

I've seen a lot of questions regarding cameras, so I'm aiming to make this into the definitive guide for using them! Hence, if I've got something wrong or there are better ways of doing something, tell me and I'll update it!
Again, if and when the forum has a category for GMS2 tutorials, I shall request this topic be moved, but for now, here it is.

I AM NO LONGER UPDATING THIS PAGE. FURTHER UPDATES OF THIS GUIDE CAN BE FOUND ON MY WEBSITE


Updated Guide Here: https://maddestudiosgames.com/gms2-meet-the-camera-system/

The website features full syntax highlighting and documentation links too, to make guide-writing much easier.

***NOTE: THIS GUIDE IS FOR THE BETA VERSION OF GAMEMAKER STUDIO 2. AS THE BETA CHANGES/UPDATES, SO MAY THIS GUIDE***

Luckily, I will leave a (dd/mm/yy) formatted date here, so you know when it was last updated: 22/11/16 (Room views are now Viewports under "Viewports and Cameras" - Images will update soon)
I'll also update the guide if there a better methods that I haven't thought of/found out about yet, or if it turns out I'm using some function totally inappropriately.

Okay, getting on with it! I'm using the forum template now! I'll update the other guide soon too, I just want to be ready for when dedicated GMS2 tutorial forums are up!

GM Version: IDE: 2.0.2.44, RUNTIME: 2.0.1.27
Target Platform: ALL
Download
: Built Project
Links: N/A

Summary:

This guide aims to give you a basic insight into the new camera system, and using it with views. Since cameras are totally new, I'm going to write this from the PoV of someone very new to views, but also understands basic GML.

The included project, which uses information from all of these guides, looks like this:



Not spectacular, but demostrates camera creation + assigning, view moving, zooming and interpolation.

The example controls for the project are simply "click to focus" and "scroll to zoom"

Relevant Documentation Links:

Tutorial:

So firstly, I'm going to point someting out - you don't actually need a camera to have a view - the room editor still has views which work in very much the same way as before. However, they are now far less flexible - you can no longer change view_[x,y,h,w]view through code anymore - unless you set up a camera to do more advanced tweaking.
If you are strictly no-code, then you will have to use the views with the room editor anyway, as there are no Drag&Drop camera functions currently.

Important things to know about this guide
  • I will be referring to "GameMaker Studio 2" as "GMS2" throughout the guide, because I am too lazy to keep writing it in full.
  • I'll be going through cameras in GML - There are no D&D camera functions - so it helps if you understand code
  • If you want to check out a function, or something is confusing you, remember to check the manual!
  • If you have any other questions or corrections, please let me know!

I recommend that you get @YellowAfterlife's plugin for forum code syntax highlighting. It makes reading code here way easier: https://yellowafterlife.itch.io/syntax-highlighting-for-gamemaker-forum

Here are the things we'll cover in this guide:
  1. Setting up a view (to use with cameras)
  2. Creating and deleting a Camera and Assigning cameras to views
  3. Setting a camera to follow an object through code (using the standard object tracking system)
  4. The other way to draw a camera without binding it to a view
  5. Camera scripts - update, begin and end
  6. Simple camera position interpolation
  7. Simple Camera Zooming
  8. Clamping the camera to the room bounds
  9. Other camera functions and what they do
Setting up a view (to use with cameras)
Okay, you may want to skip this one, depending on whether or not you already know this stuff. This is literally just going to be enabling a view in a room either through the editor, or through code.

There are two ways you can set up views in your game. One is to use the view tools in the room editor, the second is to set them up with code.
I personally prefer the latter, as it means you do not have to set up views in every single room. You can just run the same code on room start, and it's done. However, the code version will not resize the game window based on viewport, where as the room editor set up will, assuming it is the first room in the game - sometimes referred to as the "boot room". If you need to change window size later for whatever reason, there are "window_*" functions for dealing with this.

For both methods, we'll be setting up view 0 with a view port and size of 640x480, as a throwback to pre-GM:S.

Here's how to set up a view in the room editor
First, open up your room. Conveniently, GMS2 now starts with a room already created, named room0. This is located in the resource tree. You can either double click the room, or drag it into the workspace to open it.


Now, navigate to the "Viewports and Cameras". By default, this is located at the bottom left of the room editor. Click to open the settings up:


Next, you'll need to check "Enable Viewports"


Open up the settings for "Viewport 0", and edit the settings to appear as follows:


Okay, done! Basic view set up.

Note that we've not changed view borders, view speed or "Object Following". Even though the focus here is on cameras, I'm going to quickly explain what they are and what they do:
The "Object Following" makes the camera follow a chosen object.
The view border is how close the object needs to be to the bounds of the view (in pixels) before the camera follows it.
View speed is how fast the camera will follow the object when it moves past the border. Setting speeds of -1 makes the view move instantly to contain the object.

Here's how to set up a view through code
This code is best fitted to go in the "Room Start" event. You could probably put it in room creation code, however you can write the code only once in a persistent instance this way, saving a little time. (Although, using the new room parenting system, creating default views in the editor is now easier then before)
Instead of writing a whole paragraph here, I'm just going to show you the commented code:
Code:
//Enable the use of views
view_enabled = true;

//Make view 0 visible
view_set_visible(0, true);

//Set the port bounds of view 0 to 640x480
view_set_wport(0, 640);
view_set_hport(0, 480);
Setting these bounds here will not automatically resize the game window to fit - even in a boot room. The boot room must be (or have an enabled view of) the size you wish your game window to be to do this automatically.
You can still manualy resize the window to fit, using the "window_set" functions. However, calling "window_set_size()" and "window_center()" in the same event doesn't work, as the window only resizes AFTER the current event.
If you wish to resize the game window and center it all at once, add this quick snippet:
Code:
//Resize and center
window_set_rectangle((display_get_width() - view_wport[0]) * 0.5, (display_get_height() - view_hport[0]) * 0.5, view_wport[0], view_hport[0]);
surface_resize(application_surface,view_wport[0],view_hport[0]);
This will resize the window to the view port of view 0, and resize the "application_surface" to this size too.

We haven't actually set a view size, location, target object, border or speed through code - this is because we have to do it with cameras. This is explained in the next part.

From this point on, it's all code!

Creating and deleting a Camera and Assigning cameras to views
If you are used to views, cameras are going to be a little different. Instead of thinking that they are "just there" and usable (like with views), it's useful to understand that they are now more like instances - you have to create a camera, assign information to it, and delete it when you are done with it.

This section also shows you how to set view parameters through code, since they are now managed by cameras.

When creating a camera, there are two functions:
"camera_create()" and "camera_create_view()"
Both functions return a unique camera ID to be used with all other camera related functions.
"camera_create()" creates a camera that is effectively a blank canvas - you need to specify location and size manually before it can be used properly in 2D - I say this because you can use it in 3D without specifying these.
"camera_create_view()" is much better in regards to 2D development as it forces you to specify all the view parameters on creation.

We will be using "camera_create_view()" for the purpose of this guide, as we're working in the context of 2D. If you want to see basic use of "camera_create()" with 3D, check out my guide on "Getting Started with 3D in GMS2".

Here's how to create the camera. Again, 640x480 size, position will be 0, and we'll set the object following parameters to the default:
Code:
//Camera creation

//Build a camera at (0,0), with size 640x480, 0 degrees of angle, no target instance, instant-jupming hspeed and vspeed, with a 32 pixel border
camera = camera_create_view(0, 0, 640, 480, 0, -1, -1, -1, 32, 32);
If you read the above, you'll see that we can also define an "angle" on view creation - this partially replaces view_angle[0..7]. Other view angle functions are "camera_get_view_angle" and "camera_set_view_angle".

So, that covers camera creation. To destroy a camera you are finished using, use:
Code:
camera_destroy(camera);
Alright, so that's creating and destroying cameras covered.
Now we need to look at binding a camera to a view. This is done rather simply with this little bit of code:
Code:
//Set view0 to use the camera "camera"
view_set_camera(0, camera);
From here on, when wanting to update a view, we should now reference a bound camera instance with "view_camera[view_index]" (in this case, "view_camera[0]". This is simply because we want to guarantee that the camera we are updating is the camera assigned to the view, which may not always be the instance that was returned by "camera_create"

**I have observed cameras working when using "view_camera[X] = camera". While this works, I still feel that it is probably safer and more reliable to use "view_set_camera()"**

We don't actually have to bind a camera to a view to use it - I will cover how that works shortly - but binding makes life a lot easier as it grants access to automatic camera "update/begin/end" scripts, which I will also be covering.

You can get the ID of camera a view is using with both "view_camera[0..7]" and "view_get_camera(view)".

Setting a camera to follow an object through code (using the standard object tracking system)
If you set up a view through the room editor, you don't even have to worry about this. However, if you want to set up the view to follow an object through code, you have 2 options:
The first is to set up the standard parameters in "camera_create_view()" to follow an object. e.g
{code]
//Build a camera at (0,0), with size 640x480, 0 degrees of angle, targeting instance "objPlayer", instant-jupming hspeed and vspeed, with a 32 pixel border
camera = camera_create_view(0, 0, 640, 480, 0, objPlayer, -1, -1, 32, 32);
[/code]

Your next option is to build each parameter yourself - this also allows changing the fields on the fly e.g. increasing border size, focusing on an enemy object etc.
Setting up to be exactly the same as above looks like this:
Code:
//Basic set up
camera_set_view_pos(view_camera[0], 0, 0);
camera_set_view_size(view_camera[0], 640, 480);

//Setting up object target information
camera_set_view_target(objPlayer);
camera_set_view_speed(view_camera[0], -1, -1);
camera_set_view_border(view_camera[0], 32, 32);
And that's it! All you need to do to set up a camera to act like the default views when following an instance.

The other way to draw a camera without binding it to a view
If, for whatever reason, you do not want your camera applied to a view (or want to use it without even creating a view), we have the "camera_apply" function.
The code is just this:
Code:
//Apply camera settings for drawing
camera_apply(camera);
If you want the camera to be applied only to certain views (without binding) you just need to wrap it in a "view_current" test, like so:
Code:
if(view_current == 0)
{
    camera_apply(camera);
    ...
}
...
After applying, jsut run the draw code as normal.

To reset a camera to default after applying, just call this:
Code:
camera_apply(camera_get_default());
This resets the camera view to the GameMaker default camera - the one it uses when no views are enabled. Note that you can set a default camera with "camera_set_default", though I can't think of (nor have I tested) a reasonable usage case yet.

The one major issue with using "camera_apply" over binding a camera to a view is that it does not automatically call camera "update/begin/end" scripts - which are incredibly useful, for reasons I will explain soon. Though, if you made your scripts to suit the apply method, you could call them separately anway

Camera scripts - update, begin and end
Okay, we're finally on to one of may favorite things about the new camera functions - scripts!
There are three types of camera script - "update", "begin" and "end". By looking at the manual, we get this information:

The cameras for all visible and active view ports have their update script called

Then for each individual view:
The begin script for the camera for that view is called
The draw events are executed for that view (includes draw begin and draw end)
The end script for the camera is called

So, what's nice about this? Well, first of all, the scripts are only called for visible cameras assigned to views - this means you can put code in these scripts, and it won't waste time running if the view isn't being drawn - this is good when skipping frames. You also no longer have to have a High-Depth object run setup code in draw begin either.

These scripts only seem to run automatically for cameras that are bound to views, which makes a lot of sense. If your script isn't runnning, check that it is properly bound!

Okay, these functions all work and are used in similar ways - you can tell WHEN they work from the above. In this case, I assign a script to the "begin" function of our camera for view 0.
The script, which is called "update_script", contains the following - a simple, yet very bad screenshake implementation:
Code:
//Shake the camera assigned to the current view
camera_set_view_pos(view_camera[view_current], random_range(-6,6), random_range(-6,6));
We use "view_camera[view_current]" to make sure we can bind this script to any view camera, and it will just work.

So, that's the function. now let's bind it to a view. We just use this code to bind to "camera_begin":
Code:
//Bind the bad screenshake script "update_script" to the desired cameras "begin"
camera_set_begin_script(view_camera[target_view],update_script);
In order to bind to the other camera events, we can just use one of these two:
Code:
camera_set_end_script(view_camera[target_view],update_script);
camera_set_update_script(view_camera[target_view],update_script);
NOTE: As of writing, there is no way to retrieve which camera is running the current update script from within the script (just use "view_camera[view_current]" or "camera_get_active()" to identify in "begin" and "end" scripts), unless you set it up all the variable connections rigidly. I've submitted a bug report, but I don't really know if it'll be considered a bug (since drawing hasn't actually started)

To clear a script from a camera, just run the functions as normal, but use "-1" for the script argument instead.

Each of these script setting functions has a "getter" equivelant, which returns what script is currently bound to a camera.

Another Note: You cannot perform any drawing within camera scripts. This stuff must still be done in the draw event (or onto surfaces)

That just about covers the camera script feature.

Simple camera position interpolation
For this bit, we're just going to make a camera interpolate from its current position, to center wherever the user clicks. This code can be easily adapted e.g. for tracking objects, but this is a nice, easy thing to start off with.

Things we'll assume here - you've already created a 2D camera, and it is assigned to view 0. If you don't know how to do this, check the guides above.

Firts, let's initialize two variables in the create event:
Code:
click_x = 0;
click_y = 0;
these will be used to store the last position the mouse was clicked at. Because of this, when we first launch the project it will center the camera on the origin, as I have set these values to "0".

The next bit of code lies entirely in the step event (of the same object)
Code:
//Check if the mouse is clicked. If so, update the click position.
if(mouse_check_button_pressed(mb_left))
{
    click_x = mouse_x;
    click_y = mouse_y;
}

//Get target view position and size. size is halved so the view will focus around its center
var vpos_x = camera_get_view_x(view_camera[0]);
var vpos_y = camera_get_view_y(view_camera[0]);
var vpos_w = camera_get_view_width(view_camera[0]) * 0.5;
var vpos_h = camera_get_view_height(view_camera[0]) * 0.5;

//The interpolation rate
var rate = 0.2;

//Interpolate the view position to the new, relative position.
var new_x = lerp(vpos_x, click_x - vpos_w, rate);
var new_y = lerp(vpos_y, click_y - vpos_h, rate);

//Update the view position
camera_set_view_pos(view_camera[0], new_x, new_y);
As a summary - this code checks if the left mouse button is pressed. If it is, it updates the "clicked position".
Then, regardless of anything else, the view is interpolated to ceter around the last position the mouse was pressed.

How is this done?
-We retrieve the current (top-left) position and size of the view.
-We half the view size
-We establish the variable "rate", which specifies the rate of interpolation (it makes changin it later easier)
-We determine the new position of the view by lerping the current position to the click position. The halved width and heights of the view are removed from the click position, making the view focus it intothe center
-The new coordinates are set to the view, ending the process

And that's a simple view interpolation demo!

Camera Zooming
Camera zooming works similarly to before - you change your view width and height, but maintain port size. The only difference is that now you need a camera and to use some functions.

Things we'll assume here - you've already created a 2D camera, and it is assigned to view 0. If you don't know how to do this, check the guides above.

To get started, lets just define zoom variables in the create event (after a camera has been created:
Code:
zoom_level = 1;
//Get the starting view size to be used for interpolation later
default_zoom_width = camera_get_view_width(view_camera[0]);
default_zoom_height = camera_get_view_height(view_camera[0]);
You'll then need this code in the step event (of the same object)
Code:
//Move the zoom level based on mouse scrolling. Clamp the value so stuff doesn't get too silly.
zoom_level = clamp(zoom_level + (((mouse_wheel_down() - mouse_wheel_up())) * 0.1), 0.1, 5);

//Get current size
var view_w = camera_get_view_width(view_camera[0]);
var view_h = camera_get_view_height(view_camera[0]);

//Set the rate of interpolation
var rate = 0.2;

//Get new sizes by interpolating current and target zoomed size
var new_w = lerp(view_w, zoom_level * default_zoom_width, rate);
var new_h = lerp(view_h, zoom_level * default_zoom_height, rate);

//Apply the new size
camera_set_view_size(view_camera[0], new_w, new_h);
What does this do? Let's break it down:
-Adjust the zoom level, based on how the mouse is currently scrolling - mouse up zooms in while mouse down zooms out. The zoom level is also clamp to prevent over-zooming
-Get the current size of the view
-Set to interpolation rate - makes it easier to change later
-Figure out what the new sizes should by interpolating the current view size to the original size, multiplied by the zoom level
-Update the view size

And that's how that works.
Remember though, the view size changes from the upper-left corner, not the center. If you want the view to stay centered, you'll need this code:
Code:
//Get the shift necessary to re-align the view.
var shift_x = camera_get_view_x(view_camera[0]) - (new_w - view_w) * 0.5;
var shift_y = camera_get_view_y(view_camera[0]) - (new_h - view_h) * 0.5;

//Update the view position
camera_set_view_pos(view_camera[0],shift_x, shift_y);
This uses the difference in view width and height in order to determine how to translate the view to re-center it, and then applies the new position

That wraps everything up for that!

Clamping a Camera to the Room Bounds
I was asked about this on Twitter, so I thought it might be worth me quickly adding this little snippet of code:
Code:
camera_set_view_pos(Camera_ID,
                    clamp( camera_get_view_x(Camera_ID), 0, room_width - camera_get_view_width(Camera_ID) ),
                    clamp( camera_get_view_y(Camera_ID), 0, room_height - camera_get_view_height(Camera_ID) ));
This code basically gets the position of the camera with the ID stored in "Camera_ID" and locks its position between (0,0) and the room bound minus the camera size.
If you've already calculated or have the camera size and position ready, you can use those values instead of the "camera_get_view_*" functions.

Other camera functions and what they do
Well, after the rest of the guide, you should know enough about the new system to make some magic happen. Regardless, here's a few other handy functions:

-We can change viewport related stuff with view_set_[x,y,w,h]port() functions
-Views can be told to draw to a surface instead, using view_set_surface_id()
-"camera_get_active()" returns the unique ID of the currently active camera (the one that is being drawn to)
-There are two functions that use matrices - "camera_set_view_mat()" and "camera_set_proj_mat()". These are used primarily to set up 3D projections
-"camera_set_proj_mat()" takes a projection matrix, usually built with one of three matrix functions - "matrix_build_projection_ortho", "matrix_build_projection_perspective" or "matrix_build_projection_perspective_fov"
-"camera_set_view_mat()" takes a view matrix, usually built with "matrix_build_lookat"
-Fun fact - I go over basic usage of these matrix functions in my other guide "Getting Started in 3D with GMS2"
-All of the "setter" functions discussed in the guide come with "getters" too.

It is really worth me also stating that you can draw stuff relative to a camera view with the "camera_get_view_[x/y/width/height]()", though it is probably far better to draw HUD elements using the draw GUI events.

So that's that. That should be enough information to get you going with the new cameras, but if you need something more - some help, another guide with context - let me know, and I'll see what I can do!
 
Last edited:
G

GerPronouncedGrr

Guest
First of all, thank you for this, it's very helpful and well written.

One thing I'm having an issue with, however, is setting the instantiated camera to follow a moving object. So if I say

Code:
camTarget = camera_set_view_target(view_camera[0], obj_Player);
what I get is the camera snapping to centre on the player at game start, but then the player can move around freely without being followed by the camera thereafter. By contrast, if I set the view to follow the player object in the room editor, it functions as you would expect.
 
First of all, thank you for this, it's very helpful and well written.

One thing I'm having an issue with, however, is setting the instantiated camera to follow a moving object. So if I say

Code:
camTarget = camera_set_view_target(view_camera[0], obj_Player);
what I get is the camera snapping to centre on the player at game start, but then the player can move around freely without being followed by the camera thereafter. By contrast, if I set the view to follow the player object in the room editor, it functions as you would expect.
Hmm... I'm not 100% sure why it isn't working, I just tested it again myself and it seems to be working fine.
Are you calling that code after "view_set_camera"? The only case where the tracking didn't work for me was if I called that code before binding the camera.
If you are calling it after binding the camera to the view, I think we may need to see more of you code before I can help.
 
G

GerPronouncedGrr

Guest
Here is my script in its entirety:

Code:
/// @description Initial Setup

// Set & Resize View
view_enabled = true;
view_set_visible(0, true);
view_set_wport(0, 640);
view_set_hport(0, 480);

window_set_rectangle((display_get_width() - view_wport[0]) * 0.5, (display_get_height() - view_hport[0]) * 0.5, view_wport[0], view_hport[0]);
surface_resize(application_surface, view_wport[0], view_hport[0]);

// Camera Creation & Assignment
camera = view_set_camera(0, view_camera[0]);

// Camera Parameters
camPos = camera_set_view_pos(camera, 0, 0);
camSize = camera_set_view_size(camera, 640, 480);

// Follow Parameters
camTarget = camera_set_view_target(camera, obj_Player);
camSpeed = camera_set_view_speed(camera, -1, -1);
camBorder = camera_set_view_border(camera, 16, 16);
 
Here is my script in its entirety:

Code:
/// @description Initial Setup

// Set & Resize View
view_enabled = true;
view_set_visible(0, true);
view_set_wport(0, 640);
view_set_hport(0, 480);

window_set_rectangle((display_get_width() - view_wport[0]) * 0.5, (display_get_height() - view_hport[0]) * 0.5, view_wport[0], view_hport[0]);
surface_resize(application_surface, view_wport[0], view_hport[0]);

// Camera Creation & Assignment
camera = view_set_camera(0, view_camera[0]);

// Camera Parameters
camPos = camera_set_view_pos(camera, 0, 0);
camSize = camera_set_view_size(camera, 640, 480);

// Follow Parameters
camTarget = camera_set_view_target(camera, obj_Player);
camSpeed = camera_set_view_speed(camera, -1, -1);
camBorder = camera_set_view_border(camera, 16, 16);
Okay! I see the problem now!
"view_set_camera" is what is used to bind a camera to a view - you must first create the camera with either "camera_create" or "camera_create_view" (though you're specifying all the parameters later, so you don't need to use "camera_create_view"),
In your case, it should work if you just replace
Code:
// Camera Creation & Assignment
camera = view_set_camera(0, view_camera[0]);
with
Code:
// Camera Creation & Assignment
camera = camera_create();
view_set_camera(0, camera);
 
G

GerPronouncedGrr

Guest
Interestingly, this doesn't actually fix the problem. I say it's interesting because running the game after making this change gives me *exactly* the same functionality as before, which leads me to believe that manually creating the camera might not have been necessary. I mean, it seems like a good practice regardless, and I appreciate you pointing out my error, but there's something else going on here too.

One thing it could be: You say that the best place to put this is in the "Room Start" event, not in the room creation event. So, what I did was create an object, place it in the room, and run this code from the Room Start event of that object. Is that what you had intended?
 
Interestingly, this doesn't actually fix the problem. I say it's interesting because running the game after making this change gives me *exactly* the same functionality as before, which leads me to believe that manually creating the camera might not have been necessary. I mean, it seems like a good practice regardless, and I appreciate you pointing out my error, but there's something else going on here too.

One thing it could be: You say that the best place to put this is in the "Room Start" event, not in the room creation event. So, what I did was create an object, place it in the room, and run this code from the Room Start event of that object. Is that what you had intended?
I definitely intended for it to go in room start, rather than room creation (though, to be honest, initializing all these variables is usually done in create)

I just did a quick test in a fresh project (Copy&Pasted your code straight into room start, and only updated the camera creation and added rudimentary movement)
Code:
// Set & Resize View
view_enabled = true;
view_set_visible(0, true);
view_set_wport(0, 640);
view_set_hport(0, 480);

window_set_rectangle((display_get_width() - view_wport[0]) * 0.5, (display_get_height() - view_hport[0]) * 0.5, view_wport[0], view_hport[0]);
surface_resize(application_surface, view_wport[0], view_hport[0]);

// Camera Creation & Assignment
camera = camera_create();
view_set_camera(0, camera);
// Camera Parameters
camPos = camera_set_view_pos(camera, 0, 0);
camSize = camera_set_view_size(camera, 640, 480);
// Follow Parameters
camTarget = camera_set_view_target(camera, obj_Player);
camSpeed = camera_set_view_speed(camera, -1, -1);
camBorder = camera_set_view_border(camera, 16, 16);
...and it works fine. Still only worked after using "camera_create". Object is just named "obj_Player" and sets its position to the mouse
cameraTestagain.gif
I'm really not sure why it's not working for you at this point, seems really weird

I have to sleep now, but I can continue trying to troubleshoot this with you tomorrow if you need me
EDIT: oops, I pasted your code instead of the updated code. Fixed. Also, I'm awake now
 
Last edited:

ThewDev

Member
Wow, thank you for taking the time to write this guide! I've been having a bit of trouble getting used to the camera system, and this is just what I needed. Great job!
 
L

Leone_Kaiser

Guest
Sweet! Really nice read. Question: How would I keep object "A" from moving outside the viewport if the viewport is currently following object "B"?

EDIT:
nvm, figured it out!
 
Last edited by a moderator:
@MaddeMichael
Have you tried to dynamically change the camera view angle, like binding it on the left and right button ? I tried in your project and in the Arena Shooter demo and the behavior was unexpected, like I described it here.
Have you any idea ? I suspect the way GMS2 handled tiles now to be the issue.
 
@MaddeMichael
Have you tried to dynamically change the camera view angle, like binding it on the left and right button ? I tried in your project and in the Arena Shooter demo and the behavior was unexpected, like I described it here.
Have you any idea ? I suspect the way GMS2 handled tiles now to be the issue.
I haven't personally tried this yet, but I had a look at your post and it seems like a bug with the new tile system, so it's not really anything I can directly help with. It probably is to do with GM not rendering tiles outside the view, but it doesn't correctly account for camera view angle.
 
Hi there,

Great camera guide but i have a question that i don't think was shown here, i'm still new so i could of just missed it haha. I want to move the camera or view (not sure which i should be doing) to move with WASD, i have tried using something like if keyboard_check (ord("D")) view_xport += 5 and that moved the view i think but caused my background layer to smudge across the screen so wasn't working right lol...... i basicly want to move across my room with the camera using WASD no player object in my game. Hope this makes some sense.


EDIT just to give a little more info i have the camera/view set using the built in settings not through code

Thx
 
Hi there,

Great camera guide but i have a question that i don't think was shown here, i'm still new so i could of just missed it haha. I want to move the camera or view (not sure which i should be doing) to move with WASD, i have tried using something like if keyboard_check (ord("D")) view_xport += 5 and that moved the view i think but caused my background layer to smudge across the screen so wasn't working right lol...... i basicly want to move across my room with the camera using WASD no player object in my game. Hope this makes some sense.


EDIT just to give a little more info i have the camera/view set using the built in settings not through code

Thx
Okay, first problem is that you tried setting view_xport - the port operations basically effect where and how big the view is rendered to the actual window - this is probably why your background was smudging - although, if you don't have a solid colored background (no transparency) as the deepest layer, you'll get smudging anyway as the display buffer isn't automatically cleared.
The functions you need to be using are "camera_set_view_pos" for position and "camera_get_view_x/y" for getting the current positions.
I think the built in room stuff still creates a camera, so this should technically work:
Code:
var view_speed = 5;

var current_cam = view_camera[0];//Change 0 to the view port ID if necessary
var current_view_x = camera_get_view_x(current_cam);
var current_view_y = camera_get_view_y(current_cam);

//Here's a lazy trick I like to use for getting directions from key presses
var L_R = keyboard_check(ord("D")) - keyboard_check(ord("A"));
var U_D = keyboard_check(ord("S")) - keyboard_check(ord("W"));

//Only update if necessary (not sure if this yields much of a performance improvement, but whatever)
if(L_R != 0 || U_D != 0)
{
camera_view_set_pos(current_bird, current_view_x + (L_R * view_speed), current_view_y + (U_D * view_speed));
}
Untested, but I'm pretty confident that that should work.

Then, if you want to lock the camera within the view bounds:
Code:
 camera_set_view_pos(Camera_ID,
                     clamp( camera_get_view_x(Camera_ID), 0, room_width - camera_get_view_width(Camera_ID) ),
                     clamp( camera_get_view_y(Camera_ID), 0, room_height - camera_get_view_height(Camera_ID) ));
This code basically gets the position of the camera with the ID stored in "Camera_ID" and locks its position between (0,0) and the room bound minus the camera size.
If you've already calculated or have the camera size and position ready, you can use those values instead of the "camera_get_view_*" functions.
 
@MaddeMichael Since you were so kind to help me out with my first problem here's another i'm confused about......Using your code above for zooming how do i set a min/max zoom? currently i can zoom in and out forever.

Thanks
 
@MaddeMichael Since you were so kind to help me out with my first problem here's another i'm confused about......Using your code above for zooming how do i set a min/max zoom? currently i can zoom in and out forever.

Thanks
As far as the code is oncerned, this bit should deal with that:
Code:
 //Move the zoom level based on mouse scrolling. Clamp the value so stuff doesn't get too silly.
zoom_level = clamp(zoom_level + (((mouse_wheel_down() - mouse_wheel_up())) * 0.1), 0.1, 5);
This bit is already shown in my guide and it seems to work in testing, the only thing you may have to tweak is the last two numbers - "0.1" and "5" as these are the limits to the view zoom multiplier. e.g., if you change 0.1 to 0.5 the maximum zoom is x2 and changing the 5 to 2 would make the minimum zoom x0.5.
 
@MaddeMichael Hmmm didn't seem to do anything even after changing the numbers like you said here's my code and Viewport [0] settings


Viewport [0]

Camera Properties
xpos = 0 ypos = 0
width = 640 height = 480

Viewport Properties
xpos = 0 ypos = 0
width = 1920 height = 1080

Horiz border 32
Vert border 32
Horz speed -1
Vert speed -1


This code is in step event of Camera object
Code:
var view_speed = 15;


var current_cam = view_camera[0];
var current_view_x = camera_get_view_x(current_cam);
var current_view_y = camera_get_view_y(current_cam);


zoom_level = 1;
//Get the starting view size to be used for interpolation later
default_zoom_width = camera_get_view_width(view_camera[0]);
default_zoom_height = camera_get_view_height(view_camera[0]);



//Here's a lazy trick I like to use for getting directions from key presses
var L_R = keyboard_check(ord("D")) - keyboard_check(ord("A"));
var U_D = keyboard_check(ord("S")) - keyboard_check(ord("W"));

//Only update if necessary (not sure if this yields much of a performance improvement, but whatever)
if(L_R != 0 || U_D != 0)
{
camera_set_view_pos(current_cam, current_view_x + (L_R * view_speed), current_view_y + (U_D * view_speed));
}

//lock the camera within the view bounds:
camera_set_view_pos(current_cam,
                     clamp( camera_get_view_x(current_cam), 0, room_width - camera_get_view_width(current_cam) ),
                     clamp( camera_get_view_y(current_cam), 0, room_height - camera_get_view_height(current_cam) ));
                    
                    
                    
                
//Move the zoom level based on mouse scrolling. Clamp the value so stuff doesn't get too silly.
zoom_level = clamp(zoom_level + (((mouse_wheel_down() - mouse_wheel_up())) * 0.1), 0.1, 2);

//Get current size
var view_w = camera_get_view_width(view_camera[0]);
var view_h = camera_get_view_height(view_camera[0]);

//Set the rate of interpolation
var rate = 0.5;

//Get new sizes by interpolating current and target zoomed size
var new_w = lerp(view_w, zoom_level * default_zoom_width, rate);
var new_h = lerp(view_h, zoom_level * default_zoom_height, rate);

//Apply the new size
camera_set_view_size(view_camera[0], new_w, new_h);


//camera zoom from center.
//Get the shift necessary to re-align the view.
var shift_x = camera_get_view_x(view_camera[0]) - (new_w - view_w) * 0.5;
var shift_y = camera_get_view_y(view_camera[0]) - (new_h - view_h) * 0.5;

//Update the view position
camera_set_view_pos(view_camera[0],shift_x, shift_y);
 
@MaddeMichael Hmmm didn't seem to do anything even after changing the numbers like you said here's my code and Viewport [0] settings


Viewport [0]

Camera Properties
xpos = 0 ypos = 0
width = 640 height = 480

Viewport Properties
xpos = 0 ypos = 0
width = 1920 height = 1080

Horiz border 32
Vert border 32
Horz speed -1
Vert speed -1


This code is in step event of Camera object
Code:
var view_speed = 15;


var current_cam = view_camera[0];
var current_view_x = camera_get_view_x(current_cam);
var current_view_y = camera_get_view_y(current_cam);


zoom_level = 1;
//Get the starting view size to be used for interpolation later
default_zoom_width = camera_get_view_width(view_camera[0]);
default_zoom_height = camera_get_view_height(view_camera[0]);



//Here's a lazy trick I like to use for getting directions from key presses
var L_R = keyboard_check(ord("D")) - keyboard_check(ord("A"));
var U_D = keyboard_check(ord("S")) - keyboard_check(ord("W"));

//Only update if necessary (not sure if this yields much of a performance improvement, but whatever)
if(L_R != 0 || U_D != 0)
{
camera_set_view_pos(current_cam, current_view_x + (L_R * view_speed), current_view_y + (U_D * view_speed));
}

//lock the camera within the view bounds:
camera_set_view_pos(current_cam,
                     clamp( camera_get_view_x(current_cam), 0, room_width - camera_get_view_width(current_cam) ),
                     clamp( camera_get_view_y(current_cam), 0, room_height - camera_get_view_height(current_cam) ));
                   
                   
                   
               
//Move the zoom level based on mouse scrolling. Clamp the value so stuff doesn't get too silly.
zoom_level = clamp(zoom_level + (((mouse_wheel_down() - mouse_wheel_up())) * 0.1), 0.1, 2);

//Get current size
var view_w = camera_get_view_width(view_camera[0]);
var view_h = camera_get_view_height(view_camera[0]);

//Set the rate of interpolation
var rate = 0.5;

//Get new sizes by interpolating current and target zoomed size
var new_w = lerp(view_w, zoom_level * default_zoom_width, rate);
var new_h = lerp(view_h, zoom_level * default_zoom_height, rate);

//Apply the new size
camera_set_view_size(view_camera[0], new_w, new_h);


//camera zoom from center.
//Get the shift necessary to re-align the view.
var shift_x = camera_get_view_x(view_camera[0]) - (new_w - view_w) * 0.5;
var shift_y = camera_get_view_y(view_camera[0]) - (new_h - view_h) * 0.5;

//Update the view position
camera_set_view_pos(view_camera[0],shift_x, shift_y);
Ah, I see what the problem is now! The bit of code starting on line 9 from your example:
Code:
zoom_level = 1;
//Get the starting view size to be used for interpolation later
default_zoom_width = camera_get_view_width(view_camera[0]);
default_zoom_height = camera_get_view_height(view_camera[0]);
This bit of code should be declared in the create event, not the step - otherwise the default zoom bounds get reset to the current view size, and zoom level gets reset to 1 every single step, meaning it will never be able to clamp the value properly (zoom_level will always be one of 0.9, 1.0 and 1.1 in this particular example).
So, move that little snippet to create and remove it from step and it should work. I'll update the guide to clarify this too!
 
Z

zendorf

Guest
@MaddeMichael Thanks for writing up this excellent tutorial! I have used your object following with interpolation code and it works fine. I have been trying to adapt it to using a "border" region like the native camera_set_view_border function, but with custom code. What I have does function somewhat, but is jerky at times and not great. Any idea on how to go about this?
 
@MaddeMichael Thanks for writing up this excellent tutorial! I have used your object following with interpolation code and it works fine. I have been trying to adapt it to using a "border" region like the native camera_set_view_border function, but with custom code. What I have does function somewhat, but is jerky at times and not great. Any idea on how to go about this?
You're welcome!

As for the border method with lerping, I can probably help, though I would need to ask more specifically what you would like.
Do you want:
a) The view to only lerp when beyond the border region
b) The view to always lerp but never fall beyond the border region
c) Something different that I haven't considered
I'd be happy to help (and confident that I can) with anything, I just need to make sure I fully understand what you want :)

I could also have a look at your code to see if there's anything I can help with directly within it instead of creating a new function entirely.
 
Z

zendorf

Guest
@MaddeMichael thanks for your reply! This is for a platformer, so I try to avoid the camera moving with jumps and every small x axis movement. What the native set border camera function does is basically what I am after. So it would be choice "a" in youtr list...the view to only lerp when beyond the border region.

I don't have my code in front of me now, but I was only adding a simple distance check between the player and the view edge (ie a border) before applying your lerping code. It does work, but it is very "juddery" at times and not always smooth. Would love to see your solution!

cheers :)
 
@MaddeMichael thanks for your reply! This is for a platformer, so I try to avoid the camera moving with jumps and every small x axis movement. What the native set border camera function does is basically what I am after. So it would be choice "a" in youtr list...the view to only lerp when beyond the border region.

I don't have my code in front of me now, but I was only adding a simple distance check between the player and the view edge (ie a border) before applying your lerping code. It does work, but it is very "juddery" at times and not always smooth. Would love to see your solution!

cheers :)
I'm back!

Here's the result - I think this is what we wanted!

The red border is just a visualisation of the border region
I've not noticed any jerkiness either, which is good!

Here's a direct copy-paste of the code I used (minus the draw event and basic motion code). I set up the viewport in the room editor to save time, the only instance is the red circle.
Create
Code:
//The size of the border for both x and y. Technically, you could merge them, but this keeps stuff flexible
border_size_x = 128;
border_size_y = 128;

//Establish the interpolation rate
lerp_rate = 0.1;

//Create and bind the camera
cam = camera_create_view(0,0,640,480, 0, -1, -1, -1, -1, -1);
view_set_camera(0, cam);

//Initialize camera target position vars
target_x = 0;
target_y = 0;
End Step (I've done it here so it's processed after all movement is updated)
Code:
//Get all current camera values. I'm doing it ALL here instead of create incase the camera does any dynamic resizing
var cam = view_camera[0];
var cam_x = camera_get_view_x(cam);
var cam_y = camera_get_view_y(cam);

var cam_w = camera_get_view_width(cam);
var cam_h = camera_get_view_height(cam);

//The new target position. Doesn't need to be stored in a temp variable, but makes it easier to change
var newtarget_x = x;
var newtarget_y = y;


//Test all boundaries and update the main target position if necessary
if(newtarget_x < cam_x + border_size_x)
{
    target_x = newtarget_x - border_size_x;
}
else if(newtarget_x > cam_x + cam_w - border_size_x)
{
    target_x = newtarget_x + border_size_x - cam_w;
}

if(newtarget_y < cam_y + border_size_y)
{
    target_y = newtarget_y - border_size_y;
}
else if(newtarget_y > cam_y + cam_h - border_size_y)
{
    target_y = newtarget_y + border_size_y - cam_h;
}

//Update/lerp the camera pos
camera_set_view_pos(cam, lerp(cam_x, target_x, lerp_rate), lerp(cam_y, target_y, lerp_rate));
It's possible that this could be done in fewer lines, but this works and is pretty readable

I hope this helps :D
 
Z

zendorf

Guest
@MaddeMichael Brilliant...thanks so much for doing this! With a bit of tweaking and adding back in the clamp code, it is working perfectly. I had been doing something similar, but your code works much more smoothly...no judder at all.

I am continually impressed by the generosity of help on this forum. Much appreciated :)
 
A

anomalous

Guest
Thanks for the guide.

In GMS1.4 all my games/engines display a single view of a very large room, and all HUD elements, which are minimal, are drawn on top of that. I have all resolution support, its all pixel perfect (pixel art), no issues.

However, I have a new project I'm doing in GMS2 and would like to implement the below layout, a small view of the game room, surrounded by a GUI.

hud.png

I know it must be simple but aside from using 2 views, but at this point I'm just trial/error throwing darts. There is also something with the first room in the resource tree (boot room) setting canvas size, which seems like a hidden gotcha.

Obviously if I'm just drawing a small portion of a very large room in that "tiled game world" area, I don't want 2 views worth of draw calls, just to draw a HUD around it.
The HUD I can do in the draw GUI layer, if that matters. I could simply use one view and overlay the HUD, but that seems crazy to have all that hidden area still drawing, and really I want proper access to the "tiled game world" window boundary/etc. Things outside of that little box should not be drawn (from the room) Does that make sense?

Anyone know how generally to approach this in GMS2? I don't need code, just high level or pseudo-code, a point in the right direction.
 
Thanks for the guide.

In GMS1.4 all my games/engines display a single view of a very large room, and all HUD elements, which are minimal, are drawn on top of that. I have all resolution support, its all pixel perfect (pixel art), no issues.

However, I have a new project I'm doing in GMS2 and would like to implement the below layout, a small view of the game room, surrounded by a GUI.

View attachment 5833

I know it must be simple but aside from using 2 views, but at this point I'm just trial/error throwing darts. There is also something with the first room in the resource tree (boot room) setting canvas size, which seems like a hidden gotcha.

Obviously if I'm just drawing a small portion of a very large room in that "tiled game world" area, I don't want 2 views worth of draw calls, just to draw a HUD around it.
The HUD I can do in the draw GUI layer, if that matters. I could simply use one view and overlay the HUD, but that seems crazy to have all that hidden area still drawing, and really I want proper access to the "tiled game world" window boundary/etc. Things outside of that little box should not be drawn (from the room) Does that make sense?

Anyone know how generally to approach this in GMS2? I don't need code, just high level or pseudo-code, a point in the right direction.
I'd probably just assign a surface to the main world view instead with "view_set_surface_id" so it doesn't automatically draw the whole thing and stretch/fit it. Then just draw the surface manually at the same time as (but probably just before) the GUI, probably on the GUI layer too (that's what I do for split screen games, at least). I'd also disable the application surface (since I end up effectively not using it - this is assuming you have no other views that are still automatically drawing) but still make sure I'm drawing at the correct aspect/position in the window with application_get_position.
 
L

luggage

Guest
Hello

Really nice guide, it's help loads. Is there a way to convert a screen X,Y to a camera X,Y and vice versa?

Thanks!
 
T

TheGreatDane

Guest
Hi - thanks a bunch for this guide, it certainly cleared up a few issues for me.
I'm new to the forum and just as new to gms:2 - so i hope it's alright if i ask a question or two in relation to the main topic at hand:

I'm making a faily basic Agar type of game (just one level actually) - I eat the baddies and i grow relatively in size. however i also want to increase width/height of view as my player grows and a decrease when player loses mass. Iv'e done everything so far DnD wise which isn't much because it's pretty simple what i've been needing to do so far.
however i kinda hit the wall with increase/decrease of view. I wanted to keep everything DnD but now i've used code instead to set up a camera and bind it to a view.
So - how do i move on from here adjusting the view relative to the player size/mass? I guess it's probably a simple solution but i'm both a noob and old so bear with me:)
 
H

HieNa

Guest
Now i have a big important question... How this camera work with instance_ac/deactivate_region, i need that function, but idk how it's work in GMS2 :/ PLS HELP.
 
I need help to set up displaying 2 camera views at the same time. The helpfile says you can do it but the second camera overwrites the first.

Camera A I need to be full screen. Camera B as an ovelay with adjustable size. Both toggleable to be displaying at the same time.
 
D

DKR_87

Guest
Hello! Awesome tutorial. It really helped me with the RTS that I'm designing.

However, I'm banging my head on the keyboard about how to keep the view within the room when you're zooming. After all of that tutorial, if you zoom out, you can zoom out so far that you see the outside of the room. Is there a way to limit this?

Secondly, zooming to the center of the view is one thing. How could we zoom to mouse cursor? And adjust the view position when zooming to cursor to move over so that the cursor is centered again? A lot of space RTS games are like that. That's what I'm trying to figure out now. Any help on either of those two things would be appreciated.

I have a hunch it has to do with that bottom section of code. I'm trying to hammer away at it. There's gotta be some place to include the distance away the mouse is from the center of the view and add that to the amount shifted.
Code:
//Get the shift necessary to re-align the view.
var shift_x = camera_get_view_x(view_camera[0]) - (new_w - view_w) * 0.5;
var shift_y = camera_get_view_y(view_camera[0]) - (new_h - view_h) * 0.5;

//Update the view position
camera_set_view_pos(view_camera[0],shift_x, shift_y);
Thanks for the awesome tutorial!
 
Last edited by a moderator:
D

DKR_87

Guest
It appears that I've figured it out. Maybe someone can find a use for it as well? I'm sure someone else will know a better way of doing this, but I figured I'd post it.

To zoom in and out, but to keep your view within the game room as you're zooming, I added some code in between.

Code:
// Zoom Into the Center of the Current View's Position
// Get the shift necessary to re-align the view.
var shift_x = camera_get_view_x(view_camera[0]) - (new_w - view_w) * 0.5;
var shift_y = camera_get_view_y(view_camera[0]) - (new_h - view_h) * 0.5;

// If Zoom Goes Outside Room Boundaries, Bring it Back in
if _viewX < 0{
    shift_x = lerp(shift_x,0,rate);
}
if _viewY < 0{
    shift_y = lerp(shift_y,0,rate);
}
if _viewX + _viewW > room_width{
    shift_x = lerp(shift_x,room_width-_viewW,rate);
}
if _viewY + _viewH > room_height{
    shift_y = lerp(shift_y,room_height-_viewH,rate);
}


// Update the view position
camera_set_view_pos(view_camera[0],shift_x,shift_y);
I did the whole lerp thing with "rate" because anything else made it too instant, snappy, or choppy. This smoothed it out. You could obviously change the rate, but if you didn't want to effect the interpolation rate for zooming, you could do "rate*X", X being whatever you want. It looks really good. Hope someone else finds it useful.

Now to figure out how to make it centered on wherever your mouse position is! :)
 
Last edited by a moderator:

babfoxj

Member
So because of your spectacular guide, I decided to sign up to this community so I can tell you that! xD

Also on top of that, I wanted to submit another question: How can we make this work in full screen mode and switching between full screen and window mode?

The way I'm trying to make the game work is allowing the user to make the game full screen or windowed when pressing a key (F4 in my case). I added this simple code in a Begin Step event in a persistent, "game master" kind of object:
Code:
//Fullscreen
if keyboard_check_pressed(vk_f4)
{
    if (window_get_fullscreen() = 0)
    {
        window_set_fullscreen(1);
    }
    else
    {
        window_set_fullscreen(0);
    }
}
The result I get is the window not changing between full screen and window mode. It only does this after the camera is created. I'm pretty sure it has something to do with the camera code because it works fine when I remove it. Any help would be appreciated!
 

MadZenno

Member
Hi Guys,

Not sure if this is a correct thread to put it in, but I was wondering if there is a type of tutorial/example for GM Studio2 for changing in game views? From 3D view to Top down ?

Thanks!
 
M

Mallard8

Guest
Only just come across this thread after asking a question in one of the other forums. Will read it through when I can go through it from start to finish.
What I have read so far makes me think, Why are you not writing the help manual for GM? :)
 
D

DeveloperElias

Guest
Hi! This is a really good guide, but I have one question. Can this camera be used to make a camera that follows 2 players, and when one of the player runs off the screen the camera zooms out so both players are constantly in view, but then zoom in as they come closer together. If so, how would I do that? Code example would be amazing.

Thanks!
 
Top