In the normal draw events, GameMaker Studio 2 doesn't actually draw directly to the screen, but rather draws to a surface called the application surface. This surface is basically a blank "canvas" that can then later be manipulated before being drawn to the screen when needed, and in most cases GameMaker Studio 2 handles this for you (although you can also manipulate it yourself in code for shaders, scaling and many other things - further details are given below).
However, apart from this application surface, you can also create your own surfaces and use them to create stunning or subtle special effects in your game. For example, you can use surfaces to "catch" instances, which can then be destroyed, and in this way you can create a decal effect where the sprite for the instance is displayed on the surface as if it still existed, permitting graphical effects like debris, blood, etc... without any real processing overhead. Another thing you can do with surfaces is use them as textures to be manipulated, or to create sprites "on the fly", or to create complex overlays. in fact, the uses for surfaces are endless!
Normal surfaces are quite easy to use, but there are a few basic rules to be followed when you use them:
- First, you should realise that surfaces (except the application surface) are "volatile". This means that if the device or window loses focus or is minimised (good examples are when a screensaver comes up in Windows, or on an Android device when the app loses focus due to a call) then the surface may be destroyed. This is because it is stored in the texture memory and may be overwritten when the target platform needs that memory for something else which means that you should always have some type of fail-safe code in place, usually with the surface_exists function.
- Second, you should note that surfaces require large quantities of texture memory to be used, and so you should attempt to keep them as small as possible. Normally you would try and keep them no larger than the size of the view or display window.
- Third, you should only create surfaces in the draw event. If you create a surface in the Create Event of an instance, you could potentially get the same index as the application_surface. This then can cause lots of problems and confusion as you think your using your own surface, but you are actually using the current render target. You should also always try to limit drawing to a surface in the draw event too, since due to the optimised way in which GameMaker Studio 2 draws to the screen, it is recommended that you keep all draw functions within the draw event - this includes clearing a surface when it is first created, etc... Drawing to a surface outside of the draw event is possible and may even be necessary for some effects, but it's not how it should be done.
- Fourth, when drawing to a surface manually, the surface is always at the position of (0,0). This means that you may need to convert absolute coordinates into local coordinates for the surface. For example, if you have a view-sized surface and wish to draw something that is currently in view to that surface, you should subtract the camera view x and y coordinates from the actual x and y coordinates to get a relative position to the surface (0,0) position. So, the code would look something like this:
if view_current = 0
<//li>
{
surface_set_target(surf);
with (obj_Effect)
{
var _vx = camera_get_view_x(view_camera[1]);
var _vy = camera_get_view_y(view_camera[1]);
draw_sprite(sprite_index, image_index, x - _vx, y - _vy);
}
surface_reset_target();
}
else
{
draw_surface(surf, 0, 0);
}
The basic use of a surface would be as follows - You first create a surface and assign its index to a variable. You would then set the drawing target to the surface rather than the display and draw the things you wish as well as perform any other manipulations. Once you are done you reset the drawing target so that all further drawing happens on the screen again. One thing to note is that should you require drawing the whole display to a surface (including tiles, backgrounds etc...) you can simply access the application surface itself (see below for further details) or you could assign a surface to a view port using the variable view_surface_id[0..7] as with this all that is visible in that view port will be drawn to the corresponding surface.
The following functions exist to deal with surfaces (these functions are specific for creating and manipulating surfaces, but to actually draw them to the screen you should be using the specific draw functions that can be found in the section on Surface Drawing, below):
- surface_exists
- surface_create
- surface_create_ext
- surface_resize
- surface_set_target
- surface_set_target_ext
- surface_get_target
- surface_get_target_ext
- surface_reset_target
- surface_copy
- surface_copy_part
- surface_depth_disable
- surface_get_height
- surface_get_width
- surface_get_texture
- surface_get_depth_disable
- surface_getpixel
- surface_getpixel_ext
- surface_free
- surface_save
- surface_save_part
As mentioned above, all normal drawing in your game is done on the application surface, and this surface can be changed and manipulated just like a normal surface. However, it is not a normal surface and advanced users will find that it enables complete control over how and when things are drawn in GameMaker Studio 2. For more details, as well as specific functions for this feature, please see the following section:
The following variable exists for referencing the application surface:
And you also have a few special functions that are designed only for use with the application surface:
Surfaces are an incredibly powerful graphic tool that GameMaker Studio 2 provides you for creating special effects and a whole host of other things. They are relatively simple to use as they are basically a canvas that you draw to instead of the display screen, and this canvas can then be manipulated and changed before being drawn with the functions in this section.
NOTE: When working with surfaces there is the possibility that they can cease to exist at any time due to them being stored in texture memory. You should ALWAYS check that a surface exists using surface_exists before referencing them directly.The following functions exist for drawing surfaces: