Tag Archives: VueJs

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.

Vue Basics – Moving Objects Around the Screen

I’ve recently been playing around with Vue. One of the things that I typically do when I’m learning some kind of front-end technology, is to work out how I might write a game in that technology – that usually tests the boundaries of what you can do. If you’re interested, this is a car game that I created in React.

Let’s start by having a look at what a bog standard “Hello World” app looks like in Vue:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Testing Vue</title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
        
        <div id="app">
            <h1>{{ title }}</h1>
        </div>
        <script>
            new Vue({
                el: '#app',
                data: {
                    title: 'Hello World'
                }
            });
        </script>        
    </body>
</html>

Another way that you can write that, would be like this:

<p v-text="testdata"></p>

This kind of synthax actually lets you bind any property, for example:

<h1 v-bind:title="testdata"></h1>

That’s pretty much the bit that I was looking for; if you can bind the title, then you can bind the style; for example:

<div v-bind:style="location">test</div>

Inside the data section, I can expose a location; for example:

new Vue({
    el: '#app',
    data: {
        testdata: 'some data',
        location: 'width: 500px; height: 100px; left: 20px;border: 1px solid #000; position: absolute;'
                    
    },

Okay, so now we’re binding the style to a piece of data, so can we now do something like this:

data: {
    testdata: 'some data',
    posx: 100,
    location: 'width: 500px; height: 100px; left:' + this.posx + 'px; border: 1px solid #000; position: absolute;'
},

Unfortunately (or perhaps fortunately, I haven’t decided yet) not. Anything that changes needs to be in a separate section, called computed:

data: {
    testdata: 'some data',
    posx: 100                    
},
computed: {                    
    location: function() {
        return 'width:500px; height:100px; left:' + this.posx + 'px; border:1px solid #000; position: absolute;';
    } 
},

We’re actually nearly there now; all that’s missing is the thing that actually updates the variable, and therefore moves the div across the screen. The next thing we come across is the mounted method – this is a lifecycle event for each component, so we’ll need that to initialise our timer; now we can do this:

methods: {
    init() {
        setInterval(this.moveObject, 10);
    },
    moveObject() {
        this.posx += 1;                        
    }
},
mounted() {
    this.init();
}

It’s not hugely exciting, I’ll grant you, but moving an object around a screen is the basis for any game. For completeness, here’s the full code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Testing Vue</title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
        <div id="app">
            <h1>{{ testdata }}</h1>
            <p v-text="testdata"></p>
            <div v-bind:style="location">test</div>
        </div>
        <script>
            new Vue({
                el: '#app',
                data: {
                    testdata: 'some data',
                    posx: 100                    
                },
                computed: {                    
                    location: function() {
                        return 'width:500px;height:100px; left:' + this.posx + 'px;border:1px solid #000; position: absolute;';
                    } 
                },
                methods: {
                    init() {
                        setInterval(this.moveObject, 10);
                    },
                    moveObject() {
                        this.posx += 1;                        
                    }
                },
                mounted() {
                    this.init();
                }
            });
            
        </script>        
    </body>
</html>

References

Vue.js Tutorial From Scratch – e01 – Introduction, Installation & Outputting Data