Веб-программирование → Делаем игру с Роботом на jQuery, HTML, CSS
Предыдущую классную работу все выполнили? Мне скинули всего 3-4 человека 😏.
Ребята, спрашивайте если что-то не понятно! На этой неделе меня не будет, так что вам придется самим поработать. Эта работа небольшая. Будем делать маленького робота. 🤖 А чем он будет заниматься, я по ходу щас придумаю.
Здесь нам обязательно пригодится jQuery, которую можно скачать здесь (compressed production). Откройте ваш любимый редактор кода. Или скачайте: Brackets, Sublime Text, PyCharm.
Приступим..
Создаем три пустых файла classwork3.html, classwork3.css, classwork3.js. В classwork3.html подключаем стили и скрипты:
<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:
.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, то увидим на странице большой прямоугольник с тенью.
<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.
<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":
var cell_id = "cell_"+x+"_"+y;
Потом добавим тег <div> с классом "cell" и c id равным значению переменной cell_id:
document.write("<div class='cell' id='"+cell_id+"'></div>");
И в итоге у нас получится такой код:
<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 вы должны увидеть много таких элементов:
<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:
.cell{
width: 40px;
height: 40px;
display: inline-block;
background-image: url("img/tree.png");
background-size: 40px 40px;
}
Также я для примера добавил фоновое изображение дерева. Если сохранив файл, вы обновите страницу, то увидите 300 деревьев на игровом поле.
Заполнение игрового поля
Игровое поле и ячейки у нас есть. Теперь нам нужно случайным образом заполнить эти ячейки. Для этого мы откроем файл classwork3.js наконец-то и добавим туда функцию onReady() и скажем документу, чтобы он вызывал эту функцию когда он полностью загрузится:
function onReady(){
}
$(document).ready(onReady);
Теперь внутри функции onReady() мы должны заполнить ячейки. Для хранения состояния ячеек игрового поля и функций связанных с полем, мы создадим специальный объект. И назовем его GameField. Объекты, которые не только хранят какие-то данные, но и имеют свои функции я называю CamelCase-ом.
Пусть в этом объекте у нас будет свойства width(ширина), height(высота) и cells(ячейки). cells будет двумерным массивом, в котором мы будем хранить состояние игрового поля. Добавим перед функцией onReady() код:
var GameField = {
width: 20,
height: 15,
cells: [],
}
Теперь в этот объект добавим метод init(), который будет заполнять ячейки случайным образом. Значениями ячеек будут у нас будут текстовые значения "tree" - для дерева, "exit" - для выхода, "empty" - пустая ячейка, "wall" - стена, "bomb" - бомба.
Чтобы каждый раз эти значения не писать как текст, создадим константы и добавим их в начале файла classwork3.js:
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):
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.
Если вы в голове можете представить двумерный массив, то получится примерно так:
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 или крайним значеням поля, то значит это край поля, там должна быть стена:
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% вероятность), то пусть там будет бомба.
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:
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 всегда будет робот, поэтому оставим это место гарантированно пустым:
GameField.cells[1][1] = EMPTY; // здесь будет робот
И так, в итоге у нас получился такой метод init:
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:
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() по порядку:
function onReady(){
GameField.init();
GameField.show();
}
Сохраняем файл и обновляем страницу(Ctrl+Shift+R) и видим что игровое поле красиво заполнилось.
И кстати еще в CSS файле уберите стиль background-image: url("img/tree.png"); с класса .cell .
Робот
Давайте теперь добавим самого робота. Робот будет у нас элементом с id="robot" внутри game-field:
<body>
<div class="game-field">
<div id="robot"></div>
...
Он будет отдельным слоем, т.е. отображаться под слоем ячеек. Для этого добавим стиль position: relative; для класса .game-field и следующие стили для робота:
#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, в котором будем хранить координату робота и функции для движения робота.
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, то есть всей страницы. Это событие происходит когда клавиша отпускается.
$(document).on("keyup", function(event){
var key = event.keyCode;
});
функция-обработчик принимает один параметр(event), у которого есть свойство keyCode, в котором хранится код нажатой клавиши. Вы можете проверить коды разных клавиш, выводя их в Консоль:
$(document).on("keyup", function(event){
var key = event.keyCode;
console.log(event.keyCode);
});
мы будем использовать клавиши стрелки. Их коды: 37, 38, 39, 40. Создадим константы для них.
const KEY_LEFT = 37;
const KEY_UP = 38;
const KEY_RIGHT = 39;
const KEY_DOWN = 40;
Теперь внутри функции-обработчика будем проверять нажатую клавишу и вызывать соответствующий метод движения робота. Для этого создадим объект Keyboard с функцией init():
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():
function onReady(){
Keyboard.init();
GameField.init();
GameField.show();
}
Сохраняем и обновляем страницу и двигаем роботом клавиатурой.
На этом все, если у кого не так работает. То сравните ваш код:
Файл classwork3.html:
<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:
.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:
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);
Задание домой:
Разберитесь хорошо в этом коде, попробуйте сделать так, чтобы робот не мог идти по стенам, и если он наступает на бомбу, то пусть игра пусть робот взрывается, а если доходит до двери, пусть игра заканчивается.
Также добавьте кнопки на экране для управления роботом, для перегенерации игрового поля.
Или сделайте что-нибудь другое, похожее, крутое.
Удачи!