(Activity time: about 60 minutes)
(Download the Day Five Files Here)
Previous posts:
Game Jam Day 1
Game Jam Day 2
Game Jam Day 3
Game Jam Day 4
Intro
Hello, and welcome to day 5 of the Dad and Daughter Quarantine Game Jam Tutorial!
We’re making a game called Mia’s Daring Escape.
On day 1, we set up the game and put Mia on the screen.
On day 2, we gave Mia a brick level to run, and a jumping skill.
On day 3, we made falling bricks that stacked up on the ground or bopped Mia on the head.
On day 4, we added sounds and effects and gave Mia different poses for jumping and falling and standing still.
Today we add baddies.
These are our baddies.
They’re called shakos, after the little hats they’re wearing.
Once again, you’re definitely going to have to download the day five files, so you can get the shako images.
You can keep the game.js file you’ve been working on, but be sure to add the two shako images and the brick_bit image to the Art folder, and be sure to swap out plumbing.js, because I’ve added a function that makes brick bits out of broken bricks.
First Task: Put Some Baddies on the Screen!
Much like Mia, the shakos have multiple poses. So we’re going to use another container, and inside it, we’re going to put two sprites: spear forward and spear up.
The shakos will bounce around the level waving their spears up and down.
If Mia runs into a shako spear or falls onto a shako spear, she’ll go kaput. However, if she runs into a shako with his spear up, or she falls into a shako with his spear down, he’ll go kaput.
For now, we’re just going to put the shakos on the screen.
Add this code to game.js:
let mia = null;
let bricks = [];
let shakos = [];
let stacks = {};
let colors = [
color(1,0,0), // Red
color(0,1,0), // Green
color(0,0,1), // Blue
]
...
...
...
testBricks();
if (mia.y > 1200) {
stage.removeChildren();
bricks = [];
shakos = [];
stacks = {};
initializeGame();
}
followMia();
}
We’re just making a list of shakos at the beginning of the game, and at the end, where we reset the game when Mia goes kaput, we’re making sure to reset the list of shakos.
So now we have shakos that we can track, and when the game resets, we don’t have any stray shakos running around.
Now let’s make the shakos. Add this code to initializeGame:
function initializeGame() {
for (num = -1; num < 8; num += 1) {
let blue_sky = makeSprite("Art/blue_sky.png");
blue_sky.position.set(game_width * num, 0);
stage.addChild(blue_sky);
}
for (num = -8; num < 70; num += 1) {
if (num % 16 < 14) {
let brick = makeSprite("Art/brick.png");
brick.anchor.set(0.5,1);
brick.position.set(120 * num, game_height);
brick.tint = pick(colors);
stage.addChild(brick);
bricks.push(brick);
brick.column = num;
brick.y_velocity = 0;
stacks[brick.column] = 1;
}
else {
stacks[num] = -100;
}
}
for (num = 1; num <= 8; num += 1) {
// pick a random column,
let column = 5 + dice(65);
// and only make the shako if that column isn't a pit.
if (stacks[column] > 0) {
let shako = makeContainer();
shako.spear_forward = makeSprite("Art/shako_spear_forward.png");
shako.spear_forward.anchor.set(0.5, 1);
shako.addChild(shako.spear_forward);
shako.spear_up = makeSprite("Art/shako_spear_up.png");
shako.spear_up.anchor.set(0.5, 1);
shako.addChild(shako.spear_up);
shako.position.set(120 * column, game_height - 36);
shako.y_velocity = 0;
shako.x_velocity = 0;
shako.state = "ground";
shako.ground_time = 0;
shako.jumps = 0;
stage.addChild(shako);
shakos.push(shako);
}
mia = makeContainer();
...
...
...
We run a loop that makes 8 shakos. (It starts at num = 1 and ends when num <= 8, so that’s 1 to 8)
Inside, we pick a random column for the shako by calling the dice function. Instead of saying dice(70), which gives a number between 1 and 70, we say 5 + dice(65), which starts with 5 and adds a random number, giving us a number between 6 and 70.
We do this because shakos in columns 1 – 5 would be too close to Mia’s starting position.
Remember that stacks is a dictionary that keeps track of the stack of bricks in each column. If stacks[3] == 5, that tells us that there are 5 bricks stacked in column 3.
So we use an if statement, “if (stacks[column] > 0)“, to check if the stack is bigger than zero in the new column we’ve picked. This is so that we don’t make a shako over an empty pit.
If the stack is bigger than zero, then we actually make the shako.
To do that, we make a container, then we make two sprites, one for spear forward, and one for spear up, and put them both in the container.
Finally, we set the position of the shako to match the column (120 * column, and game_height – 36, which is the ground), then we add some variables we’re going to use later, then we add the shako to the stage, as well as to the list of shakos.
Whew!
Uh. But our shako has two arms.
That’s because both sprites are visible. We haven’t hidden either sprite yet.
We’re going to give the shako a function which lets us switch it from spear up to spear forward.
Add this code:
if (stacks[column] > 0) {
let shako = makeContainer();
shako.spear_forward = makeSprite("Art/shako_spear_forward.png");
shako.spear_forward.anchor.set(0.5, 1);
shako.addChild(shako.spear_forward);
shako.spear_up = makeSprite("Art/shako_spear_up.png");
shako.spear_up.anchor.set(0.5, 1);
shako.addChild(shako.spear_up);
shako.position.set(120 * column, game_height - 36);
shako.y_velocity = 0;
shako.x_velocity = 0;
shako.state = "ground";
shako.ground_time = 0;
shako.jumps = 0;
stage.addChild(shako);
shakos.push(shako);
shako.setStance = function(stance) {
if (stance == "up") {
shako.stance = "up";
shako.spear_up.visible = true;
shako.spear_forward.visible = false;
} else if (stance == "forward") {
shako.stance = "forward";
shako.spear_up.visible = false;
shako.spear_forward.visible = true;
}
}
shako.setStance("up");
}
We make a new function for the shako called setStance, which allows us to change the stance by calling either setStance(“up”) or setStance(“forward”).
This sets a variable called “stance” which we’ll use later, and it makes one sprite visible and the other sprite invisible.
Finally, we call shako.setStance(“up”) to set the new shako to spear up.
We have shakos, but they’re getting buried under bricks, and we don’t like that!
Second Task: Shakos break falling bricks!
From now on, when we drop bricks, we are going to measure every brick against every shako.
If a falling brick is close to any shako, we’re going to delete it from the brick list, remove it from the stage, and replace it with a bunch of cute little brick bits:
The box will extend 60 pixels left and right from the shako’s x position, and starting from the feet, it will extend 160 pixels upwards to the shako’s head.
Since we’re checking every brick against every shako, we’re writing a double loop, also known as a nested loop.
That means we write a loop that checks every brick, and inside that loop, we write another loop that checks every shako.
Detail: It’s a very bad idea to delete things from a list while you’re looping through that list. It messes up the variable you’re using to count how many loops you’ve done, and the loop will skip items without even telling you. For example, if you delete item #5, then the old item #6 becomes the new #5, but on your next go around, your counter will jump to #6, skipping that item.
There are two ways to safely delete things from a loop. One is to make a new list that only has the stuff you don’t want to delete, then switch lists.
The other is to go backwards. When you delete something, it doesn’t matter that you shift all the later elements of the list, because you’ve already seen them. We’ll be using this method in our code.
Also, in javascript, there isn’t a nice delete function for lists. We use a function called splice to cut one element out of the list. splice(23, 1) means “cut one element out, starting at place #23”. It’s silly, but it works, and you can ignore it.
Here’s the code. Add it to the dropBricks function:
// Every 250 milliseconds, drop a brick.
function dropBricks() {
if (Date.now() - last_brick > 250) {
// Make a new brick
let brick = makeSprite("Art/brick.png");
brick.anchor.set(0.5,1);
brick.tint = pick(colors);
// Set it in the right place and give it some drop speed.
brick.column = dice(70);
brick.position.set(120 * brick.column, -50);
brick.y_velocity = 1;
// Add it to the stage, and add it to the list of bricks.
stage.addChild(brick);
bricks.push(brick);
last_brick = Date.now();
}
// For every brick, if it has y_velocity, drop it.
for (i = 0; i < bricks.length; i += 1) {
let brick = bricks[i];
if (brick.y_velocity > 0) {
brick.y = brick.y + brick.y_velocity;
brick.y_velocity = brick.y_velocity + 0.25;
// If it goes past the brick stack, stop it,
// and increase the stack value.
if (brick.y >= game_height - 36 * stacks[brick.column]) {
brick.y = game_height - 36 * stacks[brick.column];
brick.y_velocity = 0;
stacks[brick.column] = stacks[brick.column] + 1;
}
}
}
// Delete every brick that's hit a shako.
// Note that we run the loop BACKWARDS.
for (brick_num = bricks.length - 1; brick_num >= 0; brick_num += -1) {
let brick = bricks[brick_num];
if (brick.y_velocity > 0) {
// By default, assume the brick hasn't hit a shako
let hit_a_shako = false;
// Loop through all the shakos
for (shako_num = 0; shako_num < shakos.length; shako_num += 1) {
let shako = shakos[shako_num];
// If the shako and the brick are close together,
if (Math.abs(shako.x - brick.x) <= 60 && brick.y > game_height - 36 - 160) {
hit_a_shako = true;
}
}
// If the brick has touched any shako, hit_a_shako will be true.
if (hit_a_shako) {
// This line is like "delete item number such and such from the list"
bricks.splice(brick_num, 1);
// Remove the brick from the stage
stage.removeChild(brick);
// Make a bunch of little brick debris!
makeBrickBit(stage, brick.x - 45, brick.y, brick.tint);
makeBrickBit(stage, brick.x - 15, brick.y, brick.tint);
makeBrickBit(stage, brick.x + 15, brick.y, brick.tint);
makeBrickBit(stage, brick.x + 45, brick.y, brick.tint);
// If mia is close enough, make some popping sounds.
if (Math.abs(brick.x - mia.x) < 700) {
soundEffect("pop_1");
soundEffect("pop_2");
}
}
}
}
}
You don’t have to use the sounds I put here. Remember, you have a whole bunch of sounds to choose from, and you can use whichever sounds you want, or no sounds at all!
As promised, it’s a double loop.
We check if the brick has y_velocity > 0 to make sure it’s falling.
Then we assume it hasn’t hit a shako by making a variable called hit_a_shako and setting it false.
Then we search through all the shakos. If the brick and the shako are nearby, we set hit_a_shako to true.
We do it this way because we don’t want to run the “hit a shako” code a bunch of times if we hit a bunch of shakos. Just once. So no matter how many shakos we hit, we just know, oh, we hit a shako.
Anyway, after that check, if hit_a_shako is true, we delete the brick from bricks and remove it from the stage.
Then we make a bunch of little brick bits using the new makeBrickBits function I added to plumbing, staggering them a few pixels apart. That new function takes care of throwing them off in random directions and getting rid of them later, so don’t worry about it.
Finally, if Mia is close to the brick, we play a few sounds.
Third Task: Move Those Shakos!
Now we’re going to move the shakos around on the level.
I think it’ll be cutest if the shakos move by doing bouncy little jumps.
Let’s start by making an empty function called updateShakos and calling it from updateGame. Add the following code to game.js:
// Every 250 milliseconds, drop a brick.
function dropBricks() {
...
...
...
}
function updateShakos() {
}
function testBricks() {
...
...
...
}
function updateGame(diff) {
...
...
...
updateShakos();
testBricks();
if (mia.y > 1200) {
stage.removeChildren();
bricks = [];
shakos = [];
stacks = {};
initializeGame();
}
followMia();
}
updateShakos is called late in updateGame, after Mia has been updated.
Let’s start writing updateShakos by adding the little jumps.
When we first made the shakos, we gave them a few extra properties like x and y velocity and state, and we’re going to use those properties now.
A shako can be “jumping”, “ground”, or “kaput”.
If the shako has been on the ground for a while, we want to set it to jumping and give it some negative y velocity (remember, negative is upwards because the top of the screen is 0) as well as some x velocity to move around the level.
If the shako is jumping, we want to update the x and y position using the x and y velocity. If the shako comes back down to the ground height, we want to switch its state to ground again. We do this so that the shako has a little pause between jumps. It looks better than insta-bounce.
Detail: One more thing. It would be cute if the shakos always land perfectly on one of the brick studs. It looks better than if they slide around halfway between two studs.
So, in the time it takes to do a hop, the shakos have to travel exactly from one stud to the next. Since each brick is 120 pixels, and each brick has 4 studs, the distance between each stud is 30 pixels.
Using our jump velocity and gravity, we can calculate the total time of the jump.
height = jump_velocity * time – gravity * time^2 / 2.
We want height of 0, so we solve:
0 = jump_velocity * time – gravity * time^2 / 2
and a little algebra gives:
time = 2 * jump_velocity / gravity
This works in frames just as well as seconds, as long as you add two extra frames for the start and end of the jump.
We’re going to use a nice little hop with 5 velocity and 0.5 gravity.
So time = 2 * 5 / 0.5 = 20.
So we need to move 30 pixels in 22 frames, and our x velocity will be 30/22.
Add this code to updateShakos:
function updateShakos() {
for (num = 0; num < shakos.length; num += 1) {
let shako = shakos[num];
if (shako.state == "ground" && Date.now() - shako.ground_time > 150) {
shako.state = "jumping";
shako.y_velocity = -5;
shako.x_velocity = -1 * 30/22;
if (Math.abs(mia.x - shako.x) < 700) soundEffect("jump_2")
}
if (shako.state == "jumping") {
shako.y += shako.y_velocity;
shako.x += shako.x_velocity;
shako.y_velocity += 0.5;
if (shako.y > game_height - 36) {
shako.y = game_height - 36;
shako.y_velocity = 0;
shako.state = "ground";
shako.ground_time = Date.now();
shako.jumps += 1;
}
}
}
}
Do a loop through all of the shakos.
For each shako, if the shako has been on the ground for more than 150 milliseconds, set the state to jumping, the y velocity to -5 (that’s 5 pixels per frame in the upward direction), and the x velocity to -30/22 (that’s 30/22 moving to the left).
Also give the shako a jumpy sound if it’s close to Mia. As always, you can try other sounds. I think “sheep” is particularly fun.
If the shako is jumping, update the x and y positions using velocity. Then, add 0.5 to the y velocity (this is “gravity”). If the shako has fallen past the ground height (bottom of the screen – 36 pixels), put it at the right height, stop it from falling, set the state to ground, and set the last ground landing time to right now.
Oh, and add one to the total number of jumps the shako has done.
The shakos are certainly jumping, but they don’t respect the level boundaries!
They should avoid pits, they should never jump through brick stacks, and they should turn around sometimes.
Change updateShakos like so:
function updateShakos() {
for (num = 0; num < shakos.length; num += 1) {
let shako = shakos[num];
if (shako.state == "ground" && Date.now() - shako.ground_time > 150) {
shako.state = "jumping";
shako.y_velocity = -5;
shako.x_velocity = -1 * 30/22;
shako.x_velocity = -1 * shako.scale.x * 30/22;
if (Math.abs(mia.x - shako.x) < 700) soundEffect("jump_2")
}
if (shako.state == "jumping") {
shako.y += shako.y_velocity;
shako.x += shako.x_velocity;
shako.y_velocity += 0.5;
if (shako.y > game_height - 36) {
shako.y = game_height - 36;
shako.y_velocity = 0;
shako.state = "ground";
shako.ground_time = Date.now();
shako.jumps += 1;
if (dice(100) < 10) {
shako.scale.x = -1 * shako.scale.x;
shako.x -= 30 * shako.scale.x;
}
if (shako.scale.x == 1) shako.next_col = Math.floor((shako.x + 61 - 30) / 120);
if (shako.scale.x == -1) shako.next_col = Math.floor((shako.x + 61) / 120);
if (shako.next_col < 0 || shako.next_col > 70 || stacks[shako.next_col] != 1) {
shako.scale.x = -1 * shako.scale.x;
shako.x -= 30 * shako.scale.x;
}
}
}
}
}
Now we’re using the shako’s x scale to know which way it’s facing. Since the sprite starts out facing left, 1 means it’s facing left, -1 means it’s flipped, and facing right.
We roll a 100 sided dice by calling dice(100), and if the result is less than 10, we turn the shako around.
We also compute the next column the shako is jumping towards, depending on the direction.
If that column is outside bounds (0 to 70) or the height of that column is not 1 (stacks[shako.next_col] != 1), meaning it’s a tall stack or a pit, we turn the shako around.
Detail: please don’t worry about the weird bits of math here where we’re moving the shako around or calculating from 61 pixels instead of 60. The shako sprite has weird footing. If you want to see what I mean, play with those numbers yourself and see what happens to the shako!
Fourth Task: Spears Up, Spears Down
This one is easy. We just need to add a few lines of code that call the shakos’ setStance function.
Remember modular arithmetic?
Modular arithmetic!
num % 6 is read as “num mod 6″. Mod means you wrap around, like days of the week, or hours on a clock.
5 mod 6 is 5.
6 mod 6 is 0.
7 mod 6 is 1.
8 mod 6 is 2.
9 mod 6 is 3.
We want the spears to change every three jumps. We can use mod 3.
Add this code to updateShakos:
function updateShakos() {
for (num = 0; num < shakos.length; num += 1) {
let shako = shakos[num];
if (shako.state == "ground" && Date.now() - shako.ground_time > 150) {
shako.state = "jumping";
shako.y_velocity = -5;
shako.x_velocity = -1 * shako.scale.x * 30/22;
if (Math.abs(mia.x - shako.x) < 700) soundEffect("jump_2")
}
if (shako.state == "jumping") {
shako.y += shako.y_velocity;
shako.x += shako.x_velocity;
shako.y_velocity += 0.5;
if (shako.y > game_height - 36) {
shako.y = game_height - 36;
shako.y_velocity = 0;
shako.state = "ground";
shako.ground_time = Date.now();
shako.jumps += 1;
if (shako.jumps % 3 == 1) {
if (shako.stance == "forward") {
shako.setStance("up");
} else {
shako.setStance("forward");
}
}
if (dice(100) < 10) {
shako.scale.x = -1 * shako.scale.x;
shako.x -= 30 * shako.scale.x;
}
if (shako.scale.x == 1) shako.next_col = Math.floor((shako.x + 61 - 30) / 120);
if (shako.scale.x == -1) shako.next_col = Math.floor((shako.x + 61) / 120);
if (shako.next_col < 0 || shako.next_col > 70 || stacks[shako.next_col] != 1) {
shako.scale.x = -1 * shako.scale.x;
shako.x -= 30 * shako.scale.x;
}
}
}
}
}
Every third jump (on jumps 1, 4, 7, 10, 13, etc), this code triggers.
If the shako is spear forward, make it spear up. Otherwise, make it spear forward.
Final Task: Mia vs the Shakos
We’re going to add a new function called miaVsShakos to handle the cases where Mia bumps into a shako.
There are five scenarios:
If Mia runs into the shako spear forward, Mia goes kaput.
If Mia runs into the shako spear up, shako goes kaput.
If Mia runs into the back of the shako, shako goes kaput.
If Mia jumps onto the shako spear up, Mia goes kaput.
If Mia jumps onto the shako spear forward, shako goes kaput.
We will add these one at a time.
First, let’s add the new function to the game, and call it from updateGame:
function miaVsShakos() {
}
function testBricks() {
...
...
...
}
function updateGame(diff) {
...
...
...
updateShakos();
miaVsShakos();
testBricks();
if (mia.y > 1200) {
stage.removeChildren();
bricks = [];
shakos = [];
stacks = {};
initializeGame();
}
followMia();
}
Now, let’s add the scenario where Mia runs into a shako spear forward.
We loop through all the shakos, and only test when both Mia and the shako are not already kaput.
If Mia’s x position is less than the shako, and she’s facing right and he’s facing left and they’re close, or, if Mia’s x position is greater than the shako, and she’s facing left and he’s facing right and they’re close, Mia goes kaput.
Let’s write that in code:
function miaVsShakos() {
for (shako_num = 0; shako_num < shakos.length; shako_num += 1) {
let shako = shakos[shako_num];
if (shako.state != "kaput" && mia.state == "running") {
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
}
}
}
The number 100 determines how close Mia and the shako have to be in order for Mia to go kaput. Try making it smaller or larger. If it’s larger, Mia will go kaput from further away. If it’s smaller, Mia will be able to get closer before going kaput.
Let’s add the second scenario.
function miaVsShakos() {
for (shako_num = 0; shako_num < shakos.length; shako_num += 1) {
let shako = shakos[shako_num];
if (shako.state != "kaput" && mia.state == "running") {
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "up" && Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "up" && Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
}
}
}
You may know enough programming to know that this is a messy way to do things.
In general, it would be good to reorganize these statements so that they’re less copy-paste.
As I’ve said before, I want you to have fun and make progress, rather than getting bogged down in details or formal programming practices.
That stuff comes later, when you already enjoy programming and have the habit.
Also! Separately! A good way to understand complex if statements like the ones above is to read them like they’re in normal language.
“If Mia’s x position is greater than the shako’s x position, and Mia’s scale is minus 1, and the shako’s scale is minus 1, and Mia’s x position is less than shako’s plus 80, aaaand shako’s stance is up, and the absolute value of the y positions is less than 70, then…. whew, jeez.”
Now let’s add the third scenario, where Mia runs through shakos with their backs turned. It looks almost the same, but with the shako scale flipped and no mention of the spears.
Add this to miaVsShakos:
function miaVsShakos() {
for (shako_num = 0; shako_num < shakos.length; shako_num += 1) {
let shako = shakos[shako_num];
if (shako.state != "kaput" && mia.state == "running") {
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "up" && Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "up" && Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == -1 && mia.x > shako.x - 80
&& Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == 1 && mia.x < shako.x + 80
&& Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
}
}
}
Almost done!
We can do scenarios four and five (Mia jumps on the shako) together. Add this to miaVsShakos:
function miaVsShakos() {
for (shako_num = 0; shako_num < shakos.length; shako_num += 1) {
let shako = shakos[shako_num];
if (shako.state != "kaput" && mia.state == "running") {
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "forward" && Math.abs(mia.y - shako.y) < 100) {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
}
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == 1 && mia.x > shako.x - 80
&& shako.stance == "up" && Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == -1 && mia.x < shako.x + 80
&& shako.stance == "up" && Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x < shako.x && mia.scale.x == 1 && shako.scale.x == -1 && mia.x > shako.x - 80
&& Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
if (mia.x > shako.x && mia.scale.x == -1 && shako.scale.x == 1 && mia.x < shako.x + 80
&& Math.abs(mia.y - shako.y) < 70) {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
}
if (shako.state != "kaput" && mia.state == "falling") {
if (Math.abs(mia.x - shako.x) < 80 && mia.y < shako.y - 30 && mia.y > shako.y - 160) {
if (shako.stance == "up") {
mia.setState("kaput");
mia.scale.y = -1;
mia.y_velocity = -5;
mia.y = mia.y - 175;
soundEffect("negative_2");
} else if (shako.stance == "forward") {
shako.state = "kaput";
shako.scale.y = -1;
shako.y_velocity = -5;
shako.y = shako.y - 175;
soundEffect("hurt");
}
}
}
}
}
If Mia is falling, and the shako isn’t kaput, then we check for the jump collision.
If Mia’s x position is within 80 of the shako, and Mia’s y position is higher than shako’s y minus 30, and lower than shako’s y minus 160, then we have a collision.
If the shako’s spear is up, Mia goes kaput, and if it’s forward, the shako goes kaput.
And just like that, we’re done.
Tomorrow, we’ll add… the rocket.
Hang in there!