As I enter the final phase of software engineering boot camp I was reminded by a classmate that coding can be fun. So before we have to start our capstone project and polish our resumes and refine our portfolio and… I decided to have some fun coding during this long weekend.
What better way to have fun than doing a CSS heavy project, right? Okay maybe not. If my cohort is a fair representation, CSS is the bane of many beginning front end developers so I decided to bring an old friend, Pitfall Harry (from Activison's 1982 game Pitfall!).
This post is going to show you how to code out an 8-bit style character and animate it’s movement using JavaScript, Html and CSS. There are three different animation types happening in the clip below and I will show how they are used in conjunction with each other.
Animate a Sprite using CSS
Animate Element movement using JavaScript
Animate Element movement using CSS
Sprite Animation in CSS
At the start we are going to need a sprite sheet. To create this I started with an internet search and found one for Pitfall Harry that was a little messy, so I used Piskel to clean it up to get what I needed.
Here is the final 5 frame sprite output from Piskel.
I created one more sprite, a single frame of the character standing.
Now that we have our sprites we need to create 3 files, index.html, script.js and style.css.
The html file looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Jump Guy</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="game">
<div id="character"></div>
</div>
<script src="script.js"></script>
</body>
</html>
We are defining two elements here, a game div that serves as our canvas and a character that our sprite will embody.
Now we will add some CSS styling to our elements. We are disabling the scroll bar in our body element, setting the dimensions of our game area and setting the size of our character as well as it's starting position (because our character is a child of the game element, we are setting it's position as relative to it's parent).
/* style.css */
body{
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
overflow-x: hidden;
}
#game{
width: 1000px;
height: 600px;
border: 1px solid black;
margin:0%;
}
#character{
width: 128px;
height: 170px;
position: relative;
top: 400px;
scale:.75;
left:0px;
}
Next we are adding links to our two sprite image files and the bulk of the code required to create the running animation. We are letting CSS know that one of our frames is 128 pixels wide and our 5 frame sprite sheet is 640 pixels wide. We are defining classes here that we will attach and remove as required in our JavaScript file in the next steps.
/* style.css */
.background-run{
background: url("./harry-run.png");
}
.background-stand{
background: url("./harry-stand.png");
}
@keyframes sprite-run {
from { background-position: 0px; }
to { background-position: -640px; }
}
.run{
animation: sprite-run 500ms linear;
animation-timing-function: steps(5);
animation-iteration-count: infinite;
}
.flip{
transform: scaleX(-1);
}
In our script.js file we are getting our character element and attaching our stand sprite to it. Then we are setting some global variables as well as the event listeners to handle our keystroke inputs.
var character = document.getElementById("character");
character.classList.add("background-stand")
/* Global Variables */
let pressed =false
let characterLeft=0
/* Event Listeners */
document.addEventListener("keydown",keydown);
document.addEventListener("keyup",keyup)
/* Functions */
function keydown(e){
pressed===true
if(e.code==="ArrowRight")run(1)
if(e.code==="ArrowLeft")run(-1)
if(e.code==="Space")jump()
}
function keyup(e){
if(e.code==="ArrowRight"){
clearInterval(movetimer)
character.classList.remove("run")
character.classList.remove("background-run")
character.classList.add("background-stand")
pressed=false
}
if(e.code==="ArrowLeft"){
clearInterval(movetimer)
character.classList.remove("run")
character.classList.remove("background-run")
character.classList.add("background-stand")
pressed=false
}
}
Next we define our run function taking our direction parameter and using it to tell the character in which direction to face based off of our arrow key inputs.
function run(direction){
if(pressed===true){return;}
/* Setting the direction of the character */
direction<0 ? (character.classList.add("flip")) : (character.classList.remove("flip"))
character.classList.remove("background-stand")
character.classList.add("background-run")
character.classList.add("run")
pressed=true
}
At this point when our page loads our character is standing in the bottom left corner of our game area and he will run in place if you press and hold the right or left arrow key as well as face the correct direction.
Element Animation in JavaScript
What he won't do is actually run anywhere. We will remedy this by adding a few things to our script.js file.
Add this to our global variables: let moveTimer = 0
At the end of our run function add:
moveTimer=setInterval(move,10,direction)
This passes our direction variable to a new function called move that will be executed every 10 milliseconds by a moveTimer that is created with the setInterval function.
We also need to add clearInterval(moveTimer)
to our keyup function for both the ArrowLeft and ArrowRight codes.
The move function looks like this:
function move(direction){
direction<0 ? (characterLeft-=2) : (characterLeft+=2)
/* Defining our movement boundaries */
if (characterLeft>=800){characterLeft=800}
if (characterLeft<0){characterLeft=0}
character.style.left=characterLeft+'px';
}
Element Animation in CSS
This is a different type of animation than the one we just did in JavaScript. This involves an animation that, like the sprite sheet animation, is a loop. Meaning the start and end frames are the same. In this case we are talking about jumping.
We need to add a function to handle our jump and reference it to pressing the spacebar.
/* Functions */
function keydown(e){
pressed===true
if(e.code==="ArrowRight")run(1)
if(e.code==="ArrowLeft")run(-1)
if(e.code==="Space")jump()
}
function jump(){
if(character.classList == "animate-jump"){return}
character.classList.add("background-run")
character.classList.add("animate-jump")
setTimeout(removeJump,300) //300ms = length of animation
}
function removeJump(){
character.classList.remove("animate-jump")
}
Finally we need to add the keyframes and timing for our jump to our style.css file.
@keyframes jump{
0%{top: 150px;}
30%{top: 100px;}
70%{top: 100px;}
100%{top: 150px;}
}
.animate-jump{
animation: jump 300ms linear;
}
You can find the deployed code here:
https://setlock10.github.io/jump-guy/
You can find the github repository here:
https://github.com/setlock10/jump-guy
(Almost) any feedback would be appreciated. If there are points of confusion that you think need to be cleared up please let me know in the comments.
Here are some blog posts that inspired this one:
https://blog.logrocket.com/build-a-game-with-html-css-javascript/
https://blog.logrocket.com/making-css-animations-using-a-sprite-sheet/
Top comments (1)
well,it look so fun