Code Bank - share useful code!

S

Sam (Deleted User)

Guest
This workaround has limited application, as they may fix this in the future. But draw_rectangle() is currently bugged in GMS and it draws one pixel width of an outline regardless of how many pixels it should be upscaling depending on room and view related stuff. For example, you're view could be zoomed in by 200% and the line would make sense to be 2 pixels thick, instead it is still one pixel thick.

Here's a function you can use that respects room and view zooming and window resizing / scaling....

GML:
function draw_rectangle_patched(x1, y1, x2, y2, outline) {
  left   = x1;
  top    = y1;
  width  = x2 - x1;
  height = y2 - y1;
  if (!outline) {
    draw_sprite_stretched_ext(spr_DrawRectFix, 0, left, top, width, height, draw_get_color(), draw_get_alpha());
  } else {
    draw_sprite_stretched_ext(spr_DrawRectFix, 0, left - 2, top - 2, width + 2, 2, draw_get_color(), draw_get_alpha());
    draw_sprite_stretched_ext(spr_DrawRectFix, 0, left - 2, top - 2, 2, height + 2, draw_get_color(), draw_get_alpha());
    draw_sprite_stretched_ext(spr_DrawRectFix, 0, left + width, top, 2, height + 2, draw_get_color(), draw_get_alpha());
    draw_sprite_stretched_ext(spr_DrawRectFix, 0, left, top + height, width + 2, 2, draw_get_color(), draw_get_alpha());
  }
}
....where as "spr_DrawRectFix" is a 1x1 pixel solid white color filled sprite.

Replace "2" with the desired pixel thickness -- search -> replace all (whole word only).

Probably other drawing primitives are also broken in the same manner. You can fix that similarly to this approach.
 

chamaeleon

Member
Useful code? I don't know.. Just inspired by gnysek's profile post about array_create().
GML:
function array_create_fn(size, fn) {
    if (is_undefined(size)) throw("No size provided to array_create_fn()!");
    if (is_undefined(fn)) throw("No function provided to array_create_fn()!");
    var arr = array_create(size);
    for (var i = 0; i < size; i++) {
        switch (argument_count) {
            case  2: arr[i] = fn(i); break;
            case  3: arr[i] = fn(i, argument[2]); break;
            case  4: arr[i] = fn(i, argument[2], argument[3]); break;
            case  5: arr[i] = fn(i, argument[2], argument[3], argument[4]); break;
            case  6: arr[i] = fn(i, argument[2], argument[3], argument[4], argument[5]); break;
            case  7: arr[i] = fn(i, argument[2], argument[3], argument[4], argument[5], argument[6]); break;
            case  8: arr[i] = fn(i, argument[2], argument[3], argument[4], argument[5], argument[6], argument[7]); break;
            case  9: arr[i] = fn(i, argument[2], argument[3], argument[4], argument[5], argument[6], argument[7], argument[8]); break;
            case 10: arr[i] = fn(i, argument[2], argument[3], argument[4], argument[5], argument[6], argument[7], argument[8], argument[9]); break;
            case 11: arr[i] = fn(i, argument[2], argument[3], argument[4], argument[5], argument[6], argument[7], argument[8], argument[9], argument[10]); break;
            default: throw("Too many arguments provided to array_create_fn()!"); break;
        }
    }
    return arr;
}
GML:
var arr = array_create(10, []);
for (var i = 0; i < array_length(arr); i++) { arr[i][@ 0] = i; }
show_debug_message(arr); // all entries point to the same one-element array

var arr = array_create_fn(10, function(i) { return []; });
for (var i = 0; i < array_length(arr); i++) { arr[i][@ 0] = i; }
show_debug_message(arr); // each entry is its own one-element array

var arr = array_create_fn(10, function(i, n) { return power(i, n); }, 2);
show_debug_message(arr);
Code:
[ [ 9 ],[ 9 ],[ 9 ],[ 9 ],[ 9 ],[ 9 ],[ 9 ],[ 9 ],[ 9 ],[ 9 ] ]
[ [ 0 ],[ 1 ],[ 2 ],[ 3 ],[ 4 ],[ 5 ],[ 6 ],[ 7 ],[ 8 ],[ 9 ] ]
[ 0,1,4,9,16,25,36,49,64,81 ]
 
Last edited:

FoxyOfJungle

Kazan Games
Run many instances of your game using this batch file, useful for Multiplayer games..
C:
SET PROJECTNAME=Game_Name

:: -----------------------------------------------------------------------------------------------------
@echo off
SET RUNNER_PATH=C:\ProgramData\GameMakerStudio2\Cache\runtimes
SET PLATFORM=windows
FOR /F "DELIMS=" %%F IN ('DIR /B /OD /TW Y:\%PROJECTNAME%*') DO (SET FILE=%%F)
FOR /F "DELIMS=" %%I IN ('DIR /AD /B /OD /TC %RUNNER_PATH%') DO SET LATEST_VERSION=%%I
START C:\ProgramData\GameMakerStudio2\Cache\runtimes\%LATEST_VERSION%\%PLATFORM%\Runner.exe -game Y:\%FILE%\%PROJECTNAME%.win
:: ------------------------------------------------------------------------------------------------------
  • Copy the code above, create a file with the desired name, put .bat at the end and paste the code. Example: "OpenTwoInstances.bat"
  • You just have to change the name "Game_Name" to your game's name.
  • Tip: You can place this file in your game's Included Files and execute it using execute_shell() (extension) when a key is pressed.



Works on any version of runtime and GameMaker (old Studio 2).
 
Last edited:

Bart

WiseBart
GameMaker has no built-in function to retrieve the size in bytes of a vertex format, yet the information is there somewhere.
The following function works out the size in bytes of a given vertex format by copying a single vertex from a buffer and then getting the size of the resulting vertex buffer:
GML:
function vertex_format_size(format) {
    static buff = buffer_create(256, buffer_fixed, 1);   // Fairly arbitrary size, static so we don't have to recreate every time
    var vb = vertex_create_buffer_from_buffer_ext(
        buff, format, 0, 1                               // A single vertex takes up the number of bytes of the format
    );
    var size = vertex_get_buffer_size(vb);               // How many bytes did we get?
    vertex_delete_buffer(vb);
    return size;
}
Example use:
GML:
// 3D position only
vertex_format_begin();
vertex_format_add_position_3d();
format = vertex_format_end();

size = vertex_format_size(format);
show_debug_message(size);    // 12

// Default passthrough format
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_colour();
vertex_format_add_texcoord();
format = vertex_format_end();

size = vertex_format_size(format);
show_debug_message(size);    // 24
 

chamaeleon

Member
This is Windows specific, and not "useful code", but could be "useful".

If you have compiled and exported an executable that has show_debug_message() statements in it and you want to see those messages you can change the executable subsystem to console using editbin.exe. Open up a Visual Studio development prompt and change to the directory where you have extracted/installed the executable
Code:
> cd C:\Temp\Playground
> editbin /subsystem:console .\Playground.exe
GML:
function fac(n) {
    if (n < 2) {
        show_debug_message("fac(" + string(n) + ") = 1");
        return 1;
    } else {
        show_debug_message("fac(" + string(n) + ") = " + string(n) + " * fac(" + string(n-1) + ")");
        return n*fac(n-1);
    }
}

show_debug_message(fac(5));
Code:
C:\Temp\Playground > .\Playground.exe
Setting scheduler resolution to 1
Attempting to set gamepadcount to 12
DirectX11: Using hardware device
Collision Event time(microsecs)=14
fac(5) = 5 * fac(4)
fac(4) = 4 * fac(3)
fac(3) = 3 * fac(2)
fac(2) = 2 * fac(1)
fac(1) = 1
120
Total memory used = 2403947(0x0024ae6b) bytes
**********************************.
Entering main loop.
**********************************.
 

Alice

Darts addict
Forum Staff
Moderator
I've found myself posting these file-reading/writing and JSON-reading/writing functions on multiple occasions, so I figured I might as well post these here.
This includes file functions to reading all text content, writing all text content, loading a JSON file to a value and writing a value to a JSON file.
GML:
/// @function file_read_all_text(filename)
/// @description Reads entire content of a given file as a string, or returns undefined if the file doesn't exist.
/// @param {string} filename        The path of the file to read the content of.
function file_read_all_text(_filename) {
    if (!file_exists(_filename)) {
        return undefined;
    }
    
    var _buffer = buffer_load(_filename);
    var _result = buffer_read(_buffer, buffer_string);
    buffer_delete(_buffer);
    return _result;
}

/// @function file_write_all_text(filename,content)
/// @description Creates or overwrites a given file with the given string content.
/// @param {string} filename        The path of the file to create/overwrite.
/// @param {string} content            The content to create/overwrite the file with.
function file_write_all_text(_filename, _content) {
    var _buffer = buffer_create(string_length(_content), buffer_grow, 1);
    buffer_write(_buffer, buffer_string, _content);
    buffer_save(_buffer, _filename);
    buffer_delete(_buffer);
}

/// @function json_load(filename)
/// @description Loads a given JSON file into a GML value (struct/array/string/real).
/// @param {string} filename        The path of the JSON file to load.
function json_load(_filename) {
    var _json_content = file_read_all_text(_filename);
    if (is_undefined(_json_content))
        return undefined;
    
    try {
        return json_parse(_json_content);
    } catch (_) {
        // if the file content isn't a valid JSON, prevent crash and return undefined instead
        return undefined;
    }
}

/// @function json_save(filename,value)
/// @description Saves a given GML value (struct/array/string/real) into a JSON file.
/// @param {string} filename        The path of the JSON file to save.
/// @param {any} value                The value to save as a JSON file.
function json_save(_filename, _value) {
    var _json_content = json_stringify(_value);
    file_write_all_text(_filename, _json_content);
}
 
S

Sam (Deleted User)

Guest
Create and extract tarballs. Uses DialogModule, FileManager, and xProcess, from itch.io.
(Marketplace uploader is broken, so please do not get them from there, as that source is outdated).
Compatible with Windows, macOS, and Linux. May need bsdtar installed on non-Windows.

GML:
function tar_encode(infile, outfile) {
  var f = (string_count(" ", outfile)) ? "\"" + string_replace_all(outfile, "\\", "\\\\") + "\"" : outfile;
  var d = (string_count(" ", infile)) ? "\"" + string_replace_all(filename_dir(filename_dir(infile)), "\\", "\\\\") + "\"" : filename_dir(filename_dir(infile));
  var n = (string_count(" ", infile)) ? "\"" + filename_name(filename_dir(infile)) + "\"" : filename_name(filename_dir(infile));
  return ProcessExecute("tar -cvf " + f + " -C " + d + " " + n);
}

function tar_decode(infile, outfile) {
  var f = (string_count(" ", outfile)) ? "\"" + string_replace_all(outfile, "\\", "\\\\") + "\"" : outfile;
  var d = (string_count(" ", infile)) ? "\"" + string_replace_all(infile, "\\", "\\\\") + "\"" : infile;
  return ProcessExecute("tar -xvf " + d + " -C " + f);
}

