Discussion [GUIDE] Meet the New Camera System

Discussion in 'GameMaker Studio 2 Community Tech Support' started by MaddeMichael, Nov 7, 2016.

  1. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.

    ***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:

    [​IMG]

    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.
    [​IMG]

    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:
    [​IMG]

    Next, you'll need to check "Enable Viewports"
    [​IMG]

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

    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: Dec 6, 2016
    Mana, Alireza, slojanko and 30 others like this.
  2. Davemane42

    Davemane42 Member

    Joined:
    Jun 21, 2016
    Posts:
    8
    I can't thank you enough for this highly comprehensive guide, really nice of you.
     
    MaddeMichael likes this.
  3. GerPronouncedGrr

    GerPronouncedGrr Member

    Joined:
    Oct 31, 2016
    Posts:
    26
    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.
     
  4. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.
     
  5. GerPronouncedGrr

    GerPronouncedGrr Member

    Joined:
    Oct 31, 2016
    Posts:
    26
    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);
    
    
     
  6. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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);
    
     
  7. GerPronouncedGrr

    GerPronouncedGrr Member

    Joined:
    Oct 31, 2016
    Posts:
    26
    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?
     
  8. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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: Nov 9, 2016
  9. ThewDev

    ThewDev Member

    Joined:
    Jun 20, 2016
    Posts:
    16
    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!
     
    MaddeMichael likes this.
  10. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
  11. Leone_Kaiser

    Leone_Kaiser Member

    Joined:
    Nov 9, 2016
    Posts:
    19
    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: Nov 19, 2016
    MaddeMichael likes this.
  12. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    A quick bump!
    Just want to make sure people can see this :)
     
  13. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    *UPDATE*
    Added a quick bit about locking the view position to room bounds.
     
  14. CutThroat Neko

    CutThroat Neko Member

    Joined:
    Jun 21, 2016
    Posts:
    4
    @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.
     
  15. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.
     
  16. huntermanz

    huntermanz Member

    Joined:
    Nov 18, 2016
    Posts:
    8
    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
     
  17. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.
     
    huntermanz likes this.
  18. huntermanz

    huntermanz Member

    Joined:
    Nov 18, 2016
    Posts:
    8
    @MaddeMichael Thanks a bunch this worked perfectly! Thanks for the reply, now i can move on because i was stuck on this for a little while haha.
     
    MaddeMichael likes this.
  19. huntermanz

    huntermanz Member

    Joined:
    Nov 18, 2016
    Posts:
    8
    @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
     
  20. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.
     
    huntermanz likes this.
  21. huntermanz

    huntermanz Member

    Joined:
    Nov 18, 2016
    Posts:
    8
    @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);
    
    
     
  22. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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!
     
    huntermanz likes this.
  23. huntermanz

    huntermanz Member

    Joined:
    Nov 18, 2016
    Posts:
    8
    @MaddeMichael That was it! It's all working now, thanks again for the help and putting up this guide, great stuff!
     
    MaddeMichael likes this.
  24. zendorf

    zendorf Member

    Joined:
    Sep 11, 2016
    Posts:
    102
    @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?
     
  25. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.
     
    zendorf likes this.
  26. zendorf

    zendorf Member

    Joined:
    Sep 11, 2016
    Posts:
    102
    @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 :)
     
  27. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    I'm back!

    Here's the result - I think this is what we wanted!
    [​IMG]
    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
     
    zendorf likes this.
  28. zendorf

    zendorf Member

    Joined:
    Sep 11, 2016
    Posts:
    102
    @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 :)
     
    MaddeMichael likes this.
  29. anomalous

    anomalous Member

    Joined:
    Jun 29, 2016
    Posts:
    462
    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.
     
  30. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    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.
     
    anomalous likes this.
  31. Gamerev147

    Gamerev147 Member

    Joined:
    Sep 12, 2016
    Posts:
    416
  32. luggage

    luggage Member

    Joined:
    Sep 27, 2016
    Posts:
    8
    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!
     
  33. SweetCelestia

    SweetCelestia Member

    Joined:
    Jun 20, 2016
    Posts:
    10
    Nice guide!
     
    MaddeMichael likes this.
  34. Nelson DaSilva

    Nelson DaSilva Member

    Joined:
    Mar 11, 2017
    Posts:
    2
    Thank you!
     
    MaddeMichael likes this.
  35. TheGreatDane

    TheGreatDane Member

    Joined:
    Apr 22, 2017
    Posts:
    6
    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:)
     
  36. HieNa

    HieNa Member

    Joined:
    Jun 18, 2017
    Posts:
    1
    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.
     
  37. Christopher Risdon

    Christopher Risdon Member

    Joined:
    Nov 6, 2017
    Posts:
    6
    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.
     
  38. DKR_87

    DKR_87 Member

    Joined:
    Jan 20, 2017
    Posts:
    67
    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: Dec 4, 2017
  39. DKR_87

    DKR_87 Member

    Joined:
    Jan 20, 2017
    Posts:
    67
    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: Dec 6, 2017
  40. babfoxj

    babfoxj Member

    Joined:
    Dec 28, 2017
    Posts:
    1
    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!
     
  41. MadZenno

    MadZenno Member

    Joined:
    Dec 31, 2017
    Posts:
    25
    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!
     
  42. Mallard8

    Mallard8 Member

    Joined:
    Feb 28, 2018
    Posts:
    198
    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? :)
     
  43. DeveloperElias

    DeveloperElias Member

    Joined:
    May 15, 2018
    Posts:
    5
    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!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice