Legacy GM [SOLVED] Dynamic Sprite References?

J

Jonathan E. Styles

Guest
I am rather new to GameMaker: Studio and am not very familiar with the multitude of functions that are built-in. However, I am generating a scripted Menu and I am dynamically drawing sprites into my object.

My goal is to create events to handle mouseover and lmb clicked scenarios, changing the sprite by image index, et cetera.

I have attempted to use multiple index function calls, but I just can't seem to figure out how to handle the sprite once it has been created and put into the object / room. Anything someone could contribute to at least point me in the right direction would be greatly helpful.

The overall goal is to create an overlay menu (pause menu) that pops-up over the game and provides standard functions (Resume, Save, Load, Options, Exit). How can I essentially create GUI buttons out of these sprites and create handlers for their actions dynamically?

Here is my current creation code:
Code:
draw_sprite(spr_MenResume, 0, button_draw_x, button_draw_y);
I have attempted to dynamically create an instance, but it kept getting drawn beneath the menu background element and I still couldn't get the "rollover" effect to work for me.

No need to be brief, I have lots of programming experience, but I just feel stumped at this point. If you require more information, please let me know!

Thanks!
 

makas

Member
there are several ways to do what you want to achieve, one could be construting your menu with several object, each one for each button, so you can handle del mouse roll over much more easier thanks to all the mouse events, but if you want to do it with only sprites, what you need to do is to create a rectangle in the area of the button and then check in that space if the mouse is there with a code like this:
Code:
if x>rectangle_x1 and x<rectangle_x2
    {
    if y>rectangle_y1 and y<renctangle_y2
        {
         Your code of the button menu
        }
    }
where the variables rectangle_x1,rectangle_x2, rectangle_y1 and rectangle_y2 are the coordinates of your imaginary rectangle

About the drawing, there are several tools than can help you, the most simple one is the depth of an object, you can find that in the object window, if you put for example a button with -1 depht while the rest of the elements of your game are in 0, you button will always be showed in front of the rest of the elements,

the second way to handle this are with the draw events, draw event will alway draw in front of the rest of the elements, because you are drawing the sprites manually with the function draw_self(), but for most cases when you are dealing with UI elements is recommended to use the draw GUI event... everything you draw there will always go in front of everything you put in your game
 
J

Jonathan E. Styles

Guest
there are several ways to do what you want to achieve, one could be construting your menu with several object, each one for each button, so you can handle del mouse roll over much more easier thanks to all the mouse events, but if you want to do it with only sprites, what you need to do is to create a rectangle in the area of the button and then check in that space if the mouse is there with a code like this:
Code:
if x>rectangle_x1 and x<rectangle_x2
    {
    if y>rectangle_y1 and y<renctangle_y2
        {
         Your code of the button menu
        }
    }
where the variables rectangle_x1,rectangle_x2, rectangle_y1 and rectangle_y2 are the coordinates of your imaginary rectangle

About the drawing, there are several tools than can help you, the most simple one is the depth of an object, you can find that in the object window, if you put for example a button with -1 depht while the rest of the elements of your game are in 0, you button will always be showed in front of the rest of the elements,

the second way to handle this are with the draw events, draw event will alway draw in front of the rest of the elements, because you are drawing the sprites manually with the function draw_self(), but for most cases when you are dealing with UI elements is recommended to use the draw GUI event... everything you draw there will always go in front of everything you put in your game
Thanks for such a quick and thorough reply, makas! This still doesn't quite get what I am looking for, however. What I was primarily working on right now was trying to find out how to manipulate the particular sprite. Each button sprite has two image indexes, 0 which will be the "mouse off", and 1 which will be the "mouse hover" graphic. Since these dynamically-created buttons have no names, how do I tell GameMaker to change the image index for that particular sprite?

I thought of creating invisible rectangles to capture the mouse and you quickly and decisively gave me perfect information for that! However, I don't feel I'm far enough along in the code process to incorporate that. (Nowhere for them to go except maybe the Exit button) Right now I am just working on the visual aspect of how the menu functions. If I can use the rectangles for this too that would be marvelous! Ideas?
 

makas

Member
for the mouse roll over there is an event on the mouse events, and then you can use this code to change the sprite of your button

