• 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!

GameMaker [Solved?] Maddening! New title: I'm confused by Display, GUI, Surface sizes

G

Guest

Guest
I've read the manual for rooms, viewports, cameras, windows, displays, surfaces, the application surface, and the gui layer. I've watched Pixelated Pope's scaling tutorials. I read this thread that asked a very similar question but kind of devolved. So, I'm not lazy, just slow.

My game is a grid based game intended for mobile, think of it like a chessboard. The real size of the gameboard--the room--is 160 x 224.

The problem is that I need a GUI above and below the room. The best solution I've found is to set a larger viewport at my desired ratio, 16:9, which is 162 x 288. This gives me 64 unscaled vertical pixels to use for the GUI.

I set a y offset for the viewport of 32, centering the room. Then I wrote the following Draw GUI code to try to draw blue rectangles where the GUI should go, no matter how it's scaled, using "keep aspect ratio" in the main options:

Code:
var dw = window_get_width();
var dh = window_get_height();
var dm = dh / view_get_hport(0); // calculate how much we've scaled up (display multiplier)

// top bar
draw_rectangle_color(0, 0, // origin is the top-left corner
                               (view_get_wport(0) * dm), // bottom-right x equals the width of the viewport, scaled up to the display
                               (((dh - (room_height * dm)) / 2)), // bottom-right y is half the extra space(display_h minus scaled room_h)
                               c_navy, c_navy, c_navy, c_navy, false); // navy-colored and opaque

// bottom bar
draw_rectangle_color(0, // origin x
                               dh - (((dh - (room_height * dm))  / 2)), // origin y is half the extra space up from the bottom
                               (view_get_wport(0) * dm), // bottom-right x equals the width of the viewport, scaled up to the display
                               dh, // bottom-rght // bottom-right y equals the bottom of the display
                               c_navy, c_navy, c_navy, c_navy, false); // navy-colored and opaque
This works well--mostly. At all the common resolutions, it looks like this:

upload_2017-6-2_18-17-32.png

But I can make it bug out if I drag the corner into odd positions; worse, it does this at 340x640, which I read is the "viewport" (not resolution) of the Galaxy S7. (And please let me know if a phone's "viewport" doesn't matter versus its resolution, because then I can just ignore this problem.)

The bad result, like at 340x640, looks like this, where lime green is my room background color:
upload_2017-6-2_18-20-13.png

And I absolutely cannot figure out why it does this. Any pointers would be very welcome.

(I've tried using multiple viewports, setting the GUI size to the desired viewport size and drawing the application surface in the GUI draw event, but none of this has given any better results. I need to draw over the extra space because my game uses wrapping, and GM draws objects outside of the room.)
 
Last edited by a moderator:
G

Guest

Guest
The fix lay in application_get_position(). This function provides the position of the application surface in relation to the display/window. When you've told GameMaker to "keep aspect ratio," GameMaker will put black bars in the display as necessary to maintain the aspect ratio. The application surface draws inside the black bars.

Something about the GUI's size goes weird at this point, I don't understand why this should be the intended behavior. The GUI draws starting at 0, 0 of the application surface, but it seems to think that its size is the window size, for example:

Code:
window height:  845
gui height:  845
surface height:  574
This is what makes no sense to me: Drawing to 0, 0 in the Draw GUI event draws to 0, 0 of the application surface [** edit: this is the behavior according to the manual but not necessarily the case, see below post with example of SFO/surface origin], but the gui layer size is the window size. I.e., when you draw to 0, 0 of the GUI layer, you're not drawing to 0, 0 of the GUI layer. Something is being adjusted automatically.

Therefore, my problem was that I was calculating GUI positions in relation to 0, 0 of the window size (which I thought would be 0, 0 of the draw GUI functions) rather than 0, 0 of the application surface. I noticed something was wrong when I realized my GUI wasn't drawing inside the black bars, and I read the YYG Scaling for Devices blog post, which anticipated this as the normal behavior and recommended using the display_set_gui_maximize() function for drawing inside the black bars (which I don't want to do).

