Javascript required
Skip to content Skip to sidebar Skip to footer

Game Maker Studio Draw Circle Alpha


Mouseover to activate, click to reposition the clipping star.

This is a tutorial near pretty much everything related to clipping drawn graphics in GameMaker.

That is, having fatigued graphics but brandish within a certain ("clip") area, be that a rectangle (UI regions, minimaps, etc.), circle, or a completely arbitrary shape (pictured above).

Also I'm trying new things and so this mail is nicely stuffed with interactive demos and snippets.

Rectangular prune (via surfaces)

Drawing sprites while clipping outside of rectangle in GameMaker.
Mouseover to view, click resize clip region

This one is the most common instance and the one that is the easiest to implement:

  1. Create a clip-area-sized surface.
  2. Draw the graphics into it, offsetting them (either directly or via d3d_transform) by clip expanse's top-left corner=' coordinates.
  3. Depict the clip expanse surface at it'south elevation-left corner' coordinates.

The code is straightforward likewise,

            // create a surface if it doesn't exist:            if            (!            surface_exists(clip_surface)) {            clip_surface            =            surface_create(clip_width,            clip_height); }            // clear and starting time drawing to surface:            surface_set_target(clip_surface);            draw_clear_alpha(c_black,            0);            // draw things here, subtracting (clip_x, clip_y) from coordinates:            draw_circle(mouse_x            -            clip_x,            mouse_y            -            clip_y,            40,            false);            // terminate and draw the surface itself:            surface_reset_target();            draw_surface(clip_surface,            clip_x,            clip_y);

Where clip_surface is the surface ID used for clipping (tin can be set to -one in Create), and clip_x\y\width\height define the prune' region.

Aforementioned approach is used in "scrollable content" example that I published a few years ago.

Rectangular prune (via shaders)

If y'all would prefer to use a shader over surface, you tin.

For this you would add a shader with the post-obit vertex code:

          attribute          vec3          in_Position;          aspect          vec4          in_Colour;          attribute          vec2          in_TextureCoord;          //                    varying          vec2          v_vTexcoord;          varying          vec4          v_vColour;          varying          vec3          v_vPosition;          //                    void          main() {     v_vPosition = in_Position;     gl_Position =          gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]         *          vec4(in_Position.x, in_Position.y, in_Position.z,          1.0);     v_vColour = in_Colour;     v_vTexcoord = in_TextureCoord; }

This is identical to the default "pass-through" code, except for 1 improver - information technology stores v_vPosition so that the fragment shader knows the coordinates of things being drawn. Fragment lawmaking, on other mitt, would be as post-obit:

          varying          vec2          v_vTexcoord;          varying          vec4          v_vColour;          varying          vec3          v_vPosition;          //                    uniform          vec4          u_bounds;          //                    void          principal() {          vec4          col = v_vColour *          texture2D(gm_BaseTexture, v_vTexcoord);     col.a          *=          bladder(v_vPosition.ten          >= u_bounds[0] && v_vPosition.y          >= u_bounds[ane]         && v_vPosition.10          < u_bounds[two] && v_vPosition.y          < u_bounds[3]);          gl_FragColor          = col; }

This pulls RGBA from the texture like a default laissez passer-through shader would, just sets the alpha channel to 0 if the point is exterior the rectangle (u_bounds).

Then, to utilise this, yous would prepare the shader and laissez passer in the rectangle' data:

            // debug:            draw_circle(mouse_x,            mouse_y,            40,            true);            draw_rectangle(clip_x1,            clip_y1,            clip_x2,            clip_y2,            true);            // gear up upwards shader:            shader_set(shd_clip_rect);            var            u_bounds            =            shader_get_uniform(shd_clip_rect,            "u_bounds");            shader_set_uniform_f(u_bounds,            clip_x1,            clip_y1,            clip_x2,            clip_y2);            // draw things:            draw_circle(mouse_x,            mouse_y,            twoscore,            false);            // finish:            shader_reset();

Where clip_ are the rectangle' bounds (much like with draw_rectangle).

A downloadable case of this is included at the stop of the blog post for your convenience.

Rectangular clip for sprites

Drawing sprites while clipping outside of rectangle in GameMaker.
Mouseover to preview, click to move rectangle' points.

Sometimes, you might want to draw a clipped graphic (due east.thousand. a cursor or an off-screen indicator) without creating a surface or adding a shader but for ane thing.

For example, in Don't Crawl we accept multiple layers of graphics (terrain tiles - splatter - entities - unscaled screen-space effects - entity overlays - cursors - gui), which required to clip some things to views despite them being drawn over them in the GUI events.

