Create a Simple Shooting Game Using Vue Js

The best way to learn a new programming language is by creating some small interesting application. In this tutorial, I will try to cover the basic of Vue Js and HTML Canvas by making a small interesting UFO Shooting game. It will be useful for beginners who are learning Vue Js. The Vue js is an Upcoming Progressive JavaScript Framework. It is one of the fastest growing Javascript frameworks and also very easy to learn. I am only using VanillaJS, Vue Js and Lodash ( A modern JavaScript utility library delivering modularity, performance & extras. ) for making this game and Bootstrap 4 Beta and some little custom CSS for designing.

The games logic is pretty simple. The total time will be 50 seconds and every second a UFO will appear at random position on the screen and if you click on UFO you got a point. The UFO is drowned by a regenerate method which is mounted when the Vue app loads for the first time. When the game over a New Canvas is shown with Game Over Text. So the game logic is very basic and now let's see how we can achieve it. Before we start coding let's see the Basic of HTML Canvas.

HTML Canvas

The HTML <canvas> element is used to draw graphics, on the fly, via scripting (usually JavaScript). The <canvas> element is only a container for graphics. You must use a script to actually draw the graphics. Canvas has several methods for drawing paths, boxes, circles, text, and adding images.

Draw on the Canvas With JavaScript

All drawing on the HTML canvas must be done with JavaScript. Here we will be using Vue js for simplicity.

Step 1: Find the Canvas Element

First of all, you must find the <canvas> element. This is done by using the $refs method in Vue js:

this.canvas = this.$refs.canvas;

Step 2: Create a Drawing Object

Secondly, you need a drawing object for the canvas. The getContext() is a built-in HTML object, with properties and methods for drawing.

this.ctx = this.canvas.getContext("2d");

Step 3: Draw on the Canvas

Finally, you can draw on the canvas. Set the fill style of the drawing object to the colour white.

this.ctx.fillStyle = "#fff";

fillStyle property can be a CSS colour, a gradient, or a pattern. The default fillStyle is black.

The fillRect(x, y, width, height) method draws a rectangle, filled with the fill style, on the canvas.

this.ctx.fillRect(this.x,this.y,5,5);

Canvas - Images

To draw an image on a canvas, use the following method: drawImage(image,x,y).

this.img = this.$refs.image; // Get Image Element this.ctx.drawImage(this.img,this.imgX,this.imgY); // Draw the Image

Let's Start Coding

The Complete code of the view page is given below.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <title>UFO Shooting Game</title> <!-- Bootstrap --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" > <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div id="app"> <div class="container"> <div class="row"> <div class="col-md-12"> <p> </p> <h2 class="text-center">UFO Shooting Game</h2> <canvas id="myCanvas" v-show="!ended" ref="canvas" @click="shoot" ></canvas> <canvas id="myCanvas" v-show="ended" ref="endCanvas" ></canvas> <div id="health" v-show="!ended"> <div v-bind:style="{ width: health+'%'}"></div> </div> <div class="text-center"> <h4>Points : {{ points }}</h4> </div> <div id="controls"> <button @click="start" v-show="!ended" class="btn btn-primary" >Start</button> <button @click="restart" class="btn btn-default">Restart</button> </div> <div id="hidden-items"> <img src="ufo.jpg" ref="image"> <audio ref="audio" volume="1" preload="auto"> <source src="pop.mp3"> </audio> </div> </div> <!-- end of .col-md-12 --> </div> <!-- end of .row --> </div> <!-- end of .container --> </div> <!-- end of #app --> </body> <script src="https://unpkg.com/[email protected]/dist/vue.js"></script> <script src="https://unpkg.com/[email protected]/lodash.min.js"></script> <script type="text/javascript" src="main.js"></script> </html>

There are two HTML canvas's in the view page. The first one is used to draw the game and the second one is used to show the game over message. The second canvas only displayed when the game is ended. The progress bar is used to indicate the game time and its widths attribute is bound to the health model. The start button is used to start the game which is bounded to start method at click event and the restart button is used to restart the game which is also bound to restart method on click event.

Now let's see the codes inside the main.js file.

