Code Bank - share useful code!

Samuel Venable

Time Killer
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).



Works on any version of runtime and Game Maker 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

Toolmaker of Bucuresti
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);
}
 

Samuel Venable

Time Killer
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:

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

Samuel Venable

Time Killer
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
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:
Top