Implementing simple slow-mo effect in GameMaker Studio


While working on my entry for Pirate Jam 2019, I was happy with parts of the game, but felt gameplay lacked the fun factor.

At the time I had just learned particle effects in GMS and was chuffed with myself that I had managed to set up simple particle emitter and make gun muzzle effect looking pretty good for my own standards. Also, because of the nature of on locale Priate Jam, the theme "chaos" was made public on jam's Itch page quite late, it was somewhat natural conclusion that my best option to include more "chaos" in the game was through implementing power pickups to unleash more firepower and epic action-moviesque slow mo.

I put off working on it for couple of nights as I assumed it would be lot of game-breaking changes to implement feature affecting every single part of the game, however once I had few days to consider the best way to implement it, it only took me about 20 minutes.

Being truly amazed how easy it was, I decided to write this short blog post to hopefully show other GMS developers that slow-mo effect might not be as hard to implement as first seems.


Before we start

During office hours I am a professional web-app developer and would confidently say I know what I'm talking about. However this game dev gig is purely a hobby, so some parts of this post might not make much sense or be good advice at all. Noticed a mistake or have better optimised tricks up your sleeve? Let me know in comments :)

Also, I can't remember what tutorial video I picked it up from, but because GM is case sensitive and all internal variables start with lower case letter, someone suggested always naming variables starting with capital letter. It might seem a bit awkward, but it works for me and it's a bit of a habit I find hard to shift now. If you prefer different naming convention, feel free to change things around, everything should work regardless.


Why so slow?!

Other than looking just cool, why would someone implement slow-mo in their game?

For one, you could use it for fraction of a second to indicate player just successfully hit an enemy, a popular technique to amplify user feedback in fighting games. However in our example, it's to make player feel empower by allowing them to complete considerably more actions than enemies in the same time-frame.

Now, GameMaker Studio comes with built in relative speed/time variable room_speed. Because fundamentally everything in GM works based on "step" which the game engine tries to hold steady within 1 second, changing room_speed value from 60 to 30 means something that took 60 steps to complete and ran for 1 second, will now still take 60 steps to complete, but because single step is "stretched" to last longer, the action will take 2 seconds to complete on screen.

Very simple solution, however it has some quite noticeable shortcomings. Firstly the slower you go, the choppier the animations look. And more importantly, changing room speed will apply to all movement in game, while you might want to make player movements independent from relative time speed.

Better looking and more flexible method is what movie industry calls "overcranking" - the scene is shot with speed up (more frames) camera, then played back at normal speed. In GM world that means that while our app will always run at 60 steps a second, we'll extend how many frames it takes to complete an action, to play back same action in 2 seconds we'll need to "film" 120 steps.

To achieve this we have to make movements between steps smaller, because distance we want to travel remains the same, but we now have double the steps to do it, or represented in maths terms MovementPerStep = TotalDistance / TotalSteps.


Basic object movement in GameMaker

There are couple of ways to make objects move in GameMaker, you could change object position in step event:

x += 10;

You could use built in object movement variables once and let GameMaker take care of per step calculations:

direction = 90;
speed = 10;

Or you could implement your own precise easing routine where movement in each step is different (object slows down as it approaches it's destination), by setting up movement variables first:

StartX = x; // get current start position
Distance = 100; // how many px do we want to move
StepCount = 0; // how many steps into animation are we
TotalSteps = 60; // how many steps in total will animation take

Then calculating position in object step event:

StepCount += 1; // next step
t = StepCount / TotalSteps; // get current "completion" in form of float between 0 and 1
t = t * (2 - t); // apply easing
x = StartX + (Distance * t); // set object position

For first two methods, we want to directly manipulate how many pixels object moves, but easing was slightly harder problem to crack. That was until I realised the key here is not to worry about per step movement, but to treat StepCount itself as distance - instead of moving 1 whole step every frame, inslow mo we should only move 0.5 steps towards TotalSteps. It is quite strange way to look at it, but trust me, the match checks out ;)


