• 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!

GML How to Clip a Sprite Outside a Rectangle

M

Moon Goat

Guest
I'm trying to figure out how to clip a drawn sprite so it can't be seen outside of a defined rectangle:
Untitled-1.png
(sorry if this image is a bid crude)

I assume this can be done with surfaces or blend modes or something? I'm aiming to make this as efficient as possible, since it may have to be used a lot of times in what I'm working on. Does any one have any ideas on how to achieve this? Thanks!
 
L

Lysho

Guest
The short answer is yes. The long answer is: What exactly are you trying to do? Do you want all sprites outside that rectangle to be clipped off, or just certain sprites? Are you trying to get a camera that only sees part of the map at a time? Surfaces could be a solution, but I can't give you a surefire answer unless I know the surefire question.
 
M

Moon Goat

Guest
@Lysho , I'm trying to create a clip mask that prevents selected sprites from being seen outside the rectange, like how you can only view a certain portion of a webpage at a time within a GUI window in an operating system.
 

Perseus

Not Medusa
Forum Staff
Moderator
I'm trying to create a clip mask that prevents selected sprites ... you can only view a certain portion of a webpage at a time within a GUI window in an operating system.
You could use a surface for that. Create one with dimensions of the view. Clear it with the background image or colour that's supposed to be shown outside the rectangular region in the beginning of the Draw event. Then punch a rectangle in the surface using the bm_subtract blend mode at the desired position and draw the surface onto the screen in the Draw GUI event. Region outside the rectangle won't be visible as it would be covered by the background image or colour that you cleared the surface with. The punched out part of the surface will make the things under it visible. That's the same effect that you mentioned.

But if it's "selected sprites" that you want to take into account, look into draw_sprite_part which should be able to do exactly what you want after a number of calculations.
 
L

Lysho

Guest
EDIT: RIP, got beat to the punchline xD

@Lysho , I'm trying to create a clip mask that prevents selected sprites from being seen outside the rectange, like how you can only view a certain portion of a webpage at a time within a GUI window in an operating system.
So you want to only see sprites inside that rectangle? So you want all other sprites outside of the rectangle to just be invisible or clipped from view, yes? I'm a little confused because you say you want only selected sprites from being seen outside the rectangle, but then when you say 'viewing a certain portion of a webpage', you also suggest as long as its outside of the rectangle, it should be clipped.

I'll assume you just want things inside the rectangle to be visible, and everything else to be clipped out of the picture. I spent some time tinkering with this example system but it works perfectly. It may not be exactly what you're looking for, but you could try dissecting it and then repurposing it how you like. Here's a link of a gif showing what I scrapped together:
http://gph.is/2nmoTc6

There are two objects involved in this (not counting the blue thing lol): an object that makes the black screen, and an object that "punches a hole" in the black screen so you can see through it like a window.

create an object called obj_covered with a depth more negative than every object you want to cover(so something like -1000)

create event

Code:
surf = -1; //initialize the surface variable
draw event
Code:
if !surface_exists(surf){ //surfaces are 'volatile', they can be destroyed randomly, so always check!
    surf = surface_create(room_width,room_height); //size it to the room size to cover everything
    surface_set_target(surf); //begin drawing to this surface
    draw_clear(c_black); //make it totally black to cover everything, hence why  -depth to be on top
    surface_reset_target(); //stop drawing to this surface
}else{
    draw_surface(surf, 0, 0); //if the surface exists, draw the surface to the game
}
Now create a sprite of any size (you can always scale it to a certain size in the create event of the window object. I called it obj_revealed

Step event (this is so the window moves following the mouse. you can make it move however you like)

Code:
x = mouse_x;
y = mouse_y;
Draw event (where the magic happens)

Code:
if instance_number(obj_covered) > 0 then{ //always check if it exists to avoid crashes
    if surface_exists(obj_covered.surf) then{ //again, check if the surface exists to avoid crash
        surface_set_target(obj_covered.surf); //start drawing to the surface
        draw_clear(c_black); //make it black again (or else you always erase and it never recovers itself)
        draw_set_blend_mode(bm_subtract); //time to erase or punch a hole!
        draw_self(); //whatever the sprite it, punch that out
        draw_set_blend_mode(bm_normal); //revert to normal!
        surface_reset_target(); //stop drawing to this surface
    }
} else{
    draw_self();
}
Is this what you were looking for?
 
M

Moon Goat

Guest
@Lysho , Actually, before I asked for help, I already had a few methods in mind for how I wanted to achieve this. One of my ideas was to take my selected sprite(s), draw them onto a surface as big as the whole GUI, and then use draw_surface_part to clip the said sprites. The sprites would have to be updated constantly, so the surface would be like a temporary surface that would have to be destroyed an rebuilt every time they were updated, which I am afraid would use up too much RAM. I'm trying to create this kind of functionality for a GUI with windows and lots of of other elements that could be clipped by such windows, and I'm afraid of that the method used to clip those elements might cause extreme lag if I have a bunch of them due to each one of them having an individual large surface to be stored in texture memory. Is there any more efficient way, or a way to make my proposed method more efficient?

Perhaps I can use some "master surface" that is shared among all elements for drawing and then clearing the surface when they are individually done with it?
 
Last edited by a moderator:
L

Lysho

Guest
Ah, I'm not well versed in the processing power demanded by surfaces, but from what I understand, all sprites are already being drawn every single step anyway- onto the application surface. If you give them a draw event to draw onto a specific surface instead of the application surface(which always exists and cannot be deleted), they will draw onto that only, so while that surface is now taking a little more work, the application surface is taking a little less work in trade. I don't believe it matters that you're creating and deleting surfaces every step because every sprite is already dealing out the processing power of drawing itself onto the application surface anyway. You should try your method and see if it really lags, because that would be interesting to know.
 
M

Moon Goat

Guest
I'm now thinking that it's more efficient to have all the elements share a single GUI-sized surface that they draw to, show a part of, and then clear after drawing rather then giving each of them an individual surface to draw on. This can work because Gamemaker doesn't draw/execute every object's code all at once, but rather in a specific list-order. The million dollar question is, would drawing to a surface and then clearing in a bunch of times every step cause significant slow-down? I'm certain that this would at least take up less texture memory since every object isn't given their own surface ( overloading texture memory can cause lower-end devices and older smartphones to crash).
 
L

Lysho

Guest
You do not need every sprite to have its own surface. Just make them all draw themselves onto one big surface, and use draw_sprite_part to clip the sprites you want to clip? Basically what Ragarnak suggested.
 
I'm trying to figure out how to clip a drawn sprite so it can't be seen outside of a defined rectangle:
Use the corner point coordinates of the rectangle x1,x2,y1,y2, set them to variables. Then use draw_sprite_part, and only draw the parts in that region defined by the four variable. I don't know whether that will be efficient, or not, or whether you need surfaces to be more efficient.
 
M

Moon Goat

Guest
@YellowAfterlife , your rectangle clip shader is exactly the efficient solution that I needed! Now I no longer have to use a "master surface" that gets cleared loads of times every frame! Thank you so much!
 
Top