Будем делать маленького робота. 🤖 А чем он будет заниматься, я по ходу щас придумаю.
Здесь нам обязательно пригодится jQuery, которую можно скачать здесь (compressed production). Откройте ваш любимый редактор кода. Или скачайте: Brackets, Sublime Text, PyCharm.
Приступим..
Создаем три пустых файла classwork3.html, classwork3.css, classwork3.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); |