var app = new Vue({ el: "#app", data: { canvas: "", ctx:"", img:"", imgX: "", imgY: "", x:"", y:"", points:0, health:100, started: false, ended: false, }, methods:{ start(){ this.started = true; }, shoot(event){ if(this.health > 0 && this.started == true) { this.$refs.audio.play(); // Play Shooting Sound // Get the absolute X and Y position of mouse pointer related to our canvas let rect = this.canvas.getBoundingClientRect(); // abs. size of element let scaleX = this.canvas.width / rect.width; // relationship bitmap vs. element for X let scaleY = this.canvas.height / rect.height; // relationship bitmap vs. element for Y this.x = (event.clientX - rect.left) * scaleX, // scale mouse coordinates after they have this.y = (event.clientY - rect.top) * scaleY // been adjusted to be relative to element // Check Whether Click is inside UFO Image if(_.inRange(this.x,this.imgX,this.imgX+50) && _.inRange(this.y,this.imgY,this.imgY+50) ){ this.points += 1; // Increment Point to 1 // Draw a rectangular gun shooting image on top of UFO Image this.canvas = this.$refs.canvas; this.ctx = this.canvas.getContext("2d"); this.ctx.fillStyle = "#fff"; this.ctx.fillRect(this.x,this.y,5,5); } }else{ if(this.health <= 0 ){ alert("Game Finished Please Restart"); } else{ alert("Game Not Yet Started !"); } } }, regenerate(){ if(this.health > 0 && this.started == true) { this.canvas = this.$refs.canvas; this.ctx = this.canvas.getContext("2d"); // Clear Old UFO Image this.ctx.clearRect(this.imgX,this.imgY,this.imgX + 100,this.imgY + 100 ); // Get a random X and Y axis relative to our canvas height and width this.imgX = _.random(0,this.canvas.width-50,false); this.imgY = _.random(0,this.canvas.height-50,false); this.img = this.$refs.image; // Get Image Element this.ctx.drawImage(this.img,this.imgX,this.imgY); // Draw Image in Canvas this.health -= 2; } else if(this.health <= 0) { this.ended = true; // Draw Game over in Secound Canvas this.canvas = this.$refs.endCanvas; this.ctx = this.canvas.getContext("2d"); this.ctx.font = "20px Comic Sans MS"; this.ctx.fillStyle = "white"; this.ctx.textAlign = "center"; this.ctx.fillText("Game Over!",this.canvas.width/2, this.canvas.height/2); } }, restart(){ this.started = false; this.ended = false; this.health = 100; this.points = 0; }, }, mounted(){ self = this; setInterval(function(){ self.regenerate(); }, 1000); } });

The regenerate method starts when the Vue app is initially mounted. The regenerate methods keep calling every second and is used to draw UFO image in our HTML canvas. But it will not draw UFO image unless the health is greater than 0 or game is started. The game is started by calling start method and restarted by calling restart method.

We call clearRect(x, y, width, height) method to clear old image from the canvas. The loadash method _.random([lower=0], [upper=1], [floating]) is called to get a random X axis and Y axis to place our image with respect to total width and height of our canvas.

When shoot method is called every time a shooting sound will produce by calling our hidden HTML audio player using $ref. The special attribute ref is used to register a reference to an element or a child component. The reference will be registered under the parent component’s $refs object.

The lodash method _.inRange(number, [start=0], end) checks if n is between start and up to, but not including, end . If end is not specified, it's set to start with start then set to 0 . If start is greater than end the params are swapped to support negative ranges. It is used to check whether our mouse click is inside UFO image.

Now finally let's improve the look and feel of our application. The codes in the style.css file are given below.

body{ background-size:cover; background-image: url("bgimage.jpg"); } h2,h4{ font-family: Merriweather,serif; font-weight: bolder; color : #e6e6e6; } #myCanvas{ width: 100%; height: 400px; margin : 0 auto; border: 2px solid #e6e6e6; } #health{ width: 100%; border: 2px solid #e6e6e6; margin : 20px auto 20px auto; } #health div{ height: 20px; background: linear-gradient(to bottom, rgba(30,87,153,1) 33%,rgba(48,106,168,1) 46%,rgba(119,179,227,0.7) 96%,rgba(125,185,232,0) 100%); } #controls{ width: 100%; margin: 20px auto; text-align: center; } #hidden-items{ display:none; }

The styling is pretty basic. Just give a background image and hide our hidden elements and a linear gradient for our progress bar. Rest will be handled by Bootstrap.

The output image of the game is given below.

DEMO

You can demo the above game by visiting following link.

https://shareurcodes.com/demo/shooting/

I know it is a pretty crappy game. But the main aim is to show the basic of Vue js and HTML canvas in a very simple and interesting way. If anybody has any suggestions or doubts or need any help comment below and I try will respond to every one of you as early as possible.