UI Shop Advice

JeanSwamp

Member
Hello,

I know GMS is not very friendly on the UI department, unlike how easy stuff is handled in Unity or other engines with built in UI stuff... so here's the thing. I need to make a mobile shop and I am unsure how to handle it to achieve something like this example:


I normally struggle with all "scrolling" related stuff. Any tips or advice more than welcome!

Thanks
 

NightFrost

Member
Can't open the file (wants a login I suppose) but are you talking about stuff like scrollable areas with content items moving up and down, according to mousewheel or scrollbar?
 

TailBit

Member
They could be made as simple as putting the elements in the room and scrolling by changing the view position, while the borders are just drawn in the draw GUI event
 

NightFrost

Member
As it happens I talked about various ways to implement a scrolling region in this post in another thread. I might add that I prefer to use clipping shader method in most cases.
 

JeanSwamp

Member
As it happens I talked about various ways to implement a scrolling region in this post in another thread. I might add that I prefer to use clipping shader method in most cases.
This is something I already do with an inventory, but this is a bit more tricky, because you have different offsets, headline text for item separation and a bunch of other stuff. I think I am more or less capable of doing the surface scrolling as I already did, what I struggle is to figure out how to actually do the items.

Since each item has a clickable BUY, I am guessing this is just an array and objects are created so they're clickable?
 

Mool

Member
Like this?


There are many ways to do it. You could use a surface and draw it surface_draw_part, or you have some objects, that you scroll up ad down. I use the surface.
 

NightFrost

Member
Since each item has a clickable BUY, I am guessing this is just an array and objects are created so they're clickable?
That is one alternative, yes. (I'd use pure data in structs to represent them, but both approaches are equally valid.) You'll just have to change the actual x/y positions of those objects so their clickable parts match up with what's on screen when the shop window scrolls up and down. Have the shop manager object create the surface and let all shop item objects know of it. Each shop item would redirect its draw to this surface. Then in draw end event of the manager the surface is drawn to screen.

Remember that even though a shop item instance may have scrolled off the visible region, it is still active and capable of receiving click events. They must check whether click occurred in visible region by comparing click position to that visible rectangle. Since that data is likely set up in the shop manager, it is something more it must communicate to shop items. And as it probably is responsible for creating the shop item instances according to some list, that's the natural point for sending over all the necessary data.
 

JeanSwamp

Member
Like this?


There are many ways to do it. You could use a surface and draw it surface_draw_part, or you have some objects, that you scroll up ad down. I use the surface.
And how do you make the objects clickable if is just a surface?
 

FrostyCat

Member
And how do you make the objects clickable if is just a surface?
The same way you make purely drawn things clickable: Check if the mouse button is pressed (e.g. mouse_check_button_pressed), and if the mouse coordinates lie within a given region (e.g. point_in_rectangle).

Here is an example of a drawn grid that can detect hovers and clicks, outside of a surface context:
GML:
var xbase = x;
var ybase = y;
var wcell = 16;
var hcell = 16;
var xcells = 8;
var ycells = 6;

var yy = ybase;
repeat (ycells) {
    var xx = xbase;
    repeat (xcells) {
        if (point_in_rectangle(mouse_x, mouse_y, xx, yy, xx+wcell, yy+hcell)) {
            draw_set_colour(mouse_check_button(mb_left) ? c_red : c_blue);
        } else {
            draw_set_colour(c_white);
        }
        draw_rectangle(xx, yy, xx+wcell, yy+hcell, false);
        xx += wcell;
    }
    yy += hcell;
}
A scrolled surface would have more layers of complexity (e.g. checking if the mouse is on the drawn surface region, converting mouse coordinates from in-room/on-gui to on-surface), but the idea is still the same.
 

JeanSwamp

Member
I see, thank you very much. I understand if you want to do ease_in stuff and visual stuff, an object base approach is easier to handle?
 

JeanSwamp

Member
How do you deal with headers for different items? As this is mainly for a shop you display different categories within the same scrollable area. I made some markings:

SHOPUI.png

Here they would make a Daily Deal, or whatever. followed by first title item, then coins, followed by gems header, and followed by gems.

Since I am guessing the most effective way to do this is just for loops, how do you make the breaks between for loops of "items" to add the headers.
 

NightFrost

Member
How do you deal with headers for different items?
Depends on how your shop contents are stored and generated. Assuming these are objects, the header would be another of those. Some control in your code is aware of what the list is supposed to be filled with, and is also aware of them being divided into these groups. So when it starts positioning the contents, it starts with a header and positions it at very stop. Then it loops through shop items belonging under that header and tiles their positions beneath the header. When it moves to next grouping, it again creates a header, then tiles the items beneath it, and so on. The width and height of these items - headers, shop items and whatever else needs to fill the list - would be a property of each item. They know what their contents are, so they are able to calculate how wide and tall they need to be. Or perhaps it is a general property of all items belonging to a type; items are always 200x400, and so on.
 

TailBit

Member
GML:
//create
list = [
    [instance],
    [instance],
    [instance,instance,instance]
]

// draw

var row,i,e,ins,h=0;
for(row=0;row<array_lenght(list);row++){
    e = list[row]; // get the array from that row (e because I ended up calling everything element as I wrote this)

    if(array_lenght(e) == 1){ // I let the array lenght deside how to handle it
        ins = e[0];

        // if the centered one is 200 and the big one 600
        // set x to 0 if 600 and 200 if small one
        ins.x = (ins.sprite_width<300)*200;
        // ins.y = h - (list_total_height-visible_area_height) * scroll
        with(ins)draw_self()

    }else{

        for(i=0;i<array_lenght(e);i++){
            ins = e[i];
            ins.x = 200*i;
            // ins.y = h - (list_total_height-visible_area_height) * scroll
            with(ins)draw_self()
        }

    }
    // h += e[0].sprite_height;
}
If you had a scrollbar, then the y would maybe been something like ins.y = h - (list_total_height-visible_area_height) * scroll .. (scroll being a fraction value from 0 to 1) where h is increased by the height of the instance in the current row (+ margins if you need that) h += e[0].sprite_height; before the next loop starts

I used draw_self in case you have set them to be invisible .. but it would also be possible to make a code that can take all instance sizes and center them if needed

In list I use to calculate the first visible element by taking
row = (list_total_height - height_of_visible_area) * scroll / row_height // where scroll is a fraction value 0-1
so one don't have to draw all the ones you have scrolled past

and also to stop the loop if we are past the area

but with different size elements you can't do that .. instead you have to remember what row you started drawing at, and the y cordinate it have .. and when scrolling is done then you check if it have moved up/down enough so that the position = (list_total_height - height_of_visible_area) * scroll is outside its y coordinate and height of the instance it holds and then update the variable for first row and adjust the y for it

But I do. :p
If the room don't scroll then you could lay out the elements outside the visible area, and then just use a 2nd view with a scrollbar and border drawn on it to display it.. that way you could turn it on and off and position it where you want

An array with your entries would be the better if the menu need to change at some point. (could finally answer from a keyboard)
 
Top