widget_set_owner(hwnd_main); var pid = 0;
widget_set_button_name(btn_yes, "Create");
widget_set_button_name(btn_no, "Extract");
widget_set_icon(working_directory + "icon.png");
var action = show_question_cancelable("What would you like to do?"), filter = "Tarballs (*.tar)|*.tar";
if (action == -1)  { game_end(); exit; }
var infile = (action) ? get_directory("") : get_open_filename_ext(filter, "Untitled.tar", working_directory, ""); if (infile == "") { game_end(); exit; }
var outfile = (action) ? get_save_filename_ext(filter, "Untitled.tar", working_directory, "") : get_directory(""); if (outfile == "") { game_end(); exit; }
if (action) { pid = tar_encode(infile, outfile); }
else { pid = tar_decode(infile, outfile); }

if (pid != 0) {
  show_debug_message(ExecutedProcessReadFromStandardOutput(pid));
  FreeExecutedProcessStandardOutput(pid);
  FreeExecutedProcessStandardInput(pid);
}
Demo project here.
 
Last edited by a moderator:

chamaeleon

Member
Generalized picking an item out of a collection with variable frequency.
GML:
function RandomBag() constructor {
    bag = [];
    sum = 0;
  
    static add = function(thing, frequency) {
        array_push(bag, { thing: thing, frequency: frequency });
        sum += frequency;
    }
  
    static remove = function(thing) {
        var i = 0;
        repeat array_length(bag) {
            if (bag[i].thing == thing) {
                sum -= bag[i].frequency;
                array_delete(bag, i, 1);
                break;
            }
            i++;
        }
    }
  
    static pick = function() {
        var accum = 0;
        var n = random(sum);
        var i = 0;
        repeat array_length(bag) {
            accum += bag[i].frequency;
            if (n <= accum) {
                break;
            }
            i++;
        }
        return bag[i].thing;
    }
}
GML:
var bag = new RandomBag();

bag.add("Foo", 10);
bag.add("Bar", 20);
bag.add("Baz", 70);

var foo_count = 0;
var bar_count = 0;
var baz_count = 0;

repeat 10000 {
    var thing = bag.pick();
    if (thing == "Foo") foo_count++;
    else if (thing == "Bar") bar_count++;
    else if (thing == "Baz") baz_count++;
}

show_debug_message("Foo count is " + string(foo_count));
show_debug_message("Bar count is " + string(bar_count));
show_debug_message("Baz count is " + string(baz_count));
Code:
Foo count is 1012
Bar count is 2017
Baz count is 6971
10 + 20 + 70 = 100. Foo should be picked 10 times out of 100 on average, Bar should be picked 20 times out of 100, and Baz should be picked 70 times out of 100.

The frequencies does not need to add up to any particular target like 100. It's all relative between each entry and the total sum.

Could be used for picking random instances for creation
GML:
var bag = new RandomBag();
bag.add(obj_foo, 4);
bag.add(obj_bar, 1);
instance_create_layer(x, y, "Instances", bag.pick());
The above code will create obj_foo 80% (4/(1+4)) of time and obj_bar 20% (1/(1+4)) of the time.
 

gnysek

Member
Quick setup for draw (2.3.7+, as this uses new omitting args + nullish operator features):
GML:
function draw_setup(color = undefined, alpha = undefined, font = undefined, halign = undefined, valign = undefined) {
    draw_set_color(color ?? draw_get_color());
    draw_set_alpha(alpha ?? draw_get_alpha());
    draw_set_font(font ?? draw_get_font());
    draw_set_halign(halign ?? draw_get_halign());
    draw_set_valign(valign ?? draw_get_valign());
}
example usages:
GML:
//color + alpha change
draw_setup(c_red, 0.5);
// change font aligment only
draw_setup(,,,fa_center,fa_middle)
// change font + alpha + vertical align
draw_setup(,0.5,font_something,,fa_bottom);
 
S

Sam (Deleted User)

Guest
In case anyone missed single runtime windows executables from GMS 1.4 and below:

Code:
Icon "${ICON_FILE}"
RequestExecutionLevel user
OutFile "${INSTALLER_FILENAME}"
SilentInstall silent
Section "${PRODUCT_NAME}"
    SetOutPath `$LOCALAPPDATA\__${PRODUCT_NAME}__`
    File /r "${SOURCE_DIR}\*.*"
    ExecWait `$LOCALAPPDATA\__${PRODUCT_NAME}__\${EXE_NAME}.exe`
    RMDir /r `$LOCALAPPDATA\__${PRODUCT_NAME}__`
SectionEnd
I added underscores to ${PRODUCT_NAME} so the sandbox directory doesn't clash, which would cause your saved data get removed in between runs.

Replace your NSIS script in Windows Game Options with the code above and compile as an installer. Wammo. Silent/invisible self-extracting single runtime exe.
 
Last edited by a moderator:
  • Like
Reactions: Yal

Evanski

Raccoon Lord
Forum Staff
Moderator
some quick-hack easy to slap into a project Pause menu system
of course in the draw event you draw your pause menu
and make sure code runs for what you want

but here is the main "pause" functionality for you
saved my ass this gmc jam

GML:
//====== create event ======

//flags to prevent crash and so step code runs once
can_pause = true;
pause_sprite = -4;

//==========================

//====== step event =======

//would check for a keyboard press here then run code below
if (can_pause){
        global.GAMEPaused = !global.GAMEPaused;   

        //checks if a screenshot does not exist
        if !(file_exists(pause_sprite)){
            pause_png = (working_directory + "\\ScreenShots\\Screen_pause.png");
            screen_save(pause_png);
    
            pause_sprite = sprite_add(pause_png,1,false,false,0,0);
        }
        //resets flags so this only runs once
        alarm[0] = GAMESPEED;
        can_pause = false;
    }
}

//checks global flag and operates based on the boolean
if (global.GAMEPaused){
    
    //turns all objects off
    instance_deactivate_all(true);
    
    //activate objects you dont want deactivated here
    
}else{
    //if gamepause global flag is false

    //checks to see if our pause sprite variable is not a default flag and is a sprite that exists
    if (pause_sprite != -4){
        if (file_exists(pause_sprite)){
            //cleans itself up for the next pause
            sprite_delete(pause_sprite);
            file_delete(pause_png);
        }
    }
    
    //reactivates everything
    instance_activate_all();   
}
//============================
 

FoxyOfJungle

Kazan Games
I wrote some useful functions for structs:

GML:
// copy all struct variables and values to another
function struct_copy(src, dest) {
    if is_struct(src) {
        if !is_struct(dest) dest = {};
        var _names = variable_struct_get_names(src);
        var _size = array_length(_names);
     
        for (var i = 0; i < _size; ++i) {
            var _name = _names[i];
            variable_struct_set(dest, _name, variable_struct_get(src, _name));
        }
        return true;
    }
    return -1;
}

// clear the struct without deleting it
function struct_clear(struct) {
    if is_struct(struct) {
        var _names = variable_struct_get_names(struct);
        var _size = array_length(_names);
 
        for (var i = 0; i < _size; ++i) {
            var _name = _names[i];
            variable_struct_remove(struct, _name);
        }
    }
}

// clear all variables and replace with new struct data
function struct_replace(src, dest) {
    if is_struct(src) && is_struct(dest) {
        struct_clear(dest);
        struct_copy(src, dest);
        return true;
    }
    return -1;
}

// copy one struct to many others
function struct_multiply(src, dest_array) {
    if is_array(dest_array) {
        var _size = array_length(dest_array);
 
        for (var i = 0; i < _size; ++i) {
            var _dest = dest_array[i];
            struct_copy(src, _dest);
        }
        return true;
    }
    return -1;
}

// compare if a struct has variables equal to another
function struct_compare(struct1, struct2) {
    var _equal = false;
    if is_struct(struct1) && is_struct(struct2) {
        var _names1 = variable_struct_get_names(struct1);
        var _names2 = variable_struct_get_names(struct2);
        var _size1 = array_length(_names1);
        var _size2 = array_length(_names2);
        var _total = (_size1+_size2);
        var _n = 0;
 
        for (var i = 0; i < _size1; ++i) {
            var _name1 = _names1[i];
            for (var j = 0; j < _size2; ++j) {
                var _name2 = _names2[i];
                if (_name1 == _name2) {
                    _n += 1;
                }
            }
        }
        if (_n >= _total) {
            _equal = true;
        }
    }
    return _equal;
}

// compare if a struct has variables and values equal to another
function struct_compare_vars(struct1, struct2) {
    var _s1 = json_stringify(struct1);
    var _s2 = json_stringify(struct2);
    var _equal = (_s1 == _s2) ? true : false;
    return _equal;
}

// add variable to struct
function struct_push(struct, name, value) {
    var _num = 0;
    if variable_struct_exists(struct, name) {
        while (variable_struct_exists(struct, name+string(_num))) {
            _num += 1;
        }
    } else {
        _num = "";
    }
    struct[$ name+string(_num)] = value;
}

// get and remove this variable from struct
function struct_pop(struct, name) {
    var _value = struct[$ name];
    variable_struct_remove(struct, name);
    return _value;
}

// get value from json without returning undefined (useful for savegames data loading)
function struct_var_read(struct, name, default_var) {
    var _df = default_var;
    if variable_struct_exists(struct, name) {
        var _val = struct[$ name];
        if !is_undefined(_val) {
            return _val;
        }
    }
    return _df;
}

// check if struct child or variable exists
function struct_child_exists(struct, name) {
    return (variable_struct_exists(struct, name) ||
    is_struct(struct[$ name])) ? true : false;
}

print() function here.


Usage Example:

GML:
// ### Struct Operations ###
print("-------------------------------------------------");
global.main_struct = {
    highscores : [
        {name : "George", points : 100},
        {name : "Jack", points : 200},
        {name : "Foxy", points : 335},
    ],
    funct : function() {
        var test = true;
    },
    data : {
        icon : "gamemaker",
        url : "https://website.com",
    },
    value : 150,
}

struct_test = {
    EXISTING_VAR : 99999,
}
print("Original Struct 1: ", global.main_struct);
print("Original Struct 2: ", struct_test);


// copy
struct_copy(global.main_struct, struct_test);
print("Copied struct: ", struct_test);

// compare
var test = struct_compare(global.main_struct, struct_test);
print("Equal? ", test);

// struct clear
struct_clear(struct_test);
print("Struct cleared: ", struct_test);

// struct push
var _test_name = "enemies";
var _test_vars = ["robot", "village", "wolf"];
struct_push(struct_test, _test_name, _test_vars);
print("Struct push: ", struct_test);

// struct push with same name
var _struct = {
    aa : 10,
    bb : 20,
    cc : 30,
}
struct_push(_struct, "score", 99);
struct_push(_struct, "score", 99);
struct_push(_struct, "score", 99);
print("Struct push, same name", _struct);

// compare vars
var test = struct_compare_vars(global.main_struct, struct_test);
print("Struct and variables equal? ", test);

// struct replace
struct_replace(global.main_struct, struct_test);
print("Struct Replace: ", struct_test);

// struct multiply
var _original = {
    aa : 10,
}
var s1 = {};
var s2 = {};
var s3 = {};
struct_multiply(_original, [s1, s2, s3]);
print("Struct Multiply: ", s1, s2, s3);

// struct pop
var _struct = {
    number1 : 100,
    str1 : "test",
    arrayy : [50, 350, 88],
    letters : {
        aa : 5,
        bb : 6,
        cc : 7,
    }
}

print("Struct Pop, before: ", _struct);
var test = struct_pop(_struct, "arrayy");

