Open TianbinLiu opened 1 month ago
We made two changes to our Boss Level in this 2 week sprint. This issue will focus on creating the HP Bar and adding it to to the Boss, and as a bonus, helping our teammates add it to the player in the Ancient Greece Level.
In Boss.js
this.maxHp = 100; // Maximum health points
this.currentHp = 100; // Current health points
this.hpBar = document.createElement("canvas");
this.hpBar.width = 100;
this.hpBar.height = 15;
document.querySelector("#canvasContainer").appendChild(this.hpBar);
Originally, the canvas was somehow set to be 800 x 300, and when we drew it, (we'll get to that later), it looked like this.
We noticed this when we tried to add a border radius, because it only applied to the top left corner. So, we had to add the .width and .height to fit the canvas so that we could actually style it and make it look nice.
We created a function called drawHPBox which gets called repeatedly to draw the HPBox on the screen.
In Boss.js
drawHpBox() { //Hp box
// Position and size of the health bar
const hpBarWidth = this.hpBar.width; // The width of the health bar matches the boss's width
const hpBarHeight = this.hpBar.height; // A fixed height for the health bar
const hpBarX = (this.x + this.canvasWidth/2 - this.hpBar.width/2); // Position above the boss
const hpBarY = this.y - this.canvasHeight/40; // 20 pixels above the boss
this.hpBar.id = "hpBar"; // Calculate health percentage
const hpPercentage = this.currentHp / this.maxHp;
this.hpBar.getContext('2d').fillStyle = 'gray'; // Draw the background (gray)
this.hpBar.getContext('2d').fillRect(0, 0, hpBarWidth, hpBarHeight); // Draw the health bar (green, based on current health)
this.hpBar.getContext('2d').fillStyle = 'green';
this.hpBar.getContext('2d').fillRect(0, 0, hpBarWidth * hpPercentage, hpBarHeight);
this.hpBar.style.position = 'absolute'; //code from Flag.js, define the style of the Hp Bar
this.hpBar.style.left = `${hpBarX}px`;
this.hpBar.style.top = `${hpBarY}px`;
this.hpBar.style.borderRadius = '5px';
this.hpBar.style.width = `${hpBarWidth}px`;
this.hpBar.style.height = `${hpBarHeight}px`;
this.hpBar.style.border = '2px solid black';
}
HPBarHeight and HPBarWidth determine the actual dimensions of the bar, while HPBarX and HPBarY change based on the position of the boss, so that the HPBar follows the boss around. Finally, the HP Percentage is a ratio of the current hp and the max hp (100), which determines how the health is shown on the bar.
We used the fill style and fill rect built in functions to draw the HP Bar into the Canvas. We set a background color of gray, and an overlap color with a higher z index of green. The idea is that as the boss loses health, the percentage of green in the bar slowly reduces and the gray shows up, which indicates that the boss has lost HP.
Finally, we had to reduce the HP whenever a collision occurs with the player and the boss. Naturally, this code will exist in the collision action section.
else if(this.collisionData.touchPoints.other.bottom && this.immune == 0){
if(this.currentHp == 0){
this.state.animation = "death";
if(!this.state.isDying && this.state.animation == "death"){
this.frameX = 0;
}
this.state.isDying = true;
GameEnv.invincible = true;
GameEnv.goombaBounce = true;
GameEnv.playSound("goombaDeath");
}
else{
this.currentHp -= 20;
GameEnv.goombaBounce = true;
}
}
Here, we check for collisions with the player. First, we check if the currentHp is 0 (which means that the boss has been hit 5 times). If the currentHP is 0, we set the current animation state to be death. We then set the isDying state, invincible, goombaBounce to be true. Then, we play the GoombaDeath sound as the boss dies.
Else, we reduce the currentHP by 20, and in the loop, the drawHPBox function gets called again, so the amount of Green in the Bar gets reduced and the Gray shows.
This part is rather simple.
When we remove the Boss, we remove the HPBar along with it.
this.destroy();
this.hpBar.remove();
With these changes, we now have a working HP Bar.
This was one of the challenges given to us by another team. We decided to make the background a parallax and add a icon at the top to resemble the narwal in thr winter level.
With the new and updated Parallax version that Mr. Mortensen made, integration of Parallax becomes easier.
We edited everything in GameSetup.js to add parallax features.
First, we added parallax features to the source in GameSetup.js
boss: { src: "/images/platformer/backgrounds/BossBackground.png", parallaxSpeed: 0.4, moveOnKeyAction: true
And, we changed the class from Background to be BackgroundParallax in the bossgameobjects array.
{ name: 'bossbackground', id: 'background', class: BackgroundParallax, data: this.assets.backgrounds.boss },
Now, the background moves with the player.
Like the background, we added a parallax feature in the source to the devil image.
devil: {src: "/images/platformer/backgrounds/devil.png", parallaxSpeed: 2 },
Then, we edited the bossgameobjects array by changing th class to BackgroundParallax as well.
{ name: 'devil', id: 'devil', class:BackgroundParallax, data: this.assets.backgrounds.devil},
Now, we have a moving background and moving icon on the boss level.
Player change
As you see, the player will change to a zombie when it hits the item block.
At first, we try to achieve that by removing the original player(Mario) and replacing it with our zombie player during the gameplay. We tried, and we failed. Because of the time limit, we don't want to spend more time on this hard way. So according to Mr.M's advice, we plan to make the "parallel players".
Parallel players
We create PlayerZombie.js for our zombie player and PlayerBoss.js for the Mario player(therefore we don't need to change the PlayerHills.js)
And because our zombie sprite sheet has only one direction, so we added a new Base Player file, PlayerBaseOneD.js, for sprites that only have one direction.
The only difference between PlayerBaseOneD.js and PlayerBase.js is we added a if-statement to the updateAnimation() function.
The PlayerZombie.js extends the new base file PlayerBaseOneD.js we created instead of extending the old PlayerBase.js.
To make the player invisible, we create a new property to the constructor called "this.invisible" for both player files and we set it equal to true.
And then we overwrite the function draw() and add an if-statement to it so that when "this.invisible"=true, the player image won't draw/won't appear and therefore the player is invisible.
startRandomEvent("game") is the global event includes time stop, background change, and randomly kill a Goomba in the Hill level.
startRandomEvent("zombie"); is the new event we create to change the "this.invisible"
So that when Mario player hits our itemBlock, it will run the function startRandomEvent("zombie"); and change "this.invisible" to false and let the zombie player appear and the Mario player disappear/invisible.