Implementing slow mo effect

It's certainly easier to implement this earlier in project, so it's easier to not miss out odd object here or there, even better if you use objects parent-child feature and manage to work it into top level objects.

I prefer to keep all my gameplay variables in special game controller object, but you can use global variable if that's how you like to organise your project. In my gameObj create event, I define relative speed variable:

RelativeSpeed = 1.0;

From maths point of view, you could choose to go with either multiplier (10px * 0.5 = 5px) or divider (10px / 2 = 5px), however because GameMaker is using multiplier approach for couple of other built in functions, I too prefer the same approach - 0.5 = half speed, 1.0 = normal speed, 2.0 = double speed.

Adding multiplier to movement, method 1:

x += 10 * gameObj.RelativeSpeed;

Method 2:

speed = 10 * gameObj.RelativeSpeed;

Method 3:

StepCount += 1 * gameObj.RelativeSpeed;

And that's it - you've just implemented game world speed controller, set gameObj.RelativeSpeed between 0 and 1 for slower movement, above 1 to speed things up, or 1 to get back to "normal" speed.


Taking bullet time effect further

Now that we have control over relative time, it's time to add some extra polish to make it even more awesome.

For example, in movies the bullet time effect will never go from full speed to half speed, instead it gradually slows down.

Because Method 1 and 3 already deal with changes per step event, we only have to refactor Method 2 slightly.

Add new variable that holds desired "normal" speed, set once, likely in object create event:

AbsoluteSpeed = 10;

Then update object speed in relation to current game world speed in object step event:

speed = AbsoluteSpeed * gameObj.RelativeSpeed;

Now we can manipulate RelativeSpeed per step and objects will adjust the calculations accordingly. A quick and dirty trick to slowly change speed variable is to introduce "target" speed in gameObj create event:

SpeedChange = 0.05; // how fast do we want game speed to change
TargetRelativeSpeed = 1.0; // speed we'd like the game to run
RelativeSpeed = TargetRelativeSpeed; // set current relative speed as target speed

And adjust speed in gameObj step event:

if (RelativeSpeed != TargetRelativeSpeed) {
    if (RelativeSpeed < TargetRelativeSpeed) {
        RelativeSpeed = min(RelativeSpeed + SpeedChange, TargetRelativeSpeed);
    } else {
        RelativeSpeed = max(RelativeSpeed - SpeedChange, TargetRelativeSpeed);
    }
}

Now we can set target speed from anywhere in code, and it'll gradually change to desired end speed:

if (condition for slow mo) {
    gameObj.TargetRelativeSpeed = 0.5;
}

It's not perfect maths (it takes longer to double world speed than it takes to slow down), but it gave me the effect I wanted, so I was happy to move on, but feel free to find more accurate solution where speeding up and down takes same time.

Slowing down animations and sounds

At this point I was happy with the effect, but felt there are couple of more small changes I could do to really emphasise the effect.

Remember earlier in the post I mentioned GameMaker uses 0.5 / 2.0 approach in few places? Well, turns out that's GM's internal logic for animations and sounds.

Without having to adjust or recalculate our relative speed values, in object step event, set sprite animation speed:

image_speed = gameObj.RelativeSpeed;

And when playing audio, set audio pitch:

var audio = audio_play_sound(soundReference, 1, false);
audio_sound_pitch(audio, gameObj.RelativeSpeed);

As a final touch, I played "slow down" sound effect when slow-mo was triggered.

Hope this short blog post on has helped you to discover couple of easy ways to implement slow-mo effect in your game. If you manage to implement something similar, drop a link in comments below, I'd love to see these projects in action.

Comments

Log in with itch.io to leave a comment.

Great lesson! I didn’t know that GMS2 uses the multiplier approach in other places, making some of these adjustments fairly trivial. Thank you! 🙂

Completely forgot I wrote this. I use slow-mon quite heavily in my new game, should probably re-read this and see if it still holds up or if I have something to add 😅

Nice work

Cheers! Hope it was helpful 🤓