While rotated graphics would require a fleck of fancy math to clip the drawn polygon, for a standard case, things are simple enough - calculate the resulting bounds of a graphic, bank check if it falls inside the clip area at all, draw a slice of information technology defined by intersecting rectangle if then:

            /// draw_sprite_clip(sprite, subimg, ten, y, clipx, clipy, clipw, cliph)            var            s            =            argument0;            var            sw            =            sprite_get_width(s);            var            sh            =            sprite_get_height(s);            var            sx            =            sprite_get_xoffset(due south);            var            sy            =            sprite_get_yoffset(s);            var            si            =            argument1;            var            _x            =            argument2;            var            _y            =            argument3;            var            cx1            =            argument4;            var            cy1            =            argument5;            var            cx2            =            cx1            +            argument6;            var            cy2            =            cy1            +            argument7;            //            var            bx1            =            _x            -            sprite_get_xoffset(s);            var            by1            =            _y            -            sprite_get_yoffset(s);            var            bx2            =            bx1            +            sprite_get_width(s);            var            by2            =            by1            +            sprite_get_height(s);            //            switch            (rectangle_in_rectangle(bx1,            by1,            bx2,            by2,            cx1,            cy1,            cx2,            cy2)) {            case            i:            draw_sprite(southward,            si,            _x,            _y);            return            truthful;            case            ii:            var            lx1            =            max(0,            cx1            -            bx1);            var            ly1            =            max(0,            cy1            -            by1);            var            lx2            =            sw            +            min(0,            cx2            -            bx2);            var            ly2            =            sh            +            min(0,            cy2            -            by2);            draw_sprite_part(s,            si,            lx1,            ly1,            lx2            -            lx1,            ly2            -            ly1,            _x            +            lx1            -            sx,            _y            +            ly1            -            sy);            return            true; }            return            faux;

Or, if you lot want scaling and/or blending, a slightly fancier version:

            /// draw_sprite_clip_ext(sprite, subimg, x, y, xscale, yscale, color, alpha, rx, ry, rw, rh)            var            due south            =            argument0;            var            sw            =            sprite_get_width(southward);            var            sh            =            sprite_get_height(s);            var            sx            =            sprite_get_xoffset(s);            var            sy            =            sprite_get_yoffset(s);            var            si            =            argument1;            var            _x            =            argument2;            var            _y            =            argument3;            var            mx            =            argument4;            var            my            =            argument5;            var            sc            =            argument6;            var            sa            =            argument7;            var            cx1            =            argument8;            var            cy1            =            argument9;            var            cx2            =            cx1            +            argument10;            var            cy2            =            cy1            +            argument11;            //            var            bx1            =            _x            -            sprite_get_xoffset(s)            *            mx;            var            by1            =            _y            -            sprite_get_yoffset(south)            *            my;            var            bx2            =            bx1            +            sprite_get_width(s)            *            mx;            var            by2            =            by1            +            sprite_get_height(south)            *            my;            //            switch            (rectangle_in_rectangle(bx1,            by1,            bx2,            by2,            cx1,            cy1,            cx2,            cy2)) {            case            1:            draw_sprite_ext(s,            si,            _x,            _y,            mx,            my,            0,            sc,            sa);            return            truthful;            example            2:            if            (mx            ==            0            ||            my            ==            0)            return            true;            var            lx1            =            max(0,            cx1            -            bx1)            /            mx;            var            ly1            =            max(0,            cy1            -            by1)            /            my;            var            lx2            =            sw            +            min(0,            cx2            -            bx2)            /            mx;            var            ly2            =            sh            +            min(0,            cy2            -            by2)            /            my;            draw_sprite_part_ext(s,            si,            lx1,            ly1,            lx2            -            lx1,            ly2            -            ly1,            _x            +            (lx1            -            sx)            *            mx,            _y            +            (ly1            -            sy)            *            my,            mx,            my,            sc,            sa);            return            truthful; }            return            false;          

Use is simple plenty:

            draw_sprite_ext(q,            0,            mx,            my,            1,            1,            0,            -            ane,            0.i);            // background sprite (debug)            draw_sprite_clip(q,            0,            mx,            my,            x1,            y1,            x2            -            x1,            y2            -            y1);            // clipped sprite            draw_rectangle(x1,            y1,            x2,            y2,            1);            // prune border (debug)          

Arbitrary clip area (via surfaces)


Mouseover to preview, click to move the clip-circle.

This is where things become interesting:

  1. Create a surface for the prune-mask.
  2. Fill up the surface with an opaque black colour.
  3. Cut out (via draw_set_blend_mode(bm_subtract)) hole(s) for seeing things through.
  4. Create some other surface for clip area (same size).
  5. Describe the graphics into (and relative to) clip surface area surface.
  6. Cut out the mask surface out of clip area surface (again, via bm_subtract).
  7. Draw the clip surface area surface.

