Friday, March 25, 2016

Thinkful....and Street Fighter!

TL;DR - I built a Street Fighter page that does a few fun things.  The finished product is here:

http://ryanscottbuchholtz.github.io/jquery-streetfighter/

Last week I began my enrollment with Thinkful, an on-line 'bootcamp-esq' coding academy.  It's another step forward in my mission to become a front-end developer and I couldn't be more excited.

A number of the initial projects were fairly rudimentary after all the work I've done with TeamTreehouse, but they were fun nonetheless.  Last night was a pretty cool jQuery exercise - cool enough that I thought I should share it with you.

The objective:  given a number of .gif & .png assets, write the code to a) display different images when the mouse hovers over them, b) create actions that happen with the click of the mouse and c) generate a specific action when pressing a keyboard key.  Not rocket science, but a challenge for a newbie and deeply satisfying when accomplished!

Street Fighter was a video game from the late 80's and continues to be one of the most popular video games of all time.



In the game, the main player controlled a character named Ryu.  He was a martial artist and had a number of specific attacks, executed with button/pad combinations.  One of them, Hadouken, is a key accomplishment of this jQuery exercise.  Write the code that moves Ryu into the attack mode and makes him throw a Hadouken.

To tackle this project I wrote pseudo-code that looked like this:


  1. Create a div that holds the image assets, while as default, displays ryu in 'standing still' position.
  2. Create a second div that positions the Hadouken in the initial place where it would begin to be thrown from Ryu's hands, but do not display unless a specific activity is initiated.  
Easy enough!  Taking advantage of the CSS feature display:none I was able to basically 'layer' 4 different images of Ryu while displaying only one.  The HTML looks like this:


<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Streetfighter</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
  <div class="main">
    <div class="ryu">
      <div class="ryu-still ryu2"></div>
      <div class="ryu-ready ryu2"></div>
      <div class="ryu-throwing ryu2"></div>
      <div class="ryu-cool ryu2"></div>
    </div>
   </div>
</body>
</html>

Inside of my main div I've created another div called ryu.  Further down, inside of that div are 4 separate divs, each designed to hold a specific Ryu image.  The CSS for those 4 divs looks like this:

.ryu,
.ryu-ready,
.ryu-still,
.ryu-throwing,
.ryu-cool {
  width: 659px;
  height: 494px;
}

.ryu-ready,
.ryu-throwing,
.ryu-cool {
  display: none;
}

.ryu2 {
  background-repeat: no-repeat;
}

.ryu-ready {
  background: url('../images/ryu-ready-position.gif');
}

.ryu-still {
  background: url('../images/ryu-standing-still.png');
}

.ryu-throwing {
  background: url('../images/ryu-throwing-hadouken.png');
}

.ryu-cool {
  background: url('../images/ryu-cool.gif');
}

Width/height and background has been defined for each div.  Additionally, you see that three of the divs have a display:none, which means, currently, only one div will display when the page is visited - .ryu-still.




Now it's time to get this Hadouken appropriately positioned.  First, I created another div just for the Hadouken, immediately under the <div class="ryu">.  It looks like this:  

<div class="hadouken"></div>

Nothing special, just a 'box' to store the hadouken.gif.  The challenge, however, is the position the Hadouken so that it is right by Ryu's outstretched hands when he is ready to throw it.  His position would look like this:



This can be accomplished using position: absolute and assigning top: and left: values.  Remember, using position: absolute only works when the parent element has a position: relative.

Using Chrome Developer Tools you can easily change the values in top: and left: until your get the Hadouken to where it belongs.  The initial position of the Hadouken looks like this:




With Chrom Dev Tools, seen below, you can start to experiment with the top: and left: position of the div until it is where you need it to be.




When all is said and done, the css for the Hadouken looks like this:

.hadouken {
  background: url('../images/hadouken.gif');
  width: 156px;
  height: 90px;
  display: none;
  float: left;
  position: absolute;
  top: 168px;
  left: 617px;
  z-index: 10;

}

A z-index of 10 is used to make sure the Hadouken remains above other elements on the page. And, remember, the default display of the Hadouken is hidden.  We only want to see it when the user does something.

