Little Helpers - coldrockgames/gml-raptor GitHub Wiki
This is a group of helper functions that did not fit anywhere else and are too few (and too small) to get their own script for each.
Test, if the specified value
is between the lower_bound
and the upper_bound
, whereis_between
tests the range including both bounds, and
is_between_ex
tests excluding both bounds.
/// @function is_between[_ex](val, lower_bound, upper_bound)
/// @description test if a value is between lower and upper bounds
/// @param {real/int} value
/// @param {real/int} lower_bound
/// @param {real/int} upper_bound
/// @returns {bool} y/n
function is_between(val, lower_bound, upper_bound) {
function is_between_ex(val, lower_bound, upper_bound) {
/// @function is_any_of(val, ...)
/// @description Specify any number of parameters after "val".
/// Determines if val is equal to any of them.
/// @param {any} val The value to find
/// @param {any} ... List of values to search
/// @returns {bool} y/n
function is_any_of(val1, val2, val3, ) {
Gets, how many % value
is of total
.
percent
returns the full value (Example: val 30, total 50 -> returns 60(%)) and
percent_mult
returns a multiplier for the value, so 0.6
instead of 60
.
/// @function percent(val, total)
/// @description Gets, how many % "val" is of "total"
/// @param {real} val The value
/// @param {real} total 100%
/// @returns {real} How many % of total is val. Example: val 30, total 50 -> returns 60(%)
function percent(val, total) {
function percent_mult(val, total) {
GameMaker's object_is_ancestor
has a little flaw: It returns false
if the object is exactly the ancestor. If you test object_is_ancestor(myGameObject, myGameObject)
you receive false
because it is not derived from myGameObject
.
This function here will return true in this case (and also uses object_is_anchestor
, so you get the same results, except that the case child==parent
also returns true
).
/// @function is_child_of(child, parent)
/// @description True, if the child is exactly parent type or derived from it
/// @param {object_index} child
/// @param {object} parent
/// @returns {bool}
function is_child_of(child, parent) {
Small inline-helper to convert json-strings, that contain asset names to asset indices.
/// @func asset_of(_expression)
/// @desc Checks whether _expression is the NAME of an asset (like "objEnemy")
/// and returns asset_get_index(_expression) if yes, otherwise _expression is returned.
function asset_of(_expression) {
gml_pragma("forceinline");
return is_string(_expression) ? asset_get_index(_expression) : _expression;
}
/// @func layer_of(_instance)
/// @desc retrieve the layer name or depth of _instance
/// if instance is nullish, -1 is returned (gms default for "no layer")
function layer_of(_instance) {
Note
This function is extremely powerful, as it reliably is capable of uniquely identifying struct- and object points even in the HTML runtime! Javascript does not support pointers, so comparing two identical structures would result in a false positive comparison in HTML, if you just compare the hashes of the instances. This function manages the difference and returns a solid pointer simulation even for HTML! 🎯🚀
/// @func address_of(_instance)
/// @desc Similar to name_of, but returns only the pointer
/// (a unique, but fake, pointer in html5) of the instance
/// as a string, without its type name or other informations
/// or undefined, when _instance is undefined
function address_of(_instance) {
/// @func typename_of(_object_or_script_type)
/// @desc Gets the type name (= asset name) of either an object or a script asset
function typename_of(_object_or_script_type) {
/// @func name_of(_instance)
/// @desc If _instance is undefined, undefined is returned,
/// otherwise MY_NAME or object_get_name of the instance is returned,
/// depending on the _with_ref_id parameter.
/// To cover the undefined scenario, this function is normally used like this:
/// var instname = name_of(my_instance) ?? "my default";
/// @param {object_index} _instance The instance to retrieve the name of.
function name_of(_instance, _with_ref_id = true) {
/// @func object_tree(_object_or_instance, _as_strings = true)
/// @desc Gets the entire object hierarchy as an array for the specified object type or instance.
/// At position[0] you will find the _object_or_instance's object_index and at the
/// last position of the array you will find the root class of the tree.
function object_tree(_object_or_instance, _as_strings = true) {
/// @func class_tree(_class_instance)
/// @desc Gets the entire class hierarchy as an array for the specified instance.
/// At position[0] you will find the _class_instance's name and at the
/// last position of the array you will find the root class name of the tree.
/// NOTE: This function only works if you used the "construct" function of raptor
/// and the argument MUST BE a living instance of the class!
function class_tree(_class_instance) {
Exact class match
/// @func is_class_of(_struct, _class_name)
/// @desc Returns, whether the struct has used the "construct" command
/// and the type is the specified class_name
function is_class_of(_struct, _class_name) {
Exact class match or child_of
/// @func is_child_class_of(_struct, _class_name)
/// @desc Returns, whether the struct has used the "construct" command
/// and the type is the specified class_name
/// or the specified _class_name appears anywhere
/// in the inheritance chain of this _struct
function is_child_class_of(_struct, _class_name) {
Executes the specified func
after a waiting time of delay
frames.
NOTE: The Animation ListPool
is used to count the frames, as the functionality uses an internal raptor
function to achieve the delay. Keep in mind, that calling animation_abort_all
on the owner
specified here will also stop this delayed execution.
run_delayed_ex
runs the function exclusively, and invokes animation_abort_all
before starting the waiter.
run_delayed_exf
runs the function exclusively, and invokes animation_finish_all
before starting the waiter.
See abort() vs finish(), if you are unsure, what that means.
/// @function run_delayed[_ex](owner, delay, func, data = undefined)
/// @description Executes a specified function in <delay> frames from now.
/// Behind the scenes this uses the __animation_empty function which
/// is part of the ANIMATIONS ListPool, so if you clear all animations,
/// or use animation_run_ex while this is waiting for launch,
/// you will also abort this one here.
/// Keep that in mind.
/// @param {instance} owner The owner of the delayed runner
/// @param {int} delay Number of frames to wait
/// @param {func} func The function to execute
/// @param {struct} data An optional data struct to be forwarded to func. Defaults to undefined.
function run_delayed(owner, delay, func, data = undefined) {
function run_delayed_ex(owner, delay, func, data = undefined) {
function run_delayed_exf(owner, delay, func, data = undefined) {
/// @function method_exists(_instance, _method)
/// @description Checks, whether a method with the specified name exists in _instance
/// @returns {bool} True, if a method with that name exists, otherwise false
function method_exists(_instance, _method) {
/// @function invoke_if_exists(_instance, _method, ...)
/// @description Invoke the method, if it exists, with all arguments specified after the
/// _instance and _method arguments.
/// NOTE: GameMaker supports a maximum of 16 arguments, 2 are already used for
/// _instance and _method, so this leaves a maximum of 14 arguments for your call
/// @returns {any} The return value of the method or undefined, if the method does not exist
function invoke_if_exists(_instance, _method) {
/// @function with_tag(_tag, _func, _data = undefined)
/// @description Executes the specified function for all object instances that are tagged
/// with the specified tag.
/// NOTE: The function is temporary bound to the instance, so
/// the code IN the function will run in the scope of the instance!
/// You may also specify any _data object to be sent into each of
/// the invoked functions
function with_tag(_tag, _func, _data = undefined) {
/// @function collider_first()
/// @description Checks, if this is the first collision between self and other
/// (means: there has not been the same collision in the previous frame),
/// and returns true, if that's the case.
/// The function has no arguments, as it uses "self" and "other", like you
/// would, when coding in the collision event
function collider_first() {
/// @function collider_cleanup()
/// @description This function is called in the "Room End" event of the RoomController.
/// It removes all cached collisions and works like a full reset.
function collider_cleanup() {
/// @function construct_or_invoke(_script, args...)
/// @description Tries to create the specified function (succeeds, if it is a constructor),
/// and if that fails, just invokes it and returns the result.
/// All arguments you specify are sent to the function.
function construct_or_invoke(_script, arg1, arg2, arg3,) {
Using the functions ending with _no_rebind
will not rebind methods in the structs to the new struct, instead the method pointers are just copied.
/// @function struct_join(structs...)
/// @description Joins two or more structs together into a new struct.
/// NOTE: This is NOT a deep copy! If any struct contains other struct
/// references, they are simply copied, not recursively converted to new references!
/// ATTENTION! No static members can be transferred! Best use this for data structs only!
/// @param {struct...} any number of structs to be joined together
function struct_join(structs) {
/// @function struct_join_into(target, sources...)
/// @description Integrate all source structs into the target struct by copying
/// all members from source to target.
/// NOTE: This is NOT a deep copy! If source contains other struct
/// references, they are simply copied, not recursively converted to new references!
/// ATTENTION! No static members can be transferred! Best use this for data structs only!
function struct_join_into(target, sources) {
Move multiple members from one struct to another. If you move methods, they will be rebound.
Caution
You can not move static
members!
/// @func struct_move(_source, _target, _members...)
/// @desc Moves members from the _source struct to the _target struct.
/// member existance is checked before move, only existing will be created in _target.
/// If the first _member argument is an array, it is considered, that this array contains
/// the names of the members to move.
function struct_move(_source, _target, _members) {
A comfortable wildcard engine, allowing you to create mask- oder filter-expressions.
Filter character is *
and can be placed like this:
Position | Description | Example |
---|---|---|
* at the start of the string |
Means "string ends with" |
*llo -> hello
|
* at the end of the string |
Means "string starts with" |
he* -> hello
|
* at both ends |
Means "contains" |
*ell* -> hello
|
* somewhere in the middle |
Means "start and end with" |
he*o -> hello but also hey-ho
|
Combine * freely! |
Any number, any place |
He*o*Wo*d* matches Hello, World too |
/// @func string_match(str, wildcard_str)
/// @desc Checks whether a string matches a specific wildcard string.
/// Wildcard character is '*' and it can appear anywhere in the string, any number of times.
/// * at the beginning means "ends_with" (hello -> *llo)
/// * at the end means "starts_with" (hello -> he*)
/// * on both ends means "contains" (hello -> *ell*)
/// * somewhere in the middle means "starts with and ends with" (hello -> he*o)
/// You may combine the above in any way you like! ("Hello, World" -> "He*o*Wo*d*")
/// NOTE: if no '*' is in wildcard_str, then a == exact match counts!
/// Examples:
/// string_match("hello", "hel*") -> true
/// string_match("hello", "*hel*") -> true
/// string_match("hello", "*hel") -> false
/// @returns {bool}
function string_match(str, wildcard_str) {
These are two of the most powerful helper functions.
They interpret a string at runtime against a living object or class instance and return:
- (_compare): whether the specified variable in the instance has the value you are looking for, or
- (_execute): execute the string against the specified object and return the result
/// @func string_interpret_compare(_str, _instance, _with_inheritance = true)
/// @desc Interprets a string that may contain dot-notation, like
/// "myinst.data.name:value" and interprets this against the supplied instance.
function string_interpret_compare(_str, _instance, _with_inheritance = true) {
/// @func string_interpret_execute(_str, _instance)
/// @desc Interprets a string that may contain dot-notation, like
/// "myinst.data.name" and interprets this against the supplied instance.
function string_interpret_execute(_str, _instance) {
Some examples on these two functions:
You supply a string and a value to check. It is interpreted and compared with the instance. The result is a boolean, telling you, whether the values match or not.
string_interpret_compare("data.initialization.class_name:BossEnemy", objInstance);
This will look into objInstance
and check, if it has a data.initialization.class_name
(following the entire call chain, the dot-notation!).
The value of this class_name is compared to "BossEnemy". If the object is a BossEnemy, this returns true, otherwise false.
Similar to _compare, but this function will return you the value.
string_interpret_execute("data.initialization.class_name", objInstance);
Calling this will return you, whatever value objInstance
has stored in its data.initialization.class_name
member.
This is a very convenient way to get information out of an object on a string basis, like when you load those strings from a json file in highly configurable games, where most of the runtime data is configured through json files.
/// @func extract_init(_struct, _remove = false)
/// @desc Extracts an "init" member of the specified struct, if it exists,
/// and returns it. Optionally, the "init" is also removed from the _struct.
/// This is especially useful for instance creation, when you read data
/// from json configuration files, that might or might-not contain init structs.
/// Use it like "instance_create(...., extract_init(json));
function extract_init(_struct, _remove = false) {
These functions serialize/deserialize an assets name to its reference and vice versa for saving object/asset types to json and load them back.
/// @func asset_from_string(_asset_name)
/// @desc Recreates the asset index from a type name read from json or a savegame
function asset_from_string(_asset_name) {
/// @func asset_to_string(_asset)
/// @desc Convert an asset to a string to be saved to json or a savegame
function asset_to_string(_asset) {
These functions convert a color to and from a 3-elements-array to be saved or restored from a json file.
/// @func color_from_array(_rgb_array)
/// @desc Reads a 3-element-array into an rgb color from a json array
function color_from_array(_rgb_array) {
/// @func color_to_array(_color)
/// @desc Converts any color to a 3-element-array, which can be stored in a json
function color_to_array(_color) {
/// @func get_sprite_scale_for(_target_width, _target_height, _into = undefined)
/// @desc Calculates the required x/y scale for the sprite
/// to reach the desired width/height.
/// Result is written to _into_coord2 (if supplied) and returned as Coord2.
function get_sprite_scale_for(_target_width, _target_height, _into = undefined) {
/// @func scale_sprite_to(target_width, target_height)
/// @desc Scale an instances' sprite so that it has the desired dimensions.
function scale_sprite_to(target_width, target_height) {
/// @func scale_sprite_aspect_width(new_width)
/// @desc Scale an instances' sprite to a new width by keeping the aspect ratio
/// (this means, the yscale will be calculated based on the new xscale)
function scale_sprite_aspect_width(new_width) {
/// @func scale_sprite_aspect_height(new_height)
/// @desc Scale an instances' sprite to a new height by keeping the aspect ratio
/// (this means, the xscale will be calculated based on the new yscale)
function scale_sprite_aspect_height(new_height) {
This pro-only feature gives you a very powerful tool to run loops through large data lists (or complicated, expensive calculations), without freezing/stalling the render loop of gamemaker.
You tell the function the maximum amount of microseconds for each iteration, and it will interrupt the loop, let a frame render, and continue processing in the next frame. This is as-close-as-you-can-get to multitasking in GameMaker.
/// @func foreach_async(
/// _struct_or_array,
/// _micros_per_frame = 500,
/// _item_callback,
/// _finished_callback = undefined)
/// @desc Run through a struct or an array with interrupts
/// every _micros_per_frame microseconds, rendering the next frame.
/// This is especially useful on animated loading screens and for all
/// long running operations that shall keep the render loop alive
/// without stalling the game.
function foreach_async(
_struct_or_array,
_micros_per_frame = 500,
_item_callback,
_finished_callback = undefined) {
You supply an array or struct to process, the number of microseconds (not millis!) for each iteration, and two callbacks.
The first one, the _item_callback
will be invoked for every item from the loop and you can do your calculations with it.
The second one gets invoked only once, when the entire loop has completed.
For every item in the array or struct, this callback gets invoked.
It receives 3 or 4 arguments, depending on the type:
# | Argument | Description |
---|---|---|
Array Mode: 3 Arguments | ||
0 | _item |
The currently processed item. This is equal to array[i]
|
1 | _i |
The index in the array, which is currently processed |
2 | _array |
The array, which is currently processed |
Struct Mode: 4 Arguments | ||
0 | _item |
The currently processed item. This is equal to struct[$ name[i]]
|
1 | _name |
The member name of the currently processed struct item |
2 | _i |
The index in the names-array, which is currently processed |
3 | _struct |
The struct, which is currently processed |
Here is an example:
// Processing an array
foreach_async(myarray, 500,
function(_item, _i, _array) {
_array[_i] = _item + 1;
},
function(_array) {
ilog("Processing finished.");
}
}
// Processing a struct
foreach_async(mystruct, 500,
function(_item, _name _i, _struct) {
_struct[$ _name] = _item + 1;
},
function(_struct) {
ilog("Processing finished.");
}
}
As you could see in the example above, the _finished_callback
receives 1 argument: The entire array (or struct), when processing is finished.