DEV Community

loading...
Cover image for Eggasaurus: JavaScript SPA Browser Game

Eggasaurus: JavaScript SPA Browser Game

lizlaffitte profile image Liz Laffitte ・5 min read

This post will review how I handled gameplay visualization in a JavaScript SPA browser game I built.

When working through the Software Engineering bootcamp at Flatiron, I built a JavaScript SPA as a portfolio project.

I decided to build a game that was simple enough for my (then) three-year-old to play. (Spoiler alert: he was not impressed. ) The resulting game, Eggasaurus, came from a combination of my love of browser games and my son’s fascination with dinosaurs. Users can create dinosaur pets and interact with them.

Over time, the dinosaurs get hungry, tired and bored. This is shown visually by what I call mood meters. Moods decrease constantly, over time. The player can click the care buttons to "fill" the corresponding mood meter back up.
Screenshot of Eggasaurus game

I accomplished this with a combination of HTML (divs), CSS (background colors, width) and JavaScript (setInterval, event listeners, HTML DOM style property).

Mood Meter HTML & CSS

The mood meters are built with divs. Each mood meter (there are three total: hunger, happiness and tiredness) starts with a div with the class meters.

This parent div has a border-radius, border and height to form the actual meter. If this was an old-school thermometer, it would be the outer, glass piece of the thermometer.

Screenshot of abovementioned mood meters

<div class="meters">
...
</div>
Enter fullscreen mode Exit fullscreen mode
div.meters {
  height: 20px;
  border: 4px solid gray;
  border-radius: 30px ;
  margin-bottom:5px !important;
}
Enter fullscreen mode Exit fullscreen mode

Then there is a child div with a class green and and id according to what mood it represents. It also has a width value that we're retrieving via string interpolation. This div represents the value or level of the mood. Continuing our thermometer example, it is analogous to the mercury level in the thermometer (if that mercury was green).
Screenshot of green div mood levels

<div class="meters">
  <div id="hunger-meter" class="green" style="width:${this.hungerP}">
    ...
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

The CSS gives the mood level div the same height as its parent; a left float so it appears to grow and shrink from the left; a background color to visually display the level value; and a border-radius to match our illusion of the meter.

  div.meters div.green{
    height: 20px;
    float: left;
    border-radius: 30px; 
    background: green;
  }

Enter fullscreen mode Exit fullscreen mode

Finally, there is a grandchild and a great-grandchid div. These hold the sprite that matched the mood the meter represents. So, chicken leg for hunger, in this case.

<div class="meters">
  <div id="hunger-meter" class="green" style="width:${this.hungerP}">
    <div class="sprite-holder">
     <div class="sprite"></div>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

The CSS gives the sprite holder a background color, keeping the mood level from showing through. It also places it perfectly in our top-level mood meter div; and sets the background position depending on the meter, so the correct icon is displayed.

div.sprite{
    background: url('images/meter_icons.png');
    width: 20px;
    height: 20px;
    background-size: 58px auto;
}
div.sprite-holder{
    width: 25px;
    background-color: #fff;
    float: left;
    position: relative;
    left: -5px;
    top: -5px;
    padding: 5px;
    border-radius: 50%;
}
div#hunger-meter div.sprite{
    background-position: 0px 0px;
}
Enter fullscreen mode Exit fullscreen mode

To handle the interactions with these mood meters, there are corresponding care buttons. Each button has a background image and id that corresponds with the mood meter it will affect.

Screenshot of care buttons

<div id="care-btns">
  <button id="feed" data-id="${this.id}"></button> 
  <button id="play" data-id="${this.id}"></button> 
  <button id="nap" data-id="${this.id}"></button>
</div>
Enter fullscreen mode Exit fullscreen mode

Mood Meter JS

The JavaScript takes care of changing the width of the mood levels with the use of setInterval, the HTML DOM style property and event listeners.

setInterval

First, when the game loads or a dinosaur is selected, moodTimer is called. This function accepts a Dino object id and uses setInterval to repeatedly call a function decreaseMoods on the Dino instance with the passed id.

function moodTimer(dinoId){
    newMoodAdjust = window.setInterval(() => {Dino.findDino(dinoId).decreaseMoods()}, 1000)
}
Enter fullscreen mode Exit fullscreen mode

