Lazy gamemaker placeholder menu
I needed a quick debug menu for a game project, threw this together in an hour or so, no warranty
looks like this.
Has sliders, toggles, simple buttons, dividers and text labels. Holler at me on twitter if you need any help with it.
# Sample usage
Make an object, embed this in an already existing object, whatever.
# Create Event
MainMenu = new MenuBody(); //Create the menu like this
PageMain = MainMenu.AddPage(); //Add your pages
PageContinue = MainMenu.AddPage();
PageLoad = MainMenu.AddPage();
PageNew = MainMenu.AddPage();
PageOptions = MainMenu.AddPage();
PageQuit = MainMenu.AddPage();
//Front page
PageMain.Add(new MenuButton("Continue", noone)); //Page dot Add every element
PageMain.Add(new MenuButton(
"Load", //Name/Label of element
function() { MainMenu.Goto(PageLoad); }) //Function to call when pressed can be inline or defined in a script elsewhere, you decide
); //Menu has a .Goto function to assist in switching between submenus
PageMain.Add(new MenuButton("New Game", function() { MainMenu.Goto(PageNew); }));
PageMain.Add(new MenuDivider()); //Dividers just draw a line, for breaking up lists I guess.
PageMain.Add(new MenuButton("Options", function() { MainMenu.Goto(PageOptions); }));
PageMain.Add(new MenuButton("Quit", function() { MainMenu.Goto(PageQuit); }));
//Load game
PageLoad.Add(new MenuButton("Back", function() { MainMenu.Goto(PageMain); }));
//New Game
PageNew.Add(new MenuButton("Back", function() { MainMenu.Goto(PageMain); }));
//Options
PageOptions.Add(new MenuText("Video"));
PageOptions.Add(new MenuToggle("Fullscreen", // Toggle item
function(Active) { window_set_fullscreen(Active); }, // Function on toggle (state passed via bool argument)
window_get_fullscreen()) // Default argument, stored and flipped when pressed
);
PageOptions.Add(new MenuDivider());
PageOptions.Add(new MenuText("Sound"));
PageOptions.Add(new MenuSlider("Volume", // Slider item
0,100,50, // Min,Max, Default value
function(Volume) { show_debug_message("Volume : " + string(Volume)); }) //Function called on change, value passed via a float argument
);
PageOptions.Add(new MenuDivider());
PageOptions.Add(new MenuButton("Back", function() { MainMenu.Goto(PageMain); }));
//Quit
PageQuit.Add(new MenuButton("Back", function() { MainMenu.Goto(PageMain); }));
PageQuit.Add(new MenuButton("Close game", function() { game_end(); }));
# Step event
//Link menu actions to user input like this, all behaviour is self contained
if(keyboard_check_pressed(vk_up))
MainMenu.Up();
if(keyboard_check_pressed(vk_down))
MainMenu.Down();
if(keyboard_check(vk_left))
MainMenu.Left();
if(keyboard_check(vk_right))
MainMenu.Right();
if(keyboard_check_pressed(vk_enter))
MainMenu.Use();
if(keyboard_check_pressed(vk_backspace))
MainMenu.Back();
# Destroy (or cleanup?) event
//Is there a better way of doing this? I don't think you can delete self;
MainMenu.Destroy();
delete MainMenu;
# Draw event
// x, y, width
MainMenu.Draw(64,64,128);
# Source listing
Deposit the following in it's entirety into a script file
//Elements
function MenuElement(Name = "", Action = noone) constructor
{
String = Name;
Callback = Action;
Selectable = true;
Draw = function(dx,dy,width) { return 0; };
Use = function() {};
Left = function() {};
Right = function() {};
LineHeight = string_height("X");
}
function MenuButton(Name, Action) : MenuElement(Name, Action) constructor
{
Use = function()
{
if(Callback != noone)
Callback();
}
Draw = function(dx,dy,width)
{
draw_text_ext(dx,dy,String,LineHeight,width);
return string_height(String);
}
}
function MenuToggle(Name, Action, DefValue) : MenuElement(Name, Action) constructor
{
Active = DefValue;
Use = function()
{
Active = !Active;
if(Callback != noone)
Callback(Active);
}
Draw = function(dx,dy, width)
{
draw_rectangle(dx,dy,dx + (LineHeight - 2), dy + LineHeight, true);
if(Active)
draw_rectangle(dx + 2,dy + 2,(dx + (LineHeight - 2)) - 2, (dy + LineHeight) - 2, false);
draw_text(dx + LineHeight, dy, String);
return string_height(String);
}
}
function MenuSlider(Name, Min, Max, DefValue, Action) : MenuElement(Name, Action) constructor
{
Value = DefValue;
MinValue = min(Min,Max); MaxValue = max(Min,Max);
Left = function(Speed = 1)
{
Value = max(Value - Speed,MinValue);
if(Callback != noone)
Callback(Value);
};
Right = function(Speed = 1)
{
Value = min(Value + Speed, MaxValue);
if(Callback != noone)
Callback(Value);
};
Draw = function(dx,dy,Width)
{
draw_text(dx,dy,String);
draw_rectangle(dx,dy+LineHeight,dx+Width,dy+(LineHeight*2),true);
var percent = (Value - MinValue) / (MaxValue - MinValue);
var position = dx + 2 + ((Width - 4) * percent);
draw_rectangle(position-2,dy+LineHeight,position+2,dy+(LineHeight*2),true);
draw_text(dx,dy+(LineHeight), string(Value));
return LineHeight*2;
}
}
function MenuDivider() : MenuElement() constructor
{
Selectable = false;
Draw = function(dx,dy,width)
{
draw_line(dx,dy + 5, dx + width, dy + 5);
return 10;
}
}
function MenuText(Name) : MenuElement(Name) constructor
{
Selectable = false;
Draw = function(dx, dy, width)
{
draw_text_color(dx,dy,String,c_red,c_red,c_red,c_red,1.0);
return string_height(String);
}
}
function MenuPage() constructor
{
Elements = ds_list_create();
Index = -1;
Selected = -1;
static Left = function()
{
Elements[| Selected].Left();
}
static Right = function()
{
Elements[| Selected].Right();
}
static Up = function()
{
var lastgood = Selected;
do
{
--Selected;
} until ((Selected == -1) || Elements[| Selected].Selectable)
if(Selected == -1)
{
Selected = lastgood;
}
}
static Down = function()
{
var length = ds_list_size(Elements);
var lastgood = Selected;
do
{
++Selected;
} until ((Selected == length) || Elements[| Selected].Selectable)
if(Selected == length)
{
Selected = lastgood;
}
}
static Add = function(Element)
{
ds_list_add(Elements, Element);
if(Selected == -1 && Element.Selectable) //If select pointer not preset to a good target, use this
Selected = ds_list_size(Elements)-1;
}
static Use = function()
{
if(Elements[| Selected].Use != noone)
Elements[| Selected].Use();
}
static Destroy = function()
{
for(var i = 0; i < ds_list_size(Elements); ++i)
{
delete Elements[| i];
}
ds_list_destroy(Elements);
}
static Draw = function(dx,dy,width)
{
var _y = dy;
for(var i = 0; i < ds_list_size(Elements); ++i)
{
var starty = _y;
_y += Elements[| i].Draw(dx,_y,width);
if(Selected == i)
draw_rectangle(dx-2,starty-2,dx + width + 2, _y + 2, true);
}
}
};
function MenuBody() constructor
{
Pages = ds_list_create();
PageOn = -1;
PageHistory = ds_queue_create(); //Keep track of previously visited pages
static Up = function() //Interaction
{
if(PageOn == -1)
return;
Pages[| PageOn].Up();
}
static Down = function()
{
if(PageOn == -1)
return;
Pages[| PageOn].Down();
}
static Left = function()
{
if(PageOn == -1)
return;
Pages[| PageOn].Left();
}
static Right = function()
{
if(PageOn == -1)
return;
Pages[| PageOn].Right();
}
static Use = function()
{
if(PageOn == -1)
return;
var PageChange = PageOn;
Pages[| PageOn].Use();
if(PageChange != PageOn) //if page changed
{
if(ds_queue_tail(PageHistory) != PageChange) //if returning to prevous page, add to history
ds_queue_enqueue(PageHistory, PageChange);
else
ds_queue_dequeue(PageHistory); //else remove item from history
}
}
static Back = function()
{
if(ds_queue_size(PageHistory) > 0)
PageOn = ds_queue_dequeue(PageHistory);
}
static Destroy = function()
{
ds_queue_destroy(PageHistory);
for(var i = 0; i < ds_list_size(Pages); ++i)
{
Pages[| i].Destroy();
delete Pages[| i];
}
ds_list_destroy(Pages);
}
static AddPage = function() //Interface
{
var Page = new MenuPage();
ds_list_add(Pages, Page);
var size = ds_list_size(Pages);
Page.Index = (size - 1);
if(size == 1)
PageOn = 0;
return Page;
}
static Draw = function(dx,dy,width)
{
if(PageOn == -1)
return;
Pages[| PageOn].Draw(dx,dy,width);
}
static Goto = function(Location)
{
PageOn = Location.Index;
}
}