Making Animated Sprites Interactive with JavaScript! -  (Digital Ocean Hackathon Blog: Part 2)
Tags: animation, gamedev, javascript, css
Published: 2020-12-31 20:25:08

If you haven't yet read my [previous post](https://dev.to/jpuls/simple-sprite-animations-for-games-on-the-web-digitalocean-hackathon-blog-part-1-ndb) 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](https://dev-to-uploads.s3.amazonaws.com/i/nb0ohpd9iwwht218o875.png)

![link down](https://dev-to-uploads.s3.amazonaws.com/i/65x5gdkkdioinc6jwp8x.png)

![link right](https://dev-to-uploads.s3.amazonaws.com/i/42mnyg6bwaayuu2p624g.png)

![link up](https://dev-to-uploads.s3.amazonaws.com/i/4h5b2u91f2tp7r98awko.png)

## 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.

```html
<body>
  <div class="container">
    <div class="sprite-view-frame">
      <div class="sprite-renderer"></div>
    </div>
  </div>
  <script src="script.js"></script>
</body>
```

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.

```css
: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");
}
```

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

```css
.container {
  width: 90%;
  height: 90%;
  position: relative;
  top: 5%;
  left: 5%;
}
```

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`

```css
 background-image: var(--sprite-sheet-url); /* the sprite sheet */
```

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`;

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

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.

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

```

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).

<small>It will be assumed here that you have working knowledge of basic vanilla JavaScript</small>

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.

```javascript
// grab our elements to be modified
const root = document.querySelector(".container");
const spriteRenderer = document.querySelector(".sprite-renderer");
```

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.

```javascript
// 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")
);
```

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

```javascript
let spriteDirection = "down"; // set an initial direction for the character to face
```

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

```javascript
document.addEventListener("keydown", (e) => {}
```

**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.

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

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.

```javascript
// 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;
  }
```

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.

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

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

```javascript
spriteRenderer.classList.add("animating"); // tell the CSS that we want to animate the sprite
```

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

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

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.

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

### WHEW!
![link walking](https://dev-to-uploads.s3.amazonaws.com/i/yu7o955b5foht0iujujd.gif)

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](https://retrogamezone.co.uk/)._