Code:
image_index=1;
with this function you can change the current image that is show in your object, considering that the first image index is 0 and the second one is 1, also is important to note that you need to set your image_speed to 0 in the create event, this is of course considering that you have the 2 images in the same sprite, as an animation, if you have 2 sprites then the code you need is:
Code:
sprite_index=spr_buttonrollver;
where "spr_buttonrollover" is the sprite that you want to use
 
T

The5thElement

Guest
To create a menu using sprites and not multiple objects. You will need to use a function called
point_in_rectangle. This doesn't allow for precision collision but for buttons it doesn't matter.
In the draw event raise the point_in_rectangle function to check if you are drawing a rollover sprite
or the basic sprite. Then in a global mouse event you can also use the point_in_rectangle function
to check which sprite the mouse is over.

I apologies if this post is incoherent. It is 4 a.m.
 
J

Jonathan E. Styles

Guest
for the mouse roll over there is an event on the mouse events, and then you can use this code to change the sprite of your button

Code:
image_index=1;
with this function you can change the current image that is show in your object, considering that the first image index is 0 and the second one is 1, also is important to note that you need to set your image_speed to 0 in the create event, this is of course considering that you have the 2 images in the same sprite, as an animation, if you have 2 sprites then the code you need is:
Code:
sprite_index=spr_buttonrollver;
where "spr_buttonrollover" is the sprite that you want to use
This is exactly what I am trying to do. (I did try to use an object with the sprite, but the image kept "flashing". The image_speed = 0 fixed that, but the object - even though it was created in the Draw GUI event with a more positive depth than the background sprite - kept getting drawn under it. So, we're on to just using sprites.

I get this is the idea and how to utilize the rectangles, but I am still rather unclear on how to tell GameMaker which sprite to change. I basically want to say:

Code:
EventHandler MouseRollOver(mouse_x, mouse_y)

if (mouse_x > spr_MenResume.origin_x) && (mouse_y > spr_MenResume.origin_y) && (mouse_x < spr_MenResume.origin_x + spr_MenResume.width) && (mouse_y < spr_MenResume.origin_y + spr_MenResume.height)
{
 spr_MenResume.image_index = 1;
}
else
{
spr_MenResume.image_index=0;
}

End EventHandler
I know this isn't real code, but it is the easiest way for me to explain what I am trying to accomplish. But, since the sprite was dynamically drawn into the object it isn't referenced as "spr_MenResume", correct? It has to have a name so I can directly change it, but I don't know how to reference it. (Hence, the name of this posting) If the code you are saying will work, how do I specify which sub_image in my sprite I want to use? From what I read, I want to use the image_index code to accomplish this, but I still have no clue how to tell GM which image on the screen to change.

Again, all of the wonderful help is great. I will keep checking throughout the day, but I won't be able to work with the code until after work today. Thanks!
 

makas

Member
if you want your object to draw in front you need to make the depht smaller, even with negative numbers for UI elements try numbers like -1000, and for the rest of the code the image_index is not meant to be used like that, that function refears to and object sprite, not an sprite, if you want to draw a different image of the sprite you need to use the draw_sprite_ext function, where one of the parameters is the subimage... take a look here https://docs.yoyogames.com/source/dadiospice/002_reference/drawing/drawing sprites and backgrounds/draw_sprite_ext.html

but I still recommend going in for the obj_button, solution is way easier to handle everything
 
J

Jonathan E. Styles

Guest
if you want your object to draw in front you need to make the depht smaller, even with negative numbers for UI elements try numbers like -1000, and for the rest of the code the image_index is not meant to be used like that, that function refears to and object sprite, not an sprite, if you want to draw a different image of the sprite you need to use the draw_sprite_ext function, where one of the parameters is the subimage... take a look here https://docs.yoyogames.com/source/dadiospice/002_reference/drawing/drawing sprites and backgrounds/draw_sprite_ext.html

but I still recommend going in for the obj_button, solution is way easier to handle everything
Makas,

Even though I wanted to use Sprites to reduce the number of Objects I had in my resource list I felt like utilizing an object button for a base and changing the sprite for each button would be easier. Both the objects are created in the Draw GUI function, but my button still comes up under my BG sprite. I can troubleshoot this later, but I was wondering if it is affecting my "mouse enter" event since the Background sprite is over top of it.

Here is my button creation event:

Code:
btn_Resume = instance_create(button_draw_x, button_draw_y + 35, obj_MenuButton);
with(btn_Resume)
 {
 sprite_index = spr_MenResume;
 image_speed = 0;
 }
Am I approaching this incorrectly? If not, how do I tell it that I want the sprite to change to sub_image 1? I figure I can add a Mouse Enter Event and then code it to grab the existing sprite and then change the image_index? I feel like I am getting closer.

I initialize the spr_MenuBG sprite using draw_sprite. My obj_MenuButton has a depth of -1000, do I specify the depth on the spr_MenuBG instance somehow to get it behind my button?

Also, when I create the instance of the button it does not disappear when I close the menu like the sprites did/do. Do I have to destroy these when the menu gets closed to make it work properly?

(I know I said it would be after work, but I went ahead and compiled GM into a portable application and am using it at work. (Desperate.)) ;)
 

makas

Member
how are you drawing your bg sprite? are you using it as a background? or as a sprite? there is not clear reason of why the background is in front of everything else...

I think we are a little confused I will try to go step by step how my approach would work

1. You have an sprite with 2 images like this:
1.png

2. you create an object with the sprite you have asigned like this:
2.png

3. in the create event you handle this code:

Code:
image_speed=0;
image_index=0;
4. then you add the mouse enter event:
3.png

and the code is:
Code:
image_index=1;
5. repeat the above in the mouse leave event only changing it to

Code:
image_index=0;
you can see here the diference between the mouse inside the button and outside...
4.png

I didnt used any draw events, but if you call the draw event and add a empty code with a comment in it like this "// comment", then in the draw_GUI add the line "draw_self();" you should get your button on top of everything
 
J

Jonathan E. Styles

Guest
how are you drawing your bg sprite? are you using it as a background? or as a sprite? there is not clear reason of why the background is in front of everything else...

I think we are a little confused I will try to go step by step how my approach would work

1. You have an sprite with 2 images like this:
View attachment 3687

2. you create an object with the sprite you have asigned like this:
View attachment 3688

3. in the create event you handle this code:

Code:
image_speed=0;
image_index=0;
4. then you add the mouse enter event:
View attachment 3689

and the code is:
Code:
image_index=1;
5. repeat the above in the mouse leave event only changing it to

Code:
image_index=0;
you can see here the diference between the mouse inside the button and outside...
View attachment 3690

I didnt used any draw events, but if you call the draw event and add a empty code with a comment in it like this "// comment", then in the draw_GUI add the line "draw_self();" you should get your button on top of everything
Okay, so the only way I would be able to approach this in GM is to create an individual object for each button? I was trying to be proficient in my OOP and utilize a "blank button" and create buttons on the fly with the same object as I do in various other languages. (I.E. function to create a button and handler, create multiple buttons by calling the function and passing button events through the function to the new button being made.) This allows me to use the object again and again when I suddenly find the need for a new button somewhere in my program.

If this is how I have to do it, that's fine - just not what I was attempting to do. Your answer is very thorough and excellent. I really appreciate the time and effort you have put into my seemingly remedial question. To take the time to go through and create a sample and prepare the screenshots... just... wow!

To answer your original question, though, it is being drawn as a sprite, and I am using it as a background. I call this in and then draw my buttons.

If the button objects for each button need to be made individually and done this way, I have no problem doing it -- was just trying to be a bit more inventive, I guess. If I need to go this route I will try the Draw GUI event with the draw.self() code and let you know how it goes!
 

makas

Member
You can always use parents, you create a base button with all the interactive stuff on it, then create each button being a child of the first, changing the sprite, so you dont write all the same code again for all the buttons, and just adding the code when you click on each button... thats the elegant way to use an object for every option on the menu.

But then again if you want to do it the other you can do it:

Code:
//draw GUI event

if mouse_x>rectangle_x1 and moouse_x<rectangle_x2 and mouse_y>rectangle_y1 and mouse_y<renctangle_y2
    {
    draw_sprite_ext(spr_button1,1, x, y, xscale, yscale, rot, colour, alpha );
    }