The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds).

In newMoodAdjust, decreaseMoods will be called on the Dino instance, every 1000 milliseconds.

The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.
-- W3Schools

If a user logs out or selects a different dinosaur to play with, clearInterval is called.

clearInterval(newMoodAdjust)
Enter fullscreen mode Exit fullscreen mode

If a user just selected a new dino, moodTimer() is called again, and it is passed the new dino id.

Decreasing Moods (HTML DOM style)

The newMoodAdjust calls the function decreaseMoods every second. This function is a Dino class method. It checks to see if a Dino instance's hunger, happiness and tiredness properties are above 0. If so, it calls the corresponding class methods hungry(); bored(); or tired(). These methods adjust the value of the Dino instance's properties and call another class method.

    decreaseMoods(){
        if(this.hunger > 0){
            this.hungry()
        }
        if(this.happiness > 0){
            this.bored()
        }
        if(this.tiredness > 0){
            this.tired()
        }
    }

    hungry(){
            this.hunger -= 0.5
            this.adjustHungerMeter()
    }

    bored() { 
            this.happiness -= 0.5
            this.adjustHappinessMeter()
    }

    tired() { 
            this.tiredness -= 0.5
            this.adjustTirednessMeter()
    }
Enter fullscreen mode Exit fullscreen mode

Remember that we are displaying the Dino's hunger, happiness and tiredness property values to the player visually by adjusting the width of the green div. Zero is our floor. Once those properties and the width of the green div reaches zero, we don't need to make any more adjustments.

Our adjust class methods retrieve the appropriate green div and set its width equal to the Dino's hunger, happiness or tiredness property value, using the HTML DOM style property.

this.hungerP is a getter that translates the hunger property to a percent string. So if this.hunger // 50 then this.hungerP // "50%"


    adjustHungerMeter(){
        const hungerMeter = document.getElementById("hunger-meter")
        hungerMeter.style.width = this.hungerP
    }

    adjustHappinessMeter(){
        const happinessMeter = document.getElementById("happiness-meter")
        happinessMeter.style.width = this.happinessP
    }

    adjustTirednessMeter(){
        const napMeter = document.getElementById("tiredness-meter")
        napMeter.style.width = this.tiredP
    }
Enter fullscreen mode Exit fullscreen mode

Increasing Moods (Event listeners & style)

When the game is loaded and a dino is selected, the SPA calls moodListeners().

This function adds click event listeners to each of our care buttons.

function moodListeners(){
    document.getElementById("care-btns").childNodes.forEach(btn => {
        btn.addEventListener("click", (e) => {
            const thisDino = Dino.findDino(e.target.dataset.id)
            if(e.target.id == "play"){
                thisDino.play()
            } else if(e.target.id == "feed"){
                thisDino.feed()
            } else if(e.target.id == "nap"){
                thisDino.nap()
            }
        })
    })

}
Enter fullscreen mode Exit fullscreen mode

When the buttons are clicked, different Dino class methods are called: play(); feed(); and nap().

Unsurprisingly, these methods increase the Dino instance's mood property values (setting them to 100), and call the adjust class methods discussed above.

    feed(){
        this.hunger = 100
        this.adjustHungerMeter()
    }
    play(){
        this.happiness = 100
        this.adjustHappinessMeter()
    }
    nap(){    
        this.tiredness = 100
        this.adjustTirednessMeter()
    }
Enter fullscreen mode Exit fullscreen mode

I decided to increase the moods to their max level, instead of increasing them by increments, because my son was getting frustrated with all the clicking.

Have you built any games just for fun? Are you a professional game developer or engineer? What are your favorite browser games? (Mine are CookieClicker and NGU Idle.)

Discussion (4)

pic
Editor guide
Collapse
jankapunkt profile image
Jan Küster

By the way @Liz you can add syntax highlighting to your code for improved readability by adding the name of the language / markup (javascript, html, css) after code begin tag (the three ` ).

Collapse
lizlaffitte profile image
Collapse
andrewchmr profile image
Andriy Chemerynskiy

Is it deployed somewhere so I can check it? 😊

Collapse
jankapunkt profile image
Jan Küster

Would like to play, too!