O'Reilly

Build a Street Fighter Demo with CSS Animations and JavaScript

By on  

I recently learned a cool technique from Simurai about how to animate PNG sprites with the CSS3 animations' steps() property. The main idea in this technique is to "recreate" some kind of animated GIF but with the tiles of a PNG sprite.

As with everyone I know, I played to Street Fighter in my childhood and when I saw this ... guess what popped in my head?

Check out this Pen!

If the pen doesn't render above, click here to see it in action.

Let's Create the First CSS Move

We'll start with the punch (see in the sprite bellow it's the third one). First we need to open Photoshop to create the sprite. Make all images the same size (these ones are 70px width and 80px height). There is a good app called Texture Packer which can help in creation of game sprites. Try to find the dimensions of the biggest of your frames and use these dimensions for your grid. At the end you'll get something like this:

Then we need to set up a DIV for Ken which will receive our punch move (and all our other future moves):

/* html */
<div class="ken"></div>
/* css */
.ken { 
    width:70px; height:80px; /* exactly the size of an image in our sprite */
    background-image:url('../images/sprite.png'); 
}

Let's assume vendor prefixes are implicitly working. Now we can declare the punch animation like this:

/* css */
.punch { 
    animation: punch steps(4) 0.15s infinite; 
}
@keyframes punch {
    from { background-position:0px -160px; }
    to { background-position:-280px -160px; }
}

What we just did is apply an animation (punch) to a class name (.punch) which basically animates background-position from 0px to -280px (on x axis). This animation will be broken into 4 parts (steps(4) which corresponds to the punch's 4 images), and it will take 0.15 second to perform; then it will start over infinitely.

Finally we need a way to add/remove the .punch class name on DIV.ken when another key is pressed.

/* javascript */
$(document).on('keydown', function(e) {
    if (e.keyCode === 68) { // 68 is the letter D on the keyboard
        $('.ken').addClass('punch');
        setTimeout(function() { $ken.removeClass('punch'); }, 150);
    }
});

We used jQuery to addClass('punch') if the letter "D" is pressed and then remove it after a setTimeout (a delay) of 150ms (remember our css animation takes exactly 0.15s wich is the same as 150ms). That's pretty much all you need to know to create a lot more moves.

Take it to the Next Level with SASS

If you pay attention to what we are doing, you'll notice we have some values that never change (width/height of an image in the sprite), and, after you've created some other moves, you'll notice you have a lot of code duplication which will be difficult to read and maintain in the future. SASS can help us DRY all this mess!

First we need basic @mixins like animation() and keyframes():

@mixin animation($params) { 
    -webkit-animation:$params;
    -moz-animation:$params;
    -ms-animation:$params;
    animation:$params;
}
@mixin keyframes($name) { 
    @-webkit-keyframes $name { @content }
    @-moz-keyframes    $name { @content }
    @-ms-keyframes     $name { @content }
    @keyframes         $name { @content }
}

We need to store image width / height values and SASS variables exist for this reason:

$spriteWidth:70px;
$spriteHeight:80px;

And finally we can mix those together to create a complicated new mixin which will declare moves and handle correct calculation of background positions for us:

@mixin anim($animName, $steps, $animNbr, $animParams){
    .#{$animName} { 
        @content;
        @include animation($animName steps($steps) $animParams); 
    }
    @include keyframes($animName) {
        from { background-position:0px (-$spriteHeight * ($animNbr - 1)); }
        to { background-position:-($spriteWidth * $steps) (-$spriteHeight * ($animNbr - 1)); }
    }
}

Now you can create a new move with a single line of code:

$spriteWidth:70px;
$spriteHeight:80px;

/* punch */
@include anim($animName:punch, $steps:3, $animNbr:3, $animParams:.15s infinite);
/* kick */
@include anim($animName:kick, $steps:5, $animNbr:7, $animParams:.5s infinite);
/* hadoken */
@include anim($animName:hadoken, $steps:4, $animNbr:1, $animParams:.5s infinite);
...

