• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

SOLVED DRAW GUI Button Alternative

Xer0botXer0

Senpai
Goal: Create a clickable object that displays the players gold, draw it on screen and have it follow the view.

What I tried: I created the object, assigned it a sprite, used draw_text to draw the gold value into the sprite inside the DRAW GUI event.

Problem: The object appears to follow the view as I pan around, but the object is not clickable.

Code:
Step Event:
GML:
if distance_to_point(mouse_x,mouse_y) < 1
{
if mouse_check_button_released(mb_left)
{
dollar += 15;
}
}
Draw GUI Event:
GML:
draw_self();

draw_text(x + 28,y + 8, string(dollar));
Draw Event:
GML:
///this comment in the draw event removes a ghost copy of the sprite.
Debug: I used draw_text to print the objects x/y coodinates onto screen.
Finding: The objects x/y coordinates do not change when the view is moved around even though the sprite follows.
 
Last edited:
F

FranciscoJGamer

Guest
When you draw a sprite it doesnt make clickable so you need to Create a object than Follow the view or stay static in a place of the room and you can use the mouse click Event to make it clickable(the draw gui Event Follow the camera automátic if )
 

Xer0botXer0

Senpai
I know that drawing a sprite does not make the sprite clickable, but I also know that the code I supplied makes an object clickable when it has a sprite.
I am not drawing a sprite in the Draw GUI event, I am drawing text on the object sprite.

Beyond that I'm confused at your solution ?

---

Alright, I removed the draw event (NOT the Draw GUI event) code, which made the duplicate sprite visible. The duplicate sprite is clickable..

Infact, If I remove the Draw GUI event code, then the object is back to normal. I could easily do the following:

x = view_xview[0];
y = view_yview[0];

The problem is when I zoom in the view, the sprite zooms too, hence Draw GUI Event.. When I use the Draw GUI Event, a duplicate sprite appears, but it's not the original object.

Not sure how to figure out a simple solution. The only illogical thing I can think of now is to use image_xy/scale to counteract the effects of what ever is going on, but that just seems hilariously wrong.
 
Last edited:

Xer0botXer0

Senpai
Draw GUI falls under the Draw Event category. It's the same thing just Draw GUI is used to prevent scaling and what not.
So it's perfectly fine, in fact I doubted myself there and moved draw_self() into the draw event which made things worse.
 
If you're using the Draw GUI event, why are you using relative coordinates? The sprite might appear to be moving, but it isn't, since the GUI doesn't "move" and you aren't updating the coordinates. So while your sprite might be drawing deeper into the room as the view moves, the actual instance is all the way back where you initially left it, which is why you can't click on it..

Move away from using x and y and just check absolute coordinates. Switch from using sprite collisions to simple box checks (like point_in_rectangle()).
 

Xer0botXer0

Senpai
The Draw GUI sprite moves with the view or the window, basically when I pan around it stays in that one spot on my screen.
Problem: The actual object doesn't follow ..
What I tried: I set the object to move with the view.. since I'm moving the view around.
Problem: The object now does what the GUI sprite did, it stayed on the right spot.. but the GUI sprite started moving.

This makes the GUI sprite stay at the right place, but the object doesn't follow.
x = 0;
y = 0;

This makes the object stay at the right place, but the GUI Sprite moves around.
x = view_xview[0];
y = view_yview[0];

If I get change the Draw GUI Event to Draw Event with the above x = view.. code, everything is perfect except zooming scales the object which is what I'm trying to sort out with the Draw GUI Event..
 
Last edited:
Yeah, because the game world and GUI world don't work the same way. Don't move the sprite just because the GUI moves, as it actually doesn't. Let's assume your GUI is 100x100. If you want to draw something in the bottom left corner, just draw it at 0,100. It will "follow" the camera as it pans around the room. Now, you could move the object to keep up with the camera if you want to use collision checks, but you must not alter the drawing to match because it exists on a different plane. I personally would just use simple bounding box checks as opposed to collision checks for GUI elements.
 

Xer0botXer0

Senpai
I think I'm catching onto what you're saying by treating GUI layer content differently.

Instead of using the objects relative coordinates as a button, I could set a rectangle around new coordinates a.k.a the GUI sprite.

That would mean that 1.4 really doesn't have a way for me to achieve what I wanted to, that GMS wasn't programmed to allow for that.


Nope same story:
GML:
wid = sprite_get_width(spr_dollar);
hei = sprite_get_height(spr_dollar);




///Debug Button

if point_in_rectangle(mouse_x,mouse_y,28,8, 28+wid,8+hei)
{
    if mouse_check_button_released(mb_left)
    {
        dollar += 15;
    }
}
point_in_rectangle doesn't follow the gui layer. maybe it'll follow the view I'll try that out.

Does anyone know how to find some x/y variable for the GUI Layer ? Does it have coordinates to use as an offset ?

I understand relative coordinates, absolute coordinates are apparently relative of oneself coordinates according to some article.



==


So using point_in_rectangle with the view actually sticks. It feels like one massive redesign of what objects should already do. Infact what if I've got a circular button ? or a dinosaur as a button. But that's out of the threads scope. Unless someone wants to save me time. -.-

Need to see what's going on now since the text and the sprite are drawing on the left and right hand side of the clickable rectangle.. weird.


Solved
Thanks for the participation @BattleRifle BR55 @huenix @FranciscoJGamer
Special thanks to BR for leading me in the right direction with this new way of thinking about things and stuff.

I'm still somewhat unfamiliar with this code but seems it'll work for now.
 
Last edited:
So using point_in_rectangle with the view actually sticks. It feels like one massive redesign of what objects should already do. Infact what if I've got a circular button ? or a dinosaur as a button. But that's out of the threads scope. Unless someone wants to save me time. -.-
For a circular button, you could use point_in_circle() or point_in_ellipse() with the same method. For an arbitrarily sprite-shaped button, you need an object with a precise collision mask (this must be enabled in the sprite's settings) where the button is, and call collision_point() with the object instance's id and mouse position as arguments. The simplest example is if the button object itself plays this role:

GML:
// In button draw GUI or step event or similar

// Get mouse position
var mx = device_mouse_x_to_gui(0);
var my = device_mouse_y_to_gui(0);

// Assuming here that the button has a sprite with precise collision enabled
// assigned to its sprite_index or mask_index, and is positioned in
// room coordinateswhere it will appear in the GUI:

var mouseover = collision_point(mx, my, self.id, true, false);

if (mouse_check_button_pressed(mb_left) && mouseover) {
    // Button is pressed
}
 

Xer0botXer0

Senpai
For a circular button, you could use point_in_circle() or point_in_ellipse() with the same method. For an arbitrarily sprite-shaped button, you need an object with a precise collision mask (this must be enabled in the sprite's settings) where the button is, and call collision_point() with the object instance's id and mouse position as arguments. The simplest example is if the button object itself plays this role:

GML:
// In button draw GUI or step event or similar

// Get mouse position
var mx = device_mouse_x_to_gui(0);
var my = device_mouse_y_to_gui(0);

// Assuming here that the button has a sprite with precise collision enabled
// assigned to its sprite_index or mask_index, and is positioned in
// room coordinateswhere it will appear in the GUI:

var mouseover = collision_point(mx, my, self.id, true, false);

if (mouse_check_button_pressed(mb_left) && mouseover) {
    // Button is pressed
}

Are you sure that collision_point is going to pick up the sprites collision point ? o_O I need to test that.. immediately!
 
Are you sure that collision_point is going to pick up the sprites collision point ? o_O I need to test that.. immediately!
It will pick up the collision mask of the given instance (if you pass an id) or all instances of an object (if you pass the object index). It won't detect anything that's simply drawn in a draw event, even if that's a sprite, but if the object/instance has a sprite assigned to it through sprite_index (what you set in the object editor), it will use the default mask coming with this sprite, or if you also or instead assign it through mask_index, it will use that.

Hmm, actually, I'm a bit surprised there's no point_in_sprite() and point_in_sprite_ext() collision checking function matching draw_sprite() and draw_sprite_ext(), as that should be easy to implement and would help a lot of users.

Here's an example of how that could be implemented in GML, obviously much less efficient than ideal, with all the variable swapping:
GML:
function point_in_sprite_ext(pointx, pointy, sprite, subimg, _x, _y, xscale, yscale, rot) {
    var oldmask = mask_index;
    var oldimg = image_index;
    var oldxs = image_xscale;
    var oldys = image_yscale;
    var oldrot = image_angle;
    var oldx = x;
    var oldy = y;
    mask_index = sprite;
    image_index = subimg;
    x = _x;
    y = _y;
    image_xscale = xscale;
    image_yscale = yscale;
    image_angle = rot;
    var ret = collision_point(pointx, pointy, self.id, true, false);
    mask_index = oldmask;
    image_index = oldimg;
    image_xscale = oldxs;
    image_yscale = oldys;
    image_angle = oldrot;
    x = oldx;
    y = oldy;
    return ret;
}
function point_in_sprite(pointx, pointy, sprite, subimg, _x, _y) {
    return point_in_sprite_ext(pointx, pointy, sprite, subimg, _x, _y, 1, 1, 0);
}
A bit more efficient would be having an object made specifically for this and no other purpose, so that only assigning to its variables would be necessary, but still not as efficient as it could be as a built-in function.
 
Top