else
    {
   draw_sprite_ext( spr_button1,0, x, y, xscale, yscale, rot, colour, alpha );
    }

where:

the first parameter is the sprite name and the second the index of that sprite

the rest are self explained

you will need one of this for each button sprite with their x and y positions and then you wont use more than 1 object
 
Last edited:

TheouAegis

Member
A couple things I've noticed in this thread.

1)
Here is my current creation code:
Code:
draw_sprite(spr_MenResume, 0, button_draw_x, button_draw_y);
That won't do anything. You draw to the screen in the Draw events. In any other event, you can only draw to surfaces.


2)
Both the objects are created in the Draw GUI function, but my button still comes up under my BG sprite.
The Draw GUI event is only for drawing things, not creating them. WHEN you create an object has practically no relevance to when/where it is drawn.


3)
I initialize the spr_MenuBG sprite using draw_sprite.
What? Drawing a sprite doesn't initialize anything (well, it loads up the texture page).
 
T

The5thElement

Guest
No you do not need one object for each button. I am creating a sample project for you
to look at and see if it is what you are looking for.
 
J

Jonathan E. Styles

Guest
A couple things I've noticed in this thread.

1)


That won't do anything. You draw to the screen in the Draw events. In any other event, you can only draw to surfaces.


2)


The Draw GUI event is only for drawing things, not creating them. WHEN you create an object has practically no relevance to when/where it is drawn.


3)


What? Drawing a sprite doesn't initialize anything (well, it loads up the texture page).
Let me try to respond to each of these as I understand how they work. I may not understand how GameMaker utilizes them, and is very much so why I have come here looking for help! :)

1.) This code does do something. I have an existing object named obj_GameMenu, when Escape is pressed in my "Room", it instances/shows/initializes/whatever (lol) obj_GameMenu, which calls the draw menu script in the Draw GUI event. Was my idea on how to draw a menu when pressing Escape. Even though this may be the wrong way to have done it, like I said, I am new to GameMaker and this is how I understood to do it. My sprite does successfully draw using my pre-defined x and y variables, though. If it should not work, somehow it does! *flexes accidental skill*

2.) Thank you, this is helpful to know. From using GameMaker documentation and the Draw / Draw GUI events, I assumed it would affect its position on the screen in relation to screen/window sizing and scaling.

3.) Perhaps I am using the wrong terminology? My understanding is that using the draw_sprite function was instancing my spr_MenuBG sprite and "initializing" whatever method GameMaker uses to draw it on the screen, as you point out the "texture page."

I may have a lot to learn about how GameMaker interpretes and prints to the screen, but I am trying and I greatly appreciate everyone's help so far on my very first forum post! Thank you for taking the time to read through my questions and address where I may be failing - this can only help me get better.
 
J

Jonathan E. Styles

Guest
You can always use parents, you create a base button with all the interactive stuff on it, then create each button being a child of the first, changing the sprite, so you dont write all the same code again for all the buttons, and just adding the code when you click on each button... thats the elegant way to use an object for every option on the menu.

But then again if you want to do it the other you can do it:

Code:
//draw GUI event

if mouse_x>rectangle_x1 and moouse_x<rectangle_x2 and mouse_y>rectangle_y1 and mouse_y<renctangle_y2
    {
    draw_sprite_ext(spr_button1,1, x, y, xscale, yscale, rot, colour, alpha );
    }
else
    {
   draw_sprite_ext( spr_button1,0, x, y, xscale, yscale, rot, colour, alpha );
    }

where:

the first parameter is the sprite name and the second the index of that sprite

the rest are self explained

you will need one of this for each button sprite with their x and y positions and then you wont use more than 1 object
This may be exactly what I am looking for. I will report back once I have had time to implement it. Please bear with me, rather busy today!
 
J

Jonathan E. Styles

Guest
No you do not need one object for each button. I am creating a sample project for you
to look at and see if it is what you are looking for.
Awesome! Thank you for the attention. Everyone has been such a wonderful help so far. I look forward to the outcome as I am just starting this project and how this is utilized would drastically alter the outcome of the final product. So far all I have done is create a splash screen, determine screen size and properly position my elements based on screen size. As this is a fullscreen application this menu is kind of necessary to close the application as it presents the "Exit" button. ;)

