Masking in GameMaker with surfaces

MrDave

Member
GM Version: GameMaker Studio
Target Platform: All (including HTML5)
Download: http://www.davetech.co.uk/files/gamemakermasking.gmz
Links: http://www.davetech.co.uk/gamemakermasking

Summary:
A guide on how to use surfaces to do masking in GameMaker. This allows you to erase parts of the image so you can see through them.


Tutorial:
See the download link above to get the guide. Sorry it has to be on an external website but I think it is important to see the interactive demo at the top to understand the power of masking.

 
Last edited:

chance

predictably random
Forum Staff
Moderator
Both links take me to the web page with the written guide. I don't see a GM source file for download -- which is what I expected "download" to mean. Perhaps that's an oversight, but in this case either way is fine. The online tutorial is clear, and you've provide the relevant code sample. (Personally, I prefer to see actual downloads. Because that allows me to quickly check for errors or bugs, when the example is more extensive.)

Anyway... nice little tutorial here. Your illustrations and discussion should be easy for beginners to follow. And I'm sure members can find lots of fun uses for this technique. Nice job.
 

MrDave

Member
Both links take me to the web page with the written guide. I don't see a GM source file for download -- which is what I expected "download" to mean. Perhaps that's an oversight, but in this case either way is fine. The online tutorial is clear, and you've provide the relevant code sample. (Personally, I prefer to see actual downloads. Because that allows me to quickly check for errors or bugs, when the example is more extensive.)

Anyway... nice little tutorial here. Your illustrations and discussion should be easy for beginners to follow. And I'm sure members can find lots of fun uses for this technique. Nice job.

Thanks for the feedback, I updated the post to include a project file. I will make sure I do that in the future.
 
I

itameio

Guest
this is amazing!, i can think of a couple of ways to use this (with credit ofcourse), appreciated!
 
J

Jaqueta

Guest
Awesome!
This is specially useful with shaders, like, adding radial blur only on the edges and things like this...

Another thing that would be cool is creating a custom Primitive with Raycasting, that would show only the players Field of view in a top down game... He would be able to see the world normally, but the enemies would appear only on the mask.

Really useful, thanks! It will be used, and I will give you credits ;D
 

MrDave

Member
Awesome!
This is specially useful with shaders, like, adding radial blur only on the edges and things like this...

Another thing that would be cool is creating a custom Primitive with Raycasting, that would show only the players Field of view in a top down game... He would be able to see the world normally, but the enemies would appear only on the mask.

Really useful, thanks! It will be used, and I will give you credits ;D
I have actually done both of those but I want to make sure the article is well written before I post it out: Top down casting shadows and Top down field of view
 
M

mariospants

Guest
Dave, this stuff is pretty damn inspirational, thanks for sharing!
 
M

Muetdhivers

Guest
Very interesting stuff here.
my two cent about you code example :
I try your code on the page http://www.davetech.co.uk/lighttechdemo2
as written in the page, it doesn't work as intended.
you reference the x and y of the player without using other.x and other.y inside a with statement, you need to change that to make it work.

otherwise i learn some interesting stuff from that example, so thanks !
 

MrDave

Member
Very interesting stuff here.
my two cent about you code example :
I try your code on the page http://www.davetech.co.uk/lighttechdemo2
as written in the page, it doesn't work as intended.
you reference the x and y of the player without using other.x and other.y inside a with statement, you need to change that to make it work.

otherwise i learn some interesting stuff from that example, so thanks !
Wow well spotted! I had to check my code but it is because I used a for() in my project and changed it to a with() on the website as I thought it was more obvious what was going on. It's fixed now.

Thanks for that. And thanks for looking at my other tutorials.
 

Juju

Member
Hi Dave, good job on the masking guide.

I do, however, have concerns about your shadow casting / line of sight examples. Your lengthdir-based solution is very wasteful and isn't the most efficient way of achieving the desired effect. Resources are always at a premium, nowhere moreso than HTML5.

You'd be better off using Cartesian coordinates and scaling them up relative to the light's position. You can achieve a fixed distance at the expense of using an inverse square-root or, better yet, have very large triangles and not worry about a maximum size. The code is not significantly more complex (in my opinion, it's actually simpler) and can still serve as a pedagogical tool.
 
M

Muetdhivers

Guest
@Juju, could you write an example of what you are talking about ? (i'm very bad at math, and a beginner in coding, but i'm really interested in that kind of think).
 

Juju

Member
Sure, here's some code:
Code:
var _ox = x;
var _oy = y;
var _a_large_number = 10000;
var _dx, _dy;

draw_set_colour( c_black );

with( obj_wall ) {

    draw_primitive_begin( pr_trianglestrip );

    draw_vertex( bbox_left, bbox_top );

    _dx = bbox_left - _ox;
    _dy = bbox_top - _oy;
    draw_vertex( x + _dx * _a_large_number, y + _dy * _a_large_number );

    draw_vertex( bbox_left, bbox_bottom );

    _dx = bbox_left - _ox;
    _dy = bbox_bottom - _oy;
    draw_vertex( x + _dx * _a_large_number, y + _dy * _a_large_number );

    draw_vertex( bbox_right, bbox_top );

    _dx = bbox_right - _ox;
    _dy = bbox_top - _oy;
    draw_vertex( x + _dx * _a_large_number, y + _dy * _a_large_number );

    draw_vertex( bbox_right, bbox_bottom );

    _dx = bbox_right - _ox;
    _dy = bbox_bottom - _oy;
    draw_vertex( x + _dx * _a_large_number, y + _dy * _a_large_number );

    draw_primitive_end();

}
If you want to control the maximum length of each scaled vector - which I doubt is even necessary since a GPU will clip all polygons to the viewport - you can use a slightly different format that uses an inverse square-root:
Code:
...
    _dx = bbox_left - _ox;
    _dy = bbox_top - oy;
    _n = 1 / sqrt( _dx*_dx + _dy*_dy ); //This should be var'd prior to the "with" loop
    draw_vertex( x + _dx * _n * _distance, y + _dy * _n * _distance );
...
...where _distance is the scaled vector's length. Note the manual use of Pythagoras rather than point_distance - function/script calls have a non-trivial overhead and if you're calling a script thousands of times every frame then you need to manage the CPU load carefully. Also, this fixed length method naturally fails when _dx and _dy equal 0. It's best to simply avoid this outcome by making sure lights are never in positions where this is true; however, if this isn't acceptable for some reason then a simple point-in-rectangle check should be done beforehand to work around it.
 
Last edited:

MrDave

Member
Hi Dave, good job on the masking guide.

I do, however, have concerns about your shadow casting / line of sight examples. Your lengthdir-based solution is very wasteful and isn't the most efficient way of achieving the desired effect. Resources are always at a premium, nowhere moreso than HTML5.

You'd be better off using Cartesian coordinates and scaling them up relative to the light's position. You can achieve a fixed distance at the expense of using an inverse square-root or, better yet, have very large triangles and not worry about a maximum size. The code is not significantly more complex (in my opinion, it's actually simpler) and can still serve as a pedagogical tool.
Hey Juju, thanks for your feedback. I will change the code because I think you are right. However both really are equally as good. You say you “Have concerns” Well you shouldn’t. I spent ages just performance comparing our codes and got identical results (as best you can on a computer running as many programs), 50% of the time yours performed better, 50% mine performed better.

You specifically say about performance on HTML5 and having a non-trivial overhead well I actually went through the source output to actually find the exact code that gets run behind the scenes by GameMaker when you use point_direction() and you will find the code is very trivial:

Code:
function _06(_U8,_V8,_g8,_h8){var _a4=_g8-_U8;var _b4=_h8-_V8;if(_a4===0){if(_b4>0)return 270.0;else if(_b4<0)return 90.0;else return 0.0}else {var _6v=180.0*Math.atan2(_b4,_a4)/_H8;_6v=(~~round(_6v*1000000))/1000000.0;if(_6v<=0.0){return -_6v}else {return(360.0-_6v)}}return _Tt}
Like I said you are right, but I think you are over dramatizing the problem. We are talking about a small amount of simple maths every frame.
 
GM Version: GameMaker Studio
Target Platform: All (including HTML5)
Download: http://www.davetech.co.uk/files/gamemakermasking.gmz
Links: http://www.davetech.co.uk/gamemakermasking

Summary:
A guide on how to use surfaces to do masking in GameMaker. This allows you to erase parts of the image so you can see through them.


Tutorial:
See the download link above to get the guide. Sorry it has to be on an external website but I think it is important to see the interactive demo at the top to understand the power of masking.

honestly, the link to your guide is really annoying. why? i don't seem to make a copy and paste the code part of your guide... no mention that the click right button was completely disabled...

other than that, your guide was an interesting read.
 
Top