[color:#0088aa]Matrix Bullet-time effect[/color]
Ok children!! Today we learn how to pull off an extremely convincing Bullet-Time effect, as seen in films like The Matrix (and Kung Pow! ). Say "YAY", everyone!!
Some little points before we start!
Animations are best with many frames. This allows more detail when slowing everything down, otherwise those anims will jitter.
Sounds must all be of the same frequency (e.g. 11025hz), or you'll make a lot more work for yourself!
With those little details out of the way, let the learning commence!! :sleep:
You need to have clear in mind what kind of features your slowdown effect will have. What is its purpose?
Some questions to get you started:
:confused: Will some elements run at the same speed as before? E.g. player runs at normal speed, and everything else is slowed down.
:confused: Are you going to slow down sounds as well? What sounds if any will not be slowed down? (E.g. a 'woosh' sound as you enter bullet time mode. This could sound awful if it slowed down along with the other sounds)
There is a simple example in the Example File, attached to this article. It's extensively commented, but should be understood in conjunction with this article.
The example can cover the exact events, so this article will cover only the founding principles. For this reason, it should be applicable to everyone (TGF, MMF, and Chwauxhagah... the game creator that Martians use because the Mars-Earth exchange rate puts MMF off limits).
[size:14pt][color:#0088aa]The Variable Object[/color][/size]
Here's a top tip for any game, not just this one!
Create a new Active Object, rename it to something like "VARS" (for 'Variables'), and then set about adding and renaming its alterable values in the properties panel!
These can then be used as variables for the frame, so all objects can reference these vars, without taking up precious Global Vars.
In my VARS Object, I'm adding a few variables which will make my effect easier to tweak:
Time Ratio = [/color]The Adjusting Ratio used to determine how fast or slow everything must go. We'll discuss this later.[color:#0088aa]
Base Rate = [/color]You may ignore this, but I find it useful. As the properties dialog will not accept Floating (decimal-point) values as input (as at build 247), I usually add this value to show MMF how much to divide my variables by. It allows me to change the variables to be more or less precise. A value of 100 will turn '12' into '0.12' for instance. If I then decide it needs to become '0.125', I can change '12' to '125', and change the base rate to '1000'.[color:#0088aa]
Target Time Ratio = [/color]This too is optional. I sometimes use this to create a smooth transition between speeds. You set the Target Time Ratio, and tell MMF to constantly slow down or speed up to that figure. It's more Matrixy that way. So the Time Ratio will always be trying to curve itself towards the Target Time Ratio[color:#0088aa]
Sound Base Frequency = [/color]If you're gonna slow sounds down, use this so that your sounds know what frequency to base themselves on. For this reason, the sounds must all be of the same frequency.[color:#0088aa]
...Others! = [/color]But you'll need to look in the MFA example for specifics. Rock on.[color:#0088aa]
[size:14pt][color:#0088aa][color:#FF9900]Maths Spotlight[/color]: Ratios[/color][/size]
Don't hate me! This is really easy, and very well known, so don't worry about it too much. It's just to cover you in case some may not have picked this up in basic maths!
When I talk about Ratios in this article, I mean an Adjusting Fraction.
Sounds complicated? It's not, it's just based on the principle that:
a * 1 = a
If you remember your tables, anything multiplied by 1 will not change. (once one is one, once two is two, etc).
Obviously multiplying by 2 makes it TWICE that amount.
However, multiplying by 0.5 (half of 1) has the effect of reducing or dampening the value by half.
This principle, while really really basic, is used extensively in this effect. And when I say 'extensively', I mean 'nigh unto excessively'. And when I say 'nigh unto', I mean 'very nearly'...
We use this basic mathematical effect as an adjusting ratio, in that it increases or decreases the values that we're handling in our game.
So at 100% (1.0), a bullet may travel at:
150px per second * 1.0 = 150px per second
However, at only 10% (0.1), the bullet will appear to travel at:
150px per second * 0.1 = 15px per second
So if you reduce the ratio, you reduce the screen-speed of the bullet. Increase the ratio, increase that speed. If we use the same ratio on all variables, then the whole world will appear to slow down or speed up as we tweak the timing ratio.
[size:14pt][color:#0088aa]Applied to Objects[/color][/size]
In many custom platform movements, you usually maintain four specific variables inside each object:
:confused: [color:#0088aa]XPOS[/color] - A decimal-precise version of the object's location. This allows it to sit halfway along a pixel. It doesn't affect the display at all, it's a mathematical thing. It lets you give objects a speed like 1.5px/s or 0.25px/s and watch them move UBER slow.
:confused: [color:#0088aa]YPOS[/color] - See above, this is just a Y-axis version of the same thing.
:confused: [color:#0088aa]X INERTIA[/color] - How much inertia on the X axis does the object have. This will be added to the position.
:confused: [color:#0088aa]Y INERTIA[/color] - See above.
Now, usually there will be lots of features which adjust the X and Y inertia as we calculate this frame. You jump, you have a button pressed, you hit a wall, a badguy, a spring, you stand on a slippery surface, whatever.
Only after we've calculated what's gone on in that frame, and decided your total movement for this frame, can we move the object. So we add the inertia to the current XPOS and YPOS, move the sprite to that position, maybe check for obstacles, and then let the screen render.
It is only at THIS FINAL STAGE that we apply the Time Ratio.
PLEASE keep in mind that we're NOT slowing objects down. That ain't what we're doing here, people! If an object's alterable values say he's moving at 10px/s, then that's how fast he is going. Only game objects, like walls, slippery floors, impacts with moving objects, or the player controls can alter that truth.
The ONLY thing that our bullet-time effect has the authority to change is the Display Length of a Second. In other words, how long it takes to display one second's worth of game time.
Don't go changing the speed/inertia as recorded in an object's alterable values or you will [size:11pt]MESS IT UP[/size].
So if after everything we decide that the player must move 3px this frame, we simply set his position, not to:
Position + Inertia
Position + Inertia * Time Ratio
Better! This will slow the passage of time onscreen without actually messing with the object variables. As a result, subsequently setting the time ratio back to 1 (100%) will flawlessly continue to display objects at their original, proper speed.
Here's a problem for you:
At a slower rate, more frames will pass before the object reaches wherever it's moving to, right? Well, in my example, we have a little tortoise running through the rain.
Problem is, the rain is generated every 2/100ths of a second, at gametime. Now, no matter how fast or slow my on-screen objects are moving, they don't alter the length of an actual second. Even though my character and raindrops have slowed down, there will still be 50 raindrops created per second. This means we will be creating too many raindrops per second.
To adjust this, use the Time X object or equivalent, rather than MMF's in-built 'Every' condition, and do this (remembering that 1000ms in Time X = 1 second):
Every 20ms / t
--- Create Raindrop
Note that this time, we DIVIDE. We DIVIDE. We DIVIDE. That means using DIVISION. If you multiply it by accident, you'll get a LOT of rain.
This is because:
Object speeds need to be reduced in order to slow an object down. This is the nature of speed. Multiplying by a fraction (e.g. 0.2) has this effect.
However, the 'every' event is referring to a delay. While shorter distances travelled means longer time taken to reach destination (therefore slower movement), a delay must work the opposite way.
You want MMF to wait longer before making a new raindrop. So while multiply-by-fraction cuts a speed down, so that the object moves slower, divide-by-fraction increases the delay between raindrops, neatly matching the two effects.
This is the cool part!! It wouldn't be The Matrix without the slowdown sound effects!
A sound effect can be slowed down using this equation:
Sound Frequency * t = Adjusted Frequency
So if a sample has a frequency of 11025, do this:
--- Set Frequency to 11025 * t("VARS")
A NOTE ON QUALITY: Be advised; higher frequency samples (i.e. 22khz and above) are preferable. I know a lot of samples come in 11khz, but they sound PANTS when slowed down.
This is one problem with bullet time. You need to imagine how EVERYTHING will be rendered, not just at 1:1 time, but at intense slow motion too.
[size:14pt][color:#0088aa][color:#FF9900]Maths Spotlight[/color]: Gravity Problems[/color][/size]
Heh. You know how the ratios bit was easy? ... :smirk: :blush:
...Well this'll make up for that :laugh:
This has all worked perfectly so far, or at least it should. But we haven't touched Gravity or Jumps yet, and with good reason.
I bet you were sitting there expecting us to just multiply it by the time ratio, and maybe do that to Jump Strength too while we're at it.
Well you've gone [size:11pt]MuLtIpLY mAD!![/size]
The problem lies in the fact that Gravity is applied on every frame.
A jump (the whole go up, slow, stop, fall back down process) may be all over in just 75 frames (1.5 seconds). However, if we slow the game down to 50% (time ratio = 0.5), everything on screen will take longer to move around.
As a result, our jump will now see twice as many frames before it's completed.
If we apply gravity every frame, then we'll be getting 150 lots of gravity rather than just 75. So our player will fall to the ground much sooner than he should.
This does not form part of our Bullet Time effect, it's purely an unwanted side effect that comes as a result of spreading the motion over more frames (which is in essence how this whole effect works).
To fix it, we must apparently break that REALLY IMPORTANT RULE I gave you earlier; about not applying the time ratio on an object's internal variables.
In this case, we have to. This is because this is just a bug fix.
We need to shrink the force of gravity so that it still comes to the same amount. Therefore, for GRAVITY (and gravity ONLY), we have to treat it like this:
Set Y Inertia to: Y Inertia + Gravity Strength * Time Ratio
This is JUST a bugfix, to ensure that the same amount of gravity is applied per game-second as it would have been in realtime.
Now The Jump
Now we jump. This is a one-off action, so doesn't require the bug fix above. As a result, we apply the jump in the same way we apply any other movement variable:
Set Y Inertia to: Y Inertia + Jump Strength
No mention of the time ratio.
Apply the Bullet Time Effect
Currently, we have fixed an inherent bug in gravity, so our gravity per game second is now what it should be.
So now, finally, we revert to our old principle and apply the Time Ratio to everything:
Position = Position + Inertia * Time Ratio
This seems odd in the event editor because Time Ratio is applied to the gravity element twice. But remember, this is only because the first time was fixing a side effect in the framerate. Now, we're just slowing down its display onscreen.
I hope that made sense, but I said it was a brain-painer didn't I? Unfortunately, I doubt that looking at the example will make this any clearer.
By now, I bet you just wanna see it in action! Unless you scrolled down and skipped all of the above, in which case heaven help you when you get to the Gravity element ... (see above) :P
The Example is attached, and any comments are appreciated. As always, the Geronimo sprite is MINE, used for demo purposes only, so if you steal him and use him in your game, I will personally smite you.