Skip to main content

Born Slippy

Up until now, the player control has felt very "digital" and harsh. Whilst there's an argument to be made that that is how it should be in a shoot-em-up, I didn't like it that much. I'm going to add a little bit of momentum to the player sprite to give the ship a feeling of weight.

Velocity

Previously, the arrow keys were tied directly to the player's movement: hold down right arrow and the ship instantly starts moving right at 5 pixels per frame, let go and is stops dead. In the real world, things don't happen like that - they accelerate up to a maximum speed when you apply a movement force and then gradually slow back down to a stop (due to friction/drag) when you remove that force. Ok.. so that's not really what happens in space, where there is no drag/friction to slow you back down again - but it's the sort of control that we've all been conditioned to expecting. Anyway.. you can simulate that easily by using a velocity variable. If right arrow is held down then I increase the velocity by a small amount every frame until it reaches a maximum speed, and when the key is released I decrease the velocity each frame until it reaches 0. Instead of adding a fixed value to the player's position each frame, I now add the velocity value.

The code for this (inside the player's update() function is pretty simple:

update() {
// Horizontal movement component
if (keys.ArrowLeft) {
this.xVelocity = Math.max(this.xVelocity - this.config.acceleration, -this.config.maxSpeed);
} else if (keys.ArrowRight) {
this.xVelocity = Math.min(this.xVelocity + this.config.acceleration, +this.config.maxSpeed);
} else {
if (this.xVelocity < 0) {
this.xVelocity = Math.min(this.xVelocity + this.config.drag, 0);
} else {
this.xVelocity = Math.max(this.xVelocity - this.config.drag, 0);
}
}
// Vertical movement component
if (keys.ArrowUp) {
this.yVelocity = Math.max(this.yVelocity - this.config.acceleration, -this.config.maxSpeed);
} else if (keys.ArrowDown) {
this.yVelocity = Math.min(this.yVelocity + this.config.acceleration, +this.config.maxSpeed);
} else {
if (this.yVelocity < 0) {
this.yVelocity = Math.min(this.yVelocity + this.config.drag, 0);
} else {
this.yVelocity = Math.max(this.yVelocity - this.config.drag, 0);
}
}
// Move based on velocity
this.xPos += this.xVelocity;
this.yPos += this.yVelocity;
// Limit to the bounds of the screen
this.xPos = clamp(this.xPos, 0, config.canvasSize - this.image.width);
this.yPos = clamp(this.yPos, 0, config.canvasSize - this.image.height);
}

Sluggish

This worked as expected - the player's ship now responds a bit more smoothly to the control inputs, but it felt sluggish.. and a little bit too slippy. The reason for this was that I had the game locked at an update rate of 30fps. At this rate, any smoothing can very quickly make the player control feel sluggish. It's a simple fact that player control is more responsive at higher update rates, so the solution was to change the game's update rate to 60fps. Suddenly everything felt a lot nicer and looked a lot smoother.

Choose Your Ship

With the velocity code implemented, I can make a wide range of different control styles just by tweaking the acceleration, max speed and drag values. Actually, I can make such wildly different settings that it made sense to add a few different types of player ship, each with different configurations. In the end I added three:

A slow moving ship with lots of weight, but a very small hitbox. This one is hard to steer but the tiny hitbox lets you get away with a lot.
A medium speed ship with a medium sized hitbox. This is similar to the old player ship.
A fast moving ship that has two medium sized hitboxes. This one can zip around the screen but it's very easy to get hit. As a bonus, because of the location of the hitboxes, skilled players can actually let bullets pass right through the middle of the ship without colliding.

I updated the graphics of the three ships to make the hitboxes very visible as red areas on the sprites.

Wrapping Up

In this post I made the player control a bit more interesting by adding velocity and drag. I made three different configurations and let the player choose between them from the start screen. I also changed the game's framerate from 30 to 60fps, to make the controls more responsive. To show how this affects player control I added three types of player ship, each with different stats.

Online playable version is here, and the code can be found here.

Next

There's a pretty obvious way to score high in the game - camp out on the score bullet spawner. One way to solve that is to make the score bullets start in a state where they can't be collected, and then have them change to collectable after a short period of time. A nice way of doing this would be give them a spawn animation, and only let them be collected once that animation is complete. In the next post I'll add an animation system for this purpose, but it'll be useful for other things too.

Comments

Popular posts from this blog

World In Motion

In the last post I touched on how easy it is to score in this game - you can just camp out on the score bullet spawner and watch the points rack up. I'm going to add a little delay to the score bullets so that they start out in a deactivated and uncollectable state, and then change to active and collectable after a short time - say, half a second. To make it clear what state they are in I'm going to add code to let me change the look of Objects and allow them to be animated.

Phony Game

Up until now I've been mostly focusing on the game technology and neglecting the game play . I'm going to address that a little bit by adding scoring to the game. I want to add some risk/reward to the gamplay - score higher by taking more risks. From now on I'm going to try to make sure that I remember to add gameplay features as well as tech features. So how do you score in a shoot-em-up where you can't actually shoot? There are a few possible ways that come to mind: Score bullets: Fired from spawners like normal bullets, but instead of hurting you when you collide with them they give you score Bullet grazing: Score points by flying very close to bullets  but not close enough to be killed Score zones: Marked areas of the screen that award you points when you fly over them I like all three of these scoring methods and I'll probably implement them all over time. If you have any more good scoring ideas that you'd like to see in the game, please let me know...

Funky Boss

A quick one this time. I'm heading towards getting collision into the game, but before I do that I need to get the game objects better managed. So this time I'm going to refactor the code a little bit to add an object manager. It's going to mean several small changes across the codebase..