Пишем игру с Роботом на jQuery, HTML, CSS

Администратор

Будем делать маленького робота. 🤖 А чем он будет заниматься, я по ходу щас придумаю.

Здесь нам обязательно пригодится jQuery, которую можно скачать здесь (compressed production). Откройте ваш любимый редактор кода. Или скачайте: BracketsSublime TextPyCharm


Приступим..


Создаем три пустых файла classwork3.html, classwork3.cssclasswork3.js. В classwork3.html подключаем стили и скрипты:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<html>
<head>
    <meta charset="UTF-8">
    <title>Робот!</title>
    <link rel="stylesheet" href="classwork3.css">
    <script src="jquery-3.2.1.min.js"></script>
    <script src="classwork3.js"></script>
</head>
<body>

</body>
</html>

 Все подключаемые файлы должны быть в одной папке. 

Так, теперь что у нас будет. Будет поле прямоугольное, состоящее из ячеек. Ячейка может быть пустой или заполнена чем-то: например стеной, деревом или миной. Будем случайным образом заполнять поле. Робот должен пройти невредимым до пункта назначения. Будем управлять роботом клавиатурой и кнопками на экране.

Сейчас найдем в инете несколько картинок для робота, для выхода, для стен, для деревьев и для мин. 

    

Создаем папку img и закидываем все эти картинки в эту папку. 

Игровое поле

Поле у нас будет размером 20 на 15 ячеек. Для игрового поля будем использовать class="game-field". Размер одной ячейки 40х40 пикселей. Для ячеек будем использовать class="cell". 

Если 20 ячеек по ширине и 15 ячеек по высоте и каждая по 40 пикселей, то поле у нас будет размером 800х600 пикселей. Добавим стили для игрового поля в файле classwork3.css:

1
2
3
4
5
6
7
8
.game-field{
    width: 800px;
    height: 600px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 10px;
    box-shadow: 0 0 5px grey;
}

Мы также добавили автоматические отступы слева и справа, чтобы блок был посередине экрана. Отступ сверху на 10 пикселей и тень вокруг блока. Теперь если мы внутри тега <body> добавим блок с классом game-field, то увидим на странице большой прямоугольник с тенью. 

1
2
3
4
5
<body>
    <div class="game-field">

    </div>
</body>

Откройте вашу страницу и посмотрите на прямоугольник с тенью. 

Нам нужно теперь расставить внутри поля ячейки. Ячейки мы будем создавать с помощью javascript. Сделаем двойной цикл и с помощью команды document.write() добавим html элементы внутри блока game-field. У каждой ячейки должен быть id соответствующий ее координатам, поэтому мы будем добавлять id в формате "cell_x_y" - где вместо x будет координата по x, а вместо y будет координата по y. 

Помните как обрабатывается javascript ? Браузер загружает страницу сверху вниз, и когда доходит до тега <script>, выполняет то, что внутри него и идет дальше. Поэтому мы положим наш скрипт внутри блока с классом game-field.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<body>
    <div class="game-field">
        <script>
            for (var y=0; y<15; y++){
                for (var x=0; x<20; x++){

                }
            }
        </script>
    </div>
</body>

Двойной цикл: внешний цикл(y) идет по строкам, внутренний(x) по столбцам. Внутри цикла мы будем добавлять в документ html код ячеек. Сначала в переменную сгенерируем id для ячейки в формате "cell_x_y":

1
var cell_id = "cell_"+x+"_"+y;

Потом добавим тег <div> с классом "cell" и c id равным значению переменной cell_id:

1
document.write("<div class='cell' id='"+cell_id+"'></div>");

И в итоге у нас получится такой код:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<body>
    <div class="game-field">
        <script>
            for (var y=0; y<15; y++){
                for (var x=0; x<20; x++){
                    var cell_id = "cell_"+x+"_"+y;
                    document.write("<div class='cell' id='"+cell_id+"'></div>");
                }
            }
        </script>
    </div>
</body>

Если сейчас обновите вашу страницу, на экране ничего не изменится, но в Инспекторе во вкладке Elements вы должны увидеть много таких элементов:

