Advanced Guide to Sprite Stacking using GameMaker Studio 2

dev_dwarf
7 min readMay 4, 2020

Hey again! My name is dev_dwarf, and in this text-based tutorial I’ll be showing you how to add a 3rd axis of rotation to your Sprite Stacking projects!

If you haven’t done Sprite Stacking before, or don’t know what it is, check out the previous 2 tutorials:

This tutorial will assume that you’ve already done the previous ones, and you’ll want to be working off of the same project, because we are mostly going to be making modifications and fixes to what we already have! This effect works by stretching the sprites, and then squashing the viewport by the exact same amount. This keeps the sprites about the same size, but changes the distance between the layers of the stack, making it look like the camera is rotating around the z-axis. That might not make much sense yet, but it’s much easier to see this effect than to explain it, so continue on!

The steps we’re going to take are:

  • Stretching the sprites
  • Squashing the viewport
  • Accounting for the squashed viewport

By the end, you’ll be able to make something like this:

astoundingly 3D

Stretching the Sprites and Stacks

The first thing we’re going to need to achieve our z-axis scaling are some variables in the camera object to store the amount of scaling, and a lowest and highest value. Add some variables like these to your camera object’s create event:

image_zscale = 1.25;
 zscale_min = 1.25;
 zscale_max = 2.25;
oCamera create event

You can set whatever values you want for the maximum. In fact, I encourage you to try pretty high values, like 6, just to see how it looks (Although I would not recommend this in game, because it will have bad performance)

We’re also going to need to add some code in the camera’s step event, to make sure our image_zscale stays between the min and max values, and to test moving it around:

oCamera step event

This code gets the difference between the mouse and the center of the window, and clamps the mouse to the middle of the window. This is the same technique that FPS games use to move around the camera. Then the amount of horizontal movement is added to the angle, and the vertical movement is added to the zscale. The final line makes sure that the zscale stays between the proper values.

You should also remove the code from the previous tutorial that constantly rotated the camera.

Now that we have our image_zscale variable set up, we need to do something with it! Open up your draw_stacked_sprite script, and we’re going to make some changes to the distance between the slices. As you might’ve already guessed, we’re going to multiply our _x_step and _y_step variables by the camera object’s zscale.

_x_step *= oCamera.image_zscale;
 _y_step *= oCamera.image_zscale;
Inside the draw_stacked_sprite script

This will handle increasing the distance between the slices, but that will create a lot of gaps. To fix this, we’re going to make another small change to draw each slice multiple times. Change the for loop of the script to this:

Inside the draw_stacked_sprite script

As we increase the zscale, the layers will be drawn more times. Great! But we also need to stretch our billboarded sprites based off the zscale. Open up the draw_billboarded_sprite script, so we can make a small change to that one as well. Right before we draw the billboarded sprite, add a line that stretches it out by a factor similar to the camera’s zscale.

Inside the draw_billboarded_sprite script

I found that multiplying by just image_zscale didn’t give me a great result, so I came up with the above formula. This might not give you a result you’re happy with, so feel free to play around with it however you want.

Now if you run your game and try moving the mouse around, you should have a result like this:

So that accomplishes our first step of stretching out the drawing of the sprites. But this looks nothing like the 3D effect shown above! Let’s fix that.

Squashing the Viewport

The next thing we need to do is squish the viewport down by the zscale. To do this, we increase the amount of the screen the camera is viewing by the zscale, but keep the window it exports it to at the same size, making it fit a larger amount of space into the same size, and counteracting the stretching shown above.

That sounds complicated, but it’s not going to take too much code. We just need a few lines in the camera’s step event.

I lied, its actually a decent chunk of code

The first couple lines there calculate the new width and height that the camera will use (DEFAULT_WIDTH and DEFAULT_HEIGHT are just the “base” width and height I was using for my game, you can put it whatever your normal width and height are). Lines 34–38 are used to calculate the point that the camera is centered at. Because we’re changing the height of the view, we have to recalculate the center so that the camera isn’t moving around constantly. We do this by taking the current center point (target_x and target_y ) and subtracting from it half of the width and height (because Gamemaker’s cameras are positioned based off the top left coordinate).

Finally, the last few lines use Gamemaker’s camera functions to set the new position, size and angle of the camera. Load it up, and you should have something like this:

oooooh

That’s actually it for this effect! The rest of this tutorial will cover some optimizations we can do, and some other tips and tricks for when you’re working with sprite stacking.

Other Tips/Optimizations

So first I want to start with some optimizations that we can do with this technique. To start off with, in our draw_stacked_sprite scripts, you’ve noticed that we calculate the _x_step and _y_step variables every single time, but they’ll be the same every time the script is used each step. So instead we can just calculate these values once in the oCamera step event, and use them each time in the scripts. (Make sure to initialize the variables inside the create event)

Inside the oCamera step event

The z_step here is the amount that is added to i in the for loop each time and the stacking_fidelity is a constant I set. You can see the changes I made to the stacked_sprite script here:

The final thing I want to mention is adjusting your movement code to offset the scale we’ve made to the vertical axis. I normally move my objects using the lengthdir_x (cos), and lengthdir_y (sin) functions. But what you’ll notice with this technique is that when moving up relative to the camera, it’ll look like the movement is extremely slow, since we’re squashing the vertical axis down before displaying it.

To fix this, in my move script I calculated an amount I could increase the movement by, based off which direction I was moving in:

my movement script (collision code below)

First I calculate the adjusted angle of the movement, which is basically what the direction is relative to the camera. Next, a “stretch” value is calculated for the movement. Basically, if the movement is up or down relative to the camera, then this value will be oCamera.image_zscale/oCamera.zscale_min , if it’s to the right or left it will be 1, and if it’s in-between it will be some combination of the two. Then, in lines 15–16, we multiply the speed we’re moving at by the stretch value.

That’s it for this one! Let me know if there are any other Sprite Stacking techniques you want to learn about, and if this helped you out, please help me out by following me on itch.io or twitter. If you have any problems, reach out to me for help on discord (dev_dwarf#3925) or twitter, or download the project file!

I’m gonna use this technique to make a little game about a skeleton in the forest

Update: That game about a skeleton in the forest is done, download it here: https://dev-dwarf.itch.io/lianthus

--

--

dev_dwarf

making games with noisybug. @dev_dwarf on twitter.