Using application_get_position(), I was able to get the offset between the top of the window/display/GUI-layer and the top of the application surface. I multiplied that offset by 2 to account for both the top and bottom black bars. Then, I drew my GUI objects in relation to to the actual draw space rather than the window space.

Here's my new code, and it seems to work no matter how I stretch or distort the window:

Code:
// scr_display_check
global.agp = application_get_position();
global.sxx = global.agp[0]; // left edge offset between window and surface
global.syy = global.agp[1]; // top edge offset between window and surface
global.sww = global.agp[2] - global.agp[0]; // surface bottom-right x
global.shh = global.agp[3] - global.agp[1]; // surface bottom-right y
global.ddm = global.shh / view_get_hport(0); // viewport-to-display-scale multiplier

// Alarm event delayed 30 steps  in the first room
scr_display_check();
if room == room_first room_goto_next();

// Draw GUI event
draw_rectangle_color(0, 0, // origin is the top-left corner
                               (view_get_wport(0) * global.ddm), // bottom-right x equals the width of the viewport, scaled up to the display
                               (((global.shh - (room_height * global.ddm)) / 2)), // bottom-right y is half the extra space
                               guicol, guicol, guicol, guicol, false); // monocolored and opaque
draw_rectangle_color(0, // origin x
                               global.shh - (((global.shh - (room_height * global.ddm))  / 2)), // origin y is half the extra space up from the bottom
                               global.sww, // bottom-right x equals the absolute width of the surface relative to the window
                               global.shh, // bottom-right y equals the absolute height of the surface relative to the window
                               guicol, guicol, guicol, guicol, false); // monocolored and opaque
And hopefully this means it will also adjust perfectly to any mobile display.

Edit: Corrected some comments in code, in case some poor soul finds this and thinks I knew what I was talking about.

Edit edit: Reviewing this a week later, I was basically correct. I don't know why I re-calculated ww and hh, but the basic idea is sound: to find the edges of a smaller room in a larger, offset viewport, you need to calculate the scaling multiple by dividing hh by your room height, then use that multiple to figure out your room's size in real/scaled pixels, then compare your room's real size to the surface area (0,0 upper left and ww, hh bottom right) to figure out where the top and bottom of your room (in real pixels) begins and ends.

Edit edit edit: Replaced the bad code with good code.
 
Last edited by a moderator:
G

Guest

Guest
I'm also really, really surprised that what I'm doing seems to be novel. I've got a grid based game, and I need to draw a GUI above and below it. It's basically a chessboard. It could be any number of one-screen little boardgame style games.

And yet GMS2 and every tutorial I've found assumes that your display == one room or a portion of one room. Why? In a game like mine, there's no need for any space outside your game except for the GUI.

Also, why haven't I seen anything about this relationship between GUI size, actual GUI draw location, and surface application size? I would imagine people would always be running into a problem where drawing a GUI button in relation to the bottom of the display stops working correctly as soon as GMS2 starts adding black bars to keep the aspect ratio. But after a lot of searching, I never found any internet posts on point.

My worry is that I'm overcomplicating this and there's some obvious, easy thing that I'm missing. Or maybe my google skills suck.
 
Last edited by a moderator:
G

Guest

Guest
Well, my infinitely scalable, indestructible GUI scaling method still works, but since it involves calculating the application surface coordinates every Draw GUI step [edit: not really, see postscript below], I'm still poking at it. This has, I now realize, nothing to do with my particular game. Anyone with any size room and view might run into this if they start trying to align their GUI buttons to their viewport, using weird display sizes while telling GameMaker to keep aspect ratio.

Edit: The surface position doesn't seem to get set until 10 or 20 frames into drawing a room, so, although you don't need to calculate the relationships every step, you do have to wait 10 or 20 frames until you can get them.
 
Last edited by a moderator:
Top