1
2
3
4
<div class="cell" id="cell_0_0"></div>
<div class="cell" id="cell_1_0"></div>
<div class="cell" id="cell_2_0"></div>
...

Давайте теперь зададим стиль для ячейки: Размер ячейки 40х40. Также добавим стиль display: inline-block чтобы ячейка вела себя как строчный элемент, заполняя всю ширину родительского элемента. Еще в ячейках будут установлены разные картинки как background-image. А картинки у нас могут быть разных размеров. Поэтому, чтобы они помещались в ячейке полностью, добавляем стиль background-size: 40px. Итак добавим это в файл classwork3.css:

1
2
3
4
5
6
7
.cell{
    width: 40px;
    height: 40px;
    display: inline-block;
    background-image: url("img/tree.png");
    background-size: 40px 40px;
}

Также я для примера добавил фоновое изображение дерева. Если сохранив файл, вы обновите страницу, то увидите 300 деревьев на игровом поле. 

Заполнение игрового поля

Игровое поле и ячейки у нас есть. Теперь нам нужно случайным образом заполнить эти ячейки. Для этого мы откроем файл classwork3.js наконец-то и добавим туда функцию onReady() и скажем документу, чтобы он вызывал эту функцию когда он полностью загрузится:

1
2
3
4
5
function onReady(){

}

$(document).ready(onReady);

Теперь внутри функции onReady() мы должны заполнить ячейки. Для хранения состояния ячеек игрового поля и функций связанных с полем, мы создадим специальный объект. И назовем его GameField. Объекты, которые не только хранят какие-то данные, но и имеют свои функции я называю CamelCase-ом. 

Пусть в этом объекте у нас будет свойства width(ширина), height(высота) и cells(ячейки). cells будет двумерным массивом, в котором мы будем хранить состояние игрового поля. Добавим перед функцией onReady() код:

1
2
3
4
5
var GameField = {
    width: 20,
    height: 15,
    cells: [],
}

Теперь в этот объект добавим метод init(), который будет заполнять ячейки случайным образом. Значениями ячеек будут у нас будут текстовые значения "tree" - для дерева, "exit" - для выхода, "empty" - пустая ячейка, "wall" - стена, "bomb" - бомба.

Чтобы каждый раз эти значения не писать как текст, создадим константы и добавим их в начале файла classwork3.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const WALL = "wall";
const EMPTY = "empty";
const TREE = "tree";
const BOMB = "bomb";
const EXIT = "exit";


var GameField = {
    width: 20,
    height: 15,
    cells: [],
}

Теперь вместо того чтобы писать "wall", мы будем использовать константу WALL. Это предотвращает ошибки при частос использовании одного и того же значения. 

Внутри метода init() мы запустим двойной цикл, чтобы заполнить все ячейки. По умолчанию все ячейки будут пустыми. (EMPTY):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var GameField = {
    width: 20,
    height: 15,
    cells: [],

    init: function () {
        for (var x = 0; x < GameField.width; x++) {
            GameField.cells[x] = [];

            for (var y = 0; y < GameField.height; y++) {
                GameField.cells[x][y] = EMPTY;
            }
        }
    }
}

сначала идет цикл по ширине поля (по х). от 0 до 20(width). И при этом в переменную cells мы добавляем внутренний массив на каждое значения х. Потом идет внутренний цикл по высоте поля(по y) от 0 до 15(height). И при этом в ячейку с с координатами x,y мы пишем значение EMPTY.

Если вы в голове можете представить двумерный массив, то получится примерно так:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    0     1     2     3     4     5   ... x
 
0  empty empty empty empty empty empty    

1  empty empty empty empty empty empty    

2  empty empty empty empty empty empty    

3  empty empty empty empty empty empty    

4  empty empty empty empty empty empty    

.

y

Теперь будем заполнять стенами, деревьями и бомбами. 

Сделаем вокруг поля стены. Для этого внутри цикла сделаем проверку. Если х или у равны 0 или крайним значеням поля, то значит это край поля, там должна быть стена:

