YoYo Games Wiki

Surface tutorial

From YoYoGames Wiki

!

As of GM:Studio, this article is no more up-to-date. Surface target set/reset and drawing on surface should be called only in draw event, and if you need to redraw it, you can use flag (variable) to indicate than it should be redraw in next draw event call.

Contents

Introduction

Surfaces were first implemented in Game Maker 6.1. The surface functions allow the user to use the standard drawing functions to draw on a canvas, or a surface, once and then perform operations on the surface such as saving it to a file, drawing it on the screen, applying transformations on it, or copying it to another surface.

Benefits

The benefits of using surfaces are most apparent when drawing complex figures which require many calls to various drawing functions. Instead of calling many functions each time the draw event is fired, one can simply draw to a surface and then draw this surface to the screen each time the screen is redrawn. This means that even if you draw thousands of lines on the surface, only a single function call is needed to redraw the surface each step which can tremendously reduce overhead.

Concept

To understand surfaces one must understand how the Game Maker runner draws on the screen. Each time a drawing function is called (normally in the draw event) it performs its operations on a surface known as the 'target' surface. By default this surface is a special surface which is created by the runner when the game loads. At the end of each step this special surface is automatically drawn to the screen and erased. When you opt to draw on a user-created surface you are simply changing the target surface. However, this new target surface is not the default surface and therefore is not automatically drawn to the screen or refreshed at the end of each step.

Primary Code

Basic

surface_create(w,h) Creates a new surface with the width w and height h. It then returns the surface id which is used in all the other functions. This means that when calling this function, you must set a variable's value to this function, like a_var=surface_create(5,5);
surface_exists(id) Checks whether the surface with the given id exists. Returns true or false (1 or 0, respectively).
surface_free(id) Frees the memory that the surface with the given id is using. This must be called when there is a surface you are no longer using in order to save memory.
surface_get_width(id) Returns the width of the surface with the given id. The width is what you enter in the 'w' field in the surface_create(w,h) function.
surface_get_height(id) Returns the height of the surface with the given id. The height is what you enter in the 'h' field in the surface_create(w,h) function.

First Attempt

Ok! So let's try out these functions that we have just learned in a piece of code, and see what happens! We will create an object called o_surfaces and put this code in its Create Event.

surface=surface_create(50,100); //Creates the surface
draw_text(5,5,"This text is drawn in the create event.");
/*The code above attempts to draw text in the create event!*/

Explanation of First Attempt

The code above creates a surface called surface and attempts to use it to draw text in the create event, and therefore take advantage of the surfaces by using a drawing action once to draw on a surface on the screen.

Problem

But after you place the object in the room and run the game, you will notice that no text is drawn! Why is that? You wonder!

Reason

The answer is simple! Game Maker doesn't understand automatically when to draw on a certain surface and when you need to draw on the main room, so you need to use certain functions that will switch between drawing on surfaces and the main room, and switch between making the drawings on certain surfaces between each other.

Code explanation will help:

surface_set_target(id) This function is the function that tells Game Maker what is the target of the drawing. You need to enter the id of which surface do you want the drawings to be on.
surface_reset_target() Resets the drawing target to be on the normal screen again. It is important to use this code immediately when you finish drawing what you need on the surface, as it may mess up the whole game; objects, backgrounds, and other things are drawn on the main screen, making the game draw these things on a surface will corrupt the game.

Second Attempt

Ok, so now, we will do another attempt to draw on the screen using surfaces:

surface=surface_create(50,100); //Creates the surface
surface_set_target(surface); //Sets drawing surface target
draw_text(5,5,"This text is drawn in the create event.");
/*The code above attempts to draw text in the create event!*/
surface_reset_target();
/*Resets the drawing target. The drawing target is not set as
the main screen in order for the game to continue functioning properly
without corruption.*/

Problem

Go ahead and test this code, you may think it will work. But it still wouldn't! Do you have any idea why?

Reason

