1. Hey! Guest! The 35th GMC Jam will take place between November 28th, 12:00 UTC - December 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

GMS 2 Camera system - if this helps

Discussion in 'Programming' started by Guest User, Nov 7, 2019 at 10:45 PM.

Tags:
  1. Guest User

    Guest User Guest

    Hello all,
    I am just sharing here a very basic camera 'system'. Nothing innovative, nor elegant, nor fancy... but it works. It follows the player and can zoom / shake / clamp to an area (boss fight) / focus on a specific location. This GMS 2.2.4.374
    I post it here, just in case it can help some of you. Feel free to use it as you like - or not :p - and if you are in the mood of improving it.... (also not sure this is the best place for this... but I cannot say this is a tuto or an asset...)

    In a nutshell:

    There are two parts for this to work
    • one controller object that is in charge of updating viewsize and position accordingly to the desired effects. This should be named oSYS_Camera (for the with in my scripts to work).
    • A set off scripts you can call to trigger (and stop) some effects.

    The mechanic is basic:
    • The effects are processed each step (end_step) by this oSYS_Camera based on a set of its instance variables (like zoom factor...). The end_step event simply checks these variables and if they are not at their initial values, it knows it needs to work accordingly and change the zoom, position or whatever.
      For instance, if the camera_shaking_mode is true the end_step event will 1-offset the camera position by a random number based on the shaking_force variable, 2-reduce the remaining time of shaking. And so on until there is no time left and the shaking ends.
      So the code is not optimized : all effects are checked/processed every step. 2 or 3 variables are not used... But this is just one object.
    • The set of variables is declared and initialized in the create event, so you can see everything that is required
      Also, you just need to set your width x height and the name (as in the IDE) of the object to follow (the player maybe)
    • Note that, just in case, I also reset the camera parameters in the room_start event so that it is always ready to go at each level. Also This is where I set the followed object (the player) as it may not exist when running the create event of oSYS_Camera
    • The scripts just set oSYS_Camera variables. With the same example as above, if you run the camera_set_shaking(10, 60) script, it will just set the variables in the camera object : camera_mode_shake to true, camera_shake_force to 10, and camera_shake_duraction to 60 steps. From there the end_step event processes the rest, as explained above.
    • (and yeah, the object is a persistent singleton, so the controller is placed in the first room and will be kept from room to room)
    M.

    1-To create your oSYS_Camera object:

    Set to true VISIBLE and PERSISTENT

    CREATE EVENT
    Code:
    //-----Configuration
    
    // 1-You have to set your view resolution here
    default_view_width  = 640;
    default_view_height = 480;
    
    // 2-You have to give the name of the followed object as it appears in the resource tree
    default_followed_object = asset_get_index("oCHAR_PC");
    
    //-----Basic set up
    // Singleton enforcement
    if ( instance_number(object_index) > 1 )
        { instance_destroy(); exit; }
    persistent = 1;
    
     // GUI
    display_set_gui_size(ideal_width, ideal_height);
    
    // Camera
    view_enabled    = true;
    view_visible[0] = true;
    camera_set_view_size(view_camera[0], default_view_width, default_view_height);
    
    //-----System variables used by the end-step event
    // Follow object
    camera_followed_object  = noone;                      // the object, the camera is CURRENTLY following
    
    // Camera position
    camera_center_x         = 0;                          // camera position - center of the camera
    camera_center_y         = 0;                          // camera position - center of the camera
    camera_target_x         = 0;                          // the position, the camera is moving to
    camera_target_y         = 0;                          // the position, the camera is moving to
    
    // Zooming - i.e. emphasize an object
    camera_mode_zoom_change = false;
    camera_zoom             = 1;       // the CURRENT zoom factor of the camera - 0.5 for X2
    camera_target_zoom      = 1;       // the target zoom factor, the camera is going to
    
    // Secondary focus - i.e. draw attention to a specific position
    camera_mode_secondary_focus = false;
    camera_secondary_focus_x     = 0;  // x coordinate to also focus to with the camera
    camera_secondary_focus_y     = 0;  // y coordinate to also focus to with the camera
    camera_secondary_focus_range = -1; // range where secondary focus is tigger
    
    // Clamp camera to specified zone - i.e. boss fight
    camera_mode_clamp       = false
    camera_zone_left        = 0;       // left limit for the view
    camera_zone_right       = 0;       // right limit for the view
    camera_zone_top         = 0;       // top limit for the view
    camera_zone_bottom      = 0;       // bottom limit for the view
    
    // Shake - i.e. explosion
    camera_mode_shake       = false;   // not used, the test is based on remaining life > 0
    camera_shake_force      = 0;       // intensity of the effect - pixel amplitude
    camera_shake_duration   = 0;       // duration in step
    camera_shake_life       = -1;      // counter
    
    // Offset - i.e. heavy fall
    camera_mode_offset      = false;   // not used, the test is based on remaining life > 0
    camera_offset_x         = 0;       // intensity of the effect - pixel amplitude
    camera_offset_y         = 0;       // intensity of the effect - pixel amplitude
    camera_offset_duration  = 0;       // duration in step
    camera_offset_life      = -1;      // counter
    
    ROOM START EVENT
    Code:
    // Set up camera to default... just in case
    view_enabled    = true;
    view_visible[0] = true;
    camera_set_view_size(view_camera[0], default_view_width, default_view_height);
    
    // Set all effect to default... just in case
    camera_stop_shaking();
    camera_stop_offset();
    camera_stop_secondary_focus();
    camera_reset_zoom();
    camera_reset_followed();
    camera_reset_clamp();
    
    END STEP EVENT after everyone has moved
    Code:
    /// @description Manage camera (follow, effect, parallax...)
    // Exit if followed object do not exist
    if ( !instance_exists(camera_followed_object) )
        { exit }
      
    // Set primary focus
    camera_target_x = camera_followed_object.x;
    camera_target_y = camera_followed_object.y;
    
    // Optionnal effect: Take into account secondary focus
    if ( camera_mode_secondary_focus )
    {
        // Average position between object_to_follow and secondary_focus
        var _distance_to_focus = point_distance(camera_target_x, camera_target_y, camera_secondary_focus_x, camera_secondary_focus_y)
        var _distance_coefficient = clamp(_distance_to_focus  / camera_secondary_focus_range, 0, 1);
        camera_target_x = _distance_coefficient * camera_target_x + (1 - _distance_coefficient ) * camera_secondary_focus_x;
        camera_target_y = _distance_coefficient * camera_target_y + (1 - _distance_coefficient ) * camera_secondary_focus_y;
    }
    
    // Move camera center towards its targeted position with a LERP of 50%
    var _lerp = 0.5;
    camera_center_x += (camera_target_x - camera_center_x) * _lerp;
    camera_center_y += (camera_target_y - camera_center_y) * _lerp;
    
    // Optionnal effect: Update zoom factor
    if ( camera_mode_zoom_change )
    {
        // Update zoom factor with a LERP of 50%
        var _lerp = 0.5;
        camera_zoom += (camera_target_zoom - camera_zoom) * _lerp;
        if ( abs(camera_target_zoom - camera_zoom) < 0.01 )
        {
            camera_mode_zoom_change = false;    // stop zoom change
            camera_zoom = camera_target_zoom;   // set zoom factor to target
        }
    }
    
    // Update camera to the zoom dimensions
    var _view_width  = default_view_width  * camera_zoom;
    var _view_height = default_view_height * camera_zoom;
    
    // Optionnal effect : clamp camera to a specific zone
    if ( camera_mode_clamp )
    {
        camera_center_x = clamp(camera_center_x, camera_zone_left + _view_width  * 0.5,  camera_zone_right   - _view_width  * 0.5);
        camera_center_y = clamp(camera_center_y, camera_zone_top  + _view_height * 0.5,  camera_zone_bottom - _view_height * 0.5);
    }
    
    // Clamp camera to room
    camera_center_x = clamp(camera_center_x, _view_width  * 0.5, room_width - _view_width  * 0.5);
    camera_center_y = clamp(camera_center_y, _view_height * 0.5, room_height- _view_height * 0.5);
    
    // Position camera
    var _camera_left = camera_center_x - _view_width  / 2;
    var _camera_top  = camera_center_y - _view_height / 2;
    camera_set_view_size(view_camera[0], _view_width, _view_height );
    camera_set_view_pos(view_camera[0], _camera_left, _camera_top);
    
    // Optional effect : Screen shake, can shake out of the room / clamped zone
    if ( camera_shake_life > 0 )
    {
        var _shake_left = camera_shake_force * ( camera_shake_life / camera_shake_duration);
        camera_center_x  += random_range(-_shake_left, _shake_left);
        camera_center_y  += random_range(-_shake_left, _shake_left);
        camera_shake_life -= 1;
    }
    
    // Optional effect : Screen offset
    if ( camera_offset_life > 0 )
    {
        var _x_offset_left = camera_offset_x * ( camera_offset_life / camera_offset_duration);
        var _y_offset_left = camera_offset_y * ( camera_offset_life / camera_offset_duration);
        camera_center_x   += _x_offset_left;
        camera_center_y   += _y_offset_left;
        camera_offset_life -= 1;
    }
    
    DRAW GUI EVENT this is optional - it helps for debug
    Code:
    // Draw a sprite at the center of the game_window
    var _x      = camera_get_view_width(0)  / 2;
    var _y      = camera_get_view_height(0) / 2;
    draw_sprite(sCrosshair, 0, _x, _y); // <<you have to give it one of your sprite
    
    2-SET OF SCRIPTS
    Code:
    //////////////////////////////////////////////////////////////
    /// @function camera_set_shaking(shake_force, shake_duration)
    /// @description Trigger the shaking of the camera
    /// @param shake_force
    /// @param shake_duration
    with ( oSYS_Camera )
    {
        camera_mode_shake     = true;
        camera_shake_force    = argument0;  // intensity of the effect - pixel amplitude
        camera_shake_duration = argument1;  // duration in step
        camera_shake_life     = argument1;  // counter
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_stop_shaking()
    /// @description Stop the shaking of the camera
    with ( oSYS_Camera )
    {
        camera_mode_shake      = false;
        camera_shake_force     = 0;  // intensity of the effect - pixel amplitude
        camera_shake_duration  = 0;  // duration in step
        camera_shake_life      = -1; // counter
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_set_offset(shake_force, shake_duration)
    /// @description Trigger the quick offset of the camera
    /// @param offset_x
    /// @param offset_y
    /// @param offset_duration
    with ( oSYS_Camera )
    {
        camera_mode_offset     = true;
        camera_offset_x        = argument0; // intensity of the effect - pixel amplitude
        camera_offset_y        = argument1; // intensity of the effect - pixel amplitude
        camera_offset_duration = argument2  // duration in step
        camera_offset_life     = argument2; // counter
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_stop_offset()
    /// @description Stop the offset of the camera
    with ( oSYS_Camera )
    {
        camera_mode_offset     = false;
        camera_offset_x        = 0;  // intensity of the effect - pixel amplitude
        camera_offset_y        = 0;  // intensity of the effect - pixel amplitude
        camera_offset_duration = 0;  // duration in step
        camera_offset_life     = -1; // counter
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_set_secondary_focus(focus_x, focus_y, range_of_reaction)
    /// @description Trigger a secondary focus
    /// @param focus_x
    /// @param focus_y
    /// @param range
    with ( oSYS_Camera )
    {
        camera_mode_secondary_focus  = true;
        camera_secondary_focus_x     = argument0; // x coordinate to also focus to with the camera
        camera_secondary_focus_y     = argument1; // y coordinate to also focus to with the camera
        camera_secondary_focus_range = argument2; // range where secondary focus is tigger
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_stop_secondary_focus()
    /// @description Stop the secondary focus
    with ( oSYS_Camera )
    {
        camera_mode_secondary_focus = false;
        camera_secondary_focus_x     = 0; // x coordinate to also focus to with the camera
        camera_secondary_focus_y     = 0; // y coordinate to also focus to with the camera
        camera_secondary_focus_range = -1; // range where secondary focus is tigger
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_set_zoom(zoom_factor)
    /// @description Trigger a zoom
    /// @param zoom_factor
    with ( oSYS_Camera )
    {
        camera_mode_zoom_change = true;
        camera_target_zoom = 1/argument0;
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_reset_zoom()
    /// @description Set zoom back to default
    with ( oSYS_Camera )
    {
        camera_mode_zoom_change = true;
        camera_target_zoom = 1;
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_set_followed(object)
    /// @description Change following object
    /// @param followed_object
    with ( oSYS_Camera)
    {
        camera_followed_object = argument0;
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_reset_followed()
    /// @description Set following object back to player
    with ( oSYS_Camera )
    {
        camera_followed_object = default_followed_object;
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_set_clamp(left, right, top, bottom)
    /// @description Trigger the Clamp effect of the camera
    /// @param left
    /// @param right
    /// @param top
    /// @param bottom
    with ( oSYS_Camera )
    {
    camera_mode_clamp  = true;
    camera_zone_left   = argument0;
    camera_zone_right  = argument1;
    camera_zone_top    = argument2;
    camera_zone_bottom = argument3;
    }
    
    //////////////////////////////////////////////////////////////
    /// @function camera_reset_clamp()
    /// @description Free the camera for normal room clamp
    with ( oSYS_Camera )
    {
        camera_mode_clamp  = false;
        camera_zone_left   = 0;
        camera_zone_right  = 0;
        camera_zone_top    = 0;
        camera_zone_bottom = 0;
    }
    //////////////////////////////////////////////////////////////
    
     
    Last edited by a moderator: Nov 8, 2019 at 10:11 PM
  2. robproctor83

    robproctor83 Member

    Joined:
    Sep 30, 2019
    Posts:
    49
    Awesome, two things:
    1) you should package it and publish it on the market
    2) you should also adapt the code to support delta timing (if your feeling generous)
     
  3. MCHLV

    MCHLV Member

    Joined:
    Sep 5, 2016
    Posts:
    17
    Maybe I will. I programmed this, because I needed camera effect to tell the story of my project and thought it might help. For now it is enough for me designing my levels which is where I really need to focus. But I will undoubtedly add small enhacements and update this. And why not package everything.
    (NB : yeah MCHLV = MichelV, 2 profiles)
     

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