Therefore, mask-surface acts like a stencil for preventing prune area surface' contents from showing through where they shouldn't be.

The code is but slightly more complex:

            if            (!            surface_exists(mask_surface)) {            // create the mask-surface, if needed            mask_surface            =            surface_create(256,            256);            surface_set_target(mask_surface);            draw_clear(c_black);            draw_set_blend_mode(bm_subtract);            // cut out shapes out of the mask-surface:            draw_circle(128,            128,            70,            false);            //            draw_set_blend_mode(bm_normal);            surface_reset_target(); }            if            (!            surface_exists(clip_surface)) {            // create the prune-surface, if needed            clip_surface            =            surface_create(256,            256); }            // kickoff drawing:            surface_set_target(clip_surface);            draw_clear_alpha(c_black,            0);            // describe things relative to clip-surface:            draw_circle(mouse_x            -            clipx,            mouse_y            -            clipy,            xl,            faux);            // cut out the mask-surface from it:            draw_set_blend_mode(bm_subtract);            draw_surface(mask_surface,            0,            0);            draw_set_blend_mode(bm_normal);            // cease and draw the clip-surface itself:            surface_reset_target();            draw_surface(clip_surface,            clipx,            clipy);

Where clipx, clipy are the clip surface' tiptop-left corner, and clip_surface\mask_surface are the surfaces for clip expanse and mask accordingly (to be ready to -1 in Create-consequence). Surface and mask' sizes are constant (256x256) in this case.

This allows for noticeably more advanced effects - for case, in the demo at the commencement of the mail, this approach is used with a constantly updating mask surface to fill the overlapping surface area between 2 rotating shapes.

Capricious clip surface area (via shaders)

As with other things, information technology is too possible to implement arbitrary clip masks via a shader.

Then you would make a new GLSL ES shader, name it something like shd_clip_mask, and ready it's vertex shader code to be as following:

          attribute          vec3          in_Position;          aspect          vec4          in_Colour;          attribute          vec2          in_TextureCoord;          //                    varying          vec2          v_vTexcoord;          varying          vec4          v_vColour;          varying          vec3          v_vPosition;          //                    void          chief() {     v_vPosition = in_Position;     gl_Position =          gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]         *          vec4(in_Position.x, in_Position.y, in_Position.z,          i.0);     v_vColour = in_Colour;     v_vTexcoord = in_TextureCoord; }

The idea is the same every bit in clip rectangle shader - v_vPosition is used to determine the cartoon position inside the fragment shader, but here it is used to decide the signal in mask-texture to sample pixels from.

          varying          vec2          v_vTexcoord;          varying          vec4          v_vColour;          varying          vec3          v_vPosition;          //                    uniform          vec4          u_rect;          uniform          sampler2D          u_mask;          //                    void          main() {          gl_FragColor          = v_vColour         *          texture2D(gm_BaseTexture, v_vTexcoord)         *          texture2D(u_mask, (v_vPosition.xy          - u_rect.xy) / u_rect.zw); }        

Then y'all would set up the shader, give information technology the surface (or other texture) to sample mask pixels from, give information technology the clip region via uniforms, and any things drawn will be drawn with the said mask.

            // create the mask surface if needed:            if            (!            surface_exists(mask)) {            mask            =            surface_create(256,            256);            surface_set_target(mask);            draw_clear_alpha(c_white,            0);            draw_set_color(c_white);            draw_circle(128,            128,            70,            fake);            surface_reset_target(); }            // debug drawing:            draw_set_color(make_color_rgb(94,            101,            124));            draw_circle(mouse_x,            mouse_y,            forty,            true);            draw_circle(clipx            +            128,            clipy            +            128,            seventy,            true);            // prepare up the shader:            shader_set(shd_clip_mask);            var            u_mask            =            shader_get_sampler_index(shd_clip_mask,            "u_mask");            texture_set_stage(u_mask,            surface_get_texture(clip_mask));            var            u_rect            =            shader_get_uniform(shd_clip_mask,            "u_rect");            shader_set_uniform_f(u_rect,            clipx,            clipy,            256,            256);            // draw things:            draw_circle(mouse_x,            mouse_y,            40,            false);            // finish:            shader_reset();

In conclusion

While GameMaker does not come with built-in functions for clipping graphics (although perhaps this will change in two.10 at present that it is not tied to aboriginal DirectX9), feasible workarounds exist for basically any imaginable use case.

A sample projection containing shaders (and code for some of the demos shown here) can be downloaded from crawling.io.

Take fun!

rebellrepere.blogspot.com

Source: https://yal.cc/gamemaker-draw-clip/comment-page-1/