Easy breezy!  I haven't stepped out of HTML and CSS, so this is all, at this point, second nature.  But now, it's time to dabble in jQuery and try to create some interactivity for this bad boy!  More pseudo-code:


  1. When mouse hovers over Ryu, hide still Ryu and show ready Ryu
  2. When mouse no longer hovers over Ryu, hide ready Ryu and show still Ryu
After a little research focused on 'hover', I discovered a jQuery method called .mouseenter.  The method binds an event handler to be fired when the mouse enters an element.  In this case, the element we want to trigger the event handler is <div class="ryu"></div>, which is the parent element to the 4 different Ryu images.  Sidebar:  I think hover can be used, but mouseenter, being a little more robust, made more sense to me at this point.


Into my app.js file to start writing some new code!  Remember to link this sheet to your HTML, otherwise, all the code you write will do nada, zippo!

As with all jQuery, it is advisable to wrap your code inside of:

$(document).ready(function() {

});

I know there are different philosophies, specifically re: where you put your <script> tags in your HTML.  If the <script> is put just before the </body> tag it is assumed that your document will be 'ready', thus negating the need for the code above.  I use it anyways, just in case.  It's 2 lines.  Doesn't hurt.

Converting my pseudo-code into real code yielded the following:

  $('.ryu').mouseenter(function(){
    $('.ryu-still').hide();
    $('.ryu-ready').show();
  
  }).mouseleave(function(){
    $('.ryu-still').show();
    $('.ryu-ready').hide();

  });

It couldn't be closer to a literal translation of my pseudo-code.  .mouseenter and one element hidden, one shown.  .mouseleave, the opposite.  Perfecto!

On to more pseudo-code to tackle the next goal of Ryu, which would be throwing the Hadouken when he is clicked on with the mouse.


  1. On mouse click, hide all images of Ryu, but show the image of Ryu throwing.
  2. On mouse click, show the Hadouken and have it move from left to right.
  3. When mouse is unclicked, hide all image of Ryu, but show the image or Ryu still.
Digging into jQuery, I found the methods called .mousedown and .mouseup.  These seemed perfect to accomplish the goals.  Similar to .mouseenter and .mouseleave, they bind an event handler to the event.

My code, using these methods, initially looked like this:


  $('.ryu').mousedown(function(){
    playHadouken();
    $('.ryu-still').hide();
    $('.ryu-ready').hide();
    $('.ryu-throwing').show();
    $('.hadouken').show();
  });

  $('.ryu').mouseup(function(){
    $('.ryu-still').show();
    $('.ryu-ready').hide();
    $('.ryu-throwing').hide();
    $('.hadouken').hide();
  });

Upon running in my browser, a few things work - specifically around which image of Ryu is displayed and then hidden.  But Hadouken caused some problems.  It was displayed and hidden, but it didn't move.  On to do some more digging in the jQuery API documentation.

Clicking on Effects, the first technique listed is .animate().  Seems like a good place to start.

The .animate() method has this basic syntax:

.animate( properties [, duration ] [, easing ] [, complete ] )

Digging a little deeper:


  • Properties:  An object of CSS properties and values that the animation will move toward.
  • Duration: A string or number determining how long the animation will run.
  • Easing: A string indicating which easing function to use for the transition.
  • Complete: A function to call once the animation is complete, called once per matched element.
So back to handy-dandy pseudo code I go, in an attempt to make sense of this method and use it for the first time.


  1. Move the Hadouken image to the right, which means adjusting the left: value in the css and setting it equal to a place where the animation should end.
  2. Get the Hadouken to its ending position in some amount of time.  500 = 500 milliseconds.  The higher the number, the slower the animation.
  3. When the Hadouken image has reached the end it should be hidden and the left: value should be reset to the initial value specified in the css.
OK.  That all seems to make sense.  Time to convert it to jQuery code, and it looked something like this (all inside of the .mousedown method):


 $('.hadouken'.finish().show().animate(
      {'left': '1020px'},   (end goal of element)
      500,                  (length of animation)
      function() {          (complete function)  
        $(this).hide();
        $(this).css('left', '617px');
      }
    );
  });