It's simple: the test is saved in the surface, and it is in the memory, but it is not drawn to the game's main screen! It is true that I said you don't have to use the draw functions constantly to draw all the drawings and lines, but however, you do need to draw the surface. So when you draw a 100 lines in the surface, you only need to execute one each step (the surface drawing function), not zero. Below is the code explanation for the surface drawing functions. They have been copied from the help field, then edited to make the functions clearer.

  • draw_surface(id,x,y)
    Draws the surface with the given id in the position (x,y) without any colors or alpha blending.
  • draw_surface_stretched(id,x,y,w,h)
    Draws the surface with the given id stretched to the width of w and h, in the position (x,y).
  • draw_surface_tiled(id,x,y)
    Draws the surface with the given id tiled so that it fills the entire room. It starts the tile in the position (x,y).
  • draw_surface_part(id,left,top,width,height,x,y)
    Draws the indicated part of the surface with the given id that is drawn at the position (x,y). The part of the surface starts and the position (left,top) with the width width and the height.
  • draw_surface_ext(id,x,y,xscale,yscale,rot,color,alpha)
    Draws the surface with the given id in the position (x,y) scaled by xscale and yscale (1= no scaling) and rotated using the value rot (0=no rotation), and with blending color (use c_white for no blending) and the transparency alpha value from 0 to 1, whereas 0 is transparent completely.
  • draw_surface_stretched_ext(id,x,y,w,h,color,alpha)
    Draws the surface with the given id stretched to the size of (w,h) in the position (x,y). Color is the blending color (c_white=no color blending) and alpha indicates the transparency setting from 0 to 1 whereas 0=transparent.
  • draw_surface_tiled_ext(id,x,y,xscale,yscale,color,alpha)
    Draws the surface with the given id tiled so that it fills the entire room and starts in the position (x,y), but now with scale factors (xscale,yscale) and a color blending (color) and transparency setting (alpha) from 0 to 1 whereas 0 is transparent.
  • draw_surface_part_ext(id,left,top,width,height,x,y,xscale,yscale,color,alpha)
    Draws the indicated part of the surface with the given id that starts on the position (left,top) of the surface, with the width and height (width,height), with its origin at position (x,y) but now with scale factors (xscale,yscale) and a color blending (color) and transparency setting (alpha) from 0 to 1 whereas 0 is transparent.
  • draw_surface_general(id,left,top,width,height,x,y,xscale,yscale,rot,c1,c2,c3,c4,alpha)
    The most general drawing function. It draws the indicated part of the surface with the given id, that starts on the position (left,top) in the surface, with the width and height of (width,height), with its origin at position (x,y) but now with scale factors (xscale,yscale), a rotation angle (rot), a color for each of the four vertices (top-left, top-right, bottom-right, and bottom-left) (c1,c2,c2,c4 respectively), and an alpha transparency value (alpha) from 0 to 1 whereas 0 is transparent.

Third Attempt

So in order to make the drawing on surfaces function work properly, you need to draw the surface in the draw event, all the other code that you have made in the create event is correct and must stay the same. Add the following code to the Draw Event.

draw_surface(surface,0,0); //Draws the surface 

Result

Now you can clearly see that the text is drawn, even though only the surface drawing function is called! Depending on your system and font-size settings, you may see that a part of the text is not drawn, that is because your surface width is 50, and there might not be room for all the text! This, ladies and gentlemen is the magic of surfaces!

Now let's move to the more-complex part of the surfaces, which is the once dealing with files and saving.

Secondary Code

Files and saving

surface_save(id,fname) This function saves the whole surface with the given id to a bitmap file. The filename must be a string (fname). The 'fname' could be just the name of the file or its complete target.
surface_save_part(id,fname,x,y,w,h) This function saves a part of the surface with the given id to a bitmap file. The filename must be a string (fname). The 'fname' could be just the name of the file or its complete target. The part of the surface begins at the position (x,y) on the surface, and has the width (w) and height (h).

Example

Example is adding this to the Create Event of o_surfaces:

surface_save_part(surface,working_directory+"/Surface Part.bmp",7,10,25,15); //Saves a part of it.

This saves the surface as a .bmp wherever the game is located (This means if you haven't saved the game yet, the surface will be saved as a temporary file and deleted after the game is closed).

More Functions

Now, all we have to learn which is not very complex is getting information about the pixels of the surface, and copying a surface's contents into another surface!

  • surface_getpixel(id,x,y)
    This function gets the color of the pixel in the position (x,y) in the surface with the given id.
  • surface_copy(destination,x,y,source)
    This function copies the whole surface (source) to the new surface (destination). The (source) copied will be pasted in the position (x,y) of the new surface (destination). The copying occurs without doing any blending.
  • surface_copy_part(destination,x,y,source,xs,ys,ws,hs)
    This function copies a part of the surface (source) to the new surface (destination). The (source) copied will be pasted in the position (x,y) of the new surface (destination). The part of the (source) to be copied will start on position (xs,ys) of the source and have the width (ws) and height (hs). The copying occurs without doing any blending.

Please keep the following information in mind (taken from Help File):

Quote
(Help File)

Note that there are no functions to copy part of the screen to a surface. (This is impossible due to possible format differences between the screen and the surfaces.) If this is required you must set a surface as render target and next draw the room. You can then use the surface copying routines to get parts of it.

Note that you can also create sprites and backgrounds from surfaces. See the section on changing resources for more information.

Some care must be taken when using these functions. In particular please notice the following:

  • You should never change the drawing target while you are actually drawing on the screen, that is, never use it in drawing events. This will cause serious problems with the projection and viewport.
  • Surfaces do not work correctly with 3D mode. You can use them while not in 3DD mode (by calling d3d_end() before using them, but once you start 3D mode again the surfaces will be destroyed.
  • For reasons of speed, the surface is maintained in video memory only. As a result, you might loose the surface when e.g. the screen resolution changes or the screensaver pops up.

Limitations

Surfaces will often not work correctly if they exceed certain dimensions. For technical reasons, graphics cards have a limit on the size a single texture (a surface) can be. In very old cards this can be as little as 1024x1024. 2048x2048 is the most common limit, with 4096x4096 and even 8192x8192 being possible on very modern cards. As such, it is recommended that you limit your surfaces to 2048x2048 or lower in size.

The same limitations apply to backgrounds.

See Also