Character Controls
Tags: javascript, gamedev, webdev, tutorial
Published: 2022-01-22 16:39:46

What you'll be making:

{% youtube HUTvaIyZKN8 %}

Moving a character around the screen using arrow keys or gestures/taps is a key element of creating games. Let's dig in and see one way to do this...

```js
let x = 300;
let y = 300;
let vx = 0;
let vy = 0;
function loop() {
  console.log('game loop');
  requestAnimationFrame(loop);
}
loop();
```

The above is the core of what we'll be needing to make our character jump around the screen.

Let's pop it into codepen:

{% codepen https://codepen.io/ZevanRosser/pen/vYZVzgd default-tab=js,result %}

Let's add a div to represent the character:

```js
const char = document.createElement('div');
```

and give it some styles:

```js
Object.assign(char.style, {
  position: 'absolute',
  width: '50px',
  height: '80px',
  background: 'black'
});
```

{% codepen https://codepen.io/ZevanRosser/pen/WNOagjB default-tab=js,result %}

Ok, let's make the div move and see what these four variables we defined are going to be used for.

In our loop, I'm going to do away with the test console log and replace it with some updates to the div styles. I'll also change the variables a bit:

```js
let x = 100;
let y = 100;
let vx = 2;
let vy = 2;
function loop() {
  x += vx;
  y += vy;
  Object.assign(
    char.style, {
      left: x + 'px',
      top: y + 'px'
    }
  );
  requestAnimationFrame(loop);
}
loop();
```

_If you don't see anything, click the re-run button_

{% codepen https://codepen.io/ZevanRosser/pen/eYRPLGZ default-tab=result %}

## Why does it move?
The `x` and `y` variables are where we want to store the location of the character (`char` div). In the above example we start `x` at 100. When the `loop` function is first run we then add `vx` to `x`:

```js
x += vx;
// x = x + vx
```

 Since we've set `vx` to `2`. This means that `x` becomes `102`. We then set the `left` property on the `div` to be `x + 'px'` and we get our fist step of animation.:

```js
left: x + 'px';
```

_It's pretty annoying to have to add `px` all the time like that. There are ways to get rid of it, but for simplicity I'm just going to leave it for now._

`Object.assign` is a pretty common, somewhat verbose function... If you don't know what that is, think of it as an easy way to define or update multiple properties of an object. That is an overly simplified description, so if you want more info go over to <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign">MDN and read up on it</a>. I am just using here as an alternative to:

```js
char.style.left = x + 'px';
char.style.top = y +'px';
```

`requestAnimationFrame` is a way to call a function repeatedly at approximately 60 frames per second. If you want to use `requestAnimationFrame`, generally you call it at the end of the function you want to repeatedly call, passing it that same function that you wish to repeat:

```js
function loop() {
  console.log('loop...');
  requestAnimationFrame(loop);
}
loop(); // start the loop
```

You'll need to call the function once to start the loop off.

## Controlling Velocity

With all that out of the way, try changing the initial values of `vx` and `vy` few times. See if you can intuit what they are doing and why they cause the `char` div to move in the direction that it does.

## Velocity Directions

What you'll notice is a negative `vx` values moves the `char` left and a positive one moves it right. If `vx` is `-5` and `vy` is just `0`, the character will just move to the left:

{% codepen https://codepen.io/ZevanRosser/pen/LYLgJJe default-tab=js,result %}
_If you don't see anything, click the re-run button_

...and set `vx` to `5` to move to the right:

{% codepen https://codepen.io/ZevanRosser/pen/yLXRxGO default-tab=result %}
_If you don't see anything, click the re-run button_

A larger `vx` value will cause the `char` div to move faster and a smaller one will cause it to move slower. When `vx` and `vy` values are both set you can move things diagonally. Take our first example where the `char` moved down and to the right. This was because we had `vx` and `vy` both set to `2`. If we were to look at these values we would see:

```js
x += vx
100 = 100 + 2
// 102

x += vx
102 = 102 + 2
// 104

x += vx
106 = 106 + 2
// 106
```

The `y` value would be identical, so each frame were are moving to the right 2 pixels and down 2 pixels. If you set `vx` and `vy` to `-2` it would go up and to the left diagonally.

## Why Velocity Variables?

You might wonder why we put velocity into variables. Why not just do:

```js
x += 5;
y += 5;
```

The answer is that having them in variables enables us to change them over time. For instance if we multiple the velocity values be a number less than 1, we will effectively shrink the velocity values over time:

```js
x += vx;
y += vy;

vx *= .93;
vy *= .93;
```

{% codepen https://codepen.io/ZevanRosser/pen/VwWEEWR default-tab=result %}
_click re-run to watch again_

In the same way, we could speed up velocity, reverse velocity etc... just by using a little math.

Here is how to add something like gravity:

```js
let vx = 4;
let vy = -10;
```

Starting off moving to the right with a `vx` of 4 and shooting up a little bit fast with a `vy` of `-10`. If we increase `vy` over time, by adding `1` each frame we will see a nice arc:

```
x += vx;
y += vy;

vy += 1;
```

{% codepen https://codepen.io/ZevanRosser/pen/abwRRVj default-tab=js,result %}
_rerun to watch again_

The vx value is starting off negative and slowly increasing `-10, -9, -8, -7... 0, 1, 2, 3, 4 ... 99` forever. This is what causes the arc. Taking some time to play with this and understand it better might be useful.

The character currently shoots off the screen, let's prevent this by reversing `vy`:

```js
if (y > innerHeight - 80) {
  vy *= -1;
  y = innerHeight - 80;
}
```

The minus `80` is just from the original `80px` height we gave the character div. We'll clean that code up in a bit, leaving this way for now.

{% codepen https://codepen.io/ZevanRosser/pen/bGRmmvw default-tab=js,result %}

Now the character is sort of jumping and going off the right hand side of the screen. Let's dampen the "bounciness" a bit by multiplying `vy` by a negative decimal value, this will reverse `vy` but also shrink it:

```js
if (y > innerHeight - 80) {
  vy *= -.33;
  y = innerHeight - 80;
}
```

We can also kill off some x velocity by halfing `vx` each time the character hits the ground.
```js
if (y > innerHeight - 80) {
  vy *= -.33;
  vx *= .5;
  y = innerHeight - 80;
}
```

{% codepen https://codepen.io/ZevanRosser/pen/WNOaagp default-tab=js,result %}

## Key Controls
Ok! You may want to take some time and play with what you've learned so far, but if you are feeling like everything makes sense, let's add some key controls.

One super annoying thing about key listeners in the browser is that if you say hit the SPACE key, you'll notice that the `keydown` event fires once, then there is a delay and then it continues to fire at equal intervals. This doesn't cut it for games at all, as it adds an annoying lag. We can avoid this by keeping track of which keys are down and updating our graphics in our game loop, instead of when the `keydown` event fires. 

```js
document.addEventListener('keydown', e => {
  console.log(e.key)
})
```

The above will show us a string version of the name of the key that is down. In this case we want to use the arrow keys, so we'll look for `ArrowLeft`, `ArrowRight` etc..

If we were to hardcode some checks for these it looks like this:

```js
let leftKey;
let rightKey;
let downKey;
let upKey;
document.addEventListener('keydown', e => {
  e.preventDefault();

  if (e.key === 'ArrowLeft') {
    leftKey = true
  } 
  if (e.key === 'ArrowRight') {
    rightKey = true
  }
  if (e.key === 'ArrowDown') {
    downKey = true
  }
  if (e.key === 'ArrowUp') {
    upKey = true
  }
})
document.addEventListener('keyup', e => {
  e.preventDefault();

  if (e.key === 'ArrowLeft') {
    leftKey = false
  } 
  if (e.key === 'ArrowRight') {
    rightKey = false
  }
  if (e.key === 'ArrowDown') {
    downKey = false
  }
  if (e.key === 'ArrowUp') {
    upKey = false
  }
})
```

I'll show in a bit how to make that less repetitive/ugly. For now, I'm just hardcoding it so it's easy to understand. The `preventDefault` method is preventing any keys from doing normal browser behaviors like scrolling the page etc... 

Armed with our arrow key variables. We can now check if a key is down during the main loop using:

```js
if (rightKey) {
  vx += 3;
}
```

Here we check if the right key is down and alter the x velocity to move the character to the right. All they keys follow this pattern except the up key, which needs some special logic. Have a look, you may need to click the area where the character is in order to give the keyboard focus: 

## Fully Working Demo

{% codepen https://codepen.io/ZevanRosser/pen/powxxXa default-tab=result %}

The only trick here is handling the ground. We don't want to be able to cause the character to jump, unless it is on the floor (otherwise the character will sort of be able to fly). To achieve this we add an additional check when looking at the state of the up key:

```js
if (upKey && y >= innerHeight - 80) {
  vy -= 15;
}
```

This really highlights the fact that we want to put things like `innerHeight - 80` into variables. In this case a variable called `floor`. _NOTE: you can resize the window and things will still work, the character will drop or rise up to level with the floor. Have a look on codepen to see what I mean_.

That's the main part of this tutorial. Now it's about making the code a bit more realistic. I'm also going to allow the character to go off the right of the screen and re-appear on the left etc...

```js
if (x > innerWidth + 50) {
  x = -50
}
```

That will handle going off the right side of the screen and shooting back from the left... for going off the left hand side of the screen:

```js
if (x < -50) { 
  x = innerWidth + 50;
}
```

Now I'm going to tidy everything up with variables and a few tricks and then walk through the key aspects of the changes I've made. Have a look at the new version:

{% codepen https://codepen.io/ZevanRosser/pen/xxryQOo default-tab=js,result %}

Read through that code, a fair amount has changed. Mostly just moving things into variables for readability. The main change/improvement is the way the keys are handled now. Instead of a bunch of if statements, I use an object to keep track of which keys are down:

```js
// dynamically handle keys instead of explicitly
// checking each one
const keys = {}
document.addEventListener('keydown', e => {
  e.preventDefault();
  
  // store a boolean on the `keys` object
  // to keep track of whick keys are down
  keys[e.key] = true;
});

document.addEventListener('keyup', e => {
  e.preventDefault();
   keys[e.key] = false;
});
```

If a key is down, I set `keys[e.key] = true`. So in the case of `ArrowLeft`. This is equivalent to saying:

```js
keys.ArrowLeft = true
```

If you don't already know, you can use strings to reference properties on an object using an associative array type syntax:

```js
keys['ArrowLeft'] = true
```

This is the same as using the "dot" syntax `keys.ArrowLeft = true`... but allows for a property of `keys` to be reference dynamically. If the property is not there, the first time we set it, it gets created.

Enjoy playing around with this - there is much more that can be done, platforms, enemies, power-ups etc... most of that stuff can be done with variations on what I've shown here...

## Header Image Version

I added some trails to this, just to make a more interesting screenshot for the article header.

{% codepen https://codepen.io/ZevanRosser/pen/dyRQpov %}

_If you're feeling creative - see if you can adjust the above pen so that the trails don't just disappear when the character goes from one side of the screen to another..._



_See more code over @ [Snippet Zone](https://snippet.zone/)_