DEV Community

Cover image for Making Animated Sprites Interactive with JavaScript! -  (Digital Ocean Hackathon Blog: Part 2)
Jeff Puls
Jeff Puls

Posted on

Making Animated Sprites Interactive with JavaScript! - (Digital Ocean Hackathon Blog: Part 2)

If you haven't yet read my previous post in this series, I would highly recommend doing so before you continue with this post, things will make a lot more sense!

So, if all has gone according to plan, we now have our Link sprite marching bravely in place in the center of the screen. 10/10 graphics, but it falls short in the gameplay category. Luckily, interacting with our hero is only a couple HTML/CSS tweaks and a JavaScript file away!

Below are the new sprite sheets I will be using in this project, feel free to save them to your project directory and code along!

link left

link down

link right

link up

Modifying the HTML/CSS

Okay, we will start off super simple with a couple of tweaks to our original HTML file. In order to keep our spritely friend from wandering off screen, let's give him some boundaries.

To do this, simply wrap the sprite-view-frame div element with another div, this one with a class of container. The body of your HTML should now look as such.

<body>
  <div class="container">
    <div class="sprite-view-frame">
      <div class="sprite-renderer"></div>
    </div>
  </div>
  <script src="script.js"></script>
</body>
Enter fullscreen mode Exit fullscreen mode

That will do it for the HTML, now to change some things around in the CSS.

Let's begin with introducing a few new variables under :root. These are --sprite-top, --sprite-left and --sprite-sheet-url. The names of which should be a pretty obvious giveaway as to their purpose. Make sure your code looks like the following.

:root {
  --sprite-width: 64px;
  --sprite-height: 64px;
  --sprites: 2;
  --animation-length: 0.5s;

  /* these are the values that we will modify via JavaScript to interact with the sprite */
  --sprite-top: 50%;
  --sprite-left: 50%;
  --sprite-sheet-url: url("./link-spritesheet-down.png");
}
Enter fullscreen mode Exit fullscreen mode

Next, let's style our new container element. Make special note that it purposely does not fill the entire width of the <body>.

.container {
  width: 90%;
  height: 90%;
  position: relative;
  top: 5%;
  left: 5%;
}
Enter fullscreen mode Exit fullscreen mode

After that, we need to make a slight modification to the sprite-renderer. Here, we will replace the hard-coded background-image with the variable we set in :root

 background-image: var(--sprite-sheet-url); /* the sprite sheet */
Enter fullscreen mode Exit fullscreen mode

And then, move the animation property to a new selector, this time only being active when the sprite-renderer also has a class of .animating;

/* the sprite will only animate when the 'animating' class has been set */
.sprite-renderer.animating {
  animation: animateSprites var(--animation-length) infinite
    steps(var(--sprites));
}
Enter fullscreen mode Exit fullscreen mode

One last modification to note is the addition of a short transition-duration to the sprite-view-frame. This will help smooth out the movement of the sprite across the screen.

transition-duration: 0.05s; /* adding a short duration here will smooth out the animation when moving the sprite */

Enter fullscreen mode Exit fullscreen mode

That wraps up the modifications to the HTML/CSS!

Making it interactive with JavaScript

Alright, we made it to the meat-and-potatoes of the post once again! It's finally time to take this animation and turn it into an interactive "game" (I use that term very lightly here).

It will be assumed here that you have working knowledge of basic vanilla JavaScript

After creating a script.js file in your project directory, we need to start with setting up some variables. We need to gain access to our container element, as this will be our :root in CSS. We also need to be able to modify the classList of the sprite-renderer element, so we will grab that too.

// grab our elements to be modified
const root = document.querySelector(".container");
const spriteRenderer = document.querySelector(".sprite-renderer");
Enter fullscreen mode Exit fullscreen mode

Next, let's set some variables to track where our sprite is positioned within the container. For this, we will use three built-in functions parseInt(), getComputedStyle() and getPropertyValue() to get the numerical value of our initial --sprite-top and --sprite-left CSS variables.

