Changes to the GUI layer, and the new Application Surface

Posted on 17 January 2014

This is a change we've been thinking about making for some time, but it was such a large piece of work that it's always been put off. However, coming back from the festive break seemed like a perfect opportunity to start afresh, and to just jump right in and to try and address the situation. So what's wrong with the GUI layer?

The first issue is that the rendering on each platform is different, some have a background surface the game is rendered to, while other go direct to the display (or Back Buffer as its usually known). For example, Windows has a back ground surface the same size as your room (or the bounding box of all views), while Android and iOS will scale the whole game to fit their screens.

This is not only inconsistent, but just plain annoying. It means on platforms like OUYA, you have to simulate the windows back surface yourself using the view_surface_id[], blitting it to the display in the GUI event. And on windows, the GUI isn't 1:1 rendering like Android, as it renders to the back surface. So if you have a low-res game (say 256x256), then that's the number of pixels available for the GUI, no matter what res your monitor does. This also sucks. Lastly the window version never scaled properly when in a window, this was due to the overly complex nature of the rendering API, keeping a "hidden" surface while maintaining a proper hires back buffer, and this meant your game wouldn't keep the Aspect Ratio properly if a user scaled up the window.  

These, and a hundred other little issues can give games an unpleasant end user experience, especially if you wanted to try and resize the window and scale the back buffer differently, it all just got very messy.

So, what we've been doing is basically cleaning up ALL this code, making it the way we actually want it to work (especially now we know what the GUI event is being used for - as it's still being a relative newcomer to GameMaker). The upshot is totally consistent rendering behaviour across all platforms (HTML5 is still pending at time of writing, but will be updated as well), and a very sexy set of new features!

First, the good bits. Every device will now correct for Aspect Ratio, from Windows, to iOS and Android, and no longer will you have to fudge the OUYA rendering to get a speed up- it'll just work. Second, GUI rendering will by default be 1:1 with the pixels in the buffer. This lets you do a very low-res game, but very hirers on screen displays or text - if you want! Setting the GUI size via display_set_gui_size(w,h) will still let you scale that to the size you've designed your GUI for. Also, the Window's "window" will now scale properly, fitting a game into the display with the correct aspect ratio (if selected that is), and the "black" bars on a game will now be filled with the standard windows colour (which you can set with window_set_color(col) ), again making the whole experience much nicer.

Now for the fun, and seriously sexy bit!

The hidden surface the game is being rendered to, is no longer hidden! Studio now creates a standard GameMaker surface and the whole game is rendered into that (unless of course you render a view using a surface_id[], this is still perfectly valid). This surface is also available in GameMaker via a new built in global variable called application_surface, and you can set it, clear it, and render to it however you like, giving you total control of your game rendering. This new surface is then rendered just after the Post Draw Event allowing you to use this event to change the surface before it hits the screen, or you can disable automatic rendering, and use the Post Draw Event to render it using a shader, giving you easy access to post-screen effects or your own transitions.

This surface, like all Studio games to date , starts up as the size of the 1st room - or the bounding box of all views, however we've now added a new surface_resize(id,w,h) command, allowing you to easily resize the application surface outside of the window_set_size() command. This finally breaks the bond between game and window resolution, and means in full-screen mode, it's now easy to change the resolution your game is being rendered at, and you can raise, or lower it however you like, simulating hardware resolutions, but without being forced to suffer TFT monitor blur. Please be aware that resizing the application_surface will only happen at the start of the next frame, and that a resize does not preserve the old contents.

To help all this work smoothly, we've also made some changes to how surface_set_target() works. Before, you could issue several of these, and each one would override the previous one, meaning you only had to do one surface_reset_target() at the end. No more. Surfaces are now stackable, meaning you MUST pair up each set target, with a matching reset target. NOTE: This is a breaking change!!! If you didn't do this before, the application will now throw an error! The surface stack MUST be balanced at the end of the frame. However, what this does mean, is that you can be rendering to a surface, and then decide you need to create a dynamic sprite or effect using another surface, and you'll no longer have to remember which surface you're using, just set the surface, then reset it later and then return to what you were doing.

So the new draw events that were introduced in the previous Early Access build, now take on greater meaning. First the Pre-Draw event, this event happens on the BACK BUFFER, not the application_surface. What this lets you do is change the screen directly, render whatever you want to it and prepare the display or setup other items. However because the application_surface is just a normal GameMaker surface, if you want to render to it, all you have to do is surface_set_target(application_surface) and you can render whatever you like as normal. This means you can clear it, draw a background image to it, or whatever you like.

Next, GameMaker will process views doing all the "DrawBegin, Draw, DrawEnd" events for each view. Each of these events are contained inside the view. After all views have been rendered, you now have a full game on the application_surface, ready to draw.

The Post-Draw Event then kicks it. Again, the display is reset back to the Back Buffer and this gives you the choice of effecting the game surface directly, or drawing it yourself to the Back Buffer by getting its texture (using surface_get_texture(application_surface)) and passing it to a shader for a post screen render effect. Or you could copy the surface to another surface - perhaps even a little smaller, and using it as a TV screen on the next frame, use it as the basis of a transitikon effect or even wrap it on a mesh and bounce it around the screen!. The choices are endless.

Lastly we have the GUI events. These again will now render to the back buffer at 1:1 resolution, meaning you have full use of the display, no matter what resolution your game is running at. So even if you have a very retro and block Gauntlet style game, your fonts, icons and surrounding health bars could all be in 1080p if you like. If you want to keep the retro style, then by simply using the display_set_gui_size(width, height) command, everything will once again rendered to that size.

These changes also finally make GameMaker: Studios rendering engine totally consistent, as all these changes will be available across all platforms, so you'll no longer have to contend with special case code. In fact, about the only thing you should have to worry about now, is what resolution your application_surface should be for best speed and look of the game!


We're all pretty excited about these changes, and what they'll mean for the normal Studio user. The niggles that stop you doing exactly what you want should now be removed, and on top of this the ability to do some amazing tricks, and be confident they'll all work across platform.

Back to Top