Category Archives: Game Development

Creating a Running Game in VueJS – Part 5 – Gameplay and Graphics

In this, the fifth and final instalment of my series on creating a game using VueJS, we’re going to improve the gameplay a little, and we’re also going to replace our squares and oblongs with graphics.

If you’re new to this series, I’d encourage you to jump back to the first post.

The code for this post is here.

Gameplay and Tidy up

If you’ve played around with the game, you’ll realise there’s quite an easy way to cheat – if you simply press both keys at the same time, it behaves as though you were pressing the keys in quick succession (which, in fact, you are). Let’s stop that by adding a slight delay.

  
onKeyDown(e) {      
            if (this.lastPressed + 50 > Date.now()) {
                return;
            } else {
                this.lastPressed = Date.now();
            }

lastPressed is just added to the data properties.

Next, you may have noticed that we can run past the finish line, and the game continues after it’s finished… which actually means it’s impossible to win, because if you win, once the time gets to zero, you lose.

We can fix this pretty easily by simply stopping the timer once the game is finished; in App.vue, the update function should have the following code where we check when the player passes the finish line:

if (this.playerX > this.finishLine) {
    this.isGameFinished = true;
    this.playerWon = this.isGameFinished && (this.secondsRemaining > 0);
    clearInterval(this.interval);
}
if (this.secondsRemaining <= 0) {
    this.isGameFinished = true;
    this.playerWon = this.isGameFinished && (this.secondsRemaining > 0);
    clearInterval(this.interval);
}

this.interval is set in the init method:

init() {
    this.interval = setInterval(this.update, 10);
    this.startTime = new Date();
    this.startTime.setSeconds(this.startTime.getSeconds() + 20);
},

The next thing we need is to replace our box with some actual graphics.

Graphics

The principle behind animation (any animation, not just in a game) is that you take a series of still picture, and play them rapidly.

I couldn’t find a spritesheet of anyone running, so I created some images of my own; they look absolutely ridiculous, and are essentially made up from using the filled circle and rounded corner rectangle in paint – but then I never claimed to be any good at graphics art. Anyway, for the purpose of this, I’ve dropped them in the assets folder:

It’s a pretty good bet that you (the reader) is better at graphics art than I am, so I would strongly encourage you to not use my graphics if you’re following along. However, they’re in the GitHub repo if you wish to.

To start with, we’ll need to store the names of the images, and the last time we switched the image; so we’ll add a couple of variables to the App.vue script:

const images = [ "daley-1.png", "daley-2.png", "daley-3.png" ];
let lastSwitch = (new Date()).getTime();

We’ll also need a data property to track which image we’re displaying:

    data: function() {                    
        return {
            . . .
            currentImageIdx: 0            
        };
    },

The next step is to slightly change our GameObject such that we now display an image:

<template>
    <div v-bind:style="location">
        <img v-bind:src="image" />
    </div>
</template>
<script>
export default {
  name: 'GameObject',
  props: {
    type: String,
    location: String,
    image: String
  }
}
</script>

In App.vue we’ll need to bind that image:

<GameObject
    v-bind:location="playerLocation"
    type="player"
    v-bind:image="playerImage">
</GameObject>

And a computed property to return it:

playerImage: function() {                        
    return require("./assets/" + images[this.currentImageIdx]);
},

That’s pretty much it; all that remains is to cycle through the images. I’ve done this in the update method, and tried to make it faster as you run faster:

if (this.speed > 0) {
    const refreshSpeed = 1000 - (this.speed * 3);
    if ((new Date()).getTime() > lastSwitch + refreshSpeed) {
        this.currentImageIdx = 
            (this.currentImageIdx < images.length - 1) 
            ? this.currentImageIdx + 1 : 0;
        lastSwitch = new Date().getTime();
    }
}

It’s not perfect, but it roughly looks like he’s running faster as you move him faster.

Summary

This is going to be the last post of my very poor attempt to recreate the amazing Sprectrum game of my youth. As I said at the start, I always find this a useful way to try to learn a new front-end framework – you need to learn about how and when the screen is rendered, and how it deals with interesting things like displaying graphics, updating data, etc.

Creating a Running Game in VueJS – Part 4 – The Finish Line

This post follows on from a series on creating a game in Vue. If you’d like to start from the beginning, then go here.

In the previous post we refactored the single file index.html that we’d been working on into multiple component files, and created a new Vue templated application.

In this post, we’ll do some further refactoring, and we’ll add a finish line.

Reusing Components

Since we now have components, we should re-use them; otherwise, there’s not much point in them being components. Our Player object is just a game object, and since we now need a second game object, let’s rename Player to GameObject. We can also add an additional property; for now, it will simply allow us to see which object is which:

<template>
    <div v-bind:style="location">
        
    </div>
</template>
<script>
export default {
  name: 'GameObject',
  props: {
    type: String,
    location: String
  }
}
</script>

We’ll change the template in App.vue, too:

<template>
    <GameObject
        v-bind:location="playerLocation"
        v-bind:type="player">
    </GameObject>
    <Timer 
        v-bind:seconds="secondsRemaining">
    </Timer>
    <GameObject
        v-bind:location="finishLineLocation"
        v-bind:type="finish-line">
    </GameObject>
</template>

As you can see, we’ve changed the data a little: instead of location we now have two pieces of computed data (in fact one isn’t really computed, but we’ll come back to that later):

computed: {                    
    playerLocation: function() {
        return 'width:10px; height:10px; left:' + this.playerX + 'px; top:' + this.playerY + 'px; border:1px solid #000; position: absolute;';
    },
    finishLineLocation: function() {
        return 'width:3px; height:300px; left:500px; top:50px; border:1px solid #000; position: absolute;';
    },

If you run that now, you’ll see the finish line; but alas we can “run” straight past the finish line and nothing happens!

Collision – have we won, or lost?

The first thing we’ll do here is create a new component: GameFinished. This will simply display some text for now saying whether you won or lost. The code inside the component will be very straightforward:

<template>
  <p>Game is finished, you {{ wonOrLost }}</p>
</template>
<script>
  export default {
    name: 'GameFinished',
    props: {
      isWon: Boolean
    },
    computed: {
      wonOrLost: function() {
        return this.isWon ? "Won" : "Lost";
    }
  }
}
</script>

We’ll need to both import and register the component. If you don’t, you’ll start getting errors such as

error ‘GameFinished’ is defined but never used no-unused-vars

Or:

error ‘GameFinished’ is not defined no-undef

To import the component, you’ll need this in App.vue:

<script>
    import GameObject from './components/GameObject.vue';
    import Timer from './components/Timer.vue';
    import GameFinished from './components/GameFinished.vue';

And registering is done slightly further down:

components: {    
    GameObject,
    Timer,
    GameFinished
},

We’ll add a new data property:

data: function() {                    
    return {
	. . . 
        isGameFinished: false

We’ll add a new computed function that will tell us whether we’ve won:

playerWon: function() {
    return this.isGameFinished && (this.secondsRemaining > 0);
}

And finally, the collision detection. Inside the Update method, we’ll just check the player position against the finish line:

update() {
    this.playerX += this.speed;
    if (this.speed > 0) {
        this.speed -= 0.01;
    } else {
        this.speed = 0;
    }
    this.timeNow = new Date().getTime();
    // Collision check
    if (this.playerX > this.finishLine) {
        this.isGameFinished = true;
    }
}

Where is the finish line?

Our last piece of refactoring will be to set the finish line based on a data property:

data: function() {                    
    return {
        . . . 
        finishLine: 500,

Now let’s tweak out finishLineLocation:

finishLineLocation: function() {
    return 'width:3px; height:300px; left:' + this.finishLine + 'px; top:50px; border:1px solid #000; position: absolute;';
},

In the next post, we’ll tweak the controls a little, and we’ll add some graphics.

Creating a Running Game in VueJS – Part 3 – Component Files and Installing Vue

This is the third part in a series on replicating a game similar (in some respects) to the ZX Spectrum game “Daley Thompson’s Decathlon”.

To start from the beginning of the series, go here.

In this particular post, we’ll be continuing our refactor by moving out components into their own files.

If you’d like to see the code from this post then it’s on GitHub here.

Install Vue and Create a New App

The first thing you’ll need to do here is to install Vue:

npm install vue

Once this is done, we’ll just create a brand new templated Vue project:

vue create vue-app

What we’re essentially going to do here is to copy our existing program from part 2 into the new application, but using separate files for components. All we actually have now is a single file (index.html).

Copying Code

Your new app should look broadly like this:

Let’s start by creating a new component called Timer, this will be the successor for our seconds-remaining component:

Now, let’s take the code from our component and paste it in. We’ll start with the template; the current template is:

template: `
    <p>Seconds remaining: {{ seconds }}</p>
`

And that will become:

<template>
    <p>Seconds remaining: {{ seconds }}</p>
</template>

Next we need the props; so the following:

props: ['seconds'],

Will become:

<script>
export default {
  name: 'Timer',
  props: {
    seconds: Number
  }
}
</script>

Since we have no style, that’s done:

Next, we’ll bring across the player component in the same way:

The next stage is to update App.vue component. The template will now look like this:

<template>
  <Player
      v-bind:location="location">
  </Player>
  <Timer 
      v-bind:seconds="secondsRemaining">
  </Timer>            
</template>

Inside the script tag, we’ll bring in the Player and Timer components:

<script>
    import Player from './components/Player.vue'
    import Timer from './components/Timer.vue'

Next, we need to define which components we’re using:

export default {
  name: 'App',
  components: {    
    Player,
    Timer
  },

We then need our data tag, which now needs to be a function:

  data: function() {                    
    return {
      playerX: 100,
      playerY: 100,
      speed: 0,
      toggleSpeed: 1,                    
      startTime: 0,
      timeNow: 0
    };
  },

The rest can come across as is (please see the GitHub repo for these).

Finally, you can remove HelloWorld.vue.

Now that we have the code inside components, in the next part, we’ll add a finish line, and a way for the player to win or lose.

Creating a Running Game in VueJS – Part 2 – Components

This is the second post in a series that started here. If you’d like some context, then please visit the first post. In this post, I’ll be refactoring the small amount of code that we currently have, into Vue Components.

You can find the code for this post here.

Refactoring into Global Components

Let’s start with a very simple component, and that’s the display of “Seconds Remaining”. For the time being, we’ll create this in the same file, so all we’re doing here is shuffling code around. Instead of the following code:

<p>{{ secondsRemaining }}</p>

We’ll add this:

<div id="app">
    <div v-bind:style="location"></div>
    <seconds-remaining 
        v-bind:seconds="secondsRemaining">
    </seconds-remaining>            
</div>

A quick note on this, you may see the following syntax:

<div id="app">
    <div v-bind:style="location"></div>
    <seconds-remaining 
        :seconds="secondsRemaining">
    </seconds-remaining>            
</div>

Essentially, here the v-bind is implicit. Both work.

We can then define this inside the script tag like so:

<script>
    Vue.component('seconds-remaining', {
        props: ['seconds'],
        template: `
            <p>Seconds remaining: {{ seconds }}</p>
        `
    });
    new Vue({

    . . .

You’ll notice the use of the back-tick (`); you need this if the template spans multiple lines (in our case, we could probably get away with a single line – if we had, it would look like this:

<script>
    Vue.component('seconds-remaining', {
        props: ['seconds'],
        template: '<p>Seconds remaining: {{ seconds }}</p>'        
    });
    new Vue({

    . . .

One other important thing to note here, you are not permitted to have uppercase characters in the component name. You won’t get an error as such, it just automatically lower cases all the text, meaning that it won’t find your component (and you won’t know why!)

Let’s move the player into a component, too:

<div id="app">
    <player
        v-bind:location="location">
    </player>
    <seconds-remaining 
        v-bind:seconds="secondsRemaining">
    </seconds-remaining>            
</div>

And the script:

<script>
    Vue.component('seconds-remaining', {
        props: ['seconds'],
        template: `
            <p>Seconds remaining: {{ seconds }}</p>
        `
    });
    Vue.component('player', {
        props: ['location'],
        template: `
            <div v-bind:style="location"></div>
        `
    });

    . . .

In the next post, we’ll move these components out into their own files, which will involve actually installing Vue, rather than just importing the library.

References

https://www.taniarascia.com/getting-started-with-vue/

Vue.js Tutorial From Scratch – e07 – NPM, Webpack & Single File Component

Creating a Running Game in VueJS – Part 1 – Moving

If, like me, you’re over the age of 35, you may have owned a Spectrum, Amstrad, or Commodore 64. One of the games, especially on the Spectrum, that was guaranteed to ruin your keyboard, and make your family think you were having some kind of fit, was Daley Thomson’s Decathlon.

In this post, I introduced the basics of writing a game in VueJS. In this series of posts, I’m going to try to recreate that game, at least in spirit. I’m afraid I won’t have the amazing graphics, but hopefully we can create something vaguely similar.

I’ll be putting the various parts in GitHub here. I made a similar series of posts using React; you can find them here.

In this first section, we’ll create a player (it’ll just be a box on the screen for now), and a timer. To get the code, look here.

Let’s start by seeing the HTML:

<div id="app">
    <div v-bind:style="location"></div>
    <p>{{ secondsRemaining }}</p>
</div>

We’re simply binding the style of the div to one computed property, and secondsRemaining is another. Let’s see what the computed section looks like next:

computed: {                    
    location: function() {
        return 'width:10px; height:10px; left:' + this.playerX + 'px; top:' + this.playerY + 'px; border:1px solid #000; position: absolute;';
    },
    secondsRemaining: function() {
        const diff = (this.startTime - this.timeNow) / 1000;
        return Math.round(diff);
    }
},

Remember that this section will cause a screen refresh when the variables it uses change; it does not know that the function will return a different value otherwise; which is why I’m not getting the current time inside the secondsRemaining function.

The location will force an update when the playerX or playerY properties change. Let’s see the data:

data: {                    
     playerX: 100,
     playerY: 100,
     speed: 0,
     toggleSpeed: 1,                    
     startTime: 0,
     timeNow: 0
},

There’s just two more set-up points – the first is the mounted() method:

mounted() {
    this.init();
}

This is another lifecycle event; it’s called late on in the start-up process, after everything is created. We’ll come back to Init() shortly.

Next we have created(): this is early on in the lifecycle, and here we’re just going to create an event listener:

created() {
    window.addEventListener('keydown', this.onKeyDown);
},

That’s pretty much all the set-up. Everything else is in the methods() section. Let’s start with the init() method that we saw earlier:

init() {
    setInterval(this.update, 10);
    this.startTime = new Date();
    this.startTime.setSeconds(this.startTime.getSeconds() + 20);
},

All we’re doing here is initialising the timer, and creating an update loop. Let’s see the update() method next:

update() {
    this.playerX += this.speed;
    if (this.speed > 0) {
        this.speed -= 0.01;
    } else {
        this.speed = 0;
    }
    this.timeNow = new Date().getTime();
}

This is the update method, but it’s more akin to a draw method: it’s limited to updating the variables that force a screen update. It also slows the runner down naturally (so if the player does nothing, the character will stop).

There’s just one more thing we have yet to see, and that’s the method that handles the keypress; which, in fact, is two methods:

onKeyDown(e) {                        
    switch (e.which) {
        case 37: // Left
            this.playerRun(1); 
            break;
        case 39: // Right
            this.playerRun(2); 
            break;
        default:
            break;
    }
},
                    
playerRun(toggle) {                        
    if (this.toggleSpeed !== toggle) {                           
        this.speed += 0.12;
        this.toggleSpeed = toggle;
    }
},                  

One of the defining features of the original game was that you ran by pressing the left key, and then the right in quick succession; we’ve emulated this by simply setting a flag, and comparing them.

That’s it, the first part complete:

I know you’re probably wondering how I managed to great such amazing graphics!

The code works (after a fashion), but it’s messy. In Part 2, we’ll refactor this code into components.

Creating a Car Game in React – Part 6 – Adding High Scores

This is the sixth post of a series that starts here.

As with previous posts, if you wish to download the code, it’s here; and, as with previous posts, I won’t cover all the code changes here, so if you’re interested, then you should download the code.

In this post, we’re going to create a High Score table. We’ll create an Azure function as the server, and we’ll store the scores themselves in Azure Tables.

Let’s start with the table.

Create a new storage account in Azure, then add an Azure Table to it:

You’ll see a sign trying to persuade you to use Cosmos DB here. At the time of writing, using Cosmos was considerably more expensive than Table Storage. Obviously, you get increased throughput, distributed storage, etc with Cosmos. For this, we don’t need any of that.

Create a new table:

An Azure table is, in fact, a No SQL offering, as you have a key, and then an attribute – the attribute can be a JSON file, or whatever you choose. In our case, we’ll set the key as the user name, and the score as the attribute.

Once you’re created your table storage, you may wish to use the Storage Explorer to create the tables, although that isn’t necessary.

Finally, you’ll need to add a CORS rule:

Obviously, this should actually point to the domain that you’re using, rather than a blanket ‘allow’, but it’ll do for testing.

Adding a username

Before we can store a high score, the user needs a username. Let’s add one first.

In game status, we’ll add a text box:

<div style={containerStyle}>
	<input type='text' value={props.Username}
	onChange={props.onChangeUsername} />
</div>

The state is raised to the main Game.jsx:

<GameStatus Lives={this.state.playerLives} 
	Message={this.state.message} 
	Score={this.state.score} 
	RemainingTime={this.state.remainingTime}
	Level={this.state.level}
	Username={this.state.username} 
	onChangeUsername={this.onChangeUsername.bind(this)} 
/>

And onChangeUsername is here:

onChangeUsername(e) {
	this.updateUserName(e.target.value);
}

updateUserName(newUserName) {
	this.setState({
		username: newUserName
	});
}

Update High Score

We’ll create an Azure Function to update the table. In Visual Studio, create a new Windows Azure Function App (you will need to install the Azure Workload if you haven’t already):

You’ll be asked what the trigger should be for the function: we’ll go with HttpTrigger. This allows us to call our function whenever we please (rather than the function, being say scheduled.) Next, we’ll need to install a NuGet package into our project to let us use the Azure Storage Client:

Install-Package WindowsAzure.Storage

We need some access details from Azure:

Creating the Functions

We’re actually going to need two functions: update and retrieve (we won’t be using the retrieve in this post, but we’ll create it anyway). Let’s start with a helper method:

    public static class StorageAccountHelper
    {
        public static CloudStorageAccount Connect()
        {
            string accountName = Environment.GetEnvironmentVariable("StorageAccountName");
            string accountKey = Environment.GetEnvironmentVariable("StorageAccountKey");

            var storageAccount = new CloudStorageAccount(
                new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(
                    accountName, accountKey), true);
            return storageAccount;
        }
    }

For testing purposes, add the account name and key into the local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "StorageAccountName": "pcmtest2",
    "StorageAccountKey": "C05h2SJNQOXE9xYRObGP5sMi2owfDy7EkaouClfeOSKRdijyTQPh1PIJgHS//kOJPK+Nl9v/9BlH4rleJ4UJ7A=="
  }
}

The values here are taken from above – where we copied the access keys from Azure (whilst these keys are genuine keys, they will be changed by the time the post is published – so don’t get any ideas!

First, let’s create a function to add a new high Score:

        [FunctionName("AddHighScores")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            var newScore = new HighScore(req.Query["name"], int.Parse(req.Query["score"]));            

            var storageAccount = StorageAccountHelper.Connect();

            CloudTableClient client = storageAccount.CreateCloudTableClient();
            var table = client.GetTableReference("HighScore");

            await table.ExecuteAsync(TableOperation.InsertOrReplace(newScore));

            return new OkResult();
        }

If you’ve seen the default example of this function, it’s actually not that different: it’s a POST method, we take the name and score parameters from the query string, build up a record and add the score. The function isn’t perfect: any conflicting names will result in overwritten score, but this is a copy of a spectrum game – so maybe that’s authentic!

The second function is to read them:

        [FunctionName("GetHighScores")]
        public static async Task<IList<HighScore>> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            var storageAccount = StorageAccountHelper.Connect();

            CloudTableClient client = storageAccount.CreateCloudTableClient();
            var table = client.GetTableReference("HighScore");
            var tq = new TableQuery<HighScore>();
            var continuationToken = new TableContinuationToken();
            var result = await table.ExecuteQuerySegmentedAsync(tq, continuationToken);
            
            return result.Results;
        }

All we’re really doing here is reading whatever’s in the table. This might not scale hugely well, but again, for testing, it’s fine. The one thing to note here is ExecuteQuerySegmentedAsync: there seems to be very little documentation around on it; and what there is seems to refer to ExecuteQueryAsync (which, as far as I can tell, doesn’t, or at least, no longer, exists).

Let’s run the Azure function locally and see what happens:

As you can see, Azure helpfully gives us some endpoints that we can use for testing. If you don’t have a copy already, then download Postman. Here you can create a request that calls the function.

I won’t go into the exact details of how Postman works, but the requests might look something like this:

http://localhost:7071/api/AddHighScores?name=test2&score=19
http://localhost:7071/api/GetHighScores?10

To prove to yourself that they are actually working, have a look in the table.

There is now an online Storage Explorer in the Azure Portal. Details of the desktop version can be found in this post.

Update High Score from the Application

Starting with adding the high score, let’s call the method to add the high score when the player dies (as that’s the only time we know what the final score is):

playerDies() { 
    this.setState({
        playerLives: this.state.playerLives - 1,
        gameLoopActive: false
    });

    if (this.state.playerLives <= 0) {
        this.updateHighScore();
        this.initiateNewGame();
    } else {
        this.startLevel(this.state.level);
    }

    this.repositionPlayer();
    this.setState({ 
        playerCrashed: false,
        gameLoopActive: true
    });
}

The updateHighScore function looks like this:

updateHighScore() {
	fetch('http://localhost:7071/api/AddHighScores?name=' + this.state.username + '&score=' + this.state.score, {
		method: 'POST'
	}); 
}

Note (obviously) that here I’m updating using my locally running instance of the Azure Function.

And that’s it – we now have a score updating when the player dies. Next we need to display the high scores – that’ll be the next post.

References

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

https://facebook.github.io/react-native/docs/network

Creating a Car Game in React – Part 5 – Levels and Time

This is the fifth part of a series (that began here).

In the last post, we added the concept of score. The car now can collect cups while avoiding trees; however, we don’t have any concept of what happens when there are no cups left.

In this post, we’ll add levels to the game, so that when you’ve collected all the cups, you move up. We’ll also introduce a time limit to make it progressively harder (as it currently stands, it’s not much of a challenge to collect the cups because you can take all day).

The source for this post is here. Again, not everything is in the post, so please refer to the repository.

Levels

Because we are creating levels, we’ll need to track the level that we’re on, so a new state property is in order:

this.state = {
	playerX: 100,
	playerY: 100,
	windowWidth: window.innerWidth,
	windowHeight: window.innerHeight,
	playerMomentum: 0,
	playerRotation: 0,
	playerVelocityX: 0,
	playerVelocityY: 0,
	playerLives: 3,
	playerCrashed: false,
	gameLoopActive: false,
	message: "",
	score: 0,
	level: 1,
	cupCount: 1, 
	remainingTime: 0
};

If you’ve followed this through from this first post, you may be asking yourself: “Is he ever going to refactor and clean this up!?”

To which I confidently respond:

“Probably!”

Anyway, you’ll notice that we have the level, the score, the time and the cup count. Advancing through the levels is conceptually just a number; here’s the code that completes a level:

completedLevel() {
	if (this.state.level >= 10) {
		this.updateMessage("Congratulations, you've completed the game");
	} 
	this.startLevel(this.state.level + 1);
}

startLevel is a slight refactor, which essentially sets the cup count and level to the new value – we’ll come back to that shortly.

You can only complete a level by collecting enough cups, so the trigger should be in the cup collection:

collectedCup(key) {
	this.setState({ 
		score: this.state.score + 1 
	});
	this.cups = this.cups.filter(cup => cup.key != key);
	this.updateMessage("Collected cup");
	if (this.cups.length == 0) {
		this.completedLevel();
	} 
}

As soon as we’re down to 0 cups, we call completedLevel.

Time

Now it’s time to have a look at the startLevel code:

startLevel(level) { 
	this.setState({
		level: level,
		cupCount: level * 2 
	}); 
	this.obstacles = this.buildObstacles(); 
	this.cups = this.placeCups();
	this.resetCarPosition();
	this.totalLevelTimeMS = (this.TOPLEVEL - (this.state.level - 1)) * 60 * 1000
	let startLevelTimeMS = (new Date()).getTime();
	this.endLevelTimeMS = startLevelTimeMS + this.totalLevelTimeMS; 
}

We’re working out when the user is out of time, and storing that in endLevelTime. Note that none of these are in state variables – the only state variable is in updated in the game loop:

let remaining = (this.endLevelTimeMS - (new Date()).getTime()) / 1000;
if (remaining <= 0) {
	this.updateMessage("Out of time!");
	this.playerDies();
}
this.setState({
	remainingTime: Math.round(remaining)
}); 

This is at the end of the game loop: we’re updating the remainingTime state variable, but first, we calculate it and, if it’s zero, the player dies (loses a life).

We need to tweak the code for the player dying, because otherwise the timer will never get reset:

playerDies() { 
	this.setState({
		playerLives: this.state.playerLives - 1,
		gameLoopActive: false
	});
	if (this.state.playerLives <= 0) {
		this.initiateNewGame();
	} else {
		this.startLevel(this.state.level);
	}
	this.repositionPlayer();
	this.setState({ 
		playerCrashed: false,
		gameLoopActive: true
	});
}

The last part is to make the time look a bit better with another of my patented icons. GameStatus.jsx should now return the following:

    return (      
        <div className="flex-container" style={flexStyle}>
            <label style={labelStyle}>
                Lives Remaining: {props.Lives}
            </label>
            <label style={labelStyle}>
                Score: {props.Score}
            </label>
            <label style={labelStyle}>
                Level: {props.Level}
            </label>            
            
            <div style={containerStyle}>  
                <img src={clockImg} style={imgStyle} />
                <div style={textDivStyle}>{props.RemainingTime}</div>
            </div>

            <label style={labelStyle}>
                {props.Message}
            </label>
        </div>  
    );

There are some new styles here so that the time appears over the clock icon:

    const containerStyle = {
        position: 'relative',
        textAlign: 'center',
        color: 'red'
    }

    const textDivStyle = {        
        position: 'absolute',
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)',
        zIndex: 1,
        fontWeight: 'bold'
    }

    const imgStyle = {
        width: '100%',
        zIndex: 0
    }

In the next part, we’ll implement a high score table.

Creating a Car Game in React – Part 4 – Score

This is the fourth part of a series (that began here). So far, we have a game where you can whizz around the screen avoiding trees. This effectively encompasses most aspects of a game of this type; that is, you can move, and there is a something preventing you.

The next step is to introduce something for the player to try to do, and give a score based on that. In our case, our player is going to try and collect cups – as per the original game. The asset I’ve created for the cup is here. For anyone following this series, you may have noticed that my artwork is a little… crap.

The source for this post is here.

Cups

The first thing that we’ve done here is added some code to place a few cups around the screen; the code for this was essentially the same as build obstacles:

placeCups() {
	let cups = [];
	const cupCount = 1;
	for (let i = 1; i <= cupCount; i++) {
		const centreX = Math.floor(Math.random() * this.state.windowWidth) + 1;
		const centreY = Math.floor(Math.random() * this.state.windowHeight) + 1; 
		cups.push(<GameItem key={i} image={cupImg} centreX={centreX} centreY={centreY} width={this.spriteWidth} height={this.spriteHeight} itemType={2} />);
	}
	return cups;
}

In a later post, I hope to do a full refactor, but for now, we have a separate function. This is rendered in the same way as the obstacles:

render() { 
	return <div onKeyDown={this.onKeyDown} tabIndex="0">
		<GameStatus Lives={this.state.playerLives} Message={this.state.message} Score={this.state.score} />
		
		<Background backgroundImage={backgroundImg}
		windowWidth={this.state.windowWidth} windowHeight={this.state.windowHeight} /> 
		
		<Car carImage={this.state.playerCrashed ? brokenCarImg : carImg} 
		centreX={this.state.playerX} centreY={this.state.playerY} 
		width={this.spriteWidth} height={this.spriteHeight} 
		rotation={this.state.playerRotation} /> 
		
		{this.obstacles} 
		
		{this.cups} 
	</div>
}

Collecting cups

In order to collect something, the player must collide with it. We need to change the collision code slightly to make it a little more re-usable:

detectAnyCollision(rect1) { 
	// Have we crashed or left the screen
	if (this.detectOutScreen(rect1)) {
		return true;
	}
	let collided = this.detectGameItemCollision(this.halfWidth, this.halfHeight, rect1, this.obstacles);
	if (collided !== undefined) {
		return true;
	}
	return false;
}

detectGameItemCollision(halfWidth, halfHeight, rect1, gameItemList) {
	const collided = gameItemList.find(a => {
		var rect2 = {
			x: a.props.centreX - halfWidth, y: a.props.centreY - halfHeight,
			width: this.spriteWidth, height: this.spriteHeight
		};
		return (this.detectCollision(rect1, rect2));
	}); 
	return collided;
}

As you can see, we now have a function that returns the item that we collided with, rather than a simple boolean. We then use this at the end of the game loop to determine whether we collided with a cup:

// Check for collected cup
const item = this.detectGameItemCollision(this.halfWidth, this.halfHeight, rect1, this.cups);
if (item !== undefined) {
	this.collectedCup(item.key);
} 

Score

There’s little point in zooming around collecting cups, if there’s no permanent record, so we need to add a score. Let’s start with a state variable in game.jsx:

this.state = {
	playerX: 100,
	playerY: 100,
	windowWidth: 1500,
	windowHeight: 1500,
	playerMomentum: 0,
	playerRotation: 0,
	playerVelocityX: 0,
	playerVelocityY: 0,
	playerLives: 3,
	playerCrashed: false,
	gameLoopActive: false,
	message: "",
	score: 0
};

And here’s the collectedCup function we mentioned a second ago:

collectedCup(key) {
	this.setState({ score: this.state.score + 1 });
	this.cups = this.cups.filter(cup => cup.key != key);
	this.updateMessage("Collected cup");
}

All we’re doing here is simply updating the score and then removing that cup from the list.

The final part is to display the score on the screen; let’s have a look at our updated GameStatus.jsx:

function GameStatus(props) {
	const flexStyle = {
		display: 'flex',
		position: 'absolute',
		zIndex: 1,
		margin: 20,
		justifyContent: 'center',
		alignItems: 'center',
		width: '100%'
	};
	const labelStyle = { 
		zIndex: 1,
		margin: 50
	};
	return ( 
		<div className="flex-container" style={flexStyle}>
			<label style={labelStyle}>
				Lives Remaining: {props.Lives}
			</label>
			<label style={labelStyle}>
				Score: {props.Score}
			</label>
			<label style={labelStyle}>
				{props.Message}
			</label>
		</div> 
	);
}

As you can see, we’re just displaying the score as part of the status.

In the next post, we’ll have a look at the concept of levels, and introduce a time limit.

Reference

https://www.w3schools.com/css/css3_flexbox.asp

Creating a Car Game in React – Part 3 – Collision

In this, the third post of this series, we’re going to add collision to the game. For a full list of the code, please see here.

If you’re wondering about earlier posts, please start here.

Since we’re introducing collision, we’ll also need to introduce the age old game concept of “Lives”. The premise here is that when you crash into something, you lose a life.

The first step is to add a new state variable to hold the player’s remaining lives:

this.state = {
	playerX: 100,
	playerY: 100,
	windowWidth: 1500,
	windowHeight: 1500,
	playerMomentum: 0,
	playerRotation: 0,
	playerVelocityX: 0,
	playerVelocityY: 0,
	playerLives: 3,
	gameLoopActive: false,
	message: ""
};

If you have a look in the repository, there’s a bit of refactoring, where I’ve taken some of the setState code and separated it into logical functions. I won’t list that here.

Collision Detection

At the end of the game loop, we now have a call to check if we’ve collided with anything:

if (this.detectAnyCollision()) {
	this.PlayerDies(); 
}

The collision detection code is quite straight forward, and is based on the simplistic idea that all objects can be considered rectangles. Whilst this is not precise, it’s sufficient for our purpose:

detectAnyCollision() { 
        const halfWidth = this.spriteWidth / 2;
        const halfHeight = this.spriteHeight / 2;

        let rect1 = {x: this.state.playerX - halfWidth, y: this.state.playerY - halfHeight, 
            width: this.spriteWidth, height: this.spriteHeight}

        if (this.detectOutScreen(rect1)) {
            return true;
        }

        return this.obstacles.some(a => {
            var rect2 = {x: a.props.centreX - halfWidth, y: a.props.centreY - halfHeight, 
                width: this.spriteWidth, height: this.spriteHeight}
            
            if (this.detectCollision(rect1, rect2)) {
                return true;
            } else {
                return false;
            }
        });
}

detectCollision(rect1, rect2) {
	if (rect1.x < rect2.x + rect2.width &&
	rect1.x + rect1.width > rect2.x &&
	rect1.y < rect2.y + rect2.height &&
	rect1.y + rect1.height > rect2.y) {
		return true;
	}
	return false;
}

detectOutScreen(rect1) {
	if (rect1.x < 0 || rect1.x + rect1.width > this.state.windowWidth
	|| rect1.y < 0 || rect1.y + rect1.height > this.state.windowHeight) {
		return true;
	}
	return false;
}

The collision detection code itself was pilfered from here. As you can see, all we’re doing is translating our objects into rectangles, and then seeing if they intersect each other, or if the player has left the game area.

Quick note about forEach and some

I had originally used .forEach for the detectAnyCollision() code. Whilst it would, initially make sense to a C# programmer, in fact the Javascript version of this does exactly what it says on the tin; that is, it executes for each element, and there is no way to exit early!

Player Dies and Score

Now that we have introduced collision, we should consider what to do when it happens. The usual thing in a game is that the player either “dies”, or they lose “health”. Since this is inspired by a spectrum game, we’ll go with “dies”. You saw earlier that we introduced the concept of “lives” and, because it was a spectrum, it has to be 3!

The code to deal with the player death is:

PlayerDies() { 
	this.setState({
		playerLives: this.state.playerLives - 1,
		gameLoopActive: false
	});
	if (this.state.playerLives <= 0) {
		this.initiateNewGame();
	} else {
		this.resetCarPosition();
	}
	this.repositionPlayer();
	this.setState({ 
		gameLoopActive: true
	});
}

Just a quick reminder that this isn’t a comprehensive listing of code – please see the GitHub repository for that; however, apart from the reduction in lives, the most important thing here is the gameLoopActive code.

The idea here is that we only execute the game loop while this state variable is set; which means we can stop the game loop while we’re dealing with the player’s collision.

The change in the game loop code for this is very simple:

gameLoop() {
	if (!this.state.gameLoopActive) return;

 . . . 

Crashed Car

All well and good, but as it stands, this simply results in the car stopping when it hits a tree, and then being re-positioned. We can address this by adding a small “animation” to indicate a crash. If you have a look here, you’ll see why I’ve won several awards for my graphics*!

In order to plug this in, we’re going to change the car graphic binding:

render() { 
return <div onKeyDown={this.onKeyDown} tabIndex="0">
	<GameStatus Lives={this.state.playerLives} Message={this.state.message}/>
	<Background backgroundImage={backgroundImg}
	windowWidth={this.state.windowWidth} windowHeight={this.state.windowHeight} /> 
	
	<Car carImage={this.state.playerCrashed ? brokenCarImg : carImg} 
	centreX={this.state.playerX} centreY={this.state.playerY} 
	width={this.spriteWidth} height={this.spriteHeight} 
	rotation={this.state.playerRotation} /> 
	
	{this.obstacles} 
</div>
}

So, where the crashed flag is set, we’re binding to brokenCarImg; otherwise to carImg; they are defined at the top:

import carImg from '../Assets/Car.png';
import brokenCarImg from '../Assets/Crash.png';

We also split the playerDies() function into two:

playerDying(tillDeath) {
	this.setState({
		playerCrashed: true,
		gameLoopActive: false
	});
	this.stopCar();
	setTimeout(this.playerDies.bind(this), tillDeath);
}

playerDies() { 
	this.setState({
		playerLives: this.state.playerLives - 1,
		gameLoopActive: false
	});
	if (this.state.playerLives <= 0) {
		this.initiateNewGame();
	} else {
		this.resetCarPosition();
	}
	this.repositionPlayer();
	this.setState({ 
		playerCrashed: false,
		gameLoopActive: true
	});
}

All we’re doing here is calling the first function, which effectively just changes the image and then calls the second function on a timeout. Again, don’t forget the `.bind()` when you call timeout, otherwise, you won’t be able to access `this`!

Footnotes

* I haven’t actually won any awards for graphics – I had you fooled, though!

References

https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection

https://stackoverflow.com/questions/34653612/what-does-return-keyword-mean-inside-foreach-function/34653650

https://medium.com/@benjamincherion/how-to-break-an-array-in-javascript-6d3a55bd06f6

Creating a Car Game in React – Part 2 – Steering and Obstacles

In the second part of this series, we’re going to add some steering to our car, and introduce a concept of obstacles. If you’re wondering what this is the second part of, please start here.

The GitHub repo for this post can be found here.

In the first post of the series, we added a car, and allowed the user to move it around the screen in a very crude fashion; here, we’re going to change the controls to feel a little more like a car. As with the previous post, not every single change will be here, but it will be in the GitHub repo above.

Steering and Acceleration

Let’s have a look at the controls in the Game component:

onKeyDown now looks a little different:

onKeyDown(e) {
	switch (e.which) {
		case 37: // Left
			this.playerSteer(-10);
			break;
		case 38: // Up
			this.playerAccelerate(0.3);
			break;
		case 39: // Right
			this.playerSteer(10);
			break;
		case 40: // Down
			this.playerDecelerate(-0.5);
			break;
		default:
			break;
	}
} 

So, we’re no longer simply repositioning the car, but applying forces to it. Initially, I had Down as simply a negative acceleration, meaning that if you break too hard, you go backwards! Here’s the three functions referenced above:

playerAccelerate(speed) {
	this.setState({
		playerMomentum: this.state.playerMomentum + speed
	});
}

playerDecelerate(speed) {
	if (this.state.playerMomentum > 0) {
		this.setState({
			playerMomentum: this.state.playerMomentum + speed
		});
	} else if (this.state.playerMomentum < 0) {
		this.setState({
			playerMomentum: this.state.playerMomentum - speed
		});
	}

}

playerSteer(direction) {
	this.setState({
		playerRotation: this.state.playerRotation + direction
	});
}

There are a number of new state variables, which I won’t list here. However, because we are no longer repositioning the car based on the user action, we need to apply the forces that we are changing; that is, we need a game loop.

Game Loop

The game loop code is relatively complex. Looking at this should make you seriously consider using a a game engine of some description:

gameLoop() { 
	const radians = (this.state.playerRotation - 90) * Math.PI / 180; 
	const aX = (this.state.playerMomentum * Math.cos(radians));
	const aY = (this.state.playerMomentum * Math.sin(radians));
	const velocityX = this.state.playerVelocityX;
	const velocityY = this.state.playerVelocityY;
	const velocitySq = Math.pow(velocityX, 2) + Math.pow(velocityY, 2);
	const posSq = Math.pow(aX, 2) + Math.pow(aY, 2);
	const velocityPosSq = Math.pow(velocityX * aX + velocityY * aY, 2);
	let skidFactor = (posSq == 0 || velocitySq == 0) ? 0 : 1 - (velocityPosSq / posSq / velocitySq);
	
	if (skidFactor <= 0) skidFactor = 0; 
	
	this.setState({
		playerVelocityX: (skidFactor * velocityX) + ((1 - skidFactor) * aX),
		playerVelocityY: (skidFactor * velocityY) + ((1 - skidFactor) * aY)
	}); 
	this.playerMove(
		this.state.playerX + this.state.playerVelocityX,
		this.state.playerY + this.state.playerVelocityY 
	);
	this.playerDecelerate(-(0.1 + skidFactor));
}

If you’re wondering where this brain-melting maths comes from, have a look here.

I’ve split it up in an effort to make it slightly more understandable, but the premise is that if you’re travelling fast and change direction suddenly, it doesn’t immediately turn. Again, if you’re thinking you don’t want to be messing around with this kind of thing then a lot of game engines take care of this for you.

Obstacles

Finally, we have our obstacles. There is no collision just yet, but this basically puts pictures of trees around the screen (incidentally, I did all the artwork myself, which I assume the reader to be suitably impressed by!) We’ll come back to this later to make the collision work:

buildObstacles() {
	let obstacles = [];
	const obstacleCount = Math.floor(Math.random() * 10) + 1;
	console.log('Obstacle count ' + obstacleCount);
	for (let i = 1; i <= obstacleCount; i++) {
		const centreX = Math.floor(Math.random() * this.state.windowWidth) + 1;
		const centreY = Math.floor(Math.random() * this.state.windowHeight) + 1;
		
		obstacles.push(<Obstacle key={i} image={treeImg} centreX={centreX} centreY={centreY} width={this.spriteWidth} height={this.spriteHeight} />);
	}
	return obstacles;
}

All this function does is build up an array of HTML; we then feed that into a class variable in the constructor:

constructor(props) {
	super(props);
	document.body.style.overflow = "hidden";
	this.state = {
		playerX: 100,
		playerY: 100,
		windowWidth: 1500,
		windowHeight: 1500,
		playerMomentum: 0,
		playerRotation: 0,
		playerVelocityX: 0,
		playerVelocityY: 0
	};
	this.spriteWidth = 25;
	this.spriteHeight = 25;
	this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
	this.onKeyDown = this.onKeyDown.bind(this); 
	this.obstacles = this.buildObstacles(); 
}

(It’s worth noting, as an aside, that we are preventing scrolling here by setting document.body.style.overflow)

Finally, we’ll display it in the render method:

render() { 
	return <div onKeyDown={this.onKeyDown} tabIndex="0">
		<Background backgroundImage={backgroundImg}
		windowWidth={this.state.windowWidth} windowHeight={this.state.windowHeight} /> 
		
		<Car carImage={carImg} centreX={this.state.playerX} 
		centreY={this.state.playerY} width={this.spriteWidth}
		height={this.spriteHeight} rotation={this.state.playerRotation} /> 
		
		{this.obstacles} 
	</div>
}

This technique allows you to build an array of HTML objects dynamically. The thing to notice here is the ‘key’ that we’re passing through; if you don’t pass this, you’ll start getting the following error:

Each child in a list should have a unique “key” prop.

In the next post, we’ll introduce collision.

References

https://stackoverflow.com/questions/5192983/calculating-x-y-movement-based-on-rotation-angle

https://gamedev.stackexchange.com/questions/172571/how-to-implement-a-slight-skid-in-a-car-game

https://stackoverflow.com/questions/39962757/prevent-scrolling-using-css-on-react-rendered-components