-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 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](/p5/ccbbg/index.html) ## 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](/p5/ccbbg/index.html). {{}} // 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() } } } {{}} [image1]: /support/unofficial-chicka-chicka-boom-game-in-p5js/1.png "Those are letters and they seem random" [image2]: /support/unofficial-chicka-chicka-boom-game-in-p5js/2.png "Yep, that’s the font and most of the colors from the book" [image3]: /support/unofficial-chicka-chicka-boom-game-in-p5js/3.png "Tree placed, border done, random letters in the correct palette" [image4]: /support/unofficial-chicka-chicka-boom-game-in-p5js/4.png "Holy seizure inducing moment — forgot that I need to only draw their positions once" [image5]: /support/unofficial-chicka-chicka-boom-game-in-p5js/5.png "Now that's much less insane" [image6]: /support/unofficial-chicka-chicka-boom-game-in-p5js/6.png "Letters are grayed out as you click on them" [image7]: /support/unofficial-chicka-chicka-boom-game-in-p5js/7.png "It looks a lot like the book cover" [image8]: /support/unofficial-chicka-chicka-boom-game-in-p5js/8.png "Wow! is right" [image9]: /support/unofficial-chicka-chicka-boom-game-in-p5js/9.png "That was the fastest time I got while doing this. As it went late into the night I was getting around 45 seconds" -----BEGIN PGP SIGNATURE----- iQIcBAEBAgAGBQJc8zChAAoJELuvSL2hxBDYEoYP/31rpsPZQRnhKpTcBq9nyVRC zVmZSwZdBnat8ZdzrhjO7Dx536JPVrlSKt9fuGcQ7eOzGHpPX6HqbOgw2ydKJzCu B+KldUcl/o2c/rAPTlhUA9N6nje/UHYe1JG/UF6caXhjppWHxSdqL64/CIZRK9Ph 8CcyubXoVoiGH80pdiKKa5Dj3hWv85Y/jxO0LoZB5aDl3zLP7mvWQaVcgHnDnKkj dFmCm6oLeCdpTkxKfZ853XdjLcoEMUQgoZamCJTmGbbJDnDZh2Vc2heCIXTG2tVM TKUjvBKzWUO0lnWLu5rKoeK38zKZ8IrHw0vgM/hZg7sHTWdtX5LIVrFmbDK1JSb9 OMlmE/NcCrBq96cY6shhziBhJL6XLAw8wJ1baWQYHLDUW8UExJtGQ9lqZm1tTN5e jkdo5PBTHzzAdDTqmZCqzZqQvTwGtQ4GP6aXWPoQY0EprZIsJ1Uo9sU3ycqERvqv ngV8ChoTBaLDsZm9PuVQPMKFMffZj+57DK7vyqTyr3GomjrIXvoEVMKXxKLu/67I uQNjzHF6XlODE6/UAbmiPs372lD0nQvCIc0jhNz9XqrCcF34ppMT5nrEI6/TRi50 3z4Fp26UYObilWABcUA33Vw7nfDYcZ1g58bA13EifQXJMlU/7em5mH0uPR9m+bJE BO5tVrcw/CxPByhcov47 =zA4R -----END PGP SIGNATURE-----