· 14 min read coding javascript p5

Unofficial Chicka Chicka Boom game in p5js

When I was tasked with creating an interactive story based on a children’s book that had to include some form of randomness and build on some of the things we’ve learned in the first 2 weeks of my class on p5.js — Chicka Chicka Boom Boom was the first thing that came to mind — so I ran with it.

tldr; Made a game that you or your kids should play: Chicka Chicka Boom Boom Game Demo

The setup

Chicka Chicka Boom Boom is a children’s book that helps kids to learn the alphabet by turning the letters into characters and telling a story of how they go to a party and fall out of a tree — something like that. I only read it a handful of times when my kids were little and I was a bit too old to grow up on it (it was published in 1989).

The design of the book is bright, the typeface is bold, and the rhyming wants to make you get loud and excited when reading it.

Design Goals

My idea seems simple — Create an interactive game based on the story — letters will be piled up on each other after they’ve fallen out of the tree and the player will need to find each letter in ABC order.

The design should mimic the bold lettering and bright visuals from the book.

After placing the letters back into the tree, the player should be given the opportunity to play it again.

Development Goals

  • Each letter will be assigned a color and added onto the canvas in the scene so that that are spread out, skewed and in no particular order.

  • At the top of the screen there will be an indicator on which letter the player should be looking for and direction on how to play.

  • If a player selects the wrong character, it will make a sound indicating an error.

  • If the player selects the correct character, a sound will indicate that they were correct, it will be removed from the pile, placed on top of the tree and the indicator will change to the next letter.

  • After the user finds the last letter, a sound will indicate they’ve completed it and will be prompted to play it again.

The code in this morphed quite a bit as I added in features and effed up some things so I’m only going to share some chunks of code, but you can check out the working demo and the final code at the end. Let’s make this happen.

Random letters on the canvas

I started out by creating the alphabet and storing it into an array, then cycling through each letter and assigning a random position on the canvas:

image1

The look of the letters

The typeface in the book used for the letters is very blocky. A quick google search and of course the Chicka Chicka Boom Boom font is a thing and easily imported.

The color palette used in the book is bright and very limited — maybe 10 colors in all, so I stored those in a color palette array and assigned the letters a random color as I placed them on the canvas.

image2

Mimicking the book design

There is one page in the book when all the letters fall out of a tree that I used as inspiration for the game page — I made the tree in SVG and created the border around the whole canvas by looping through the dots one by one and assigning a small bit of randomness. I could have been more clever with the measurements but, for the sake of sanity, I used a bunch of magic numbers to get it done.

function drawCirclePattern() {
  // Creates the dots that surround the canvas
  var numberOfRows = 19
  var numberOfColumns = 38
  var radius = 14
  var marginX = 34
  var marginY = 38
  var xPosition = 20
  var yPosition = 16
  var rotation = 0

  fill ( 215, 21, 101 )

  // Create each row
  for (j = 0; j < numberOfRows; j++ ){
    // Create each column's circle
    for (i = 0; i < numberOfColumns; i++ ){
      // Add the circle with a little bit of randomness for the Y position
      ellipse( xPosition, yPosition + ( random( -3, 3 ) ), radius, radius )
      xPosition += marginX
    }
    // Offset the even numbered rows
    if ( j % 2 == 0 ){
      xPosition = -41
    }
    else{
      xPosition = 20
    }
    yPosition +=  marginY
  }
}

and got it looking pretty darn close to the actual look of the page:

image3

Positioning the letters on the canvas

I couldn’t get away with random positions on the canvas — the letters needed to be in their own spot and not overlapping the other characters.

I chose to actually define 26 different positions on the canvas that each letter could go into so that I could get more control of their placement. I stored those in an array and assigned each letter randomly to one of those positions.

I also created a small bar at the bottom of the screen to indicate what letters had been found.

At this point I had forgotten the fact that I wasn’t storing the random positions, rotation and color for each of the letters so when I ran it, it produced a seizure inducing affect of creating and randomizing the alphabet on top of the existing one:

image4

Whoops. 10 minutes of trying to figure out if I could get away with noLoop() later and ended up extending my alphabet array to include the positions, color and rotations — only calling that once. Whew:

image5

Mouse events and keeping track of the letters

At the start of the program, I set which letter is the first to be found and use that as the alphabet is being drawn onto the page so that I can color it gray and also to display, at the bottom, which ones the player has found.

The coloring was simply done like this during the loop of placing the letters on the page:

if (alphabet.indexOf(currentLetterToFind) > i){
  fill( 220 , 220, 220 )
}
else{
  // Assign the stored color value
  fill( alphabet[i][4] )
}

A mouse looks to see if the click happens close to the coordinates of the current letter to be found:

var clickDistance = dist ( mouseX, mouseY, currentLetterToFind[1], currentLetterToFind[2] )
if (clickDistance < 50 ){
  var currentLetterIndex = alphabet.indexOf(currentLetterToFind)
  var nextLetterIndex = currentLetterIndex + 1
}

And the game ends up looking this this as it progresses:

image6

A starting and congratulations screen

It’s not much of a game without a great intro screen and a screen for indicating you’ve won! So I set out to create 2 additional scenes. A new vector that looks like the book cover and some more color choices and buttons later and I had this:

Intro screen

image7

Play again screen

image8

QA and the need for some competition

At this point, the game was working smoothly, so I volunteered my kids to do a little quality assurance. They thought it was cute and were kinda surprised I had it in me — but it wasn’t enticing enough for them to play it more than once.

They definitely came up with some far out ideas on how to improve it, but not anything that I’d want to try to make happen for this assignment.

But, I decided that there’s nothing better than a little healthy competition to get them on each other’s nerves — so I decided I’d add in a timer so that we could race each other’s best times.

Time is complicated — mainly because of math and the fact that noon rolls into 1 and 59 rolls into 1 and their aren’t any good built in functions for calculating how much time has passed.

So I decided I would just keep track of each drawn frame and assuming there’s about 60 frames running per second, I would just estimate the number in seconds.

Ending up with this:

image9

The final code and working demo

I did my best to make the code as clear and commented as I could, but before you delve into it, check out the working demo of the Chicka Chicka Boom Boom Game.

// Set the starting scene
var scene = 0

// Setup some global variables
var alphabet = []
var currentLetterToFind
var positions

// Store the frames as the game is played to be used as a timer
var numberOfFrames = 0

// Set a default winning text to be randomized later
var finalWinningText = "Wow!"

// Color of the letters
var colorPalette = [
  [ 240, 0, 100 ],
  [ 247, 90, 29 ],
  [ 8, 170, 143 ],
  [ 242, 24, 67 ],
  [ 243, 162, 32 ],
  [ 1, 170, 254 ],
  [ 86, 72, 157 ],
  [ 0, 120, 194 ],
  [ 120, 100, 170 ],
  [ 251, 196, 14 ]
]

// Different congradulations text
var congratsText = [
  "Great Job!",
  "Wow!",
  "You Rock!",
  "Fantastic!"
]

function setupPositions(){
  // Positions of the letters on the canvas
  positions = [
    [ 497,362 ],
    [ 613, 362 ],
    [ 181, 437 ],
    [ 352, 437 ],
    [ 468, 437 ],
    [ 584, 437 ],
    [ 700, 437 ],
    [ 816, 437 ],
    [ 932, 437 ],
    [ 164, 525 ],
    [ 336, 525 ],
    [ 452, 525 ],
    [ 568, 525 ],
    [ 684, 525 ],
    [ 800, 525 ],
    [ 916, 525 ],
    [ 1032, 525 ],
    [ 134, 623 ],
    [ 245, 623 ],
    [ 365, 623 ],
    [ 481, 623 ],
    [ 597, 623 ],
    [ 713, 623 ],
    [ 829, 623 ],
    [ 956, 623 ],
    [ 1058, 623 ]
  ]
}

function preload(){
  // Import Font
  font = loadFont( "ccbb.ttf" )

  // Import Images
  treeImage = loadImage( "tree.svg" )
  treeIntroImage = loadImage( "tree-intro.svg" )

  // Import Sounds
  soundFormats( "mp3" )
  yeahSound = loadSound( "yeah.mp3" )
  waSound = loadSound( "wa.mp3" )
}