1
2
3
if(x==0 || y==0 || x==GameField.width-1 || y==GameField.height-1){
    GameField.cells[x][y] = WALL;
}

А если это не край поля, то случайным образом выберем чем заполнять: Сгенерируем слуйчайное чилсло от 0 до 100. Если это число меньше 30 (30% вероятность), то пусть там будем стена. Если слуйчайно число больше 30 и меньше 50 (20% вероятность), то пусть там будет дерево. Если случайное число больше 50 и меньше 60 (10% вероятность), то пусть там будет бомба. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
if(x==0 || y==0 || x==GameField.width-1 || y==GameField.height-1){
    GameField.cells[x][y] = WALL;
} else{
    var random = Math.random()*100;
    if(random < 30){
        GameField.cells[x][y] = WALL;
    } else if(random<50) {
        GameField.cells[x][y] = TREE;
    } else if(random<60) {
        GameField.cells[x][y] = BOMB;
    }
}

так мы заполнили почти все поле, 40% поля осталось пустым. Теперь случайным образом сгерируем координаты выхода. Выход не должен быть на краю, поэтому добавляем 1+ и -2:

1
2
3
4
var exit_x = 1 + Math.round(Math.random()*(GameField.width -2));
var exit_y = 1 + Math.round(Math.random()*(GameField.height -2));

GameField.cells[exit_x][exit_y] = EXIT;

А по координатам 1,1 всегда будет робот, поэтому оставим это место гарантированно пустым:

1
GameField.cells[1][1] = EMPTY; // здесь будет робот

И так, в итоге у нас получился такой метод init:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var GameField = {
    width: 20,
    height: 15,
    cells: [],

    init: function () {
        for (var x = 0; x < GameField.width; x++) {
            GameField.cells[x] = [];

            for (var y = 0; y < GameField.height; y++) {
                GameField.cells[x][y] = EMPTY;

                if(x==0 || y==0 || x==GameField.width-1 || y==GameField.height-1){
                    GameField.cells[x][y] = WALL;
                } else{
                    var random = Math.random()*100;
                    if(random < 30){
                        GameField.cells[x][y] = WALL;
                    } else if(random<50) {
                        GameField.cells[x][y] = TREE;
                    } else if(random<60) {
                        GameField.cells[x][y] = BOMB;
                    }
                }
            }
        }

        var exit_x = 1 + Math.round(Math.random()*(GameField.width -2));
        var exit_y = 1 + Math.round(Math.random()*(GameField.height -2));

        GameField.cells[exit_x][exit_y] = EXIT;
        GameField.cells[1][1] = EMPTY; // здесь будет робот
    },
}

Игровое поле сгенерировано. Теперь отобразим это на странице. Для этого создадим еще одну функция в объекте GameField с названием show:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var GameField = {
    width: 20,
    height: 15,
    cells: [],

    ....

    show: function(){
        for (var x = 0; x < GameField.width; x++) {
            for (var y = 0; y < GameField.height; y++) {
                if(GameField.cells[x][y] == WALL){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/wall.png')");
                } else if(GameField.cells[x][y] == TREE){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/tree.png')");
                } else if(GameField.cells[x][y] == BOMB){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/bomb.png')");
                } else if(GameField.cells[x][y] == EXIT){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/exit.png')");
                }
            }
        }
    }
};

Как вы видите, тут тоже двойной цикл по ячейкам. Проверяем каждую ячейку: если значение ячейки равна значению константы WALL, то по этим координатам есть стена. Чтобы отобразить там стену, возьмем ячейку с id "cell_x_y" где вместо x и y координаты ячейки. У этой ячейке поменяем стиль background-image на "url('img/wall.png')".

И так со всеми типами ячеек. 

У нас все готово для отображения. Теперь чтобы все это запустить, в функции onReady(), сделаем вызов двух методов: init() и show() по порядку:

1
2
3
4
function onReady(){
    GameField.init();
    GameField.show();
}

Сохраняем файл и обновляем страницу(Ctrl+Shift+R) и видим что игровое поле красиво заполнилось. 

