Week 11 - Squirrel Eat Squirrel World


MY FINAL SQUIRREL EAT SQUIRREL

It’s a Squirrel-eat-Squirrel world out there and I’ve got to program it.

This week in MDSWC (cool little abbreviation there for ya), we’ve been tasked with creating our own version of Squirrel eat Squirrel by programmer Al Sweigart. For brevity’s sake, I am going to copy and paste the description of the game from our class website for anyone who may be unfamiliar:

Squirrel Eat Squirrel is loosely based on the game Katamari Damacy. The player controls a small squirrel that must hop around the screen eating smaller squirrels and avoiding larger squirrels. Each time the player’s squirrel eats a squirrel that is smaller than it, it grows larger. If the player’s squirrel gets hit by a larger squirrel larger than it, it loses a life point. The player wins when the squirrel becomes a monstrously large squirrel called the Omega Squirrel. The player loses if their squirrel gets hit three times.

For this assignment, we need to meet the following basic requirements:

  • The game must work in fullscreen mode
  • The squirrel is controlled by the arrow keys
  • Each squirrel is procedurally generated
  • Both the Player and the Squirrel must have their own class
  • When two squirrels collide, true beauty happens. Just kidding. The larger one eats the smaller one and grows bigger
  • If the player collides with a larger squirrel, their squirrel loses a life (out of 3)
  • If the player loses all 3 of their lives, game over

There are also optional Advanced features we can implement into our game. I’ll list out the ones that I will attempt to incorporate into my work:

  • Procedurally generated grass
  • Procedurally generated flowers
  • Eating sound
  • Background music

STEP ONE - Getting Assets I wanted to make my game look pixelated like Al Sweigart’s Squirrel eat Squirrel. Therefore, I did quick google searches for pixel art images of everything I’d need and then brought said images into Photoshop to do some basic color correction and resizing. I attached a screenshot of the folder I’ve got them in so you can see all of the thumbnail images for each asset. Regarding sound, I brought the Minecraft eating sound effect into Audacity and extracted just the last second of it. As for my background music, I found a nice elevator jazz track on Pixabay. I mainly want it to sound like something to be ignored if that makes any sense.

STEP TWO - The Basics (movement and canvas) The next step I’d like to tackle is the player squirrel’s movement. I’ll begin by writing it’s own class:

class Player {
  constructor(x,y){
    this.x = x;
    this.y = y; 
    this.size = 20;
    this.speed = 4;
  }
  
  update(){
    let move = createVector(0,0);
   
    if (keyIsPressed) {
    if (keyCode === UP_ARROW) {
      move.y -= 1;
    }
    if (keyCode === LEFT_ARROW) {
      move.x -= 1;
    }
    if (keyCode === DOWN_ARROW) {
      move.y += 1;
    }
    if (keyCode === RIGHT_ARROW) {
      move.x += 1;
    }
  }
    
    
    move.setMag(this.speed);
    this.x += move.x;
    this.y += move.y;
  

  this.x = constrain(this.x, 0, width);
  this.y = constrain(this.y, 0, height);
  }
  
  display(){
    image(p1img, this.x, this.y, this.size, this.size);
  }
}

I’m having difficulty with diagonal movement–not sure how to the fix current issue of the player’s acceleration not stopping after two simultaneous key strokes. Going to leave it relatively rigid for now.

As for the canvas, I selected a green hex code similar to our screenshot of Sweigart’s game.

STEP THREE - Procedural Environment Assets

Before we get to coding the NPC enemy squirrels, I’d like to work on the procedural grass and flower generation. I’ll begin by creating a new ‘startGame()’ function:

function startGame(){
  player = new Player(width / 2, height / 2);
  grassSpawn = [];
  
  for (let i = 0; i < 50; i++) {
    grassSpawn.push({
      x: random(width),
      y: random(height),
      size: random(40, 80)
    });
  }
}

function draw() {
  background("#49ed3f");

  for (let spawn of grassSpawn) {
    image(grass, spawn.x, spawn.y, spawn.size, spawn.size);
  }
  
  player.update();
  player.display();
}

I’ve included my draw() in this excerpt as well to show how I am generating the grass procedurally in this sketch. Basically, I just have to repeat this process for our flowers and we should have our environment completed! The editing of this portion of my devlog is a little peculiar. The end result is actually going to be the image just below the title of this step!

STEP FOUR - UI I want to continue on my trend of attempting to emulate the original game with my code. Therefore, I’ll make the three rectangular health bars at the top left of the canvas. Here’s what that function looks like:

function healthBar(x, y, w, h, maxBar, nowBar) {
  stroke(255);
  strokeWeight(2);

  for (let i = 0; i < maxBar; i++) {
    fill(i < nowBar ? color(255,0,0) : color(0));
    rect(x, y + i*(h + 4), w, h);
  }
}

‘maxBar’ is set to 3 and ‘nowBar’ is a variable dependent on the player’s current health. If the player collides with a larger enemy squirrel, that collision will result in one of the health bars being set to black.

