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!
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>
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");
}
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%;
}
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 */
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));
}
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 */
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");
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")
);
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
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) => {}
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;
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;
}
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
);
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
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}%`);
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
});
WHEW!
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.
Top comments (0)