Проект Web #3 "Дом с привидениями"

Задача

Изучая условные операторы, вы делали маленький проект "Дом с привидениями", используя ввод/вывод данных при помощи alert() и prompt(). Сделайте похожий проект, используя canvas и выбор номера двери кликом по ней.

Видео с готовым результатом

Ход работы

1. Создание и подготовка проекта.

1.1. Создайте в вашей папке с сайтами новую папку "3 House with ghosts" и в ней создайте такую структуру файлов и папок:
--js
----script.js
--css
----style.css
--index.html

1.2. Заполните файлы базовым содержимым:
Файл разметки index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="css/style.css">
    <title>Дом с привидениями</title>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script src="js/script.js"></script>
</body>
</html>

Файл со стилями style.css
body, html {
 margin:0;
 width:  100%;
 height: 100%;
 background-color: #2d2d2d;
}

Файл со скриптами script.js
const canvas = document.getElementById("canvas");

canvas.width = window.innerWidth; 
canvas.height = window.innerHeight; 

let WIDTH = canvas.width;
let HEIGHT = canvas.height;

const ctx = canvas.getContext('2d');

Обратите внимание, что мы начали использовать константы const. В отличии от переменных let, константы – это то, что не изменяется при выполнении программы, в них нельзя присвоить новые значения.

2. Отрисовка текста об игре и двери

2.1. Добавим текст на холст, чтобы пользователю было понятно, что ему делать.
ctx.font = "20px Arial";
ctx.textBaseline = "top";
ctx.fillStyle = "White";
ctx.fillText("Перед вами 3 двери. За одной из них привидение. Кликните на дверь, за которой привидения нет.",20,20);
ctx.fillText("Если вам повезёт, вы заработаете очки и пройдёте дальше. Если нет - привидение вас съест!",20,44);

2.2. Отрисуем первую дверь.
    ctx.fillStyle = "Green";
    ctx.fillRect(100,100,100,240);
    ctx.beginPath();
    
    ctx.fillStyle = "White";
    ctx.arc(180,220,10,0,2*Math.PI);
    ctx.closePath();
    ctx.fill();
Запустите программу, у вас должна быть отрисована дверь и текст приветствия.

2.3. Посмотрите ещё раз на видео. Обратите внимание, что все 3 двери очень похожи друг на друга. У них отличаются только координаты и цвет двери и ручки. Если мы видим похожие объекты, это означает, что для их создания необходимо использовать функцию. Создадим её:
// в качестве параметров функции используем координаты x, y, цвет двери colorDoor и цвет дверной ручки colorDoorHandle
const drawDoor = function(x,y,colorDoor,colorDoorHandle) {
    ctx.fillStyle = colorDoor;
    ctx.fillRect(x,y,100,240);
    ctx.beginPath();
    
    ctx.fillStyle = colorDoorHandle;
    ctx.arc(x+80,y+120,10,0,2*Math.PI);
    ctx.closePath();
    ctx.fill();
}

2.4. Теперь чтобы создать дверь нам нужно просто вызвать эту функцию несколько раз с разными параметрами.
    ctx.fill();
}

drawDoor(100,100,"Red","Black");
drawDoor(240,100,"Green","White");
drawDoor(380,100,"Blue","Orange");

3. Кликабельность дверей

3.1. Добавим событие клика мышки по холсту. Добудем координаты курсора мышки при клике.
canvas.onmousedown = function(e){
    let mouseX = e.clientX;
    let mouseY = e.clientY;
}

3.2. При каждом клике будет случайно генерироваться число – номер двери, за которым может оказаться привидение! Мы добудем случайное число от 1 до 3.
    let mouseY = e.clientY;

    let ghostChoose = Math.ceil(Math.random()*3);

3.3. Теперь нужно проверить, в какую именно дверь тыкнул игрок. Добавим переменную playerChoose, которая будет хранить в себе номер двери. Далее будем проверять область клика на попадание по той или
    let ghostChoose = Math.ceil(Math.random()*3);
    let playerChoose = null;

    if(mouseX>=100 && mouseX<=200 && mouseY >=100 && mouseY<=340) {
        playerChoose = 1;
    } else if(mouseX>=240 && mouseX<=340 && mouseY >=100 && mouseY<=340) {
        playerChoose = 2;
    } else if(mouseX>=380 && mouseX<=480 && mouseY >=100 && mouseY<=340) {
        playerChoose = 3;
    }

3.4. Совершим проверку: если игрок тыкнул на дверь, номер которой равен номеру двери привидения, то выведем в консоль сообщение о проигрыше. Иначе – сообщение о выигрыше.

    if(playerChoose === ghostChoose) {
        console.log("Вы проиграли");
    } else if (playerChoose!==ghostChoose && playerChoose !== null) {
        console.log("Вам повезло, играете дальше");
    }

3.5*. Ответьте на вопрос: зачем мы проверяем условие playerChoose !== null ?