I seem to dwell on the little things a bit too much, and I try to research things as much as I can before asking any questions - but I am kind of at a loss on this one.
 

TheouAegis

Member
Re: 1) Oh, I didn't put 1 and 2 together. So what is preventing the button instances from being spawned every step? The Begin Step, Step, End Step, Begin Draw, Draw, End Draw, and Draw GUI events run over and over and over. So if you're creating an instance in the Draw GUI event, it's running that creation code with the draw call (which is okay) for that step, then the next time the Draw GUI event comes around, it'll create another instance of the button. If you're just drawing the sprites and not creating instances, then yeah, your terminology is horribly confusing.

Re: 3) You don't need to initialize texture pages in most cases. Just let stuff happen on its own. A lot of the things GM does on its own are better left up to GM.


The order in which instances are created determines the order in which their codes are checked for each event EXCEPT the drawing events. When the drawing events roll around, backgrounds are handled first (actual backgrounds). GM then looks through every instance and every tile layer, comparing depths. The higher the depth, the sooner an instance/tile gets drawn, so anything with a lower depth will be drawn on top. Afterward, all foregrounds are drawn (actual backgrounds flagged as foregrounds). Then GM moves on to the Draw GUI events. Whereas the drawing events are based on coordinates in the room, the Draw GUI event is based on coordinates in the game window (which has its own slough of issues). Anything drawn in the Draw GUI event, which as far as I know is still based on depth, is drawn over the application surface (which is where the Draw event draws to). So if your obj_menuBG is drawing in the Draw GUI event as well, it should probably have a very high depth like 10000.

Note however that depth only applies to differences between separate instances. Within a single instance, depth has no correlation to draw order; draw order within an individual instance is based on the order of the draw commands. So
Code:
draw_sprite(spr_menubg,0,0,0);
draw_sprite(spr_menubutton,0,0,0);
would draw the button over the background, but
Code:
draw_sprite(spr_menubutton,0,0,0);
draw_sprite(spr_menubg,0,0,0);
would draw the background over the button.
Code:
depth = -1000;
draw_sprite(spr_menubutton,0,0,0);
depth = 1000;
draw_sprite(spr_menubg,0,0,0);
would have the same result of drawing the background over the button, because depth has no correlation to draw order within an individual instance.
 
T

The5thElement

Guest
I have created the example menu for you to look at. Note that this does not use DND actions
other than the Execute Code action and is bare bones. You can download the example here.

Shout out to @slayer 64
 
J

Jonathan E. Styles

Guest
I have created the example menu for you to look at. Note that this does not use DND actions
other than the Execute Code action and is bare bones. You can download the example here.

Shout out to @slayer 64
Wow. That is pretty much exactly on target, @The5thElement ! I had a feeling I would have to utilize an array to do it properly, but I didn't even want to start digging into figuring that out yet. I would need to pick apart the code piece-by-piece to understand what each section is doing in each Event.

Using the rectangle and mouse detection as recommended by @makas I have gotten my menu displaying and working correctly after changing my background element from a Sprite to an actual background image. The "mouse over" image swap is working perfectly too!

Perhaps utilizing your method I can figure out how to handle the mouse events for clicks now. Almost done! I'm going to hammer away at this a bit more and let you know what happens!

Thanks!
 
T

The5thElement

Guest
You're very welcome. It is fairly complex but I commented the code to the best of my ability.
If you have any questions about how it works or what's doing what don't hesitate to ask.
 
J

Jonathan E. Styles

Guest
Well, I have cobbled together something that works. I took a bit of this and a dash of that until it eventually gave me the object that I wanted. Please note, my code includes variables and lines from other parts that are used to draw the screen. Please feel free to look at what I have done and help me improve! So far everything works, though. It generates the menu on [Escape] along with the sprite buttons and has a functioning "rollover" effect and a click handler. Pressing [Escape] or the "Resume" button closes the menu. I'll throw the code here along with the project file in case you want to look at it - who knows, it may even help someone else!

First, the obj_GameMenu object. This object can just sneakily be put into any room to add the Events needed to capture the [Escape] key and draw the menu. Here are the Events I used and the code:

