Phaser Platformer Series: 15 Invincibility
posted on 30 June 2021
Invincibility is a power-up that makes you, well, invincible. It stops you from dying, obstacles that previously hurt you do no damage, and enemies die just by touching you. It generally makes the hero go all flashy and super fast, but only for a limited amount of time.
Here’s what we will be making:
See the Pen
Phaser Platformer Series: 15 Invincibility by Digitherium (@digitherium)
on CodePen.
If you are viewing on a mobile, you can open a version of it here without all the codepen chrome taking up screen space so you play it in landscape mode.
As usual, we need some more global vars to store our game object:
invicibility,
emitter,
superPlayerSpeed = 400,
That’s one for the powerup itself and an emitter that we’re going to use for visual effect when the hero is in invincible mode. We also add supperPlayerSpeed as a new maximum speed for when they are in invincible mode. An emitter, emits particles so is great for things like smoke and fire effects. We need a sprite to use for the particles. We’re loading a new one called ‘dust’, along with the powerup sprite.
this.load.image('powerup', 'powerup.jpg');
this.load.image('dust', 'dust.jpg');
We add the invincibility powerup sprite to the game as usual in the create function:
//add invincibility powerup and set it to rotate around its center
invicibility = this.physics.add.sprite(220, 360, 'powerup');
invicibility.setOrigin(.5, .5);
And then setup our emitter:
var particles = this.add.particles('dust');
emitter = particles.createEmitter();
emitter.setPosition(player.x, player.y);
emitter.setSpeed(150);
emitter.setBlendMode(Phaser.BlendModes.ADD);
emitter.pause();
The emitter will use our dust sprite and is positioned where the player is. We’ll add some code in update so the emitter always follows the player. setSpeed sets the radial speed of the particles and also sets it to be a radial emitter – this means particles will appear in a circle going outwards from the center of it. We set a blend mode for another visual effect, and also set the emitter to pause which stops any particles from appearing. The idea is that the emitter is always there following the player, but we’ll only turn it on, and therefore you’ll only see it, when the player is invinvible.
We then add an invincible flag to our player, just like we did for invulnerable when they are hit:
//and set an invincible flag for the invincibility powerup
player.invincible = false;
Like our coins and other objects, we want our invincibility powerups to sit nicely on the ground so we add a collider with our platforms in the create function:
this.physics.add.collider(invicibility, platforms);
And then we need an overlap for when our hero walks over and collects a powerup:
this.physics.add.overlap(player, invicibility, goInvincibile, null, this);
This will call the function goInvincible that looks like this:
function goInvincibile(player, invicibility) {
//stop invicibility for being collected twice, as it will stick around on the screen as it animnates
invicibility.disableBody(false, false);
//change players max velocity so they can go super fast
player.body.maxVelocity.x = superPlayerSpeed;
//start our emitter
emitter.resume();
...
Upon collection of the powerup, we disable its physics so it can only be collected once, and we set the player to go faster using superPlayerSpeed. emitter.resume(); starts our emitter spitting out particles so we get a nice effect. We then want our hero to flash. We can use player.setTint() to tint our sprite a color, but it turns our that tweens between two colours is actually quite tricky.
Unfortunately we can’t just tell Phaser to do it for us, we have to do some of the workings ourselves. Instead of a tween we use a counter and update our sprite in the onUpdate function of our counter. It looks like this:
//flash animations using a tint
this.tweens.addCounter({
from: 0,
to: 100,
duration: 500,
yoyo: true,
repeat: 5,
onUpdate: function(tween) {
var value = Math.floor(tween.getValue()),
newColorObject = Phaser.Display.Color.Interpolate.ColorWithColor(heroColor, invincibleColor, 100, value),
color = Phaser.Display.Color.GetColor(newColorObject.r, newColorObject.g, newColorObject.b);
player.setTint(color);
},
onComplete: function() {
resetInvincibility();
}
});
The idea is at each step, we are working out what the blend of these two colours is, and applying it to our hero with setTint(). It’s almost like writing a tween ourselves, and working out the new value at each step and tinting our hero with it. We need to add some more global variables to store these colours, and colours in Phaser are in a RGB format (red,green,blue). We create heroColour which is white (255,255,255) and a new color invincibleColor that is a light blue. Our hero sprite was just white, so it wasn’t even tinted, but for the maths to work out we need a white color to work with, and then we’ll be tinting our white sprite white, which is obviously a little bit redundant, but hey, it works.
The counter will literally count from the from var to the to var, so in this case from 0 to 100. It will take 0.5 (500 milliseconds) to do that as we specified that as the duration. It will yoyo (go back and forth) and repeat 5 times. Once it’s finished resetInvincibility is called to turn off our heroes invincibility. The heavy lifting goes on in the onUpdate function of our counter.
Here we get the current value of our counter using tween.getValue(). That will give us the number from 0 to 100 depending on how far along it is. We floor (round it down to the nearest whole number) that value for good measure amd then create a new colour using Phasers built in Color Interpolater. This takes in two colours, what the total should be (100), and then the step (the value we just got which will be between 0 – 100). I’ve chosen 100 because it keeps it nice and simple for the maths. When we hit 100 we are at 100% of the way through our tween. When the value is 46, that’s 46% of between the two colours.
To further complicate things, the interpolater doesn’t return us a color, it returns a color object which won’t work with setTint()! So we have to convert the colorObject to a color first by extracting it’s RGB values. This new colour that is created is then used to tint our hero. Phew, that was pretty full on for such a simple thing. Supertommy form ourcade has a great video on this topic which I leaned on very heavily to get this code working.
So that’s the particles and the flashing done, we just need to tween the powerup being collected and we’re done with the heroes visuals.
//tween powerup to scale up and disappear
var tween = this.tweens.add({
targets: invicibility,
alpha: 0.3,
angle: 90,
scaleX: 3,
scaleY: 3,
ease: 'Linear',
duration: 500,
onComplete: function() {
destroyGameObject(invicibility);
},
});
//set players invincible flag to true
player.invincible = true;
}
We also set the invincible flag on our hero so we have that state stored. We now update the hit checks on player and baddie collisions to include this invincible flag. First in our playerHit() function:
//if you are not already invulnerable OR invincible
if (!player.invulnerable && !player.invincible) {
We check for both invulnerable AND invincible states. Then also for a bit more visual appeal, we’ll have a different death animation for the baddie if they are killed by invincibility:
if (player.invincible) {
baddie.disableBody(false, false);
//play baddies super death animation
var tween = this.tweens.add({
targets: baddie,
alpha: 0.3,
y: "-=150",
scaleX: 2.5,
scaleY: 2.5,
angle: 180,
ease: 'Linear',
duration: 200,
onComplete: function() {
destroyGameObject(baddie);
},
});
} else {
Finally, when our flashing animation runs out, we called resetInvincibility to turn our player back to normal:
function resetInvincibility() {
//reset the invincible flag
player.invincible = false;
//reset max speed of player
player.body.maxVelocity.x = maxPlayerSpeed;
//pause emitter and remove any existing particles
emitter.pause();
emitter.killAll();
}
This gets fired from the onComplete of our flashing tweens, which was set to 0.5 seconds, repeating 5 times, so that’s 2.5 seconds of visibility. If we play with these values we can have a shorter or longer invincibility time.
That was quite a dense one! We’re starting to run out screen space to fit all these features in, so in the next tutorial we’re going to make a larger level, and have the camera follow the hero as they move around the larger scene in camera.
chromeless mobile version
view all the code on codepen
download the source on github.