4. Программируем путь выигрыша

После того, как игрок кликает на дверь, где нет привидения, его очки должны увеличиться и это должно отобразиться на экране.
4.1. Добавим глобальную переменную score, в которой будем хранить счёт игры. При запуске программы её следует обнулять. Все глобальные переменные мы обычно храним вначале программы.
const ctx = canvas.getContext('2d');
let score = 0;

ctx.font = "20px Arial";

4.2. Напишем функцию drawScore(), которая будет отвечать за вывод количества очков на экране. Вызываем её после отрисовки дверей.
    ctx.fill()
}

const drawScore = function() {
    ctx.font = "20px Arial";
    ctx.textBaseline = "top";
    ctx.fillStyle = "green";
    ctx.fillText("Счёт: "+score,100,360);
}

drawDoor(100,100,"Red","Black");
drawDoor(240,100,"Green","White");
drawDoor(380,100,"Blue","Orange");
drawScore();

4.3. Изменим команды в выигрышном варианте:
    } else if (playerChoose!==ghostChoose && playerChoose !== null) {
        console.log("Вам повезло, играете дальше");
        score++;
        drawScore();
    }

4.4. Запустите программу. При успешной ситуации, вылезает один баг: надпись с количеством очков рисуется множество раз, накладываясь одна на другую. Исправим это, очищая холст в том месте, где у нас вывод очков.
const drawScore = function() {
    ctx.clearRect(0,340,400,100);
    ctx.font = "20px Arial";

5. Программируем вариант проигрыша

В случае, если игрок попадает на дверь, за которой есть привидение, игра должна окончиться и на экран должно вывестись общее количество очков.
5.1. Напишем функцию, отвечающую за конец игры.
const finishGame = function() {
    ctx.clearRect(0,0,WIDTH,HEIGHT);
    ctx.font = "40px Arial";
    ctx.textBaseline = "top";
    ctx.textAlign = "center";
    ctx.fillStyle = "Red";
    ctx.fillText("За дверью привидение! Игра окончена.",WIDTH/2,HEIGHT/2-40);
    ctx.fillStyle = "White";
    ctx.fillText("Ваш общий счёт: "+score,WIDTH/2,HEIGHT/2+40);
}

const drawScore = function() {

5.2. Не забудьте вызвать эту функцию в нужном месте программы.

6. Усовершенствование дверей. 

Сейчас от игры до игры двери не изменяются, а было бы здорово сделать их динамичнее, чтобы каждый клик игрока вызывал ответное действие. Давайте сделаем так, чтобы двери брали свой цвет и цвет дверной ручки случайно.
6.1. Добавим массив цветов:
const colors = ["Brown","Red","Black","Blue","White","Green","Navy","Orange","DarkRed"];
const ctx = canvas.getContext('2d');
let score = 0;

6.2. Для того, чтобы добыть случайный цвет из массива мы будем пользоваться примерно такой записью:
colors[randomNumber], где randomNumber будем добывать при помощи функции-помощника, которую пропишем далее.
const getRandom = function(number) {
    return Math.floor(Math.random()*number); // возвращает число от 0 до number
}

6.3. Изменим код создания дверей:
drawDoor(100,100,colors[getRandom(9)],colors[getRandom(9)]);
drawDoor(240,100,colors[getRandom(9)],colors[getRandom(9)]);
drawDoor(380,100,colors[getRandom(9)],colors[getRandom(9)]);

6.4. Теперь нам нужно перерисовывать все эти 3 двери при каждом удачном клике на дверь. То есть, делать множество раз. Поэтому, код создания дверей лучше записать в функцию и вызывать её в случае успеха.
const drawDoors = function() {
    drawDoor(100,100,colors[getRandom(9)],colors[getRandom(9)]);
    drawDoor(240,100,colors[getRandom(9)],colors[getRandom(9)]);
    drawDoor(380,100,colors[getRandom(9)],colors[getRandom(9)]);
}

6.5. Не забудьте вызвать эту функцию в нужных местах.

В конспект 

Горизонтальное выравнивание текста

ctx.textAlign = "center"; // left, center, right

Вертикальное выравнивание текста

ctx.textBaseline = "top"; // top, center, bottom

Константы

const – это то, что не изменяется при выполнении программы

Задания

Задание 1***. Добавьте переменную с очками здоровья. Выведите её на экран ниже счёта. Пусть вначале их будет 3. При каждом неудачном попадании по двери, уменьшайте очки здоровья на 1. Когда они закончатся, игра завершается.

Задание 2**. 3 двери – это неимоверно мало для того, чтобы интересно поиграть. Добавьте ещё 2 двери.

Задание 3**. Добавьте нарисованную на холсте кнопку "Начать заново". При нажатии на неё выполните команду window.location.reload(); Страница перезагрузится и игра начнётся заново.


Комментарии