Back to Forum
Mario Game - Creating a Boss Fight
b1u3ch1p posted 4 years ago

Hello all,

I'm trying to design a boss fight for the end of my Mario game, and I'm finding a considerable mental block when it comes to the actual logic.

I have a Boss class, which is just the Player class copied in full, that draws just fine onto the canvas

class Boss{
    constructor() {
        this.speed = 10 //for testing
        this.position = {
            x: 300,
            y: 500
        }
        this.velocity = {
            x:0,
            y:0
        }
        this.scale = 0.3
        this.width = 398 * this.scale
        this.height = 353 * this.scale

        this.image = createImage(spriteMarioStandRight)
        this.frames = 0

        this.sprites = {
            stand: {
                right: createImage(spriteMarioStandRight),
                left: createImage(spriteMarioStandLeft),
                //cropWidth: 398,
                //width: 398 * this.scale
                fireFlower: {
                    right: createImage(spriteFireFlowerStandRight),
                    left: createImage(spriteFireFlowerStandLeft)
                }
            },
            run: {
                right: createImage(spriteMarioRunRight),
                left: createImage(spriteMarioRunLeft),
                //cropWidth: 398,
                //width: 398 * this.scale,
                fireFlower: {
                    right: createImage(spriteFireFlowerRunRight),
                    left: createImage(spriteFireFlowerRunLeft)
                }
            },
            jump: {
                right: createImage(spriteMarioJumpRight),
                left: createImage(spriteMarioJumpLeft),
                //cropWidth: 398,
                //width: 398 * this.scale
                fireFlower: {
                    right: createImage(spriteFireFlowerJumpRight),
                    left: createImage(spriteFireFlowerJumpLeft)
                }
            }

        }

        this.currentSprite = this.sprites.stand.left
        this.currentCropWidth = 398
        //this.powerups = {fireFlower: false, policies: false, tech: false, people: false}
        this.invincible = false
        this.opacity = 1
        this.health = 10000
        //this.attack = "jump"
        //this.attackMap = ["jump", "lunge", "techAttack", "policyAttack", "peopleAttack", "none"]

        


    }

    draw() {
        c.save()
        c.globalAlpha = this.opacity
        c.fillStyle = 'rgba(255,0,0, .2)'
        c.fillRect(this.position.x, this.position.y, this.width, this.height)
        c.drawImage(
            this.currentSprite,
            this.currentCropWidth * this.frames,
            0,
            this.currentCropWidth,
            353, 
            this.position.x, 
            this.position.y, 
            this.width, 
            this.height
            )
        c.restore()        


    }

    update() {
        this.frames++

        const {currentSprite, sprites} = this

        if (this.frames > 58 && ((currentSprite === sprites.stand.right) || (currentSprite === sprites.stand.left) || (currentSprite === sprites.stand.fireFlower.left) || (currentSprite === sprites.stand.fireFlower.right))){
            this.frames = 0
        }
        else if (this.frames > 28 && ((currentSprite === sprites.run.right) || (currentSprite === sprites.run.left) || (currentSprite === sprites.run.fireFlower.left) || (currentSprite === sprites.run.fireFlower.right))){
            this.frames = 0
        }
        else if (currentSprite === sprites.jump.right || currentSprite === sprites.jump.left || currentSprite === sprites.jump.fireFlower.right || currentSprite === sprites.jump.fireFlower.left){
            this.frames = 0
        }

        this.draw()        
        this.position.y += this.velocity.y
        this.position.x += this.velocity.x




        if ((this.position.y + this.height + this.velocity.y) <= canvas.height){
            this.velocity.y += gravity
            //console.log(this.position.y)
        }

        if (this.invincible) {
            if (this.opacity === 1) {
                this.opacity = 0
            }
            else{
                this.opacity = 1
            }


        } else this.opacity = 1


    }

}

I instantiate the boss object same as the player:

let boss = new Boss()

Then I follow the same rules as the flagpole for confirming the object exists:

    if(boss) {
        boss.update()
        boss.velocity.x = 0 }

At the end of the level, I want to have the player character fight a boss with 5 unique mechanics, and it's here that I'm stuck logic wise. Here are the 5 mechanics I have in mind:

  1. Technology based attack: boss throws 1s and 0s shaped objects at the player, player has a specific power up to defend from this

  2. Policy based attack: boss charges the player quickly on the ground, player has to jump on top of the boss.

  3. People based attack: boss is cloned multiple times, new boss objects created above the player, player has to dodge and shoot the boss objects falling towards them

  4. Jumping attack: boss simply jumps towards the player in random fashion

  5. None: boss taunts the player in the logs

I was thinking of using an attack map similar to the platformsMap array functionality from the videos so I can easily change up the order if the game gets stale. I don't have working code but here's what I had thought up for this:

while boss.health != 0 {
	if(player.health == 0) {
		 loseCondition()
	}
	else {
		action = bossAttackMap[getRandomInteger(0, bossAttackMap.length-1)]//getRandomInteger is a function I made to return a random result from two integers
		switch (action) {
			case "jump":{
				boss.velocity.y -= 10
				boss.position.x = getRandomInteger(boss.position.x, lastPlatform.width)
				break
			}
			case "technologyAttack" :{
				flood = getRandomInteger(25, 75)
				while (flood >= 0){
					goombas.push(new Goomba{//goomba constructor})
					flood -= 1
				}
				//goombas just push themselves offscreen and roll over into garbage collection
				break
			}
			case "policyAttack":{
				boss.speed = getRandomInteger(10,30)
				boss.velocity.x += boss.speed
				boss.position.x += boss.velocity.x
				//simulated charging
				break
			}
			case "peopleAttack" :{
				let bosses = []
				flood = getRandomInteger(10, 30)
				while (flood >= 0){
					bosses.push(new Boss())
					clone = bosses[bosses.length-1]
					clone.position.y = getRandomInteger(lastPlatform.height, canvas.height)
					clone.position.x = getRandomInteger(lastPlatform.position.x, lastPlatform.width)
					flood -= 1
				}
				//boss clones fall towards the ground and roll over into garbage collection
			}
			case "none" :{
				let taunts = ["pre canned taunt 1", "pre canned taunt 2",..."pre canned taunt n"]
				let randomTaunt = getRandomInteger(0, taunts.length - 1)
				console.log(taunts[randomTaunt])
			}
		
		}

	}
}

The first problem I ran into was having this code execute rapidly. I'd like there to be delays so my jump attack doesn't just yeet the boss right off the screen, but I can't seem to get the timeout functionality to work like this, and I'm all out of ideas.

Any help is appreciated, thanks in advance!

Want to participate?

Create a free Chris Courses account to begin

Providing the lift to launch your development career

© 2026 Chris Courses. All rights reserved.