GMS 2 Camera system - if this helps

G

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

MCHLV

Member
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)
 
Top