Created
July 16, 2025 01:58
-
-
Save davydmaker/bc55c720c5247cd7472a40a9da823563 to your computer and use it in GitHub Desktop.
GameMaker GML: Unique Frame Selector System
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Frame Selector System - Randomly selects frames without repetition | |
* Ensures each frame is used exactly once before any frame is repeated | |
*/ | |
/** | |
* Creates a shuffled frame selector for the current sprite | |
* | |
* @return {struct} Frame selector object with methods | |
* | |
* @example | |
* // Initialize frame selector | |
* frame_selector = create_frame_selector(); | |
* | |
* // Get next unique frame | |
* var next_frame = frame_selector.get_next_frame(); | |
* image_index = next_frame; | |
*/ | |
function create_frame_selector() { | |
var selector = {}; | |
// Initialize with current sprite's frame count | |
selector.total_frames = sprite_get_number(sprite_index); | |
selector.unused_frames = []; | |
selector.reset_pool = function() { | |
unused_frames = []; | |
// Populate array with sequential frame numbers (0, 1, 2, ...) | |
for (var i = 0; i < total_frames; i++) { | |
array_push(unused_frames, i); | |
} | |
// Shuffle the array for better randomness | |
shuffle_array(unused_frames); | |
}; | |
/** | |
* Gets the next unique frame from the pool | |
* @return {real} Frame index, or -1 if no frames available | |
*/ | |
selector.get_next_frame = function() { | |
// Reset pool if empty (all frames used) | |
if (array_length(unused_frames) == 0) { | |
reset_pool(); | |
} | |
// Safety check | |
if (array_length(unused_frames) == 0) { | |
show_debug_message("Warning: No frames available in sprite"); | |
return -1; | |
} | |
// Get last frame (already shuffled, so effectively random) | |
var selected_frame = array_pop(unused_frames); | |
return selected_frame; | |
}; | |
/** | |
* Gets remaining frames count | |
* @return {real} Number of unused frames | |
*/ | |
selector.get_remaining_count = function() { | |
return array_length(unused_frames); | |
}; | |
/** | |
* Checks if pool needs reset (all frames used) | |
* @return {bool} True if pool is empty | |
*/ | |
selector.is_pool_empty = function() { | |
return array_length(unused_frames) == 0; | |
}; | |
// Initialize the pool | |
selector.reset_pool(); | |
return selector; | |
} | |
/** | |
* Utility function to shuffle an array in-place using Fisher-Yates algorithm | |
* More efficient and better randomness than multiple random selections | |
* | |
* @param {array} arr - Array to shuffle | |
*/ | |
function shuffle_array(arr) { | |
var n = array_length(arr); | |
for (var i = n - 1; i > 0; i--) { | |
var j = irandom(i); | |
// Swap elements | |
var temp = arr[i]; | |
arr[i] = arr[j]; | |
arr[j] = temp; | |
} | |
} | |
/** | |
* Simple standalone function for basic use cases | |
* | |
* @param {array} frame_pool - Array of available frame indices | |
* @return {real} Selected frame index, or -1 if pool is empty | |
*/ | |
function choose_frame_unique(frame_pool) { | |
if (array_length(frame_pool) == 0) { | |
return -1; | |
} | |
// Use array_pop for O(1) operation instead of array_delete | |
return array_pop(frame_pool); | |
} | |
// ============================================================================= | |
// USAGE EXAMPLE | |
// ============================================================================= | |
/* | |
// In Create Event: | |
global.frame_selector = create_frame_selector(); | |
// In Step Event or when you need a new frame: | |
var next_frame = global.frame_selector.get_next_frame(); | |
if (next_frame != -1) { | |
image_index = next_frame; | |
show_debug_message("Selected frame: " + string(next_frame)); | |
show_debug_message("Remaining frames: " + string(global.frame_selector.get_remaining_count())); | |
} | |
// Alternative simple usage: | |
// Initialize once | |
if (!variable_instance_exists(id, "frame_pool")) { | |
frame_pool = []; | |
for (var i = 0; i < sprite_get_number(sprite_index); i++) { | |
array_push(frame_pool, i); | |
} | |
shuffle_array(frame_pool); | |
} | |
// Use whenever needed | |
var frame = choose_frame_unique(frame_pool); | |
if (frame != -1) { | |
image_index = frame; | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment