Recently I posted an article called How To Make A Platformer FEEL Good. In it, I displayed several GIFs where the grass and hanging vines would move a bit when the character interacted with them. Several people asked me if I made a ton of animations to make that happen or if I was doing some form of programming magic. It was the former. I had a bunch of four-frame animations playing. However, that is by no means necessary. In this article, I’d like to show off one way to achieve a particular procedural animation effect without the need for any sprite animations.



Sprite Skewing

Skewing is basically just the process of moving the vertices of a sprite so that the sprite is no longer viewed as a perfect box. In Game Maker Studio we have variables that allow us to change the proportions of the box (image_xscale and image_yscale). We also have variables that allow us to change the rotation and opacity of the box (image_angle and image_alpha). However, we have no way of warping the box into some other shape. As an example of when we may need this, look back at my rope + water physics platformer engine. The surface of the water is done by moving the vertices that lie on the surface of the water.

Triangle Strip

A triangle strip is a combination of one or more triangles that is used as a surface to display textures. When you draw a sprite, you are creating two triangles to make a box. Then you draw the texture on top of it. The triangles are then stretched and rotated to give you your scale and rotation. The following script can be used to extend Game Maker’s draw_sprite_ext() function.

/// draw_sprite_skew_ext(sprite_index, image_index, x, y, image_xscale, image_yscale, image_angle, image_blend, image_alpha, image_xskew, image_yskew); var sprite = argument0; var index = argument1; var xx = argument2; var yy = argument3; var xscale = argument4; var yscale = argument5; var cosAngle = cos(degtorad(argument6)); var sinAngle = sin(degtorad(argument6)); var tint = argument7; var alpha = argument8; var hskew = argument9; var vskew = argument10; // Calculate common operations var sprTex = sprite_get_texture(sprite, index); var sprWidth = sprite_get_width(sprite); var sprHeight = sprite_get_height(sprite); var sprXOrig = sprite_get_xoffset(sprite); var sprYOrig = sprite_get_yoffset(sprite); var tempX, tempY, tempDir, tempDist; // Begin drawing primitive draw_primitive_begin_texture(pr_trianglestrip, sprTex); // Top-left corner tempX = (-sprXOrig + (sprYOrig / sprHeight) * hskew) * xscale; tempY = (-sprYOrig + (sprXOrig / sprWidth) * -vskew) * yscale; draw_vertex_texture_color(xx + tempX * cosAngle - tempY * sinAngle, yy + tempX * sinAngle + tempY * cosAngle, 0, 0, tint, alpha); // Top-right corner tempX = (sprWidth + (sprYOrig / sprHeight) * hskew - sprXOrig) * xscale; tempY = (-sprYOrig + (1 - sprXOrig / sprWidth) * vskew) * yscale; draw_vertex_texture_color(xx + tempX * cosAngle - tempY * sinAngle, yy + tempX * sinAngle + tempY * cosAngle, 1, 0, tint, alpha); // Bottom-left corner tempX = (-sprXOrig + (1 - sprYOrig / sprHeight) * -hskew) * xscale; tempY = (sprHeight - sprYOrig + (sprXOrig / sprWidth) * -vskew) * yscale; draw_vertex_texture_color(xx + tempX * cosAngle - tempY * sinAngle, yy + tempX * sinAngle + tempY * cosAngle, 0, 1, tint, alpha); // Bottom-right corner tempX = (sprWidth - sprXOrig + (1 - sprYOrig / sprHeight) * -hskew) * xscale; tempY = (sprHeight - sprYOrig + (1 - sprXOrig / sprWidth) * vskew) * yscale; draw_vertex_texture_color(xx + tempX * cosAngle - tempY * sinAngle, yy + tempX * sinAngle + tempY * cosAngle, 1, 1, tint, alpha); // Finish drawing primitive draw_primitive_end();

You can use that script to skew sprites in relation to its origin. An image_xskew/yskew or zero will display the sprite normally.

Note: Make sure to crop your images. This skews your sprite in relation to the entire texture and will warp the image if you have unnecessary blank space.

Application

For those of you who have followed my blog, you may have seen my new pet project, #HackyZack. During the prototyping stages I always like to have the game in a visually-pleasing state. Even prior to adding professionally quality pixel art. In HackyZack, I used some tall grass that reacts to both the player and the hackysack. I felt like this would add a good amount of polish and depth (background and foreground objects).

I used the skewing script above to accomplish this.

// oFgGrass1 'create event' xskew = 0; xset = 0;

// oFgGrass1 'end step event' xskew = Approach(xskew, xset, 0.5); if (xskew == xset) xset *= -0.5;

Feel free to use any lerp, tween, or ease function here. I basically wanted the grass to wiggle back and forth while being less and less aggressive with each pass. This is a somewhat odd way of doing that, but it worked with the scripts that I was already using.

// oFgGrass1 'collision (w/ oEntity) event' // Move if (abs(other.vx) <= 1 && abs(other.vy) <= 1) { } else if (abs(other.vx) 1 || abs(other.vy) > 1) xset = xskew;

Result

-Z