-
Notifications
You must be signed in to change notification settings - Fork 12
1. The Framework Controller
The core of the framework is its main object: c_framework
. It is divided into several components, each responsible for handling a specific task:
- Audio
- Background
- Camera
- Common
- Collision
- Distortion
- Fade
- Input
- Palette
- Sprite Animator
There are things that operate outside of these components, but the listed components primarily define what c_framework
is. Each of them is essentially a structure of variables (i.e., an object that exists not as an asset in the project library).
c_framework
occupies the following events for processing:
- Begin Step
- End Step
- Pre-Draw
- Draw Begin
- Draw End
- Draw GUI Begin
For Step events, all objects in your game should be processed in the regular Step event to avoid potential conflicts and delays in updating the framework. The Draw events in c_framework
are the very first Draw events executed in the game, as the object's depth is set to the maximum possible value (16000). Because of this, try to avoid using this depth for any other objects.
Below, the key aspects of each c_framework
component are discussed.
GameMaker does not have encapsulation (i.e., separation into public and private fields and properties), so any fields or properties not described in this section are considered private. For some of these fields, there are corresponding functions, which are listed here and covered in detail in this chapter.
Structure:
c_framework
Available Fields:
state
– Indicates the framework’s current state. Possible values include:
-
STATE_NORMAL
: The framework is actively updating. -
STATE_STOP_OBJECTS
: Allc_object
instances are paused, except those with specific culling configurations (CULLING.NONE
orCULLING.PAUSEONLY
). -
STATE_PAUSED
: All systems are stopped, except for Fade and Input.
allow_pause
- a flag that determines whether the framework can be manually paused (which will trigger the pause menu)frame_counter
- the current room timer, which increments each frame ifstate
is not set toSTATE_PAUSED
ring_spill_counter
- the timer for the scattered rings animation. This variable is specific to Sonic-style gameplay, so if you're developing a non-Sonic game, this field can be removed
Setup Function:
scr_fw_game_setup()
These are not a component, but rather fields that are characteristic of the controller itself. If you decide to add something to c_framework
but don't think it warrants an entire sub-system, you can place it here.
Structure:
c_framework.audio
Available Fields:
emitter_sfx
- emitter for playing sound effectsemitter_bgm
- emitter for playing musicbus_sfx
- bus linked to the sound effects emitterbus_bgm
- bus linked to the music emitter
Setup Function
scr_fw_audio_setup()
Related Functions:
audio_play_sfx()
audio_play_bgm()
audio_mute_bgm()
audio_unmute_bgm()
audio_reset_bgm()
audio_stop_bgm()
audio_bgm_is_playing()
This component is a well-tuned system for playing music and sounds. By default, the framework provides 4 channels for playing music (macro ENGINE_AUDIO_BGM_CHANNELS
), with the last channel reserved for playing jingles (macro AUDIO_CHANNEL_JINGLE
).
Music played on the jingle channel cannot be looped. While any audio file is playing on this channel, all other channels are muted (but playback is not paused).
Any sound is played through an emitter, which is linked to a bus. Separate emitters and buses are designated for music and sound effects: emitter_bgm
+ bus_bgm
and emitter_sfx
+ bus_sfx
, respectively. So, when you want to apply an effect to sounds or music, you can simply refer to these buses.
Structure:
c_framework.background
Available Fields:
back_colour
- the solid colour that fills the background. Default:c_black
layer_count
- the current number of layersscroll_offset
- the additional scroll distance of the background. This value can be used for act transitions
Related Functions:
bg_add_layer()
bg_set_perspective_x()
bg_set_perspective_y()
bg_set_colour()
bg_clear()
The background can be set up in the Create Event of your room controller using the functions mentioned in the Related Functions section.
The background does not consist of separate objects for each layer, instead, it draws sprites. The backgrounds can use several sprites if parallaxing layers may overlap - however, it is ideal to only draw from portions of a sprite, or a few, to divide up the layers if they do not overlap.
Horizontal scrolling is handled via the shader, which offsets and loops the sprite texture within itself. Vertical scrolling is done traditionally by offsetting the sprite's draw position.
Important: the width of the background sprite you draw should not be less than the camera width for proper rendering. Additionally, avoid large empty vertical spaces in the sprite texture, as it will take up space on the texture map. In such cases, consider breaking the background into several sprites.
Structure:
c_framework.camera_data[camera_index]
Available Fields:
index
- the GameMaker camera index used for this Orbinaut camera (ranging from 0 to 7)allow_movement
- a flag that controls whether the camera is allowed to update its positiontarget
- a reference to the object the camera follows. This overrides the camera's default behavior of following the player, as the player object itself does not use this variable but instead updates the camera behavior directlysurface_x, surface_y
- the position of the camera's surface on the screen, i.e., where the camera's content projection is displayed in the game windowvel_x_max, vel_y_max
- the camera's maximum velocityvel_x, vel_y
- the camera's current velocitypos_x, pos_y
- the camera's current "raw" position, without accounting for offsets and limits. The final position of the camera in the room is obtained using the functionscamera_get_x()
andcamera_get_y()
pos_x_prev, pos_y_prev
- the camera's "raw" position from the previous framedelay_x, delay_y
- the delay during which the camera does not update its positionmin_x, min_y
- the minimum possible final position for the top-left corner of the cameramax_x, max_y
- the maximum possible final position for the bottom-right corner of the cameraoffset_x, offset_y
- the offset added to the camera's "raw" positionshake_x, shake_y
- the shake offset added to the camera's "raw" positionshake_timer
- the duration of the camera shake; the higher the value, the stronger the shake effect
Related Functions:
camera_new()
camera_update()
camera_update_surface()
camera_delete()
camera_get()
camera_get_data()
camera_get_x()
camera_get_y()
camera_get_width()
camera_get_height()
The camera system in Orbinaut Framework is essentially a "wrapper" around GameMaker's internal camera system. It utilises view_camera
and view_surface_id
, completely bypassing view_port
. A camera is considered an "Orbinaut camera" if it is created using the camera_new()
function, in which case an additional data structure is allocated for the camera, as described above.
By default, the framework disables and removes all GameMaker cameras in the room and creates a single Orbinaut camera. For more details on how rendering is handled, see the Rendering section.
Structure:
c_framework.collision
Available Fields:
none
Setup Function
scr_fw_collision_setup()
Related Functions:
collision_generate()
collision_load()
collision_load_binary()
Collision data should be loaded in the Create Event of your room controller using either the
collision_load()
orcollision_load_binary()
function.
At the c_framework
level, Collision component only contains data related to tileset collisions and ds_lists
for real-time collision visualisation. These do not require manual modification or access during gameplay. Collision data for individual objects is stored in c_object
.
In Orbinaut, level collision is tile-based. Like the original engine, tiles are divided into three types: Full Solid (black tiles), Top Solid (white tiles), and Left-Bottom-Right Solid (LBR Solid, yellow tiles). Each type has different collision behaviors:
-
Full Solid tiles: These respond to
tile_find_()
checks from all sides. -
Top Solid tiles: These respond to
tile_find_()
checks only from above (downward). - LBR Solid tiles: These respond to checks from the right, left, and bottom.
For more complex cases, such as when the player runs up a loop (switching to "right wall" mode) or runs on the ceiling ("ceiling mode"), standard tile checks may not apply. To handle this, the game makes tile properties "rotate" based on the player’s current state. This is managed by an optional argument in the tile_find_()
function called behaviour
, which defaults to TILEBEHAVIOUR.DEFAULT
. The player also has a variable that stores specific tile properties just for them.
Note that, just like in the original engine, in this GIF, the player cannot run on LBR Solid tiles and falls downward. The tile_find_v()
check occurs upwards, but since the player's tile properties are rotated 180 degrees when on the ceiling, the tile interprets this check as happening downward.
The process of creating and generating collision data is covered in detail in this chapter.
Structure:
c_framework.distortion
Available Fields:
fg_layers
- an array of strings representing the foreground layer names to which the distortion effect is appliedsplit_bound
- the boundary (Y-axis coordinate in room space) that separates the two types of distortion
Related Functions:
dist_set_fg()
dist_set_bg()
dist_clear_fg()
dist_clear_bg()
dist_get_data()
The distortion effect can be set up in the Create Event of your room controller using the
dist_set_fg()
anddist_set_bg()
functions.
The distortion effect in Orbinaut is implemented using a GameMaker Effect, which is a standard project saved in a specific way, containing a shader. Orbinaut supports two different (or identical) distortions simultaneously, divided by the split_bound
coordinate. This is mainly done to mimic the classic Sonic engine's above-water and underwater effects, but you are not limited to using distortion specifically for these scenarios.
Structure:
c_framework.fade
Available Fields:
state
- the fade state, represented by theFADESTATE
enum (indicating whether the screen is currently fading, fully covered with a solid colour, or inactive)routine
- the fade routine type, represented by theFADEROUTINE
enum (fade in or fade out)
Related Functions:
fade_perform()
fade_perform_black()
fade_perform_white()
fade_perform_flash()
fade_perform_dull()
draw_toggle_fade()
Probably one of the features we are most proud of in the Orbinaut Framework! This is an extremely accurate recreation of the fading algorithm from the original engine, where instead of blending colours, the colour channels are edited directly.
Three types of fades from the original quadrilogy are recreated (represented as FADETYPE.BLACK-
, FADETYPE.WHITE-
, and FADETYPE.FLASH-
), and an additional type was created following the same rules - FADETYPE.DULL-
. Each type has two variations (-SYNC
and -ORDER
), the differences of which you can observe by trying them out yourself.
However, there's an easier option: you can use the pre-built template functions, which already have the necessary types and variations selected for different routines.
Structure:
c_framework.input
Available Fields:
down*
- ds_list of structures representing pressed buttonspress*
- ds_list of structures representing button presses (registers a button press, not its release)system_device_count
- the maximum number of supported gamepads on the current platform
Related Functions:
input_create()
input_reset()
input_get()
input_get_pressed()
input_set_rumble()
*It is recommended to use the provided function for easy access to the desired structure
The Orbinaut Framework includes its own input system for simplified handling, providing two structures for button states, implemented through the input_create()
function.
Fields of the structure returned by the function. Each field is a boolean flag:
action1
action2
action3
action_any
up
down
left
right
start
The component reads button presses from both the keyboard and gamepads, storing their states in the corresponding structures by index. All gamepads supported by GameMaker are compatible. We do not use any third-party libraries to implement the input system.
The keyboard and the first gamepad are considered the FIRST device, logging button presses into input.down[| 0]
and input.press[| 0]
structures respectively. Consequently, the second gamepad logs its button presses into input.down[| 1]
and input.press[| 1]
, and so on for additional devices.
By default, key remapping is NOT SUPPORTED. If you require this functionality, you will need to implement it yourself.
Structure:
c_framework.palette
Available Fields:
split_bound
- the boundary (Y-axis coordinate in room space) that divides the two active palettes.
Related Functions:
pal_load()
pal_run_rotation()
pal_get_index()
pal_set_index()
The palette can be loaded in the Create Event of your room controller using the
pal_load()
function.
Colour replacement is handled through a shader and functions similarly to the Distortion component, but operates on a per-sprite basis rather than across the entire screen. This allows for colour replacement on semi-transparent objects, with support for two simultaneous palettes, separated by the split_bound
coordinate (e.g., for above-water and underwater palettes in Sonic games, though you’re free to use this feature however you like).
The process is straightforward: the shader scans the first column of the palette map for the original colours and replaces them with the colours in the second column (and further columns, if palette rotation is needed). The colours in these subsequent columns that replace the originals are referred to as the "replacement palette."
Structure:
c_framework.animator
Available Fields:
none
Related Functions:
ani_add_sprite()
Sprites are added to the system using the
ani_add_sprite()
function in the Create Event of your room controller.
This is a simple system that animates sprites placed in the room, based on the current c_framework.state
.
Important: when adding a sprite instance from the project library into the room, its speed multiplier will default to 0. Be sure to set it to 1 for the animation to play properly. This issue is due to a recent GameMaker update, and this note will be removed once the bug is resolved.