print("Struct Pop, variable: ", test);
print("Struct Pop, after: ", _struct);

var test = struct_pop(_struct.letters, "bb");
print("Popped child variable: ", test);

print("Struct Pop, after all: ", _struct);


// for loading game data
my_apples = 0;
my_oranges = 0;
my_potatoes = 0;
my_array = [];

var _json = "{\"myObj\": { \"apples\":10, \"oranges\":12, \"potatoes\":100000 }, \"myArray\":[0, 1, 2]}";
var _data = json_parse(string(_json));

if struct_child_exists(_data, "myObj") {
    var _struct = _data.myObj;
    my_apples = struct_var_read(_struct, "apples", my_apples);
    my_oranges = struct_var_read(_struct, "oranges", my_oranges);
    my_potatoes = struct_var_read(_struct, "potatoes", my_potatoes);
}
 
if struct_child_exists(_data, "myArray") {
    var _array = _data.myArray;
    my_array = _array;
}

print("Vars: ", my_apples, my_oranges, my_potatoes, "-", "Array: ", my_array);
-----------------------------------

RESULT:
Original Struct 1: | { data : { icon : "gamemaker", url : "https://website.com" }, value : 150, highscores : [ { name : "George", points : 100 },{ name : "Jack", points : 200 },{ name : "Foxy", points : 335 } ], funct : function gml_Script_anon____struct___1_gml_Object_Object1_Create_0_252____struct___1_gml_Object_Object1_Create_0 } |
Original Struct 2: | { EXISTING_VAR : 99999 } |
Copied struct: | { data : { icon : "gamemaker", url : "https://website.com" }, value : 150, highscores : [ { name : "George", points : 100 },{ name : "Jack", points : 200 },{ name : "Foxy", points : 335 } ], EXISTING_VAR : 99999, funct : function gml_Script_anon____struct___1_gml_Object_Object1_Create_0_252____struct___1_gml_Object_Object1_Create_0 } |
Equal? | 1 |
Struct cleared: | { } |
Struct push: | { enemies : [ "robot","village","wolf" ] } |
Struct push, same name | { score : 99, score0 : 99, aa : 10, score1 : 99, bb : 20, cc : 30 } |
Struct and variables equal? | 0 |
Struct Replace: | { data : { icon : "gamemaker", url : "https://website.com" }, value : 150, highscores : [ { name : "George", points : 100 },{ name : "Jack", points : 200 },{ name : "Foxy", points : 335 } ], funct : function gml_Script_anon____struct___1_gml_Object_Object1_Create_0_252____struct___1_gml_Object_Object1_Create_0 } |
Struct Multiply: | { aa : 10 } | { aa : 10 } | { aa : 10 } |
Struct Pop, before: | { str1 : "test", arrayy : [ 50,350,88 ], letters : { aa : 5, bb : 6, cc : 7 }, number1 : 100 } |
Struct Pop, variable: | [ 50,350,88 ] |
Struct Pop, after: | { str1 : "test", letters : { aa : 5, bb : 6, cc : 7 }, number1 : 100 } |
Popped child variable: | 6 |
Struct Pop, after all: | { str1 : "test", letters : { aa : 5, cc : 7 }, number1 : 100 } |
Vars: | 10 | 12 | 100000 | - | Array: | [ 0,1,2 ] |