Because of the function() run inside of .animate, we no longer need to .hide() the Hadouken element on the .mouseup method.  It happens as soon as the animation is complete, per the function written inside of the .animate() method.  Wow!  Awesome.  

One outstanding issue is the completion of the .animate when the user clicks repeatedly.  In doing so, the animation will continue, regardless of how many times the user clicks on Ryu.  To fix that, the .finish() method is called before .show().  .finish() will stop the currently running animation and reset CSS to the settings in the last function inside of .animate().  Voila!

With that code I now have a cool Martial Artist throwing some mean Hadouken.  Very, very cool.





But there is more!  There is still one image of Ryu that has remained hidden.  Ryu looking damn cool. 




The goal is to only display this image when a certain key (the letter x) is pressed on a keyboard.  Hi ho, hi ho it's off to jQuery API I go!

Knowing what I know, it's safe to presume there must be some kind of .keydown() and .keyup() method in jQuery and, what would you know, there sure is!  And they work an awful lot like .mouseenter(), .mouseleave(), .mousedown() and .mouseup().

Because we only want the specific activity to occur when the 'X' key is pressed, it appears that you can pass an event object (e) to the handler function and examine it.  That code looks a little something like this:

  $(document).keydown(function(e){
    if(e.keyCode === 88) {
      $('.ryu-still').hide();
      $('.ryu-throwing').hide();
      $('.ryu-cool').show();
    }
  });
  $(document).keyup(function(e){
    if(e.keyCode === 88) {
      $('.ryu-still').show();
      $('.ryu-throwing').hide();
      $('.ryu-cool').hide();
    }

  });

e, the event object is passed to the handler function() and is then examined.  Because every button on your keyboard has a numeric value, I had to look that up and found that the letter x is equal to 88.  So, the if statement must be true for the remains jQuery code to be executed.


There is one last feature that would be awesome - playing the trademark sound when the Hadouken is thrown.  Some code needs to be structured that is executed on the .mousedown() method.

There is a .mp3 stored in a sound folder, of that specific sound.  HTML5 has an <audio> tag and it makes sense to plug that in so we can use the file when we need it.  To do so, inside of the HTML file, and just above the <script> tags, I added in the following:

<audio id="hadouken-sound" src="sound/hadouken.mp3"></audio>

It's been given an id so that we can reference the file using jQuery.

Because, when working with an audio file there are a number of methods that must be called, it makes sense to wrap all of those into a single function, and then call that function when it makes sense - specifically, when Ryu is clicked on.  That function looks like this:

function playHadouken() {
  $('#hadouken-sound')[0].volume = 0.5;
  $('#hadouken-sound')[0].load();
  $('#hadouken-sound')[0].play();

}


Because we are working with an embedded audio file, we are not using jQuery methods, but rather JavaScript being called on DOM elements.  By using $('#hadouken-sound')[0] we are using a jQuery syntax, but it could be typed like this, too:

document.getElementById<'hadouken-sound').volume=0.5

The [0] specifies the first element with the id #hadouken-sound, and is unnecessary in the JavaScript syntax because there should only ever be one element per ID name.

I've named the function playHadouken and, inside of the function, three things happen, all of which are using .  First, the volume level is set (range is 0 to 1), then the .load() method is called, which is simply a method which fetches data from the server.  Lastly, .play() is called, which plays the audio file. 

The function is now placed inside of the .mousedown method, and the very beginning of the function.  The finalized function now looks like this:

  $('.ryu').mousedown(function(){
    playHadouken();
    $('.ryu-still').hide();
    $('.ryu-ready').hide();
    $('.ryu-throwing').show();
    $('.hadouken').finish().show().animate(
      {'left': '1020px'},
      1000,
      function() {
        $(this).hide();
        $(this).css('left', '617px');
      }
    );

  });


And that's it!  I know there are some quirks, and I will report back when I a) find them and b) fix 'em!

Happy Coding!

No comments:

Post a Comment