function sceneIntro() {
      // Start Screen

      // Create the white canvas over the background border
      var canvasWidth = width - 156
      var canvasHeight = height - 80

      fill( 254, 207, 65 )
      rect( 78, 80, canvasWidth, canvasHeight )

      // Title
      textSize( 120 )
      textAlign( RIGHT, RIGHT );
      fill( 120, 71, 179 )
      text( "Chicka", 1050, 200 )
      text( "Chicka", 1050, 300 )
      fill( 88, 203, 181 )
      text( "Boom Boom", 1050, 400 )
      text( "Game", 1050, 500 )

      // Image
      image( treeIntroImage, -10, 70 )

      // Start Button
      fill( 204, 59, 76)
      rect( 750, 600, 300, 100)
      textSize( 60 )
      fill( 255, 255, 255)
      text( "START", 1000, 670 )

}

function sceneCongrats(){
  // Ending Screen

  // Create the white canvas over the background border
  var canvasWidth = width - 156
  var canvasHeight = height - 80

  fill( 255,255,255 )
  rect( 78, 80, canvasWidth, canvasHeight )

  // Congradulations Text
  textSize( 120 )
  textAlign( RIGHT, RIGHT );
  fill( 120, 71, 179 )
  text( finalWinningText, 1050, 300 )
  fill( 88, 203, 181 )
  text( "You Won", 1050, 400 )

  // Show how long it took based on number of frames accumulated
  var calulatedSeconds = Math.floor( numberOfFrames / 60 )
  textSize( 50 )
  fill( 253, 155, 30 )
  text( "In  " + calulatedSeconds + "  seconds", 1050, 500 )

  // Tree Image
  image(treeIntroImage, -10,70)

  // Play Again Button
  fill(204,59,76)
  rect(650, 600, 400, 100)
  textSize( 60 )
  fill(255,255,255)
  text("Play Again", 1000, 670)
}

function randomizeAlphabet(){
  // Assign the position on the canvas, rotation and color values for each letter
  for ( i = 0; i < 26; i++ ){
    var randomColor = Math.floor( random( 0, 9 ) )

    // Pick a random position on the canvas to go into
    var randomPosition = Math.floor( random( 0, positions.length ) )

    // Take the positions stored in the position array and store them
    // as the character's position
    var characterPositionX =  positions[ randomPosition ][ 0 ]
    var characterPositionY = positions[ randomPosition][ 1 ]

    // Push that posision into the alphabet array
    alphabet[i][1] = characterPositionX
    alphabet[i][2] = characterPositionY

    // Randomly rotate the letter and store that in the array
    var randomRotation
    var positiveOrNegative = random( 0, 1 )
    if (positiveOrNegative > .5 ){
      randomRotation = random( 0, 120 )
    }
    else{
      randomRotation = -random( 0, 120 )
    }
    alphabet[ i ][ 3 ] = randomRotation

    // Randomly assign a color from the stored color palette
    alphabet[ i ][ 4 ] = colorPalette[ randomColor ]

    //remove the used position from the array
    positions.splice(randomPosition, 1)
  }
}

function setup() {
  createCanvas( 1200, 750 )
  noStroke()
  angleMode( DEGREES )
  background( 253, 155, 30 )

  // Setup the array of characters with placeholder values for x, y, color, and rotation
  // using the ascii character code, in case I want to do lowercase or other characters
  for ( var z = 65; z < 91; z ++ ){
     var currentCharacter = String.fromCharCode( z )
     alphabet.push( [currentCharacter, 0, 0, 0, 0 ] )
  }
  // Set the first letter to be found in the game
  currentLetterToFind = alphabet[ 0 ]

  // Create the dots that surround the canvas
  drawCirclePattern()

  // Get the list of all the positions available on the canvas
  setupPositions()

  // Randomize the order of the alphabet and attach x, y, rotation, and color
  randomizeAlphabet()
}

function drawCirclePattern() {
  // Creates the dots that surround the canvas
  var numberOfRows = 19
  var numberOfColumns = 38
  var radius = 14
  var marginX = 34
  var marginY = 38
  var xPosition = 20
  var yPosition = 16
  var rotation = 0

  fill ( 215, 21, 101 )

  // Create each row
  for (j = 0; j < numberOfRows; j++ ){
    // Create each column's circle
    for (i = 0; i < numberOfColumns; i++ ){
      // Add the circle with a little bit of randomness for the Y position
      ellipse( xPosition, yPosition + ( random( -3, 3 ) ), radius, radius )
      xPosition += marginX
    }
    // Offset the even numbered rows
    if ( j % 2 == 0 ){
      xPosition = -41
    }
    else{
      xPosition = 20
    }
    yPosition +=  marginY
  }
}

