Skip to content

kvebox/sanrio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sanrio

Background and Overview

A Mario + Slime Rancher parody, Sanrio is a multilevel sidescrolling platformer where the player aims to reach the end of a level, collecting coins for maximum points, while deflecting enemy obstacles.

Functionality and MVP

  • Player creation and movement
  • Procedural level generation
  • Enemy obstacles
  • Player shooting
  • Items and powerups

Bonus

  • Level map
  • Enemy projectiles + diversified attacks
  • Boss battles

Wireframe

Menu

Menu

Gameplay

Art direction proof of concept

Technologies and Technical Challenges

Technologies

  • HTML/CSS/Javascript
    • Vanilla Javascript for game logic
    • HTML5 Canvas for rendering
  • Webpack to bundle various scripts

Technical Challenges

  • Character collision and boundaries against platform planes

  • Procedural level generation

  • Seamless character movement + interaction

  • Keyboard generation

    • For the controls instruction, I wanted to do something a little different than a literal image diagram. By making an interactive keyboard that instantly reflected the result, I thought learning the controls would come much more intuitively than having to memorize and reference the menu. controls keyboard
    • I wanted to reflect changes in the keyboard without having to load multiple static images and so ended up generating one in JS. I was able to avoid hard-coding html for each key by creating an array of arrays that represented each row of keys, with each number reflecting a different type of key (ex. 1 = numbers, 2 = letters) and relying on styling to create the correct configuration.
       generateControls() {
         let keyboard = 'QWERTYUIOPASDFGHJKLZXCVBNM';
         let nums = '1234567890';
         let number = 0;
         let letter = 0;
         let keyArray = [
             [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 12],
             [5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5],
             [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 0],
             [8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 5, 5, 5],
             [6, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 6],
             [0, 0, 0, 0, 7, 0, 0, 9, 10, 11]
         ];
         
         for (let row = 0; row < keyArray.length; row++) {
             let list = document.getElementById(`row${row}`);
             keyArray[row].forEach(key => {
                 let element = document.createElement('LI');
                 switch (key) {
                     // number keys
                     case 1:
                         element.setAttribute("class", "keyboardKey");
                         element.setAttribute("id", `key${nums[number]}`);
                         let textnode = document.createElement('P');
                         let textContent = document.createTextNode(`${nums[number]}`);
                         textnode.setAttribute("class", "keyNumberContent");
                         textnode.appendChild(textContent);
                         element.appendChild(textnode);
                         list.appendChild(element);
                         number += 1;
                         break;
                     // letter keys
                     case 2:
                         element.setAttribute("class", "keyboardKey");
                         element.setAttribute("id", `key${keyboard[letter]}`);
                         let letternode = document.createElement('P');
                         let letterContent = document.createTextNode(`${keyboard[letter]}`);
                         letternode.setAttribute("class", "keyLetterContent");
                         letternode.appendChild(letterContent);
                         element.appendChild(letternode);
                         list.appendChild(element);
                         letter += 1;
                         break;
                     case 7:
                         element.setAttribute("id", "space");
                         list.appendChild(element);
                         break;
                         
                     // arrow keys
                     // blank keys
    
                     default:
                         break;
                 }
             });
          }
       }

The reactive tutorial image was rendered in another HTML canvas, similar to the game but limited within the confines of the menu box, instantiating a new character and therefore able to map to the same controls but under a different state.

For scene generation, I wanted to be able to quickly generate multiple maps, without having to calculate the positions of each decorative element or platform. Using a text file where I could easily see layout provided a visual display for level creation.

text file

By parsing a text file, I was able to use a simple switch case statement to create objects mapping different categories of elements that could be manipulated in the main game code. Keeping track of how many lines have been parsed in an independent variable, I was then able to instantiate elements at different 'depths' with respective colors.

    for (let i = 0; i < level.length; i++){
      switch (level[i]){
          case ' ':
              width += WIDTHSPAN;
              break;
          case '\n':
              height += HEIGHTSPAN;
              width = 0;
              break;
          case '\t':
              width += WIDTHSPAN*4;
              break;
                case 'c':
              items[itemCount] = (new Coin(width, height - HEIGHTSPAN, itemCount));
              itemCount += 1;
              width += WIDTHSPAN;
              break;
          case 'T':
              deco.push(new Tree(width, height + HEIGHTSPAN, 1.5));
              width += WIDTHSPAN;
              break;
          case 't':
              deco.push(new Tree(width, height + HEIGHTSPAN, 1));
              width += WIDTHSPAN;
              break;
          case 'b':
              deco.push(new Bush(width, height + HEIGHTSPAN, randomInt(0,5)));
              width += WIDTHSPAN;
              break;
          case 'x':
              blockCount += 1;
              break;
          case 'X':
              blockCount += 1;
              let depth = 5;
              switch (height > 0) {
                  case (height >= 0 && height < 100):
                      depth = 5;
                      break;
                  case (height >= 100 && height < 200):
                      depth = 4;
                      break;
                  case (height >= 200 && height < 300):
                      depth = 3;
                      break;
                  case (height >= 300 && height < 400):
                      depth = 2;
                      break;
                  case height >= 400:
                      depth = 1;
                      break;
              }
              sceneObjects.push(new PlatformBuilder(blockCount, width, height, depth));
              width += WIDTHSPAN*blockCount;
              blockCount = 0;
              break;
          default:
      }
  }

Implementation Timeline

Day 1 - Wednesday (5/15/19)
  • Establish webpack + file structure set up
  • Establish Git workflow and branching strategies
  • Wireframes, art assets, review resources
Day 2 - Thursday (5/16/19)
  • Scene rendering + basic shapes
  • Placeholder platforms for character interaction
  • Player movement: left, right, jump
  • Game loop: player lives
Day 3 - Friday (5/17/19)
  • Procedural platform generation
  • Game loop: win/lose cycle
  • Player loses lives when falling off screen
Day 4 - Saturday (5/18/19)
  • Github pages deployment
  • Enemies and obstacles
  • Player shooting
Day 5 - Sunday (5/19/19)
  • Improve UX/UI
  • Items + powerups
  • *Bonus features: Enemy shooting
  • *Bonus features: Boss battles
Day 6 - Monday (5/20/19)
  • Production README

About

Frontend Mario parody, written in HTML/CSS/JavaScript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published