obj_GameMenu -> Create Event:
Code:
global.gamepaused = false;
obj_GameMenu -> Draw GUI -> [DnD Action] Execute Script:
Code:
script: PauseMenuPopup

//no arguments needed here.
obj_GameMenu -> press <Escape>:
Code:
if (global.gamepaused == false)
 {
  global.gamepaused = true;
 }
else
 {
  global.gamepaused = false;
 }

This concludes the entire of the obj_GameMenu object. Note, this object does not have a sprite assigned to it.

PauseMenuPopup Script:

Code:
if (global.gamepaused == true)
{

 // Get screen center point.
 mid_x = room_width / 2;
 mid_y = room_height / 2;

 // Find starting coordinates for the menu background and draw.
 cnt_x = mid_x - (background_get_width(bck_MenuBG) / 2);
 cnt_y = mid_y - (background_get_height(bck_MenuBG) / 2);
 draw_background(bck_MenuBG, cnt_x, cnt_y);
 
 // Determine starting point for button drawing and create buttons.
 // Note, the first button will be 35 pixels from the top. Change
 // button spacing by altering the 'buttonspacing_y' variable.
 
 btn_w = sprite_get_width(spr_MenResume);
 btn_h = sprite_get_height(spr_MenResume);
 btn_x = mid_x - (btn_w / 2);
 btn_y = cnt_y + 35;
 buttonspacing_y = 85; // Change to alter button vertical spacing.
 
 // Create buttons and events.
 
 script_execute(MenuButtonCreate, btn_x, btn_y, spr_MenResume, ResumePressed);
 btn_y += buttonspacing_y;
 script_execute(MenuButtonCreate, btn_x, btn_y, spr_MenSave, );
 btn_y += buttonspacing_y;
 script_execute(MenuButtonCreate, btn_x, btn_y, spr_MenLoad, );
 btn_y += buttonspacing_y;
 script_execute(MenuButtonCreate, btn_x, btn_y, spr_MenOptions, );
 btn_y += buttonspacing_y;
 script_execute(MenuButtonCreate, btn_x, btn_y, spr_MenExit, QuitGame);
 
}

What this is doing: Essentially, we are getting the centerpoint of the room the object is in, using the background sprite to determine where to start drawing it on the screen and then developing the x and y coordinates to start drawing buttons. After that we call my secondary script to draw each button. I have elaborately named this button generation script as MenuButtonCreate. This script will accept arguments and draw buttons based on those arguments.

MenuButtonCreate Script:
Code:
///MenuButtonCreate(draw_x, draw_y, sprite_to_use, perform_function)

btn_w = sprite_get_width(spr_MenResume);
btn_h = sprite_get_height(spr_MenResume);

if (mouse_x>argument[0] and mouse_x<argument[0] + btn_w and mouse_y>argument[1] and mouse_y<argument[1] + btn_h)
{ draw_sprite_ext(argument[2],1,argument[0],argument[1],1,1,0,c_white,1);
 if mouse_check_button_pressed(mb_left) { script_execute(argument[3]); }
}
else
{ draw_sprite_ext(argument[2],0,argument[0],argument[1],1,1,0,c_white,1); }

Again, we get the button sprite dimensions for placement utilization. (I guess I could have placed these in a global? Oh well!) The script then gathers the previously-sent arguments and builds the button based on the starting point calculated from the previous script and uses the sent sprite index name and it will then execute the "sub script" named in the arguments. Instead of drawing a rectangle as previously suggested, I went ahead and just watched for a mouse in the drawn coordinates of each sprite. This then either draws the image index of 0 or 1 based on the mouse position in relation to the button.

Lastly, I created two small "sub scripts" to handle the two buttons I can use right now (Resume and Exit) as I do not have any functions lined-up for the other options.

ResumePaused Script:
Code:
global.gamepaused = false;
QuitGame Script:
Code:
game_end();
So far, the code is extremely convoluted. If you see anything that could be spruced-up or think I could be helped in some way, please let me know.

The outcome of this code shows a pop-up menu over a fullscreen room with five buttons that change color when rolled over. Screenshot below:


Menuss.png


A copy of my trial can be found here.
 
Last edited by a moderator:
Top