GameMaker sufaces from other objects

I have the following code under the draw event of an object. This creates a surface then pops some bits inside it. This code works fine. My problem is I want to draw things inside the surface from another object. I assumed I could prefix obj_differentobject. followed by the surface name. However when I try to do this I get an error saying the surface does not exist. In summary my question is how to draw to a surface that was created with a different object from the one using the surface. Thanks in advance.

if(!surface_exists(grid_surface)) // Ensures that grid does not already exist (Prevents multiple instances)
{
grid_surface = surface_create(grid_surface_width, grid_surface_height); // Create grid surface
}
surface_resize(grid_surface, grid_surface_width, grid_surface_height); // Update surface parameters
surface_set_target(grid_surface); // Set grid surface target
draw_clear_alpha(grid_surface_colour, 1); // Clear grid surface

// DRAW CODE GOES HERE

surface_reset_target(); // Reset grid surface
draw_surface(grid_surface, grid_surface_x_offset, grid_surface_y_offset); // Draw grid surface
 
I don't see what the draw code has to do with it. ok then the draw code is draw_sprite(spr_test, 0 , 0, 0);

The problem is not that it doesn't work in the object that created it, it's that other objects won't recognise it exists.
 
Update: I didn't bother to check if it existed before hand. I assumed because the object that created the surface first that I would not have to bother checking. Must be something to do with the order it gets loaded in. I'm happy now anyway thanks.
 

NightFrost

Member
Yeah, GMS makes no guarantees about code execution order beyond event order. Further, with surfaces being volatile, an exists check needs to run anyway each time you intend to manipulate one.
 

NightFrost

Member
As I'm a web developer I can only speak from that angle, so someone else has to speak for C++ and such... While one could argue a webpage runs on a somewhat different principle, to generalize there are cases where you need data that is not available. Like when user switches page on paginated view. This is solved with asynchronity. Name it "callbacks" or "promises" or whatever your language of choice uses, the idea is that a routine goes to work to fetch that data, and when it finishes at some point in the future, the callback activates and makes use of that data. But that's not really comparable to GMS, as instances are more like a bunch of machines running in parallel.

Or did you mean surface volatility (other environments might have different names)? That's how GPUs work. I suppose this is because VRAM is a scarce resource, so when an active operation requires it, necessary amount will be made available if it comes to it by deallocating older reservations. Not expert on the matter though.
 
L

Lycanphoenix

Guest
Would you be willing to share some screenshots so we can observe the issue visually?
 

Hyomoto

Member
@Daniel Mallett - Surfaces are a topic that lead to a bit of confusion. To cover the basics of surfaces, they do need to be checked before you use them, however, you do not have to check each time you want to use them. It is not possible for them to cease to exist during part of a frame. Thus, it is generally sufficient to check once per frame. Your issue is with the order of operations. If the first object is creating and managing the surface, but the other object attempts to access it before that check/creation has occurred, the program will crash because the surface doesn't exist. You could put in another protection to test if the surface exists before you use it, but it would be easier/safer/more stable to manage it yourself. One method is to have one object manage the other objects, thus ensuring their draw events fire in the proper order, another might be to place the draw code in a different step such as a step event. Contrary to popular belief, you are able to draw and create surfaces during the step event, though normally the former action isn't useful. As for the latter,
GML:
// end step
if ( surface_exists( mySurface ) == false ) {
  mySurface = surface_create( some_width, some_height );

}
This would be sufficient to ensure your surface exists before the draw step. You could even throw some clearing code in there, but at that point I would strongly push you towards just managing your event order better. The problem you are having is a "coupling" issue. One object is trying to manage the surface, the other object is trying to use it. This is a unsafe operation, as you have discovered, because it relies on some part of the VM you have no control over to ensure that everything happens in the right order. You could write in some protections for it, but at some point it would be simpler/safer/more stable to have one object handle both functions:
GML:
surface_set_target( mySurface );
  for( var _i = 0; _i < ds_list_size( myObjects ); ++_i ) {
    with ( myObjects[| _i ] ) { event_perform( ev_draw, 0 ); }

  }
surface_reset_target();
In this way you can be absolutely sure the surface exists and the objects are drawn afterwards without creating a bi-directional coupling. Besides, when you get down to it, an object draws itself. What surface it draws to isn't explicitly a function of that object: it doesn't need to know where it is drawn. After all, do you worry about whether or not an object is drawn to the application surface? No. You have the expectation that it will be and simply tell the object what to draw. Using other surfaces doesn't have to change that. However, in that scenario you do want to set an object's `visible` flag to false. Visible, behind-the-scenes, simply decides whether or not an object is included in the draw events. If you choose to draw the object yourself, you can omit it form GM's normal draw pipeline. Not doing so could cause visual artifacts (the object being drawn twice) but most importantly just wastes processing power doing something twice.
 
Last edited:
Top