Javascript required
Skip to content Skip to sidebar Skip to footer

Gms2 Draw Sprite in Circle


Mouseover to activate, click to reposition the clipping star.

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

That is, having fatigued graphics only display inside a certain ("prune") surface area, exist that a rectangle (UI regions, minimaps, etc.), circle, or a completely arbitrary shape (pictured to a higher place).

Also I'k trying new things so this post 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 i is the most common case 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 prune area'due south elevation-left corner=' coordinates.
  3. Draw the prune area surface at it'south top-left corner' coordinates.

The code is straightforward too,

            // create a surface if it doesn't exist:            if            (!            surface_exists(clip_surface)) {            clip_surface            =            surface_create(clip_width,            clip_height); }            // clear and start cartoon 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);            // stop 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 (can be set to -1 in Create), and clip_x\y\width\height ascertain the clip' region.

Same arroyo is used in "scrollable content" case that I published a few years agone.

Rectangular clip (via shaders)

If you would prefer to use a shader over surface, you lot tin.

For this you would add a shader with the following vertex code:

          aspect          vec3          in_Position;          attribute          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.ten, 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 one addition - information technology stores v_vPosition so that the fragment shader knows the coordinates of things being drawn. Fragment code, on other hand, would be as following:

          varying          vec2          v_vTexcoord;          varying          vec4          v_vColour;          varying          vec3          v_vPosition;          //                    uniform          vec4          u_bounds;          //                    void          primary() {          vec4          col = v_vColour *          texture2D(gm_BaseTexture, v_vTexcoord);     col.a          *=          float(v_vPosition.x          >= u_bounds[0] && v_vPosition.y          >= u_bounds[1]         && v_vPosition.x          < u_bounds[ii] && v_vPosition.y          < u_bounds[3]);          gl_FragColor          = col; }

This pulls RGBA from the texture like a default pass-through shader would, but sets the alpha channel to 0 if the point is outside the rectangle (u_bounds).

Then, to utilize this, you would set the shader and pass in the rectangle' data:

            // debug:            draw_circle(mouse_x,            mouse_y,            40,            true);            draw_rectangle(clip_x1,            clip_y1,            clip_x2,            clip_y2,            true);            // set 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,            40,            false);            // finish:            shader_reset();

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

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

Rectangular prune for sprites

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

Sometimes, you might want to draw a clipped graphic (e.g. a cursor or an off-screen indicator) without creating a surface or adding a shader simply for one thing.

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

While rotated graphics would require a scrap of fancy math to clip the fatigued polygon, for a standard instance, things are simple enough - calculate the resulting bounds of a graphic, check if it falls inside the prune area at all, draw a piece of information technology defined by intersecting rectangle if and so:

            /// draw_sprite_clip(sprite, subimg, x, y, clipx, clipy, clipw, cliph)            var            s            =            argument0;            var            sw            =            sprite_get_width(southward);            var            sh            =            sprite_get_height(s);            var            sx            =            sprite_get_xoffset(southward);            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(southward);            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            one:            draw_sprite(s,            si,            _x,            _y);            return            true;            case            2:            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);            render            true; }            return            simulated;

Or, if you lot desire 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            southward            =            argument0;            var            sw            =            sprite_get_width(south);            var            sh            =            sprite_get_height(s);            var            sx            =            sprite_get_xoffset(southward);            var            sy            =            sprite_get_yoffset(due south);            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(southward)            *            mx;            var            by1            =            _y            -            sprite_get_yoffset(s)            *            my;            var            bx2            =            bx1            +            sprite_get_width(south)            *            mx;            var            by2            =            by1            +            sprite_get_height(s)            *            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            true;            instance            two:            if            (mx            ==            0            ||            my            ==            0)            return            truthful;            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(due south,            si,            lx1,            ly1,            lx2            -            lx1,            ly2            -            ly1,            _x            +            (lx1            -            sx)            *            mx,            _y            +            (ly1            -            sy)            *            my,            mx,            my,            sc,            sa);            return            true; }            return            simulated;          

Employ is unproblematic enough:

            draw_sprite_ext(q,            0,            mx,            my,            1,            one,            0,            -            i,            0.ane);            // 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);            // clip border (debug)          

Arbitrary prune surface area (via surfaces)


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

This is where things get interesting:

  1. Create a surface for the prune-mask.
  2. Make full the surface with an opaque black color.
  3. Cut out (via draw_set_blend_mode(bm_subtract)) pigsty(s) for seeing things through.
  4. Create some other surface for prune area (same size).
  5. Draw the graphics into (and relative to) clip area surface.
  6. Cut out the mask surface out of clip expanse surface (once again, via bm_subtract).
  7. Describe the clip 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,            imitation);            //            draw_set_blend_mode(bm_normal);            surface_reset_target(); }            if            (!            surface_exists(clip_surface)) {            // create the clip-surface, if needed            clip_surface            =            surface_create(256,            256); }            // starting time drawing:            surface_set_target(clip_surface);            draw_clear_alpha(c_black,            0);            // depict things relative to clip-surface:            draw_circle(mouse_x            -            clipx,            mouse_y            -            clipy,            40,            imitation);            // cut out the mask-surface from information technology:            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' top-left corner, and clip_surface\mask_surface are the surfaces for prune area and mask accordingly (to exist set 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 kickoff of the post, this approach is used with a constantly updating mask surface to fill up the overlapping area betwixt two rotating shapes.

Arbitrary clip surface area (via shaders)

Every bit with other things, information technology is also possible to implement arbitrary clip masks via a shader.

So y'all would brand a new GLSL ES shader, proper name it something like shd_clip_mask, and gear up it'south 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          main() {     v_vPosition = in_Position;     gl_Position =          gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]         *          vec4(in_Position.10, in_Position.y, in_Position.z,          1.0);     v_vColour = in_Colour;     v_vTexcoord = in_TextureCoord; }

The idea is the same equally in clip rectangle shader - v_vPosition is used to determine the drawing position inside the fragment shader, but hither it is used to determine the point in mask-texture to sample pixels from.

          varying          vec2          v_vTexcoord;          varying          vec4          v_vColour;          varying          vec3          v_vPosition;          //                    compatible          vec4          u_rect;          compatible          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); }        

So you would ready the shader, give it the surface (or other texture) to sample mask pixels from, give it 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,            false);            surface_reset_target(); }            // debug cartoon:            draw_set_color(make_color_rgb(94,            101,            124));            draw_circle(mouse_x,            mouse_y,            forty,            true);            draw_circle(clipx            +            128,            clipy            +            128,            70,            true);            // set 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,            fake);            // stop:            shader_reset();

In decision

While GameMaker does not come up with congenital-in functions for clipping graphics (although possibly this will alter in 2.x at present that information technology is not tied to ancient DirectX9), viable workarounds exist for basically whatever imaginable use instance.

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

Accept fun!

johnsonwhailee77.blogspot.com

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