function collectedArea(){
  // Create the area that indicates what letter needs to be found
  // Position on the canvas for the text to sit
  var abcPositionX = 150

  // The current letter that needs to be found
  var currentLetterIndex = alphabet.indexOf( currentLetterToFind )

  // The "Letters Found " text
  fill( 204, 59, 76 )
  rect( 0, 680, width, 70 )
  fill( 255, 255, 255 )
  textSize( 30 )
  text( "Letters Found: ", abcPositionX, 715 )

  // Position for the letters on the canvas
  abcPositionX = 280
  for (k = 0; k < currentLetterIndex; k++ ){
    text( alphabet[k][0], abcPositionX, 715 )
    abcPositionX +=35
  }
}

function draw() {
  textFont(font)
  // Determine which scence should be shown
  if (scene == 0) { sceneIntro() }
  else if(scene == 3){ sceneCongrats() }
  else{
    // The game screen
    // Create the white canvas over the border
    numberOfFrames++
    var canvasWidth = width - 156
    var canvasHeight = height - 80

    fill( 255, 255, 255 )
    rect( 78, 80, canvasWidth, canvasHeight )

    setupPositions()

    // Add in the tree
    image(treeImage, 160,-10)

    // Draw the letters on the page
    for ( i = 0; i < 26; i++ ){
      // Keep track of what the current letter to find is
      var currentLetterIndex = alphabet.indexOf(currentLetterToFind)

      // If the letter has already been found, make it gray
      if (alphabet.indexOf(currentLetterToFind) > i){
        fill( 220 , 220, 220 )
      }
      else{
        // Assign the stored color value
        fill( alphabet[i][4] )
      }

      // Text settings
      textSize( 120 )
      textAlign( CENTER, CENTER );

      // This is where we rotate the letters
      // Move the origin of the canvas to the center of the character
    	translate(alphabet[i][1], alphabet[i][2]);
    	rotate(alphabet[i][3])

      // Set the letter, position is 0,0 becuase the orgin shifted to its stored
      // coordinates
      text( alphabet[i][0], 0, 0)

      // Reset the translation and rotation, because those are accumulated
      rotate(-alphabet[i][3])
      translate(-alphabet[i][1], -alphabet[i][2])

      // Remove the used position from the array, can't have more than one letter
      // in the same spot
      positions.splice(alphabet[i][3], 1)
    }
    // Add in the "Letters Found" area
    collectedArea()
  }

}

function mouseClicked(){
  // Check if it's the intro splash scene
  if (scene == 0){
    // Only allow clicking on the button
    if (mouseX > 750 && mouseX < 1050 && mouseY > 600 && mouseY < 700){
      setup()
      scene = 1
    }

  }
  // Check if it's the congrats scene
  else if (scene == 3){
    // Only allow clicking on the button
    if (mouseX > 750 && mouseX < 1050 && mouseY > 600 && mouseY < 700){
      setup()
      scene = 1
      // Reset the timer
      numberOfFrames = 0
    }

  }
  else{
    // During the game, check if the user has clicked the current letter
    var clickDistance = dist ( mouseX, mouseY, currentLetterToFind[1], currentLetterToFind[2] )
    if (clickDistance < 50 ){
      // Play a sound that you found the right letter
      yeahSound.setVolume(1);
      yeahSound.play();
      // Increment the next letter to be found
      var currentLetterIndex = alphabet.indexOf(currentLetterToFind)
      var nextLetterIndex = currentLetterIndex + 1
      if (nextLetterIndex == 26){
        // All the letters were found!
        // Pick a random congrats text
        finalWinningText = congratsText[Math.floor(random(0,3))]
        // Go to the congrats scene
        scene = 3
        setup()
      }
      else{
        // User is still finding the letters, go find the next letter
        scene = 1
        currentLetterToFind = alphabet[nextLetterIndex]
      }
    }
    else{
      // Didn't find the right one
      waSound.setVolume(1)
      waSound.play()
    }
  }
}

Document Metadata


Raw File With Signature: unofficial-chicka-chicka-boom-game-in-p5js.txt.asc

Hash of Raw File: 2c291afd2a64409f2c9dada0af834889646c39ed9879b9cce19864636b3be7d6

Ethereum Proof of Existance Transaction: 0x15ddbcc2edf79ac32c4584dce5bd4994f4a966bfaa53f28d169d7922ff6caad4

This text is Creative Commons:Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)