И кстати еще в CSS файле уберите стиль background-image: url("img/tree.png"); с класса .cell .

Робот

Давайте теперь добавим самого робота. Робот будет у нас элементом с id="robot" внутри game-field:

1
2
3
4
<body>
    <div class="game-field">
        <div id="robot"></div>
    ...

Он будет отдельным слоем, т.е. отображаться под слоем ячеек. Для этого добавим стиль position: relative; для класса .game-field и следующие стили для робота:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#robot{
    position: absolute;
    background-size: 40px 40px;
    background-image: url("img/robot.png");
    z-index: -1;
    width: 40px;
    height: 40px;
    left: 40px;
    top: 40px;
}

position: absolute означает что местоположение робота будет относительно родителя и независимыми от других элементов, а координаты будут задаваться стилями top и left. Вначале координаты робота 40px, 40px - это координаты ячейки 1, 1. Стиль z-index: -1 означает чтобы данный элемент будет находиться под другими обычными элементами. 

Обновив страницу вы увидите робота теперь.

Движение робота

Чтобы двигать роботом будем использовать клавиатуру. Для начала создадим объект Robot, в котором будем хранить координату робота и функции для движения робота. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var Robot = {
    x: 1,
    y: 1,
    show: function(){
        var $robot = $("#robot");
        $robot.css("left", Robot.x*40);
        $robot.css("top", Robot.y*40);
    },

    moveDown: function(){
        Robot.y += 1;
        Robot.show();
    },

    moveUp: function(){
        Robot.y -= 1;
        Robot.show();
    },

    moveLeft: function(){
        Robot.x -= 1;
        Robot.show();
    },

    moveRight: function(){
        Robot.x += 1;
        Robot.show();
    }
};

Методы движения меняют одну координату робота и вызывают метод show(). Метод show() берет элемент робота и меняет его стили left и top соответственно координатам робота. Умножаем на 40, потому что размер одной ячейки 40 пикселей. 

Теперь если вы в Консоли напишете Robot.moveDown() то робот переместится вниз и т.д. 

Нам нужно добавить управление через клавиатуру. Для этого мы должны отлавливать события клика и если кликнуты нужные клавиши, двигать робота. Для этого будем использовать событие "keyup" у объекта document, то есть всей страницы. Это событие происходит когда клавиша отпускается. 

1
2
3
$(document).on("keyup", function(event){
    var key = event.keyCode;
});

функция-обработчик принимает один параметр(event), у которого есть свойство keyCode, в котором хранится код нажатой клавиши. Вы можете проверить коды разных клавиш, выводя их в Консоль:

1
2
3
4
$(document).on("keyup", function(event){
    var key = event.keyCode;
    console.log(event.keyCode);
});

мы будем использовать клавиши стрелки. Их коды: 37, 38, 39, 40. Создадим константы для них.

1
2
3
4
const KEY_LEFT = 37;
const KEY_UP = 38;
const KEY_RIGHT = 39;
const KEY_DOWN = 40;

Теперь внутри функции-обработчика будем проверять нажатую клавишу и вызывать соответствующий метод движения робота. Для этого создадим объект Keyboard с функцией init():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var Keyboard = {
    init: function(){
        $(document).on("keyup", function(event){
            var key = event.keyCode;
            switch(key){
                case KEY_DOWN: Robot.moveDown(); break;
                case KEY_UP: Robot.moveUp(); break;
                case KEY_LEFT: Robot.moveLeft(); break;
                case KEY_RIGHT: Robot.moveRight(); break;
            }
        })
    }
};

и теперь вызовем функцию init() внутри функции onReady():

1
2
3
4
5
function onReady(){
    Keyboard.init();
    GameField.init();
    GameField.show();
}

Сохраняем и обновляем страницу и двигаем роботом клавиатурой.

На этом все, если у кого не так работает. То сравните ваш код:

Файл classwork3.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<head>
    <meta charset="UTF-8">
    <title>Робот!</title>
    <link rel="stylesheet" href="classwork3.css">
    <script src="jquery-3.2.1.min.js"></script>
    <script src="classwork3.js"></script>
