📜 ⬆️ ⬇️

Riding a tank - Part 1 - The battlefield

On this beautiful, no different from the rest of the day (if you do not take into account the fact that the cold weather broke into fragments all the dreams of at least a couple of warm days this year that would ride a bike), I had a good mood. And it’s good even despite the fact that I despaired of putting hackintosh on my laptop, and came to the disappointing thought that if I want to write at least something under iOS, then I need to save on Apple’s products. But the post itself is not about that, but about the small life of a tiny toy that we will try to create with you. Honestly, in good times I didn’t play so much in the ancestor of our creation (which doesn’t even exist), but there are still some fond memories in my heart. So ... Tanks! Tanchiki! Battle Tanks! BattleCity!
We will write on JavaScript with output to canvas.

0. Instead of introducing


Well, you clicked on the link, and got to the point after which the return is no longer, and that means you at least once, and see the result of what we do. As a rule, those who read such posts are divided into two categories: the first are those who read the post and consistently try code snippets (for them, actually, the post), and those who quickly read the post and in the end prefer to look at the result, which I post separately. As you know, html files and JavaScript are nothing but ordinary text files, if you never wrote either the first or the second (which I doubt a little considering the habr audience, but you never know), then in principle you can use any notebook-like program to fill I’m writing these files in IDE Geany and it’s pretty much out of date for me, the test browser isn’t particularly critical, the requirement for it is only that it’s relatively modern, and preferably the latest version. Let's write the game into four posts:

1. What we want to see


And to describe the modest plans for the game, I singled out a separate part of the post. The result should be a game similar to the tanks, which are familiar to us all from the NES console (more familiar in the CIS as Dendy). From the original game we will borrow the appearance of a brick block, a concrete block, as well as the first level itself.
The game should work on all major browsers (yes, yes, and even under Internet Explorer), and also try to make it comfortable to play in mobile browsers (or, more precisely, mobile Safari, Android browser, and again mobile donkey).

2. The folder structure of the game and the HTML file


Initially, we need an HTML5Tank folder in which we will place only one index.htm file, and in it another data folder, in which the JavaScript files will be located. index.htm should contain approximately the following:
<html> <head> <meta charset = "utf-8"> <title>HTML5Tank</title> <!--      ,        init.js --> <script src="data/init.js"></script> </head> <body> <!--   --> <canvas id="game">?!</canvas> <!--   init,   ,       --> <script>init();</script> </body> </html> 


3. Draw a map


A small educational program: a tile is a small fragment that can be drawn, for example, on our map, for example, a fragment of a brick wall, a fragment of grass, and so on. A tileset is an image on which all the necessary fragments are plotted.
We will not use image files, but there will be some kind of tileset drawn on canvas buffer, from which we will take the pieces, and draw them on the canvas, creating a battlefield. All elements of the tiles will be drawn with rectangles, so that if you wish, you can give them any size.
Initially, we will create the init.js file in the data folder and describe the init function in it, which changes the size of the canvas and paints it in black. It is also worth noting that we will immediately describe the cellSize variable which will have the value of the width of a large cell (it is the width of the tank). Create an init.js file in the data folder and describe the init function in it that changes the size of the canvas.
 var size = 32; //    function init () { var canvas = document.getElementById("game"); canvas.width = 16 * size; //    canvas.height = 14 * size; //    var context = canvas.getContext("2d"); //   context.fillStyle = "#000"; //   context.fillRect(0, 0, canvas.width, canvas.height); } 

Next, we need to draw the tileset, about which I have already written, and for the sake of interest, we will display its contents on the main canvas. Subsequently, of course we will not do this, but now we cannot do without it. Add in html file line:
 <script src="data/tiles.js"></script> 

We will create the tiles.js file and we will need to write the function drawTiles in it. In the init function, create a new canvas buffer, and draw the contents of the tileset into it. So, let's add the following to init:
 //  - var tileSetBuffer = document.createElement("canvas"); //     drawTiles(tileSetBuffer); //    ,   ,   ,    //        context.drawImage (tileSetBuffer, canvas.width/2-tileSetBuffer.width/2, canvas.height/2-tileSetBuffer.height/2); 

And what would be some effect we need to draw tiles:
 //   "" function drawTiles (canvas) { var context = canvas.getContext('2d'); context.globalAlpha = 1; //   var empty = function () { context.fillStyle = '#000'; context.fillRect(0, 0, size/2, size/2); } //   var brick = function () { //     context.fillStyle = "#ffff00"; context.fillRect(0, 0, size/2, size/2); //   context.fillStyle = "#808080"; context.fillRect(0 , 0 , size/2 , size/16); context.fillRect(0 , 0+size/4 , size/2 , size/16); context.fillRect(size/4 , 0 , size/16, size/4 ); context.fillRect(size/16, size/4 , size/16, size/4 ); //     context.fillStyle = "#c0c0c0"; context.fillRect(0 , 3*size/16, size/2 , size/16); context.fillRect(0 , 7*size/16, size/2 , size/16); context.fillRect(3*size/16, 0 , size/16, size/4 ); context.fillRect(0 , 3*size/16, size/16, size/4 ); } //   var hbrick = function () { //    context.fillStyle = "#c0c0c0"; context.fillRect(0, 0, size/2, size/2); //   context.fillStyle = "#808080"; context.beginPath(); context.moveTo(0 , size/2); context.lineTo(size/2, size/2); context.lineTo(size/2, 0 ); context.fill(); //     context.fillStyle = "#fff"; context.fillRect(size/8, size/8, size/4, size/4); } //  - canvas.width = 3 * size/2; //   canvas.height = size/2; //   //   context.save(); empty(0, 0); context.translate(size/2, 0); brick(size/2, 0); context.translate(size/2, 0); hbrick(size, 0); context.restore(); } 

If you open now the file index.htm, then you can see the first result. These rectangles in the center of the canvas are small parts of our large map, which we are going to draw up now.
So, the battlefield has dimensions of 13 to 13 fragments. We define it with a two-dimensional array (or rather, due to some features of JavaScript array files), which will be filled with some values. We agree on the fact that 0 this will be our empty cell, 1 is a brick block, 2 is a block of concrete.
I got this array:
 [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1], [2, 2, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ]; 

But before we create some method that will run over these values ​​and draw them ... again to the buffer. And already from the buffer on the main canvas. Why do you need it? Now it does not really matter where to draw, on the canvas immediately or in the buffer, but in the future this will make life a little easier for us.
So, we need another map.js file which we again create in the data folder and add a line to index.htm
 <script src="data/map.js"></script> 

In the map.js file, create a new map class:
 function map () { var arr; //       //   this.setArr = function (a) { arr = a; } //      this.draw = function (canvas, tileSet) { var ctx = canvas.getContext('2d'); canvas.height = 13 * size; canvas.width = 13 * size; ctx.globalAlpha = 1; //          //   1     //  2,    for (var j = 0; j < 26; j++) { for (var i = 0; i < 26; i++) { switch (arr[j][i]) { case 0: ctx.drawImage(tileSet, 0, 0, size/2, size/2, i*size/2, j*size/2, size/2, size/2); break; case 1: ctx.drawImage(tileSet, size/2, 0, size/2, size/2, i*size/2, j*size/2, size/2, size/2); break; case 2: ctx.drawImage(tileSet, size, 0, size/2, size/2, i*size/2, j*size/2, size/2, size/2); break; } } } } } 

Now we will make changes to the init function, adding everything necessary to draw a map on the canvas:
 //  -   var mapBuffer = document.createElement("canvas"); //   field var field = new map(); //  - field.setArr([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1], [2, 2, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ]); //     field.draw(mapBuffer, tileSetBuffer); context.save(); context.translate(size/2, size/2); //     context.drawImage (mapBuffer, 0, 0); context.restore(); 

Result



Well, I think it’s enough for the first post, the code was written almost simultaneously with the writing of the post, and therefore it can limp somewhere. In the next post we will put a tank on the map and teach it to drive around the map, shoot and crush the walls, and do some more various dirty tricks. Well, that's all for now. And since the author saves on MacMini (to fill up AppStor with his own graphic HellWorld), he sometimes needs to earn money. Thanks to all.

')

Source: https://habr.com/ru/post/127694/


All Articles