$animNbr is very important: the calculation is based on this number. In fact it's just the moves count in the sprite. Our first example was the punch, right? And in our sprite it's the move number 3. The kick is number 7, etc.

Add Collision Detection for the Fireball

We need a very fast loop for collision detection. It will test the fireball position (offset) every 50 milliseconds, compare it to something else position (here we test the end of the screen). If the fireball's left position is bigger than the window width, then it means the fireball overtaken the screen so we immediately apply an .explode class.

Here's how I did it; it's not perfect but it works very well:

var $fireball = $('<div/>', { class:'fireball' });
$fireball.appendTo($ken);

var isFireballColision = function(){ 
    return $fireballPos.left + 75 > $(window).width();
};

var explodeIfColision = setInterval(function(){
    $fireballPos = $fireball.offset();
    if (isFireballColision()) {
        $fireball.addClass('explode'); 
        clearInterval(explodeIfColision);
        setTimeout(function() { $fireball.remove(); }, 500); 
    }
}, 50);

What's Next?

We could easily add some sound effects, background music, sprite another character, mix this up with web RTC to allow multiple computers to control characters (I don't know something with NodeJS and Socket.io or maybe the cool new Meteor framework); that's what I love with web development: it's almost limitless.

Julien Knebel

About Julien Knebel

I'm a freelance front-end developer living and coding in Paris, France. I love combining design techniques with web development technologies.

Track.js Error Reporting

Recent Features

  • Creating Scrolling Parallax Effects with CSS

    Introduction For quite a long time now websites with the so called "parallax" effect have been really popular. In case you have not heard of this effect, it basically includes different layers of images that are moving in different directions or with different speed. This leads to a...

  • CSS Animations Between Media Queries

    CSS animations are right up there with sliced bread. CSS animations are efficient because they can be hardware accelerated, they require no JavaScript overhead, and they are composed of very little CSS code. Quite often we add CSS transforms to elements via CSS during...

Incredible Demos

  • Highlight Table Rows, Columns, and Cells Using MooTools 1.2.3

    Row highlighting and individual cell highlighting in tables is pretty simple in every browser that supports :hover on all elements (basically everything except IE6). Column highlighting is a bit more difficult. Luckily MooTools 1.2.3 makes the process easy. The XHTML A normal table. The cells...

  • Implement jQuery&#8217;s hover() Method in MooTools

    jQuery offers a quick event shortcut method called hover that accepts two functions that represent mouseover and mouseout actions. Here's how to implement that for MooTools Elements. The MooTools JavaScript We implement hover() which accepts to functions; one will be called on mouseenter and the other...

Discussion

  1. stefan

    very cool dude !

  2. Tony

    Alright Adam, this is good stuff here, thanks for sharing

  3. Tony

    Um I meant David lmao

  4. towry

    Great stuff !!!!

  5. towry

    comment post without refreshing??

  6. Mitch

    This is sweet. I saw this thing last week and thought it was incredibly awesome. I’m probably going to link to this post in DZone’s link roundup.

  7. Très beau travail Julien, merci pour le partage :)

  8. WOW!

    that is awesome work!

  9. This is such a detaild demo and I love the free vectors that you showed. I will try to put it together since I`m a new in game developing , and see how it works. Best wished dude.

  10. Hah! I don’t think I might need to use this any time soon, but it’s nonetheless a wicked way to use CSS & company :-)

    One thing though, the CodePen demo does not play nicely with arrows–the page scrolls indeed (yes, a laptop doesn’t have as much screen space as a huge monitor!)

  11. Daryl

    Wowwwwwwww great job David !!!!!!!!!!

  12. Bien vu dude ! ;)

  13. Barun Saha

    That’s superb! Hope to try it soon.

  14. Wow! Cool! Great job!

  15. Mind blowing, i visited on your website cause i am crazy to learn.. But i bit upset cause JavaScript sections is not in English language, and hard to understand and learn :(

    @Julien Please add some kind of translator or convert it in Eng. language, so that the snippet by you will be helpful for everyone :)

    • Argh sorry bro i didn’t except this demo would attract so much people. I’ll let you know asap cheers!

  16. I am not a game fan but doing this using CSS3 is amazing. Good job!

  17. Its really cool. Hats of to you and keep it up. There is so much to learn.

    Arun

  18. Tony Brown

    The reverse kick sprite does not complete it’s animation???

  19. Great Job David … Just keep it up , bunch of techniques learned by you … thank you .

  20. MrK

    this is a nice demostration of why html5 should never be an standar for games.

  21. wOw great JOB man ,super cool stuff. sharing is good thnx man

  22. Rather a nice article. I am going to try it myself a couple of times. :-)

  23. Elvis

    Oh Shit!!!!! floor is equal to Bison, levitating, that pig!

  24. Cauê
    • OMG man it rocks! On the MDN Demo home page :D You made my day. Happy to see people taking it a step further. Well done.

  25. Catz

    Explain please where did you get these sprites? Manual extraction?

  26. Will be my default Easter Egg! Nice!

  27. I have just started using similar techniques for animations myself, however I’ve run into a problem that your demo suffers from as well…browser zoom. When zooming all my responsive images work as they should do, but because of the nature of the sprites the animations break. I’m going to work on a solution but just wondered whether you had realised this and worked out a solution yourself?

    • Nop had the same issu, but I don’t feel like it’s a real problem after all? I guess… :)

  28. Agustin

    Just found this today and thought it might be a nice addition to the post. Multiple animated gif backgrounds from arcade games: http://abduzeedo.com/wicked-fighting-game-background-gifs

    • Man it’s totally rocking awesome!! :D I wish I could have time to continue my demo with one of these gif backgrounds.

  29. Amazing code! If you don’t mind I may try a similar project and reference this demo.

  30. Sure! I’d love to see that when it’s ready :)

  31. Ikraam

    Dude you’ve opened doors to many innovations here. Well done! thank you!!!

  32. Ikraam

    How would the code look like if you wanted the user to execute certain commands before executing a move? e.g down,forward,down,froward + hit = shoryuken. Thanks in advance it is highly appreciated as always! :)

  33. Doesn’t work in IE9. :-) But,… neither does my website,… so I’m okay with that. LOL

  34. Thanks David

    I built this site
    http://havenramos.com/
    based on the techniques outlined on your post. I tweaked the examples to make it responsive.

  35. Amazing code!
    I just discovered TexturePacker.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Chris Coyierâs Favorite CodePen Demos II

    Hey everyone! Before we get started, I just want to say it’s damn hard to pick this few favorites on CodePen. Not because, as a co-founder of CodePen, I feel like a dad picking which kid he likes best (RUDE). But because there is just so...

  • GSAP + SVG For Power Users: Motion Along A Path

    Now that the GreenSock API is picking up steam, there are many tutorials and Getting Started guides out there to provide good introductions to the library, not to mention GreenSock’s own Forum and Documentation. This article isn’t intended for beginners, but rather a...

  • Copy a Directory from Command Line

    Copying a directory for the sake of backup is something I do often, especially when I'm trying to figure out why something isn't working when I use an external library.  I'll copy the directory structure as a backup, mess around with the original source until I find a solution,...

  • Hotjar &#8211; All-in-one Analytics and Feedback

    Website analytics are a massive business -- the more data you can collect with regard to your users' behaviors on your site, the more you can increase and maximise conversion...and increased conversion is always good.  Sometimes increase conversion means more money, improved user experience, viewer retention,...

  • Crafting a 3D React Carousel

    There is something in me that is amazed but beautiful 3D interfaces. And it doesn’t matter whether they’re functional like Gyroscope features menu, technology demonstrators like the amazing periodic table demo from famous or they’re artistic representation pushing the limits of...