STEP FIVE - Enemy Squirrels, Collision, and Splash Screens

I’ve made a new Bots class to house our procedurally generating enemies. I want them to range in size from ~25 to 60pixels, seeing as our player squirrel is 30 at the start of our game. I may up the largest size to 75 if the game needs to become more challenging. On top of all this, I am going to need to make our health bar responsive to any collisions with larger squirrels, and our squirrel grow when it collides with one that is smaller. I’d also like to add in the Minecraft eating sound effect I worked on here. Let’s get started!

class Bots {
  constructor(x, y, size){
    this.x = x;
    this.y = y;
    this.size = size;
    this.speed = random(0.5, 1);
    this.angle = random(TWO_PI)
  }
  
  update() {
    this.angle += random(-0.05, 0.05);
    this.x += cos(this.angle) * this.speed;
    this.y += sin(this.angle) * this.speed;
    
    if (this.x < 0) this.x = width;
    else if (this.x > width) this.x = 0;
    if (this.y < 0) this.y = height;
    else if (this.y > height) this.y = 0;
  }
  
  display() {
    push();
      translate(this.x, this.y);
      let f = (cos(this.angle) > 0) ? -1 : 1;
      scale(f, 1);
      image(npcimg, 0, 0, this.size, this.size);
    pop();
  }
}

This code is the final product of a few runs of the game so I can make necessary tweaks. Instead of the size range for enemies being between 25 and 60, I’ve made it between 25 and 90. I found it made the gameplay a bit more challenging and fun once there was a plethora of larger enemies to weave through. The accompanying change to this is the max player size being 100 for the win condition to trigger. These bots have been written to also move randomly across the canvas at varying speeds between 0.5 and 1. I think these speeds are fine, however, if the gameplay is hindered by fast-moving killers, I will make the lower-end of our range greater. Here’s how that’s looking:

COLLISION

spawnTimer++;
  if (spawnTimer >= spawnInt) {
    bots.push(new Bots(random(width), random(height), random(25, 90)));
    spawnTimer = 0;
    spawnInterval = max(30, spawnInt - 0.5);
  }

  if (player.invincible && millis() - player.invStart > GRACE_PERIOD) {
      player.invincible = false;
    }  
  
  for (let i = bots.length - 1; i >= 0; i--) {
    let b = bots[i];
    b.update();
    b.display();

    if (!player.invincible) {
    let d = dist(player.x, player.y, b.x, b.y);
    if (d < (player.size/2 + b.size/2)) {
      if (player.size < b.size) {
        health--;
        player.invincible = true;
        player.invStart = millis();
      } else {
        player.size *= 1.125;
        bots.splice(i, 1);
      }
    }
  }
}

In my draw() function, I’ve written collision detection for our player squirrel with any enemy players. I found that I died immediately after spawning a few times so I added in a grace period of 3 seconds for our squirrel friend whenever it spawns/takes damage. With this collision, whenever the distance between a player and enemy is less than the sum of half of both the player character and npc image sizes, they will collide. If that collision is triggered when the player is smaller than the enemy squirrel, then the player loses a life. If it’s triggered when they’re bigger, they grow slightly bigger. I suppose the only way to make this more sophisticated would be to make the player grow in proportion to the size of the enemy squirrel they consume, but I’m lazy.

MY SPLASH SCREEN and SFX code are pretty uninteresting to discuss–not too much new information to glean there. All you need to know is that when our player squirrel consumes a smaller squirrel, the eating sound will play, and when it is hurt, an “oof” sound will play. Each loop of the game also triggers a jazz song to play. I do not want to trigger damage audio per squirrel collision because I think that would be an auditory nightmare.

STEP SIX - Final Edit I forgot to give the enemy squirrels collision with one another + their respective growth if they do collide. My idea of tackling this would be to essentially emulate the rules for our player character, however, write it so that once an enemy squirrel reaches a size of 100, they burst into two squirrels equally sized at 50 each. Here’s what that fix looks like:

   for (let i = bots.length - 1; i >= 0; i--) {
      for (let j = bots.length - 1; j >= 0; j--) {
        if (i !== j && bots[i].collidesWith(bots[j])) {
          if (bots[i].size > bots[j].size) {
            bots[i].grow();
            bots.splice(j, 1);
            bots.push(new Bots(random(width), random(height), random(10, 60)));
          }
          break;
      }
    }
  }

IN ADDITION TO

collidesWith(other) {
  let d = dist(this.x, this.y, other.x, other.y);
  return d < (this.size/2 + other.size/2);
}

grow() {
  this.size *= 1.125;
  if (this.size >= 100) {
    this.size = 50;
    bots.push(new Bots(this.x + random(-20, 20), this.y + random(-20, 20), 50));
  }
}

This tackled my idea. Woohoo!

As a finishing touch, I wanted to add ‘yay’ and ‘boo’ sounds to the win and game over splash screens. That’s all for now!

Get Drawing, Moving and Seeing with Code - Spring 2025

Leave a comment

Log in with itch.io to leave a comment.