Edit:
I'll also leave here a script made by: @Sad posted here:
(I don't recommend using ds_maps anymore, use structs!)

GML:
function ds_map_from_struct(struct) {
    var ds_map = ds_map_create();
    var keys = variable_struct_get_names(struct);
    for (var i = 0; i < array_length(keys); i++) {
        var key = keys[i];
        var value = variable_struct_get(struct, key);
        ds_map_add(ds_map, key, value);
    }
    return ds_map;
}

function struct_from_map(ds_map) {
    var struct = {};
    var keys = ds_map_keys_to_array(ds_map);
    for (var i = 0; i < array_length(keys); i++) {
        var key = keys[i];
        var value = ds_map_find_value(ds_map, key);
        variable_struct_set(struct, key, value);
    }
    return struct;
}

function struct_from_array(array) {
    var struct = {};
    for (var i = 0; i < array_length(array); i++) {
        variable_struct_set(struct, i, array[i]);
    }
    return struct;
}

function struct_from_instancevars(instanceid) {
    var struct = {};
    var keys = variable_instance_get_names(instanceid);
    for (var i = 0; i < array_length(keys); i++){
        var key = keys[i];
        var value = variable_instance_get(instanceid, key);
        variable_struct_set(struct, key, value);
    }
    return struct;
}
 
Last edited:
S

Sam (Deleted User)

Guest
Added an async twist of the file_find_* family of functions, (renamed to directory_contents_*), available with my file manager extension with Windows/Mac/Linux support. Free and open source. MIT License.

GML:
/// Helper Scripts:
function directory_contents_async_create(dname, pattern, includedirs, recursive) {
  global.directory_contents_index = 0;
  global.directory_contents_array = undefined;
  directory_contents_first_async(dname, pattern, includedirs, recursive);
}

function directory_contents_async_result(cancel_on_escape) {
  if (cancel_on_escape && keyboard_check_released(vk_escape)) {
    directory_contents_set_completion_status(true);
  }
  if (directory_contents_get_completion_status()) {
    while (global.directory_contents_index < directory_contents_get_length()) {
      global.directory_contents_array[global.directory_contents_index] = directory_contents_next();
      global.directory_contents_index++;
    }
    directory_contents_close();
  }
  return global.directory_contents_array;
}

function directory_contents_async_destroy() {
  global.directory_contents_array = undefined;
}
GML:
/// Create Event:
// random order for directory contents list
directory_contents_set_order(DC_RAND);
// begin search for files matching given criteria
directory_contents_async_create(directory_get_current_working(), "*.*", true, true);
GML:
///Step Event:
// update globals; escape key cancels search
arr = directory_contents_async_result(true);
if (!is_undefined(arr) && is_array(arr)) {
  for (var i = 0; i < array_length(arr); i++) {
    // print directory contents
    show_debug_message(arr[i]);
  }
  // set array back to undefined
  directory_contents_async_destroy();
}
Note: directory_contents_set_order() can have one of these values passed to it:
GML:
  // Directory Contents
  #define DC_ATOZ   0 // Alphabetical Order
  #define DC_ZTOA   1 // Reverse Alphabetical Order
  #define DC_AOTON  2 // Date Accessed Ordered Old to New
  #define DC_ANTOO  3 // Date Accessed Ordered New to Old
  #define DC_MOTON  4 // Date Modified Ordered Old to New
  #define DC_MNTOO  5 // Date Modified Ordered New to Old
  #define DC_COTON  6 // Date Created Ordered Old to New
  #define DC_CNTOO  7 // Date Created Ordered New to Old
  #define DC_RAND   8 // Random Order
Download this update to my file manager extension here:

 
Last edited by a moderator:

chamaeleon

Member
A naive translation of the Poisson Disk sampling algorithm from https://sighack.com/post/poisson-disk-sampling-bridsons-algorithm
GML:
/// @function PoissonDisk(width, height, radius, k);
/// @param {number} width The width of the poission distribution
/// @param {number} height The height of the poission distribution
/// @param {number} radius The radius for preventing overlap
/// @param {number} k The number of points to try in each grid cell
/// @description Create a struct for poisson distribution
function PoissonDisk(_width, _height, _radius, _k) constructor {
    width = _width;
    height = _height;
    radius = _radius;
    k = _k;
    /* Figure out no. of cells in the grid for our canvas */
    cellsize = floor(radius/sqrt(2));
    ncells_width = ceil(width/cellsize) + 1;
    ncells_height = ceil(height/cellsize) + 1;

    static __isValidPoint = function(grid, cellsize, gwidth, gheight, p, radius) {
        /* Make sure the point is on the screen */
        if (p.x < 0 || p.x >= width || p.y < 0 || p.y >= height)
            return false;

        /* Check neighboring eight cells */
        var xindex = floor(p.x / cellsize);
        var yindex = floor(p.y / cellsize);
        var i0 = max(xindex - 1, 0);
        var i1 = min(xindex + 1, gwidth - 1);
        var j0 = max(yindex - 1, 0);
        var j1 = min(yindex + 1, gheight - 1);

        //log("Checking grid [%1 %2] -- [%3 %4, %5 %6]", xindex, yindex, i0, i1, j0, j1);
        for (var i = i0; i <= i1; i++)
            for (var j = j0; j <= j1; j++)
                if (grid[i][j] != undefined)
                    if (point_distance(grid[i][j].x, grid[i][j].y, p.x, p.y) < radius)
                        return false;

        /* If we get here, return true */
        return true;
    }

    static __insertPoint = function(grid, cellsize, point) {
        var xindex = floor(point.x / cellsize);
        var yindex = floor(point.y / cellsize);
        grid[xindex][yindex] = point;
    }

    /// @function generate();
    /// @description Generate and return an array of poisson distributed points
    static generate = function() {
        var points = [];
        /* The currently "active" set of points */
        var active = [];
        /* Initial point p0 */
        var p0 = { x: random(width), y: random(height) };
        var grid = [];
  
        /* Allocate the grid an initialize all elements to null */
        for (var i = 0; i < ncells_width; i++) {
            grid[i] = array_create(ncells_height, undefined);
        }

        __insertPoint(grid, cellsize, p0);
        array_push(points, p0);
        array_push(active, p0);

        while (array_length(active) > 0) {
            var random_index = irandom(array_length(active)-1);
            var p = active[random_index];
  
            var found = false;
            for (var tries = 0; tries < k; tries++) {
                var theta = random(360);
                var new_radius = random_range(radius, 2*radius);
                var pnewx = p.x + new_radius * dcos(theta);
                var pnewy = p.y + new_radius * dsin(theta);
                var pnew = { x: pnewx, y: pnewy };
    
                if (!__isValidPoint(grid, cellsize, ncells_width, ncells_height, pnew, radius)) {
                    continue;
                }
    
                array_push(points, pnew);
                __insertPoint(grid, cellsize, pnew);
                array_push(active, pnew);
                found = true;
                break;
            }
  
            /* If no point was found after k tries, remove p */
            if (!found) {
                array_delete(active, random_index, 1);
            }
        }
      
        return points;
    }
}
Sample usage
Create event
GML:
var disk = new PoissonDisk(400, 400, 32, 10);
points = disk.generate();
Draw event
GML:
var i = 0;
repeat array_length(points) {
    draw_circle(points[i].x, points[i].y, 8, true);
    i++;
}
Array content returned from the generate() function.
Code:
[ { x : 360.77, y : 323.51 },{ x : 320.31, y : 310.53 },{ x : 318.73, y : 362.06 }, ... ]
Poisson.PNG
 
Last edited:

chamaeleon

Member
Reading lines from a buffer and finding the position of a substring in a buffer
GML:
global.buffer_string_copy = buffer_create(1, buffer_grow, 1); // Remains allocated throughout the lifetime of the program

function buffer_string_readln(buf) {
    var start_pos = buffer_tell(buf);
    var end_pos = start_pos;
    var ch = buffer_peek(buf, end_pos, buffer_u8);
   
    while (end_pos < buffer_get_size(buf) && ch != ord("\n")) {
        ch = buffer_peek(buf, end_pos, buffer_u8);
        end_pos++;
    }
   
    buffer_copy(buf, start_pos, end_pos-start_pos, global.buffer_string_copy, 0);
    buffer_seek(buf, buffer_seek_start, end_pos);
    buffer_seek(global.buffer_string_copy, buffer_seek_start, end_pos-start_pos);
    buffer_write(global.buffer_string_copy, buffer_u8, 0);
    buffer_seek(global.buffer_string_copy, buffer_seek_start, 0);
    var result = buffer_read(global.buffer_string_copy, buffer_string);
    return result;
}

function buffer_check_pos(buf, pos, substr) {
    buffer_copy(buf, pos, string_length(substr), global.buffer_string_copy, 0);
    buffer_seek(global.buffer_string_copy, buffer_seek_start, string_length(substr));
    buffer_write(global.buffer_string_copy, buffer_u8, 0);
    buffer_seek(global.buffer_string_copy, buffer_seek_start, 0);
    var buf_substr = buffer_read(global.buffer_string_copy, buffer_string);
    return substr == buf_substr;
}

function buffer_find_substr(buf, substr) {
    var buf_size = buffer_get_size(buf);
    var pos = buffer_tell(buf);
    while (pos < buf_size) {
        if (buffer_check_pos(buf, pos, substr)) {
            buffer_seek(buf, buffer_seek_start, pos);
            return true;
        }
        pos++;
    }
    return false;
}
Usage
GML:
var buf = buffer_create(1, buffer_grow, 1);
buffer_write(buf, buffer_text, "Hello, World!\n");
buffer_write(buf, buffer_text, "Once upon a time...\n");
buffer_write(buf, buffer_text, "Alice, Bob, and Charlie\n");
buffer_write(buf, buffer_text, "See Spot. See Spot Run. Run, Spot, Run.");
buffer_save(buf, "testing.txt");
buffer_delete(buf);

show_debug_message("Testing Reading Lines");
var buf = buffer_load("testing.txt");
while (buffer_tell(buf) < buffer_get_size(buf)) {
    var line = buffer_string_readln(buf);
    if (string_copy(line, string_length(line), 1) == "\n")
        line = string_copy(line, 0, string_length(line)-1);
    show_debug_message("Line [" + line + "]");
}
buffer_delete(buf);

show_debug_message("Testing finding a substring");
var buf = buffer_load("testing.txt");
if (buffer_find_substr(buf, "Alice")) {
    var pos = buffer_tell(buf);
    var line = buffer_string_readln(buf);
    if (string_copy(line, string_length(line), 1) == "\n")
        line = string_copy(line, 0, string_length(line)-1);
    show_debug_message("Found [" + line + "] at position " + string(pos));
}
buffer_delete(buf);

file_delete("testing.txt");
Code:
Testing Reading Lines
Line [Hello, World!]
Line [Once upon a time...]
Line [Alice, Bob, and Charlie]
Line [See Spot. See Spot Run. Run, Spot, Run.]
Testing finding a substring
Found [Alice, Bob, and Charlie] at position 34
(Yes, creating a new string for every position called for buffer_check_pos() instead of creating a buffer once for substr and using it over and over for comparison is, well, inefficient is a word that comes to mind)
 
Last edited:

Evanski

Raccoon Lord
Forum Staff
Moderator
Probably not the most ground breaking script but if you hate math like me then this is pretty helpful

A simple script that will return the requested percent of a supplied number
GML:
function percent(per=100,value=100){
    /// @function percent(percent,value)
    /// @desc Returns the percent of the given inputs
    /// @param {real} percent A whole number representing the percent you wish to get, will auto convert to percentage
    /// @param {real} value A base number you wish to get the supplied percent of
    
    //convert input number to actual percent
    percentage = (per / 100);
    
    //get the requested percent of the value number
    return_percent = (percentage * value);
    
    return(return_percent);
}

//example code
var per = percent(1,289631);
show_message(per);

game_end();
 
S

Sam (Deleted User)

Guest
As GML gets more and more advanced, the more people are beginning to realize C++ isn't that much more difficult to learn.

Here's something I wrote recently to help put that into perspective:
C++:
/*
** Alternative main.cpp - replace libfiledialogs/filedialogs/main.cpp with below contents
** then if on Windows open the solution in Visual Studio and build with that otherwise run
** the build.sh script if you are on macOS, Linux, FreeBSD, DragonFly, NetBSD, or OpenBSD
*/

#include <iostream> // std::cout, std::endl
#include <string>   // std::string, std::to_string
#include <vector>   // std::vector
#include <cstddef>  // std::size_t

#if defined(_WIN32)
#include "filesystem.hpp" // GHC File System
#endif

#include "ImFileDialogMacros.h" // Easy Localization
#include "filedialogs.h"        // NGS File Dialogs
#include "filesystem.h"         // NGS File System

/* setup home directory
environment variable */
#if !defined(HOME_PATH)
#if defined(_WIN32) // Windows x86 and Window x86-64
#define HOME_PATH "USERPROFILE"
#else // macOS, Linux, FreeBSD, DragonFly, NetBSD, and OpenBSD
#define HOME_PATH "HOME"
#endif
#endif

// for SDL2
#if defined(_WIN32)
#undef main
#endif

// for MSVC
#if defined(_MSC_VER)
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

namespace {
  void init() {
    #if defined(__APPLE__) && defined(__MACH__)
    // hide icon from dock on macOS to match all the other platforms
    [[NSApplication sharedApplication] setActivationPolicy:(NSApplicationActivationPolicy)1];
    [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
    #endif

    // set imgui file dialogs window width and height; default is 600x400 pixels
    ngs::fs::environment_set_variable("IMGUI_DIALOG_WIDTH", std::to_string(800));
    ngs::fs::environment_set_variable("IMGUI_DIALOG_HEIGHT", std::to_string(400));
  
    // load all *.ttf and *.otf fonts of varying languages and combine them into one font from directory
    ngs::fs::environment_set_variable("IMGUI_FONT_PATH", ngs::fs::executable_get_directory() + "fonts");
    ngs::fs::environment_set_variable("IMGUI_FONT_SIZE", std::to_string(24)); // font size for dialogbox

    // setup imgui file dialog favorites config text file absolute pathname
    ngs::fs::environment_set_variable("IMGUI_CONFIG_PATH", ngs::fs::directory_get_temporary_path());
    ngs::fs::environment_set_variable("IMGUI_CONFIG_FILE", "imfiledialog.tmp");
  
    // get favorites config text file absolute pathname
    std::string path = ngs::fs::environment_get_variable("IMGUI_CONFIG_PATH");
    std::string file = ngs::fs::environment_get_variable("IMGUI_CONFIG_FILE");
  
    // if favorites config text file does not exist
    if (!ngs::fs::file_exists(path + "/" + file)) {
      // setup favorites std::vector
      std::vector<std::string> favorites;
      favorites.push_back(ngs::fs::directory_get_desktop_path());
      favorites.push_back(ngs::fs::directory_get_documents_path());
      favorites.push_back(ngs::fs::directory_get_downloads_path());
      favorites.push_back(ngs::fs::directory_get_music_path());
      favorites.push_back(ngs::fs::directory_get_pictures_path());
      favorites.push_back(ngs::fs::directory_get_videos_path());
      // add custom favorites to config text file
      int desc = ngs::fs::file_text_open_write(path + "/" + file);
      if (desc != -1) { // success; file can now be written to
        for (std::size_t i = 0; i < favorites.size(); i++) {
          // write favorite from vector at current index "i"
          ngs::fs::file_text_write_string(desc, favorites[i]);
          ngs::fs::file_text_writeln(desc); // write new line
        }
        // close file descriptor  
        ngs::fs::file_text_close(desc);
      }
    }
  }
} // anonymous namespace

int main() {
  init(); // setup all initialization related settings
  std::cout << ngs::imgui::get_open_filename("Portable Network Graphic (*.png)|\
  *.png|Graphic Interchange Format (*.gif)|*.gif", "Untitled.png") << std::endl;
  return 0;
}
The code and VS solution / build script are all available on GitHub.
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
Some color functions, mostly useful for shader uniforms or stuff like that, but who knows what you might find a use for these for
These have also been added to my toolbox

GML:
function color_conv_hex_rgb(hex=000000){
    /// @function color_conv_hex_rgb(hex)
    /// @desc returns the input hex color to an RGB color
    /// @param {real} hex code of input color
    
    var R = color_get_red(hex);
    var G = color_get_green(hex);
    var B = color_get_blue(hex);
    
    return( make_color_rgb(R,G,B) );
}

function color_conv_hex_rgb_array(hex=000000){
    /// @function color_conv_hex_rgb_array(hex)
    /// @desc returns the input hex color to an RGB color in an array
    /// @param {real} hex code of input color
    
    var R = color_get_red(hex);
    var G = color_get_green(hex);
    var B = color_get_blue(hex);
    
    return( [R,G,B] );
}

function color_conv_hex_hsv(hex=000000){
    /// @function color_conv_hex_hsv(hex)
    /// @desc returns the input hex color to an hsv color
    /// @param {real} hex code of input color
    
    var H = color_get_hue(hex);
    var S = color_get_saturation(hex);
    var V = color_get_value(hex);
    
    return( make_color_hsv(H,S,V) );
}

function color_conv_hex_hsv_array(hex=000000){
    /// @function color_conv_hex_hsv_array(hex)
    /// @desc returns the input hex color to an HSV color in an array
    /// @param {real} hex code of input color
    
    var H = color_get_hue(hex);
    var S = color_get_saturation(hex);
    var V = color_get_value(hex);
    
    return( [H,S,V] );
}

function color_conv_hsv_rgb_array(HSV_color){
    /// @function color_conv_hsv_rgb_array
    /// @desc returns the input hsv color to an rgb array
    /// @param {real} hsv of input color
    
    var R = color_get_red(HSV_color);
    var G = color_get_green(HSV_color)
    var B = color_get_blue(HSV_color);
    
    return( [R,G,B] );
}

function color_conv_rgb_hsv_array(RGB_color){
    /// @function color_conv_rgb_hsv_array
    /// @desc returns the input rgb color to an hsv array
    /// @param {real} rgb  of input color
    
    var H = color_get_hue(RGB_color);
    var S = color_get_saturation(RGB_color);
    var V = color_get_value(RGB_color);
    
    return( [H,S,V] );
}

function color_conv_hsv_rgb(HSV_color){
    /// @function color_conv_hsv_rgb
    /// @desc returns the input hsv color to an rgb
    /// @param {real} hsv of input color
    
    var R = color_get_red(HSV_color);
    var G = color_get_green(HSV_color)
    var B = color_get_blue(HSV_color);
    
    return( make_color_rgb(R,G,B) );
}

function color_conv_rgb_hsv(RGB_color){
    /// @function color_conv_rgb_hsv
    /// @desc returns the input rgb color to an hsv
    /// @param {real} rgb of input color
    
    var H = color_get_hue(RGB_color);
    var S = color_get_saturation(RGB_color);
    var V = color_get_value(RGB_color);
    
    return( make_color_hsv(H,S,V) );
}
 

Evanski

Raccoon Lord
Forum Staff
Moderator
Super simple Pixilation shader

Pixels is a float that is used for the "resolution" of the output, higher the better quality

Uploaded to the same github as my above post

Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float Pixels;

void main()
{   
    
    vec4 base = ( v_vColour * texture2D( gm_BaseTexture, v_vTexcoord ) );
    
    float dx = 15.0 * (1.0 / Pixels);
    float dy = 10.0 * (1.0 / Pixels);
    vec2 Coord = vec2(dx * floor(v_vTexcoord.x / dx), dy * floor(v_vTexcoord.y / dy));

    gl_FragColor = v_vColour * texture2D(gm_BaseTexture, Coord);
}
 

Evanski

Raccoon Lord
Forum Staff
Moderator
Found this useful script

It gets the distance from a point, to an rectangle bounding box object

GML:
function point_distance_to_rectobj( px,py, obj ){
    /*
    by wamingo
    https://www.reddit.com/r/gamemaker/comments/92x9ws/finding_the_distance_from_a_point_to_the_edge_of/
    
        returns distance to the bounding box of an object,
        or 0 if point lies anywhere inside it.

        Note: limited to rectangular bounding boxes!

        based on: https://gamedev.stackexchange.com/questions/44483/how-do-i-calculate-distance-between-a-point-and-an-axis-aligned-rectangle
    */
    ///@func point_distance_to_rectobj( px,py, obj )
    ///@arg px
    ///@arg py
    ///@arg obj
    var n  = obj;
    var cx = max(min(px, n.bbox_right ), n.bbox_left);
    var cy = max(min(py, n.bbox_bottom), n.bbox_top);
    return sqrt( (px-cx)*(px-cx) + (py-cy)*(py-cy) );
}
 

FoxyOfJungle

Kazan Games
Nothing too advanced, but just a quicker way to create an alarm (aka call a function/method after a certain time):

GML:
function invoke(callback, args, time, repetitions=1) {
    var _ts = time_source_create(time_source_game, time, time_source_units_frames, callback, args, repetitions);
    time_source_start(_ts);
    return _ts;
}
 
Last edited:

__bdb__

Member
I'm pretty terrible about minding performance. So I built a utility for memoizing expensive calls...

GML:
function __Callable (func, args) constructor {
    __func = func;
    __args = args;

    toString = function () {
        return string(__func) + string(__args);
    }

    call = function () {
        switch (array_length(__args)) {
            case 0: return __func(); break;
            case 1: return __func(__args[0]); break;
            case 2: return __func(__args[0], __args[1]); break;
            case 3: return __func(__args[0], __args[1], __args[2]); break;
            case 4: throw "Please stop, reconsider the number of arguments you're passing.";
        }
    }

    return self
}

function Memoizable () constructor {
    memoizations = {};

    // @param {__Callable} callable
    __applyMemoization = function (callable) {
        var value = callable.call()
        memoizations[$ callable.toString()] = value;
  
        return value;
    }

    // @param {__Callable} callable
    __retrieveMemoization = function (callable) {
        return memoizations[$ callable.toString()];
    }

    // @param {Function} func
    memoize = function ( func ) {
        return method({
            func: func,
            applyMemoization: __applyMemoization,
            retrieveMemoization: __retrieveMemoization,
        }, function () {
            var count = argument_count;
            var args = array_create(count);
            for (var i = 0; i < argument_count; i++) {
                args[@ i] = argument[i];
            }
            var callable = __Callable(func, args);
            var memoized = retrieveMemoization(callable);
      
            if memoized == undefined {
                return applyMemoization(callable);
            }
            else {
                return memoized;
            }
        });
    }
}
Which can be used like this:

GML:
function MyExpensiveCode () : Memoizable () constructor {
    memoizedPlaceMeeting = memoize (
        function (x, y, obj) {
            return place_meeting(x, y, obj);
        }
    )
}
The `Memoizable` struct will always return the cached value if it's (a) been computed before, and (b) the arguments are the same. Pretty neat :)
 
Last edited:
S

Sam (Deleted User)

Guest
Requires my xProcess / ExecuteShell Extension. Some helper functions to make life easier when constructing a command line to execute.

Execute process from argv[] array (synchronous):
GML:
function ExecProcessFromArgV(kinfo_argv) {
  var cmdline = "";
  for (var i = 0; i < array_length(kinfo_argv); i++) {
    var tmp = string_replace_all(kinfo_argv[i], "\\", "\\\\");
    tmp = "\"" + string_replace_all(tmp, "\"", "\\\"") + "\"";
    if (i < array_length(kinfo_argv) - 1) tmp += " ";
    cmdline += tmp;
  }
  return ProcessExecute(cmdline);
}
Execute process from argv[] array (asynchronous):
GML:
function ExecProcessFromArgVAsync(kinfo_argv) {
  var cmdline = "";
  for (var i = 0; i < array_length(kinfo_argv); i++) {
    var tmp = string_replace_all(kinfo_argv[i], "\\", "\\\\");
    tmp = "\"" + string_replace_all(tmp, "\"", "\\\"") + "\"";
    if (i < array_length(kinfo_argv) - 1) tmp += " ";
    cmdline += tmp;
  }
  return ProcessExecuteAsync(cmdline);
}
The functions above allow you to do stuff like this, for example:
GML:
var argv;
argv[0] = "mspaint.exe";
argv[1] = working_directory + "foobar.png";
ExecProcessFromArgV(argv); // or ExecProcessFromArgVAsync(argv);
 

Joe Ellis

Member
I made a pixel art friendly version of lerp which reduces that jittery effect you often get when the camera moves smoothly with lerp:

GML:
function lerp_smooth(val1, val2, amount)
{

var diff = round(val2 - val1);

return round(val1 + round(diff * amount))

}
I'm not sure if all of the rounds are necessary, I haven't tested it thoroughly yet, cus it seemed to work pretty well straight away

I think why it works\makes it look better with pixel games is cus it removes the subpixel part, and old sega games etc used integer math for everything, or they had much less precision, so they had to like make whole pixel movements spread across certain periods of time, which probably took a while to get right. It's something we take for granted now but there's a certain part that's lost in the "rift" between 16 bit and 32 bit.. I recommend to try and explore other time based integer movements (to go further into this realm)
 
Last edited:

Prrz

Member
Not sure how useful this might be, or if it has been posted before (too lazy to read through everything right now, sorry) - but here's a quite simple script to turn all tiles on a specified layer into a specific object.

GML:
function tiles_to_objects(tile_layer, instance_layer, obj) {
   
    var _layer_id = layer_get_id(tile_layer);
    var _map_id = layer_tilemap_get_id(_layer_id);
    var _tile_w = tilemap_get_tile_width(_map_id);
    var _tile_h = tilemap_get_tile_height(_map_id);
   
    for(var i = 0; i <= room_width; i += _tile_w) {
        for(var j = 0; j <= room_height; j += _tile_h) {
            if(tilemap_get_at_pixel(_map_id, i, j) > 0) {
                instance_create_layer(i, j, instance_layer, obj);
            }
        }
    }
   
    layer_tilemap_destroy(_map_id);
}
 

Prrz

Member
Another maybe useful maybe not-so-useful thing: A physics script for elastic collisions - only to be used inside a collision event in lieu of GM's built in physics. Uses the built in variables speed and direction to keep things simple.
_m1 and _m2 are the relative masses of the colliding objects, _x1, _y1 etc. are the relative positions of each object, other is just well... the keyword other. Maybe someone can find a good use for it, or make it better using true vectors.

// Probably should use a bool to make sure this only happens once per specified interval though, otherwise it might get spooky.
example: elastic_collision(sprite_width, other.sprite_width, x, y, other.x, other.y, other);


GML:
function elastic_collision(_m1, _m2, _x1, _y1, _x2, _y2, _other) {
  
    // <i, j> (x/y) 'vectors'
    var _nx = abs(_x2 - _x1); // Difference between x terms (normalized)
    var _ny = abs(_y2 - _y1); // Difference between y terms (normalized)
  
    var _unx = _nx / sqrt(sq(_nx) + sq(_ny)); // x-unit normal vector
    var _uny = _ny / sqrt(sq(_nx) + sq(_ny)); // y-unit normal vector
  
    var _utx = -_uny; // x-unit tangent vector
    var _uty = _unx; // y-unit tanget vector

    var _v1n = dot_product(_unx, _uny, speed, speed); // projection of velocity for first object onto unit normal vectors
    var _v1t = dot_product(_utx, _uty, speed, speed); // projection of velocity for first object onto unit tangent vectors
    var _v2n = dot_product(_unx, _uny, _other.speed, _other.speed); // projection of velocity for 2nd object onto unit normal vectors
    var _v2t = dot_product(_utx, _uty, _other.speed, _other.speed); // projection of velocity for 2nd object onto unit tangent vectors
  
    var _v1tp = _v1t; // Tangent velocities do not change after collision
    var _v2tp = _v2t; //
  
    // --- One Dimensional Collision Formulas --- //
    var _v1np = (_v1n * (_m1 - _m2) + 2 * _m2 * _v2n) / (_m1 + _m2); // normal velocity for 1st object
    var _v2np = (_v2n * (_m2 - _m1) + 2 * _m1 * _v1n) / (_m1 + _m2); // normal velocity for 2nd object
  
    // --- Convert scalar normal and tangential velocities to 'vectors'
    var _fnx1 = _v1np * _unx;
    var _fny1 = _v1np * _uny;
    var _fnx2 = _v2np * _unx;
    var _fny2 = _v2np * _uny;
  
    var _ftx1 = _v1tp * _utx;
    var _fty1 = _v1tp * _uty;
    var _ftx2 = _v2tp * _utx;
    var _fty2 = _v2tp * _uty;
  
    // --- Add normal and tangential velocities together to calculate final velocities --- //
    var _fv1x = _fnx1 + _ftx1;
    var _fv1y = _fny1 + _fty1;
    var _fv2x = _fnx2 + _ftx2;
    var _fv2y = _fny2 + _fty2;
  
    // --- Find direction of impact for both objects --- //
    var dir_2 = point_direction(_x1, _y1, _x2, _y2);
    var dir_1 = point_direction(_x2, _y2, _x1, _y1);
  
    // --- Move collided instance out of the way --- //
    x += lengthdir_x(_fv1x, dir_1);
    y += lengthdir_y(_fv1y, dir_1);
    direction = dir_1; // Set direction of collided instance
  
  
    // --- Same thing but for the colliding instance 'other' --- //
    with(_other) {
        x += lengthdir_x(_fv2x, dir_2);
        y += lengthdir_y(_fv2y, dir_2);
        direction = dir_2;
    }
  
    if(_m2 >= _m1) { // _m2 is the mass of the colliding instance, 'other'.
        speed = abs(_fv1x + _fv1y) / sqrt(abs(_fv1x + _fv1y)); // Set the speed of the collided instance
        _other.speed = abs(_fv2x + _fv2y) / sqrt(abs(_fv2x + _fv2y)); // Set the speed of the colliding instance
        _other.speed *= (_m1 / _m2); // Scale larger objects speed by the ratio of the masses
    }
}


function sq(_val) {
    return(_val * _val); 
}

 

chamaeleon

Member
Drawing a spiral with width and colored by a sprite. Segment counter is used to start a new triangle strip after some number of segments as there is a maximum number of points it can contain (the subtraction angle -= angle_step is to avoid a gap due to the increment at the end of each for loop iteration).

Draw event
GML:
var width = window_get_width();
var height = window_get_height();

draw_spiral_primitive_gradient(width/2, height/2, spr_gradient, 0, 20, 10*360, 2, 30);
Script
GML:
function draw_spiral_primitive_gradient(xc, yc, sprite, subimage, width, final_angle, angle_step, radius_step) {
    var texture = sprite_get_texture(sprite, subimage);
    var segment_radius_step = radius_step * angle_step/360;
    var radius = 0;
    var segment_counter = 0;
    draw_primitive_begin_texture(pr_trianglestrip, texture);
    for (var angle = 0; angle <= final_angle; {angle += angle_step; radius += segment_radius_step}) {
        var texture_x = angle/final_angle;
        var _xx = xc + lengthdir_x(radius, angle);
        var _yy = yc + lengthdir_y(radius, angle);
        draw_vertex_texture(_xx + lengthdir_x(width/2, angle), _yy + lengthdir_y(width/2, angle), texture_x, 0);
        draw_vertex_texture(_xx - lengthdir_x(width/2, angle), _yy - lengthdir_y(width/2, angle), texture_x, 1);
        if (++segment_counter == 256) {
            draw_primitive_end();
            draw_primitive_begin_texture(pr_trianglestrip, texture);
            segment_counter = 0;
            angle -= angle_step;
        }
    }
    draw_primitive_end();
}
spr_gradient
0f90888d-a500-4b7d-a4e1-39369fd1fc0c.png

Result
gradient-spiral.png
 
Last edited:

chamaeleon

Member
Drawing a path with a texture in a similar manner to the spiral above

Create event
GML:
path = path_add();
path_set_closed(path, false);

for (var i = -128; i <= 128; ++i) {
    var angle = i/256*4*pi;
    var xp = 50*angle + window_get_width()/2;
    var yp = 100*sin(angle) + window_get_height()/2;
    path_add_point(path, xp, yp, 100);
}
Draw event
GML:
draw_path_primitive_gradient(path, spr_gradient, 0, 20, 200);
Script
GML:
function draw_path_primitive_gradient(path, sprite, subimage, width, segments) {
    var texture = sprite_get_texture(sprite, subimage);
    var segment_counter = 0;
    draw_primitive_begin_texture(pr_trianglestrip, texture);
    var old_dir = 0;
    for (var i = 0; i <= segments; ++i) {
        var fraction = i/segments;
        var xp = path_get_x(path, fraction);
        var yp = path_get_y(path, fraction);
        var dx = path_get_x(path, fraction + 1/(10*segments)) - xp;
        var dy = path_get_y(path, fraction + 1/(10*segments)) - yp;
        if (i < segments)
            var dir = point_direction(0, 0, dx, dy);
        else
            var dir = old_dir;
        old_dir = dir;
        var wx = lengthdir_x(width/2, dir+90);
        var wy = lengthdir_y(width/2, dir+90);
        draw_vertex_texture(xp + wx, yp + wy, fraction, 0);
        draw_vertex_texture(xp - wx, yp - wy, fraction, 1);
        if (++segment_counter == 256) {
            draw_primitive_end();
            draw_primitive_begin_texture(pr_trianglestrip, texture);
            segment_counter = 0;
            --i;
        }
    }
    draw_primitive_end();
}
Result using the same gradient as the spiral
sine-gradient.png

(Edit: Both the spiral and the path drawing code was written somewhat in haste, and can probably use optimizations and quite possibly has corner case bugs)
 
Last edited:

Prrz

Member
Simple slap-into-the-project resolution management that can be as complex as you want it to be:

Call: resolution_manager_init() in your camera objects create event.
Function has two optional arguments:
- _h: The resolution height you wish your game to be, default is the height of your display.
- _scale: For scaling the application surface, default scale is *1.

Assumptions:
- 1 view per room, no views or cameras are being setup in the IDE or code anywhere else. We are just using the default GM view_camera[0]
- Your game is "landscape", ie. more width than height.
- GUI / Camera / Window are all the same size.

GML:
#macro VIEW view_camera[0]

function display_get_aspectratio() {
    return(display_get_width() / display_get_height());
}

function display_get_idealwidth(_ideal_height) {
    var _AR_ = display_get_aspectratio();
    var _ideal_width = round(_ideal_height * _AR_);
 
    if(_ideal_width & 1) {
        _ideal_width ++;
    }
 
    return(_ideal_width);
 
}

function display_get_idealheight(_ideal_width) {
    var _AR_ = display_get_aspectratio();
    var _ideal_height = round(_ideal_width / _AR_);
 
    if(_ideal_height & 1) {
        _ideal_height ++;
    }
 
    return(_ideal_height);
 
}


function resize_all(_ideal_width, _ideal_height, _scale) {    
    surface_resize(application_surface, _ideal_width * _scale, _ideal_height * _scale);
    window_set_size(_ideal_width, _ideal_height);
    camera_set_view_size(VIEW, _ideal_width, _ideal_height);
    display_set_gui_size(_ideal_width, _ideal_height);
}


function resolution_manager_init(_h = display_get_height(), _scale = 1) {
    view_enabled = true;
    view_visible[0] = true;
   
    resolution_width = display_get_idealwidth(_h);
    resolution_height = display_get_idealheight(resolution_width);
 
    if(resolution_height & 1) {
        resolution_height ++;
    }

    if(display_get_width() mod resolution_width != 0) {
        var _d = round(display_get_width() / resolution_width);
        resolution_width = display_get_width() / _d;
    }

    if(display_get_height() mod resolution_height != 0) {
        var _d = round(display_get_height() / resolution_height);
        resolution_height = display_get_height() / _d;
    }
 
    resize_all(resolution_width, resolution_height, _scale);
 
}
 

Prrz

Member
Inspired by @angelwire's todo function I made one that is a little more "robust" because I have the same concerns within my own projects...

GML:
function todo(_string) {
    if(!is_string(_string)) { _string = string(_string); }
    
    if(!script_exists(lw_list)) {
        exit;   
    }
    
    if(!variable_global_exists("debugging")) {
        global.debugging = new lw_list();
    }
    
    
    var where = string(debug_get_callstack(2));
    where = string(where);
    where = string_delete(where, 1, 23);
    where = string_delete(where, string_length(where) - 4, 5);
    
    if(is_string(_string)) {
        var find_str = global.debugging.find_index("\nIN: " + where + "\nYOU NEED TO: " + string_upper(_string));
        if(find_str == -1) {
            global.debugging.append("\nIN: " + where + "\nYOU NEED TO: " + string_upper(_string));   
        }
    }
}

Which depends on the lw_list() function which can be found below (inspired directly by the frostiest of cats own lightweight ds_list solution).
Code:
function lw_list() constructor {
    data = [];

    static insert = function(pos, val) {
        array_insert(data,pos,val);   
    };

    static append = function(val) {
        array_push(data, val);
    };
    
    static find_last = function() {
        var size = array_length(data);
        if(size == 0) { exit; }
        var last = array_get(data,size - 1);
        return(last);
    };
    
    static find_first = function() {
        if(array_length(data) != 0) {
            return(data[0]);
        } else { return(noone); }
    };

    static del_pos = function(pos) {
        array_delete(data,pos,1);
    };
    
    static del_last = function() {
        array_pop(data);
    };
    
    static del_first = function() {
        array_delete(data,0,1);   
    };

    static clear = function() {
        array_delete(data, 0, array_length(data));
        data = [];
    };
    
    static delete_list = function() {
        delete data;
    };

    static size = function() {
        return(array_length(data));
    };

    static find_value = function(pos) {
        return(data[normPos(pos)]);
    };
    
    static normPos = function(pos) {
        var length = array_length(data);
        if (pos >= length || pos < -length) { exit; }
        else { return (pos >= 0) ? pos : (length+pos); }
    };
    
    static find_index = function(val) {
        var length = array_length(data);
        for (var i = 0; i < length; ++i) {
            if (data[i] == val) return i;
        }
        return -1;
    };
    
}

To use it is as simple as putting todo("start a new project without finishing this one and then abandon it"); and the debug console will tell you what you still need to todo, and where in your code it is (I think, test it out and let me know).

Capture.PNG
 

Evanski

Raccoon Lord
Forum Staff
Moderator
This is a super simple function, but I felt like a genius trying to figure it out in my head rather then sleeping, then waking up to make a test project working out how to do the function


GML:
function point_between_num_ratio(_min, _max, num){
  
    ///@desc returns a number between 0-1 depending on how close the input number is to each of the two other numbers provided
    // if the num is closer to _min its closer to 0, if the num is closer to _max its closer to 1, so you return a ratio of where you are between the two numbers 
  
    return( ( (num - _min) / (_max - _min) )  );
}
getratioed.gif

edit:
added to the toolbox
 
Last edited:

Juju

Member
This is a super simple function, but I felt like a genius trying to figure it out in my head rather then sleeping, then waking up to make a test project working out how to do the function
Ah, the ol' inverse lerp. I've never known what to call this function.
 

FoxyOfJungle

Kazan Games
This function can also be called linearstep(), which is very similar to smoothstep(), which we have in GLSL ES.

Note the difference between the two:



GML:
function linearstep(minv, maxv, value) {
    return (value - minv) / (maxv - minv);
}

function smoothstep(minv, maxv, value) {
    var t = clamp((value - minv) / (maxv - minv), 0, 1);
    return t * t * (3 - 2 * t);
}
 
Last edited:

Prrz

Member
It's interesting that this "inverse lerp" / "point between num ratio" / "linear step" snippit hasn't popped up in this thread before, because it really is super useful especially in GM.

In fact, I already had this very thing in my current project for spitting out alpha values based on distance - but let's call it what it is please ;)

function normalize_0to1(_val, _min, _max)
 

kburkhart84

Firehammer Games
It's interesting that this "inverse lerp" / "point between num ratio" / "linear step" snippit hasn't popped up in this thread before, because it really is super useful especially in GM.

In fact, I already had this very thing in my current project for spitting out alpha values based on distance - but let's call it what it is please ;)

function normalize_0to1(_val, _min, _max)
If you don't enforce it being 0 to 1 and instead let it be any range to any other range, the technical term is "remap" although my personal version is called "relerp" as that's how I ran across it here some years ago.

"Normalize" normally refers to vectors, specific unit length ones, length of 1 in fact. Normalizing them makes the length be one, and of course "normalized" is when it is already length of 1.
 
S

Sam (Deleted User)

Guest
I was able to verify most of my xProcess extension works on Android via the free Cxxdroid Compiler and IDE Android app.

Feel free to test it on your end, it's as simple as installing the app and copy/paste this code into it, and hit compile:


Possible output:


Code:
pid[0]: 0, ppid: 0
pid[0]: 0, cpid[0]: 0
pid[1]: 25616, exe: /data/data/ru.iiec.cxxdroid/files/busybox
pid[1]: 25616, cwd: /storage/emulated/0/Android/data/ru.iiec.cxxdroid/files
pid[1]: 25616, ppid: 24730
pid[1]: 25616, cpid[0]: 25630
pid[1]: 25616, cmd[0]: /data/user/0/ru.iiec.cxxdroid/files/busybox
pid[1]: 25616, cmd[1]: sh
pid[1]: 25616, env[0]: PATH=/system/bin:/system/xbin:/vendor/bin
pid[1]: 25616, env[1]: DOWNLOAD_CACHE=/data/cache
pid[1]: 25616, env[2]: ANDROID_BOOTLOGO=1
pid[1]: 25616, env[3]: ANDROID_ROOT=/system
pid[1]: 25616, env[4]: ANDROID_ASSETS=/system/app
pid[1]: 25616, env[5]: ANDROID_DATA=/data
pid[1]: 25616, env[6]: ANDROID_STORAGE=/storage
pid[1]: 25616, env[7]: EXTERNAL_STORAGE=/sdcard
pid[1]: 25616, env[8]: ASEC_MOUNTPOINT=/mnt/asec
pid[1]: 25616, env[9]: BOOTCLASSPATH=/system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/bouncycastle.jar:/system/framework/apache-xml.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/sprengine.jar:/system/framework/android.hidl.base-V1.0-java.jar:/system/framework/android.hidl.manager-V1.0-java.jar:/system/framework/knoxsdk.jar:/system/framework/timakeystore.jar:/system/framework/fipstimakeystore.jar:/system/framework/sec_edm.jar:/system/framework/knoxanalyticssdk.jar:/system/framework/smartbondingservice.jar:/system/framework/ucmopensslenginehelper.jar:/system/framework/esecomm.jar:/system/framework/securetimersdk.jar:/system/framework/sec_sdp_sdk.jar:/system/framework/sec_sdp_hidden_sdk.jar:/system/framework/framework-oahl-backward-compatibility.jar:/system/framework/android.test.base.jar:/system/framework/knoxvpnuidtag.jar:/system/framework/SemAudioThumbnail.jar:/system/framework/knoxguard.jar:/system/framework/drutils.jar
pid[1]: 25616, env[10]: SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar:/system/framework/com.android.location.provider.jar:/system/framework/hqm.jar:/system/framework/hcm.jar
pid[1]: 25616, env[11]: MC_AUTH_TOKEN_PATH=/efs
pid[1]: 25616, env[12]: KNOX_STORAGE=/data/knox/ext_sdcard
pid[1]: 25616, env[13]: ENC_EMULATED_STORAGE_TARGET=/storage/enc_emulated
pid[1]: 25616, env[14]: ANDROID_SOCKET_zygote=11
pid[1]: 25616, env[15]: BACKCOMPAT_SHELL=/data/user/0/ru.iiec.cxxdroid/files/sh
pid[1]: 25616, env[16]: BACKCOMPAT_BUSYBOX=/data/user/0/ru.iiec.cxxdroid/files/busybox
pid[1]: 25616, env[17]: BACKCOMPAT_SHEBANG=1
pid[1]: 25616, env[18]: TERM=screen
pid[1]: 25616, env[19]: HOME=/data/user/0/ru.iiec.cxxdroid/app_HOME
pid[2]: 25630, exe: /data/data/ru.iiec.cxxdroid/files/iiec_tmp_binary
pid[2]: 25630, cwd: /storage/emulated/0/Android/data/ru.iiec.cxxdroid/files
pid[2]: 25630, ppid: 25616
pid[2]: 25630, cmd[0]: /data/user/0/ru.iiec.cxxdroid/files/iiec_tmp_binary
pid[2]: 25630, env[0]: MC_AUTH_TOKEN_PATH=/efs
pid[2]: 25630, env[1]: BACKCOMPAT_SHEBANG=1
pid[2]: 25630, env[2]: EXTERNAL_STORAGE=/sdcard
pid[2]: 25630, env[3]: KNOX_STORAGE=/data/knox/ext_sdcard
pid[2]: 25630, env[4]: SHLVL=1
pid[2]: 25630, env[5]: LD_LIBRARY_PATH=/data/user/0/ru.iiec.cxxdroid/files/lib
pid[2]: 25630, env[6]: HOME=/data/user/0/ru.iiec.cxxdroid/app_HOME
pid[2]: 25630, env[7]: OLDPWD=/storage/emulated/0
pid[2]: 25630, env[8]: ANDROID_ASSETS=/system/app
pid[2]: 25630, env[9]: BOOTCLASSPATH=/system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/bouncycastle.jar:/system/framework/apache-xml.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/sprengine.jar:/system/framework/android.hidl.base-V1.0-java.jar:/system/framework/android.hidl.manager-V1.0-java.jar:/system/framework/knoxsdk.jar:/system/framework/timakeystore.jar:/system/framework/fipstimakeystore.jar:/system/framework/sec_edm.jar:/system/framework/knoxanalyticssdk.jar:/system/framework/smartbondingservice.jar:/system/framework/ucmopensslenginehelper.jar:/system/framework/esecomm.jar:/system/framework/securetimersdk.jar:/system/framework/sec_sdp_sdk.jar:/system/framework/sec_sdp_hidden_sdk.jar:/system/framework/framework-oahl-backward-compatibility.jar:/system/framework/android.test.base.jar:/system/framework/knoxvpnuidtag.jar:/system/framework/SemAudioThumbnail.jar:/system/framework/knoxguard.jar:/system/framework/drutils.jar
pid[2]: 25630, env[10]: BACKCOMPAT_BUSYBOX=/data/user/0/ru.iiec.cxxdroid/files/busybox
pid[2]: 25630, env[11]: CMAKE_INCLUDE_PATH=/data/user/0/ru.iiec.cxxdroid/files/sysroot:/data/user/0/ru.iiec.cxxdroid/files/sysroot/include:/data/user/0/ru.iiec.cxxdroid/files/sysroot/usr/include
pid[2]: 25630, env[12]: TMPDIR=/data/user/0/ru.iiec.cxxdroid/cache
pid[2]: 25630, env[13]: DOWNLOAD_CACHE=/data/cache
pid[2]: 25630, env[14]: TERM=screen
pid[2]: 25630, env[15]: ANDROID_DATA=/data
pid[2]: 25630, env[16]: APP_PACKAGE=/data/app/ru.iiec.cxxdroid-HTEXjzxZX17wOZxLq2Af6w==/base.apk
pid[2]: 25630, env[17]: PATH=/data/user/0/ru.iiec.cxxdroid/files:/data/user/0/ru.iiec.cxxdroid/files/bin:/data/user/0/ru.iiec.cxxdroid/files/sysroot/bin:/busybox-virtual:/system/bin:/system/xbin:/vendor/bin
pid[2]: 25630, env[18]: ANDROID_ROOT=/system
pid[2]: 25630, env[19]: ENC_EMULATED_STORAGE_TARGET=/storage/enc_emulated
pid[2]: 25630, env[20]: ANDROID_SOCKET_zygote=11
pid[2]: 25630, env[21]: SHELL=/data/user/0/ru.iiec.cxxdroid/files/sh
pid[2]: 25630, env[22]: CXX=clang++
pid[2]: 25630, env[23]: BACKCOMPAT_SHELL=/data/user/0/ru.iiec.cxxdroid/files/sh
pid[2]: 25630, env[24]: CMAKE_LIBRARY_PATH=/data/user/0/ru.iiec.cxxdroid/files/sysroot:/data/user/0/ru.iiec.cxxdroid/files/sysroot/lib:/data/user/0/ru.iiec.cxxdroid/files/sysroot/usr/lib
pid[2]: 25630, env[25]: CONFIG_SHELL=/data/user/0/ru.iiec.cxxdroid/files/sh
pid[2]: 25630, env[26]: ASEC_MOUNTPOINT=/mnt/asec
pid[2]: 25630, env[27]: PWD=/storage/emulated/0/Android/data/ru.iiec.cxxdroid/files
pid[2]: 25630, env[28]: LC_ALL=en_US.UTF-8
pid[2]: 25630, env[29]: SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar:/system/framework/com.android.location.provider.jar:/system/framework/hqm.jar:/system/framework/hcm.jar
pid[2]: 25630, env[30]: ANDROID_STORAGE=/storage
pid[2]: 25630, env[31]: CLANG_SYSROOT=/data/user/0/ru.iiec.cxxdroid/files/sysroot
pid[2]: 25630, env[32]: CC=clang
pid[2]: 25630, env[33]: ANDROID_BOOTLOGO=1

[Program finished]
Disclaimer: You will need your android device rooted to inspect all processes.
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
Edit: Apparently Merge_color is a thing, hours of work wasted smh

Some color scripts I never ended up using

GML:
function decimal_to_rgb(decimal, mutation=0){
  // Extract the individual red, green, and blue values from the decimal
  //gamemaker is big endien so we swap red and blue
  var blue = (decimal >> 16) & 0xff;
  var green = (decimal >> 8) & 0xff;
  var red = decimal & 0xff;

  // Apply the mutation to the red, green, and blue values
  if (mutation != 0){
      red = red + irandom_range(-mutation, mutation);
      green = green + irandom_range(-mutation, mutation);
      blue = blue + irandom_range(-mutation, mutation);
  }

  // Clamp the red, green, and blue values to the range 0-255
  red = clamp(red, 0, 255);
  green = clamp(green, 0, 255);
  blue = clamp(blue, 0, 255);

  // Return the RGB values as an array
  return [red, green, blue];
}

function mix_colors(color1, color2){
  // Extract the individual red, green, and blue values from the colors
  var red1 = color1[0];
  var green1 = color1[1];
  var blue1 = color1[2];
  var red2 = color2[0];
  var green2 = color2[1];
  var blue2 = color2[2];

  // Calculate the average of the red, green, and blue values
  var red = (red1 + red2) / 2;
  var green = (green1 + green2) / 2;
  var blue = (blue1 + blue2) / 2;

  // Return the mixed color as an array of RGB values
  return [red, green, blue];
}
Added to my Tool Box
 
Last edited:

Joe Ellis

Member
Simple sine wave floating motion that you can add to any object

Call float_init() in an object's create event and float_step() in the step event

Code:
function float_init()
{
    float_speed = pi; //This can be any speed, it doesn't have to be pi
    float_range = 2; //You need to adjust this based on the scale of the game (1-3 is good for pixel scale, about 5-10 for hd)

    float_angle = random(360);
    float_y = 0;
}

function float_step()
{
    float_angle += float_speed;
    y -= float_y;
    float_y = lengthdir_y(float_range, float_angle);
    y += float_y;
}

It works with objects with other movement aswell cus of the part where it subtracts the previous float movement (float_y), so it makes it accurately float around it's current position
 

chirpy

Member
Script function converting sprite_get_uvs to croppings in pixels on each side:
https://forum.gamemaker.io/index.ph...-to-complete-bounding-box.101899/#post-615377

GM v2022.11, v2023.1, with only limited manual testing on a few frames:
GML:
function sprite_get_croppings(_spr_index, _img_index=0)
{
    var info = sprite_get_uvs(_spr_index,_img_index);
    return {
        left: info[4],
        top: info[5],
        right: sprite_get_width(_spr_index)*(1-info[6])-info[4],
        bottom: sprite_get_height(_spr_index)*(1-info[7])-info[5],
    };
}
======

Legacy GM 1.4 (ported by @follow-the-fun, returning an array instead of a struct)
GML:
///sprite_get_croppings(spr,subimg)
/*
Returns the real croppings (in pixels) of a sprite on the texture
page, (taking into account the crops made by game maker to remove
white space).
*/

// Arguments
var _spr_index = argument0; // The sprite to check for croppings
var _img_index = argument1; // The subimage of that sprite

// Retrieve built in uvs
var info = sprite_get_uvs(_spr_index,_img_index);

// Assemble an array containing the croppings
var croppings;
croppings[3] = sprite_get_height(_spr_index)*(1-info[7])-info[5]; // Bottom (order reversed)
croppings[2] = sprite_get_width(_spr_index)*(1-info[6])-info[4]; // Right
croppings[1] = info[5]; // Top (these two are as default from game maker)
croppings[0] = info[4]; // Left

// Return new array;
return croppings;
Code used to test from @follow-the-fun
GML:
// NOTE: This code only works if the rotation point of the sprite is 0,0 (not centered).
// To use a centered sprite, you'd need to offset all coordinates by it rotation point (use sprite_get_xoffset)
// Test 1
var testX = 200; var testY = 200;
var testSprite = spr_Test;
var testSub    = 0;
var croppings  = sprite_get_croppings(testSprite,testSub);

draw_sprite(testSprite,testSub,testX,testY);
draw_rectangle_colour(testX,testY,testX+sprite_get_width(testSprite),testY+sprite_get_height(testSprite),c_black,c_black,c_black,c_black,1); // Sprite bounding box
draw_rectangle_colour(testX+croppings[0],testY+croppings[1],testX+sprite_get_width(testSprite)-croppings[2],testY+sprite_get_height(testSprite)-croppings[3],c_red,c_red,c_red,c_red,1);

// Test 2
var testX = 420; var testY = 200;
var testSprite = spr_Test;
var testSub    = 1;
var croppings  = sprite_get_croppings(testSprite,testSub);

draw_sprite(testSprite,testSub,testX,testY);
draw_rectangle_colour(testX,testY,testX+sprite_get_width(testSprite),testY+sprite_get_height(testSprite),c_black,c_black,c_black,c_black,1); // Sprite bounding box
draw_rectangle_colour(testX+croppings[0],testY+croppings[1],testX+sprite_get_width(testSprite)-croppings[2],testY+sprite_get_height(testSprite)-croppings[3],c_red,c_red,c_red,c_red,1);
 

Joe Ellis

Member
Im going to make this a sticky thread as this thread is full of really helpful and usefull scripts

@Nocturne can slap me with a newspaper if he doesnt like it being sticky
I actually thought this the other day, it really should be
I was going to request if it could be, also I thought an index on the first post would be really useful, like a list of each code post and description, I know it'd be work to keep up to date, but it'd make the thread a lot more helpful, cus it is hard to scroll through the whole thing and spot all the things that could be useful to you
 
Last edited:

Prrz

Member
As simple as it gets, but I have not seen something like this posted here yet.

A function to check whether two values are approximately equal - a simple way to manage floating point precision errors. Default tolerance is 0.01, can be optionally set to whatever you need it to be.

GML:
function approxEqual(value, target, tolerance = 0.01) {
    if(abs(value - target) <= abs(tolerance)) { return(true); }
    return(false);
}
 
S

Sam (Deleted User)

Guest
I actually thought this the other day, it really should be
I was going to request if it could be, also I thought an index on the first post would be really useful, like a list of each code post and description, I know it'd be work to keep up to date, but it'd make the thread a lot more helpful, cus it is hard to scroll through the whole thing and spot all the things that could be useful to you
I dont have time to do that, but the moderators and admins are welcome to add that to the OP If they like, maybe do as they feel lead and not on a schedule so it feels like less of a chore. (Not expecting them to have the time for that, either, tho)
 
Here is an excellent example of how to do working if relative in GML (Game Maker Language)
Create Event
Code:
hp = 21; //variable hp is equal to 21
hp_divided_by = 1; //will be used to help reduce variable hp by a multiple of times
hp_minus = 0; //will be used to help reduce variable hp
hp_plus = 0; //will be used to help increase variable hp
hp_times_by = 1; //will be used to help increase variable hp by a multiple of times


Step Eveent
Code:
if (hp_times_by <> 1) //if variable hp_times_by is less than (<) or greater than (>) 1
{
  hp *= hp_times_by; //variable hp gets increased by multiple of whatever variable hp_times_by is equal to
  hp_times_by = 1; //variabe hp_times_by is now equal to 1
}
if (hp_plus <> 0) //if variable hp_plus is less than 0 or greater than 0
{
  hp += hp_plus; //variable hp_plus gets increased by whatever variable hp_plus is equal to
  hp_plus = 0; //variable hp_plus is now equal to 0
}
if (hp_minus <> 0) //if variable hp_minus is less than 0 or greater than 0
{
  hp -= hp_minus; //variable hp gets reduced by whatever variable hp_minus is equal to
  hp_minus = 0; //variable hp_minus is now equal to 0
}
if (hp_divided_by <> 1) //if variable hp_divided_by is less than 1 or greater than 1
{
  hp_divided_by != 0; //variable hp_divided_by does not equal to 0 to prevent errors of being divided by 0
  hp /= hp_divided_by; //variable hp gets divided by whatever variable hp_divided_by is equal to so reduce hp by multiple of times
  hp_divided_by = 1; //variable hp_divided_by is now equal to 1
}
if (keyboard_check_pressed(ord("D"))) //if D button is pressed (pushed)
{
  hp_divided_by = 3; //variable hp_divided_by is equal to 3
}
if (keyboard_check_pressed(ord("M"))) //if M button is pressed
{
  hp_minus = 3; //variable hp_minus is equal to 3
}
if (keyboard_check_pressed(ord("P"))) //if P button is pressed
{
  hp_plus = 3; //variable hp_plus is equal to 3
}
if (keyboard_check_pressed(ord("T"))) //if T button is pressed
{
  hp_times_by = 3; //variable hp_times_by is equal to 3
}
if (hp_times_by == 3) //if variable hp_times_by is equal to 3
{
  y += 3; //go down by three pixels
  show_message("hp: "+string(hp)); //show message of how much hp is there
}
if (hp_plus == 3) //if variable hp_plus is equal to 3
{
  x -= 3; //go left by three pixels
  show_message("hp: "+string(hp)); //show message of how much hp is there
}
if (hp_minus == 3) //if variable hp_minus is equal to 3
{
  y -= 3; //go up by three pixels
  show_message("hp: "+string(hp)); //show message of how much hp is there
}
if (hp_divided_by == 3) //if variable hp_divided_by is equal to 3
{
  x += 3; //go right by three pixels
  show_message("hp: "+string(hp)); //show message of how much hp is there
}
 

Evanski

Raccoon Lord
Forum Staff
Moderator
I dont have time to do that, but the moderators and admins are welcome to add that to the OP If they like, maybe do as they feel lead and not on a schedule so it feels like less of a chore. (Not expecting them to have the time for that, either, tho)
I might do this if I have the time and bored one afternoon 🤔
 

FoxyOfJungle

Kazan Games

TheSnidr

Heavy metal viking dentist
GMC Elder
Made a script for wall-crawling enemies which could be useful to others! See this topic for background info: https://forum.gamemaker.io/index.php?threads/wall-crawling-zoomers.102847/

Code:
//Script for making an object follow the surface of a wall
//Written by TheSnidr, 2023
//Source 1: https://forum.gamemaker.io/index.php?threads/wall-crawling-zoomers.102847/post-620852
//Source 2: https://forum.gamemaker.io/index.php?threads/code-bank-share-useful-code.60575/post-620940

//Settings, could be placed in create event if you'd like, but also works perfectly fine in step event
spd = 2;                        //Positive to move to the right, negative to move to the left
radius = 8;                        //This is the distance to the probe we use to measure the angle of the wall
pixelsPerStep = 2;                //A value of 1 means pixel-perfect movement. A higher value means less precision and less processing.
angularStepSize = 3;            //The step size in the angle. A higher value means less precision and less processing.
angleDampening = .7;            //This softens the changes in image_angle
wallParent = obj_Parent_Solid;    //The object the wall-crawler collides with

//Start iteratin'
var w = sign(spd);
var l = radius * w;
var c =   dcos(direction);
var s = - dsin(direction);
var d = abs(spd) / pixelsPerStep;
repeat ceil(d)
{
    //Attach to walls
    repeat 4
    {
        var t = place_meeting(x, y, wallParent) * 2 - 1;
        x += s * t;
        y -= c * t;
        if t break;
    }
    if !t continue;

    //Adjust angle
    var p = 0;
    repeat 10
    {
        var t = place_meeting(x + l * c, y + l * s, wallParent) * 2 - 1;
        if (p > 0 && !t) break;
        direction += angularStepSize * w * t;
        c =   dcos(direction);
        s = - dsin(direction);
        if (p < 0 && t) break;
        p = t;
    }

    //Move along the surface of the wall
    var t = pixelsPerStep * w * min(d--, 1);
    x += c * t;
    y += s * t;
}
direction = (direction + 360) mod 360;
image_angle = (image_angle + angleDampening * angle_difference(direction, image_angle) + 360) mod 360;
EDIT:
And here's a slightly shorter and sweeter version that makes use of speed and direction rather than manually moving the object pixel by pixel:
Code:
//Script for making an object follow the surface of a wall
//Written by TheSnidr, 2023
//Source 1: https://forum.gamemaker.io/index.php?threads/wall-crawling-zoomers.102847/post-620852
//Source 2: https://forum.gamemaker.io/index.php?threads/code-bank-share-useful-code.60575/post-620940
//Settings, could be placed in create event if you'd like, but also works perfectly fine in step event
wallcrawler_speed = -3;                        //Positive to move to the right, negative to move to the left
wallcrawler_radius = 8;                        //This is the distance to the probe we use to measure the angle of the wall
wallcrawler_angleDamping = .7;                //This softens the changes in image_angle
wallcrawler_angularStepNum = 10;            //Number of iterations allowed when adjusting the angle
wallcrawler_angularStepSize = 4.5;            //The step size in the angle. A higher value means less processing, but less precision. Preferably a divisor of 45.
wallcrawler_wallParent = obj_Parent_Solid;    //The object the wall-crawler collides with

//Attach to walls, place in step event
for (var i = 0, p = 0; ++i <= 5 && !p; p = speed)
{    //This loop makes sure the object stays attached to the wall by moving it vertically until it finds a collision
    speed = place_meeting(x, y, wallcrawler_wallParent) * 2 - 1; //It uses speed as an alternative to manually doing trig. Speed is 1 or -1 depending on wether there was a collision.
    x += vspeed; //We want to move vertically compared to direction. Instead of changing the direction, we can swap the axes and change the sign of one of them to rotate 90 degrees.
    y -= hspeed;
}
if (++speed) //This line both checks the value of speed (which is 1 if there is a collision), and sets it to 0 if there was no collision.
{
    speed = wallcrawler_radius * sign(wallcrawler_speed); //Another abuse of speed instead of manually doing trig
    for (var i = 0, p = 0; ++i <= wallcrawler_angularStepNum && p < 2; p = (p == -t) ? 2 : t)
    {    //For loop abuse. We want to end the loop if the previous step had no collision and this step does, or vice versa.
        var t = place_meeting(x + hspeed, y + vspeed, wallcrawler_wallParent) * 2 - 1;
        direction += wallcrawler_angularStepSize * sign(wallcrawler_speed) * t * (p <= 0 || t);
    }
    speed = wallcrawler_speed; //We're done abusing speed for trig, this is the actual speed we want the object to have.
}
direction = (direction + 360) mod 360; //Make sure direction is within the range [0, 360]
image_angle = (image_angle + wallcrawler_angleDamping * angle_difference(direction, image_angle) + 360) mod 360; //Smoothly interpolate image_angle towards direction
 
Last edited:
Top