GameMaker: draw_sprite_ext_skew

(click to interact) (mouseover/click to play GIF) Drag sliders and click things (if JS is enabled)

This post is about a slightly fancier version of draw_sprite_ext - maybe you want to apply skewing/shearing to the sprite, or scale it after rotating it, or use a 2d matrix transformation.

The built-in function isn't made for that, but, with a little bit of math and draw_sprite_pos, you can have a script for this.

The idea

First, meet draw_sprite_pos.

draw_sprite_pos allows you to draw sprites with arbitrary affine transformations by providing coordinates of each corner of the sprite. That's exactly what the situation demands.

From hereon, the trick is to calculate coordinates of each point correctly.

Fortunately, this is no complicated matter, and I already wrote a tutorial about this before.

The parameters

The first bunch of these are the same as those of regular draw_sprite_ext - you have sprite index, image index, position, scale, rotation, and opacity (no color blending because draw_sprite_pos doesn't have that). Then come the 4 additional parameters,

Skew/shear X

Essentially, this determines how much every pixel of sprite is offset on X axis for each subsequent pixel of Y axis.

So, a value of 1 would cause a normally rectangular sprite to be drawn as a rhombus, left and right edges aligned to point between -X-Y and +X+Y.

Skew/shear Y

Same as above, but for affecting Y axis. Can also be utilized for isometric projection.

Mult X/Y

This is much like regular scale but it is applied after rotating and skewing the image.

So, for example, if you set Mult Y to 0.5, the image will always be vertically squished, regardless of rotation angle.

The code

With aforementioned things in mind, everything is pretty straightforward here.

A pair of global arrays is created and reused for corner coordinates as it doesn't make much of a difference in this case, but you probably don't want to create-destroy a new array every time you draw a sprite.

/// draw_sprite_ext_skew(sprite,subimg, x,y, xscale,yscale, rot,alpha, kx,ky, xmult,ymult)
/// https://yal.cc/draw_sprite_ext_skew
/// @arg sprite
/// @arg subimg
/// @arg x
/// @arg y
/// @arg xscale
/// @arg yscale
/// @arg rot
/// @arg alpha
/// @arg kx How much X skews per each pixel of Y
/// @arg ky How much Y skews per each pixel of X
/// @arg xmult Post-skew, post-rotate scale X
/// @arg ymult Post-skew, post-rotate scale Y

// get the arguments:
var sprite = argument0, subimg = argument1, _x = argument2, _y = argument3,
    scalex = argument4, scaley = argument5, rot = argument6, alpha = argument7,
    skew_kx = argument8, skew_ky = argument9, skew_sx = argument10, skew_sy = argument11;

// compute values that will be reused:
var rcos = dcos(rot);
var rsin = -dsin(rot);
var x1 = -sprite_get_xoffset(sprite) * scalex;
var x2 = x1 + sprite_get_width(sprite) * scalex;
var y1 = -sprite_get_yoffset(sprite) * scaley;
var y2 = y1 + sprite_get_height(sprite) * scaley;

// compute corner coordinates:
for (var c = 0; c < 4; c++) {
    // pick local corner
    var lx; if (c & 1) lx = x2; else lx = x1;
    var ly; if (c & 2) ly = y2; else ly = y1;
    // see https://yal.cc/2d-pivot-points/:
    var rx = lx * rcos - ly * rsin;
    var ry = lx * rsin + ly * rcos;
    // transform and store corner coordinates:
    global._draw_sprite_ext_skew_x[c] = _x + (rx + ry * skew_kx) * skew_sx;
    global._draw_sprite_ext_skew_y[c] = _y + (ry + rx * skew_ky) * skew_sy;
}

// draw the sprite quad:
draw_sprite_pos(sprite, subimg,
    global._draw_sprite_ext_skew_x[0],
    global._draw_sprite_ext_skew_y[0],
    global._draw_sprite_ext_skew_x[1],
    global._draw_sprite_ext_skew_y[1],
    global._draw_sprite_ext_skew_x[3],
    global._draw_sprite_ext_skew_y[3],
    global._draw_sprite_ext_skew_x[2],
    global._draw_sprite_ext_skew_y[2],
    alpha
);

Related posts:

2 thoughts on “GameMaker: draw_sprite_ext_skew

  1. did they fix the distortion in draw_sprite_pos at the center diagonal line between the 2 facets , because the thing used 2 facets instead 4 with a center point…

    • That’s non-affine transformation (see link), and adding two more triangles won’t really fix that – for correct perspective you need either a shader (someone wrote one) or a 3d matrix, both of which are much more computation-intensive to switch to/from.

Leave a Reply

Your email address will not be published. Required fields are marked *