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,
global._draw_sprite_ext_skew_y,
global._draw_sprite_ext_skew_x,
global._draw_sprite_ext_skew_y,
global._draw_sprite_ext_skew_x,
global._draw_sprite_ext_skew_y,
global._draw_sprite_ext_skew_x,
global._draw_sprite_ext_skew_y,
alpha
);

Related posts:

| | 4 Responses You can also find me on Twitter or Tumblr if you'd like.

4 thoughts on “GameMaker: draw_sprite_ext_skew”

1. Zoyous on said:

I think this technique would work well for a type of “dynamic shadow” that my character casts. As the sun moves across the sky, the shadow (a separate object and sprite) could skew appropriately. The only thing is, I think I would also need to dynamically move the origin of the shadow sprite at the same time as it skews, so that it will stay attached to the character’s feet. I’ll play around with it and see if I can figure it out. Thank you for this post!

• Vadim on said:

The origin is the part of the calculation (see “y1”), so you can make a version of the script that accepts custom origin arguments fairly easily.

2. icuurd12b42 on said:

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…

• Vadim on said:

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.

This site uses Akismet to reduce spam. Learn how your comment data is processed.