Проект Web #2 "Простой кликер"

Задача

На экране в случайном месте появляется прямоугольная область – регион. Кликнув по ней мышкой, игрок получает +1 очко, а прямоугольная область перемещается в новое случайное место. У игрока есть 20 секунд чтобы успеть набрать наибольшее количество очков.

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


Ход работы

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

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


1.2. Заполните файлы базовым содержимым:
Файл разметки index.html
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Аркада на canvas</title>
 <link rel="stylesheet" href="css/style.css">
</head>
<body>
 <canvas id="canvas-main"></canvas>
 <script src="js/script.js"></script>
</body>
</html>

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

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

canvas.width = window.innerWidth; // ширина холста занимает всю ширину экрана
canvas.height = window.innerHeight; // высота холста занимает всю высоту экрана

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

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

2. Понятие игрового цикла

В работе с canvas мы используем анимацию. Для того, чтобы что-то изменилось на холсте, необходимо проводить такой цикл:
1) очистить весь холст
2) отрисовать объекты в их координатах
И делать это столько раз в секунду, чтобы человеку казалось это плавным. Например, базово это можно делать 25 кадров в секунду (то есть, с интервалом в 1000/25 = 40 миллисекунд).
2.1. Напишем игровой цикл в конце программы:
var ctx = canvas.getContext('2d');

setInterval(function(){

},40);

3. Добавление объекта – региона.

3.1. Для создания региона будем использовать функцию-конструктор
var ctx = canvas.getContext('2d');

// Объявляем переменные, характеризующие свойства региона – координаты и размеры
var regionX,regionY,regionW,regionH;
function createRegion() {
    regionX = Math.random()*WIDTH;
    regionY = Math.random()*HEIGHT;
    regionW = 20;
    regionH = 20;
}
// Сразу же вызываем эту функцию
createRegion();

setInterval(function(){

3.2. Напишем функцию для отрисовки региона и будем вызывать её в игровом цикле.

createRegion();

function update() {
    // Очищаем холст
    ctx.clearRect(0,0,WIDTH,HEIGHT);

    // Отрисовываем прямоугольник
    ctx.fillStyle="green";
    ctx.fillRect(regionX,regionY,regionW,regionH);
}

setInterval(function(){
    update();
},40);

3.3. Запустите index.html и пообновляйте её несколько раз. Если всё было написано без ошибок, то вы увидите небольшой прямоугольник зелёного цвета, который каждый раз появляется в новых координатах.

4. Программируем событие нажатия мышки

setInterval(function(){
    update();
},40);

document.onmousedown = function(event) {
    var mouseX = event.clientX;
    var mouseY = event.clientY;

    if(mouseX>=regionX && mouseX<=regionX+regionW) {
        if(mouseY>=regionY && mouseY<=regionY+regionH) {
            // Если попали мышкой по региону, то генерируем новый регион
            createRegion();
        }
    }
}

5. Добавление очков за клик по региону

5.1. Сейчас у нас за клики ничего не даётся. Негоже. Пора добавить очки. Вначале игры они равны нулю:
var ctx = canvas.getContext('2d');

var score = 0;

5.2. При каждом удачном клике по региону будем увеличивать очки на 1.
        if(mouseY>=regionY && mouseY<=regionY+regionH) {
            createRegion();
            score = score + 1;
        }

5.3. Набранные очки хранятся в памяти, но было бы круто вывести их на экран, не так ли? Всё, что мы выводим, прописываем в функции update. Дополним эту функцию.
function update() {
    // Очищаем холст
    ctx.clearRect(0,0,WIDTH,HEIGHT);

    // Отрисовываем прямоугольник
    ctx.fillStyle="green";
    ctx.fillRect(regionX,regionY,regionW,regionH);

    // Выводим количество очков
    ctx.font = "20px Arial"
    ctx.fillStyle="white";
    ctx.fillText(score,30,40);
}

6. Добавление таймера

6.1. Чтобы ограничить время игры и увеличить её интересность, добавим таймер. Объявим переменную, которая будет хранить количество времени.
var ctx = canvas.getContext('2d');

var score = 0;
var time = 0;

6.2. Теперь рассмотрим, каким образом можно увеличивать таймер. Мы сделали так, чтобы игровой цикл повторялся каждые 40 миллисекунд – это 25 кадров в секунду. Найдите место в коде, где это прописано. Идея в том, что если нам нужно что-то повторять с какой-нибудь периодичностью, мы можем это делать каждые N кадров. Например, если нам что-то нужно повторять каждые 3 секунды (например, генерировать врагов или пищу в игре), то мы проверяем, прошло ли 75 кадров (25 кадров * 3 секунды = 75).
С таймером увеличение будет каждые 25 кадров. Для проверки, прошло ли 25 кадров мы используем операцию остатка от деления на это количество кадров
// Объявляем переменную с кадрами, вначале их 0
var frames = 0;
setInterval(function(){
    frames++; // увеличиваем количество кадров на 1
    if(frames%25==0) { // Если прошло 25 кадров, то
        time = time + 1;
    }
    update();
},40);

6.3. Как и со счётом, нам нужно выводить время на экран. Сделайте это самостоятельно.

7. Финал игры

Добавим ограничение в 20 секунд. Если проходит 20 секунд, то на экран выведется количество набранных очков и игра окончится (кликать больше будет нельзя)
7.1. Добавим функцию проверки времени. Если время закончится, будем выводить alert (это пока что).
function checkTime() {
    if(time>=10) {
        alert("Game over! You have " + score + " scores!");
    }
}
Вызываем эту функцию в игровом цикле:
    if(frames%25==0) {
        time = time + 1;  // Увеличили время
        checkTime();  // Проверили, не закончилось ли оно
    }

7.2. Рассмотрим два состояния игры:
1) игра не окончена (на экране регион, на который можно кликать, увеличиваются очки и время)
2) игра окончена (больше очки не добавляются, на экране надпись об окончании игры)
За эти состояния будет отвечать переменная булева переменная isGameOver, которая вначале будет равной false, а в момент, когда время выйдет, сделаем её true.
var score = 0;
var time = 0;
var isGameOver = false;