// grab the initial values where we placed the sprite in CSS
let spriteTop = parseInt(
  getComputedStyle(document.documentElement).getPropertyValue("--sprite-top")
);
let spriteLeft = parseInt(
  getComputedStyle(document.documentElement).getPropertyValue("--sprite-left")
);
Enter fullscreen mode Exit fullscreen mode

The last variable we need to track is the direction our friend Link is facing.

let spriteDirection = "down"; // set an initial direction for the character to face
Enter fullscreen mode Exit fullscreen mode

To make Link react to the standard W A S D movement keys, let's add a keydown listener to the document.

document.addEventListener("keydown", (e) => {}
Enter fullscreen mode Exit fullscreen mode

The following code will be within the callback function of the EventListener

When a key is pressed, we want to begin by checking if that key is one of our desired directional keys. If not, we ignore the keypress, this saves on computation resources.

  // the following code will only run if we have pressed one of our directional keys
  if (!["w", "a", "s", "d"].includes(e.key)) return;
Enter fullscreen mode Exit fullscreen mode

If the key pressed is valid, we move to a switch block to decide what variables need to be changed, depending on the intended direction.

// set up directional keys for moving the sprite (w, a, s, d)
  switch (e.key) {
    case "w":
      spriteDirection !== "up" && (spriteDirection = "up"); // change direction if not already facing up
      spriteTop >= 0 && (spriteTop -= 1); // move the character up if not at the edge
      break;
    case "s":
      spriteDirection !== "down" && (spriteDirection = "down"); // change direction if not already facing down
      spriteTop <= 100 && (spriteTop += 1); // move the character down if not at the edge
      break;
    case "a":
      spriteDirection !== "left" && (spriteDirection = "left"); // change direction if not already facing left
      spriteLeft >= 0 && (spriteLeft -= 0.5); // move the character left if not at the edge
      break;
    case "d":
      spriteDirection !== "right" && (spriteDirection = "right"); // change direction if not already facing right
      spriteLeft <= 100 && (spriteLeft += 0.5); // move the character right if not at the edge
      break;
    default:
      break;
  }
Enter fullscreen mode Exit fullscreen mode

Next, we will tell the CSS which sprite sheet it should be using (note that we need to set the entire url() string here due to the way CSS parses variables.

root.style.setProperty(
    "--sprite-sheet-url",
    `url(./link-spritesheet-${spriteDirection}.png)` // tell the CSS what sprite sheet to use based on direction
  );
Enter fullscreen mode Exit fullscreen mode

With our sprite now facing the correct direction, we'll let the CSS know to start animating the sprite.

spriteRenderer.classList.add("animating"); // tell the CSS that we want to animate the sprite
Enter fullscreen mode Exit fullscreen mode

Last but not least, we will tell the CSS to move the sprite to the new coordinates calculated in the switch statement.

// move our sprite to the new coordinates
  root.style.setProperty("--sprite-top", `${spriteTop}%`);
  root.style.setProperty("--sprite-left", `${spriteLeft}%`);
Enter fullscreen mode Exit fullscreen mode

The finish line is in sight! Just one more piece of JavaScript to take care of. This time, we just need to tell the CSS to stop animating the sprite when we release the key.

document.addEventListener("keyup", () => {
  spriteRenderer.classList.remove("animating"); // when the key is released, stop animating the sprite sheet
});
Enter fullscreen mode Exit fullscreen mode

WHEW!

link walking

If all has gone according to plan, you should now be able to make Link walk in all 4 directions across the screen! How's that for gameplay!? (I told you I was using "game" very lightly here 😂)

Conclusion

In the third and final post to this series, I will be introducing what I have created with all of these concepts for the Digital Ocean Hackathon. (Spoiler alert: if you were a kid growing up in the 90s with access to a computer running Windows 3.1 or higher, there's a good chance you've played this before!)

Stay Tuned!

DISCLAIMER:
I did not create, nor do I own any of the pixel art depicted in this post, I simply edited it such that it meets the requirements for this project. Credit for the sprites used goes to RetroGameZone.

Latest comments (0)