• 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!
  • Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

Question - Code [solved] Two problems with GUI layer (resolution and shader)

nesrocks

Member
Problem 1: My GUI layer's resolution is matching the window resolution, not the real internal resolution. Since my game is a scaled low resolution, the game itself is pixelated (as it should), but the GUI is high res. I'm having trouble setting the GUI to be the same resolution as the game.

Game without any shader applied. The green box is on a Draw GUI event:



Problem 2: When using a shader for the application_surface, everything drawn on the Draw GUI events isn't getting drawn with the shader.

Both problems in one image:



That shader is a variation of this one (just tweaked some values): https://marketplace.yoyogames.com/assets/701/crt-pixel-shader

So, I understand this probably has to do with surfaces, but I have trouble with it. It feels like my GUI isn't being draw to the application surface?

init:
Code:
global.shader_to_use = shr_bw_CRT;
global.zoom = 2;
global.gamewidth = 640;
global.gameheight = 360;
window_set_size(global.gamewidth * global.zoom, global.gameheight * global.zoom);
display_set_gui_size(global.gamewidth, global.gameheight);
display_set_gui_maximise(global.zoom,global.zoom,0,0)
shader init:
Code:
application_surface_draw_enable(false);

crt_surface_scale = global.zoom;

crt_surface_width  = global.gamewidth * crt_surface_scale;
crt_surface_height = global.gameheight * crt_surface_scale;

uni_crt_sizes = shader_get_uniform(global.shader_to_use, "u_crt_sizes");
distort = shader_get_uniform(global.shader_to_use, "distort");
distortion = shader_get_uniform(global.shader_to_use, "distortion");
border = shader_get_uniform(global.shader_to_use, "border");

var_distort = false;

var_border = true;

surface_resize(application_surface, global.gamewidth, global.gameheight);
And finally, at the Post-Draw event of a persistent "control" object:
Code:
shader_set(global.shader_to_use);
shader_set_uniform_f(uni_crt_sizes, global.gamewidth, global.gameheight, crt_surface_width, crt_surface_height);
shader_set_uniform_f(distort, var_distort);
shader_set_uniform_f(distortion, global.var_distortion_ammount);
shader_set_uniform_f(border, var_border);
 
draw_surface_part_ext(application_surface, 0, 0, global.gamewidth, global.gameheight, 0, 0, global.zoom, global.zoom, c_white, 1);
 
shader_reset();
 
Last edited:
  • Like
Reactions: Ock

Mick

Member
I'm having trouble setting the GUI to be the same resolution as the game.
Instead of using the gui layer, you could draw the gui items to a surface which is the same resolution of your game. You would then draw that gui surface in the draw end event. You could also just draw the gui elements "normally", taking the view position in account.

So, I understand this probably has to do with surfaces, but I have trouble with it. It feels like my GUI isn't being draw to the application surface?
You feel correctly, the GUI is separate and not drawn to the application_surface. The solution above will fix that.
 
Last edited:

nesrocks

Member
Instead of using the gui layer, you could draw the gui items to a surface which is the same resolution of your game. You would then draw that gui surface in the draw end event.
I tried this yesterday, but I couldn't get it to work. Besides, the docs say the surface may disappear if the window loses focus, so I feel like I shouldn't even use my own surfaces?

You could also just draw the gui elements "normally", taking the view position in account.
This is what I had been doing for the whole project but it felt horrible, because there is a GUI layer designed for this. Well, since I already have this system in place I'll go with this solution then, as it fixes both problems at once.
 

Mick

Member
I tried this yesterday, but I couldn't get it to work. Besides, the docs say the surface may disappear if the window loses focus, so I feel like I shouldn't even use my own surfaces?
You should always check if the surface exists, and re-create it if it doesn't before using it. You could have "surf = -1;" in the create event of the gui/hud object and in the draw event:

Code:
if(!surface_exists(surf) {
  surf = surface_create(view_wview, view_hview);
  surface_set_target(surf);
  draw_clear_alpha(c_black, 0);
  surface_reset_target();
}

surface_set_target(surf);

// draw stuff (to surface)

surface_reset_target();
draw_surface(surf, view_xview, view_yview);
Surfaces can be very useful, as long as you check/re-create before use, you are safe. :)

This is what I had been doing for the whole project but it felt horrible, because there is a GUI layer designed for this. Well, since I already have this system in place I'll go with this solution then, as it fixes both problems at once.
I don't personally think this is horrible, as long as you want the gui to have the same resolution as the game it's fine.
 

nesrocks

Member
I've just thought of a potential problem, my game uses split screen gameplay (different views at once). So drawing normally with a calculated offset will be a problem when I want a GUI element accross the screen, not to mention having to detect on which view the element would have to be drawn on. I'll try the surface approach once again.

edit: it worked for the first problem! Some tweaks in the code:
Code:
if !surface_exists(GUIsurface)
{
    GUIsurface = surface_create(640,360);
    draw_clear_alpha(c_black, 0);
}
surface_set_target(GUIsurface);

draw_set_color(c_white);
draw_circle(320,180,150,true); // test graphic

surface_reset_target();
draw_surface(GUIsurface, 0, 0);
Screenshot of the test graphic matching the resolution and being drawn on top of different views:



The screen shader is acting up though, I guess it does not like being drawn on top of itself (being drawn twice on two different surfaces on top of one another). There wouldn't be any way to draw my surface to the application surface before applying the shader to it, would there? Maybe surface_copy? I'll make some tests.

edit: I'm going to dump the screen shader for now, I don't think I have the know-how to use it. The language seems too complicated for me to grasp right now, and I can't find a way to merge the surfaces.

edit2: it has nothing to do with the shader being drawn twice, it's just that the shader doesn't properly take into account colors from other surfaces below it, so even if I draw the game on the application surface without a shader, and then draw just the HUD on my own surface and apply the shader to that surface, the other pixels bellow will be very dark.
 
Last edited:

Mick

Member
if !surface_exists(GUIsurface) { GUIsurface = surface_create(640,360); draw_clear_alpha(c_black, 0); }
This will not clear the created surface, it will instead clear the application_surface. I remember having graphical glitches on some computers if the created surface wan't initially cleared.

EDIT: Just read about this in the manual, the surface should be cleared because initially it can contain noise.

edit2: it has nothing to do with the shader being drawn twice, it's just that the shader doesn't properly take into account colors from other surfaces below it, so even if I draw the game on the application surface without a shader, and then draw just the HUD on my own surface and apply the shader to that surface, the other pixels bellow will be very dark.
I'm not really sure how you have things set up, but basically you would want to draw the gui surface to the application_surface without any shader and then you draw the application_surface to screen (I do it in the draw gui event) with the shader enabled.
 

nesrocks

Member
you would want to draw the gui surface to the application_surface
How is this done? This is the idea I had but I don't know how this would be possible. Merge two surfaces into one.

edit: okay, I think I got it. At the GUI begin draw event I set the target to the application surface, then at the GUI end draw event I reset the target and draw the application surface with the shader applied. I'll test further and see if this did it right.

edit2: yeah, everything indicates that this is it! Wish I could double-like your post. Didn't need to use the two surfaces or calculate view offset approaches, this one solved everything. It basically means that I can use the GUI draw event on any object as it should be used, because I only change the surface target at the control object before and after the GUI draw event, so no other object has to worry about it.

 
Last edited:

josyanf1

Member
How is this done? This is the idea I had but I don't know how this would be possible. Merge two surfaces into one.

edit: okay, I think I got it. At the GUI begin draw event I set the target to the application surface, then at the GUI end draw event I reset the target and draw the application surface with the shader applied. I'll test further and see if this did it right.

edit2: yeah, everything indicates that this is it! Wish I could double-like your post. Didn't need to use the two surfaces or calculate view offset approaches, this one solved everything. It basically means that I can use the GUI draw event on any object as it should be used, because I only change the surface target at the control object before and after the GUI draw event, so no other object has to worry about it.

Hi. You have the code?
 

Mike

nobody important
GMC Elder
These give you total control over the resolution of the GUI layer...you can set it to whatever you like, including 1:1 with the native display, or 1:1 with the game resolution. You can even scroll the whole UI about if you wanted...

If you want to pass it through a shader, then set the resolution using gui_size() and create a surface of that size, then render using a shader... You can also pass in both the UI and the application_surface to the same shader, so you can do things like scanlines in one,seamless pass.

display_set_gui_size()
display_set_gui_maximise()
 
L

Lonewolff

Guest
Code:
if !surface_exists(GUIsurface)
{
    GUIsurface = surface_create(640,360);
    draw_clear_alpha(c_black, 0);
}
surface_set_target(GUIsurface);

draw_set_color(c_white);
draw_circle(320,180,150,true); // test graphic

surface_reset_target();
draw_surface(GUIsurface, 0, 0);
Just a quick side note. The reason the surface isn't clearing is because you haven't set the target at the time of clearing. Better off moving the clear code to below the surface_set_target call. :)
 

nesrocks

Member
@Mike It wasn't working at the time, no matter what I did. As you can see from the code I posted, I was using display_set_gui_size() and display_set_gui_maximise(), but it wasn't doing anything. Perhaps it was a problem with using the shader. It doesn't matter, the problem was fixed a long time ago, the thread was ressurected by josyanf1.

@Ghost in the IDE Yeah I had fixed that already, but ultimately the solution given by Mick was what solved the issue.
 
Top