7.3. Теперь поправим код с учётом этих двух состояний – игровой цикл будет увеличивать фреймы и осуществлять перерисовку только если игра не окончена.
setInterval(function(){
    if(isGameOver===false){
        frames++;
        if(frames%25==0) {
            time = time + 1;
            checkTime();
        }
        update();
    } else {
        ctx.clearRect(0,0,WIDTH,HEIGHT);
    }
},40);

7.4. Добавим изменение состояния игры – это происходит в момент, когда время заканчивается
function checkTime() {
    if(time>=10) {
        alert("Game over! You have " + score + " scores!");
        isGameOver=true;
    }
}

7.5. Выведем на экран надпись об окончании игры и количестве очков
    else {
        ctx.clearRect(0,0,WIDTH,HEIGHT);
        ctx.font = "40px Arial"
        ctx.fillStyle="red";
        ctx.fillText("The end",WIDTH/2-100,HEIGHT/2);
        ctx.fillStyle="green";
        ctx.fillText("You've got " + score + " scores",WIDTH/2-100,HEIGHT/2+50);
    }

Задания

Задание 1*. Сделайте так, чтобы каждый новый регион создавался не только в случайных координатах х и у, но и со случайной шириной и высотой.

Задание 2***. Увеличьте время игры до 20 секунд. Добавьте в конце объявления результата игры, какого успеха достиг игрок:
если он набрал до 3 очков, добавить сообщение "Надо качать скилл управления мышкой"
от 4 до 10 – "Я в детстве в 1 классе и то больше набирал"
от 11 до 20 – "Норм так"
от 21 до 30 – "А ты хорош!"
выше 31 – изменить цвет холста на другой и начать выводить на холст надписи "Красавчег" в случайной позиции.

Задание 3*. Сделайте так, чтобы регион реагировал не на клики мышки по нему, а на попадание курсора по нему при движении мышки.

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

Комментарии