</head>
<body>
    <div class="game-field">
        <div id="robot"></div>
        <script>
            for (var y=0; y<15; y++){
                for (var x=0; x<20; x++){
                    var cell_id = "cell_"+x+"_"+y;
                    document.write("<div class='cell' id='"+cell_id+"'></div>");
                }
            }
        </script>
    </div>
</body>
</html>

Файл: classwork3.css:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.game-field{
    width: 800px;
    height: 600px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 10px;
    box-shadow: 0 0 5px grey;
    position: relative;
}

.cell{
    width: 40px;
    height: 40px;
    display: inline-block;
    background-size: 40px 40px;
}

#robot{
    position: absolute;
    background-size: 40px 40px;
    background-image: url("img/robot.png");
    z-index: -1;
    width: 40px;
    height: 40px;
    left: 40px;
    top: 40px;
}

Файл classwork3.js:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
const WALL = "wall";
const EMPTY = "empty";
const TREE = "tree";
const BOMB = "bomb";
const EXIT = "exit";

const KEY_LEFT = 37;
const KEY_UP = 38;
const KEY_RIGHT = 39;
const KEY_DOWN = 40;

var GameField = {
    width: 20,
    height: 15,
    cells: [],

    init: function () {
        for (var x = 0; x < GameField.width; x++) {
            GameField.cells[x] = [];

            for (var y = 0; y < GameField.height; y++) {
                GameField.cells[x][y] = EMPTY;

                if(x==0 || y==0 || x==GameField.width-1 || y==GameField.height-1){
                    GameField.cells[x][y] = WALL;
                } else{
                    var random = Math.random()*100;
                    if(random < 30){
                        GameField.cells[x][y] = WALL;
                    } else if(random<50) {
                        GameField.cells[x][y] = TREE;
                    } else if(random<60) {
                        GameField.cells[x][y] = BOMB;
                    }
                }
            }
        }

        var exit_x = 1 + Math.round(Math.random()*(GameField.width -2));
        var exit_y = 1 + Math.round(Math.random()*(GameField.height -2));

        GameField.cells[exit_x][exit_y] = EXIT;
        GameField.cells[1][1] = EMPTY; // здесь будет робот
    },

    show: function(){
        for (var x = 0; x < GameField.width; x++) {
            for (var y = 0; y < GameField.height; y++) {
                if(GameField.cells[x][y] == WALL){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/wall.png')");
                } else if(GameField.cells[x][y] == TREE){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/tree.png')");
                } else if(GameField.cells[x][y] == BOMB){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/bomb.png')");
                } else if(GameField.cells[x][y] == EXIT){
                    $("#cell_"+x+"_"+y).css("background-image", "url('img/exit.png')");
                }
            }
        }
    }
};

var Robot = {
    x: 1,
    y: 1,
    show: function(){
        var $robot = $("#robot");
        $robot.css("left", Robot.x*40);
        $robot.css("top", Robot.y*40);
    },

    moveDown: function(){
        Robot.y += 1;
        Robot.show();
    },

    moveUp: function(){
        Robot.y -= 1;
        Robot.show();
    },

    moveLeft: function(){
        Robot.x -= 1;
        Robot.show();
    },

    moveRight: function(){
        Robot.x += 1;
        Robot.show();
    }
};

var Keyboard = {
    init: function(){
        $(document).on("keyup", function(event){
            var key = event.keyCode;
            switch(key){
                case KEY_DOWN: Robot.moveDown(); break;
                case KEY_UP: Robot.moveUp(); break;
                case KEY_LEFT: Robot.moveLeft(); break;
                case KEY_RIGHT: Robot.moveRight(); break;
            }
        })
    }
};

function onReady(){
    Keyboard.init();
    GameField.init();
    GameField.show();
}

$(document).ready(onReady);


7 марта 2022 г., 8:21
☆Кто? Если не МЫ☆
Facebook Vk Ok Twitter Telegram Whatsapp