Делаем игру Слоты внутри title сайта

от автора

Приветствую, в этой статье мы сделаем игру «Слоты» внутри названия сайта(пример, TitleRun). Механика игры будет очень простая. нажимаешь на кнопку — получаешь случайные слоты, если все слоты совпадают — выигрываешь.

image

Зачем вообще делать игры в названии сайта?

Да, возможно это и звучит как нечто совсем странное, но, всё же, это довольно интересно. Во всяком случае, интереснее, чем делать игру обычным способом.

Анимации

Анимации можно реализовать разными способами:

setInterval

function animation() {     let i = 0;     setInterval(() => {         document.title = `Таймер - ${i}`;         i++;     }); } 

setTimeout

let timer = 0; function animation() {     document.title = `Таймер - ${timer}`;     timer++;     setTimeout(animation, 1000) } 

while и await

Этот способ был в
исходниках TitleRun`a

function sleep(ms) {     return new Promise((r) => setTimeout(r, ms)); } async function animation() {     let i = 0;     while(true) {         document.title = `Таймер - ${i}`;         await sleep(1000);     } } 

Механика

Как слоты я буду использовать эмодзи(смайлики, emoji, как вам удобнее):

image

Вот так я смогу получать случайные слоты:

const getSlot = () => {     const slot = Math.ceil(Math.random()*slots.length-1);     return slots[slot]; }; const getSlots = () => {     return slots.map(getSlot); }; 

При нажатии на кнопку, будет анимация разных слотов, скорость которой будет постепенно увеличиваться:

const spin = document.getElementById('spin'); const slotsAnimation = async () => {     let speed = 20;     while (true) {         //получаю слоты         const slotsRandom = getSlots();         //вывожу         document.title = slotsRandom.join('');         if(speed >= 1000) {             checkWin(slotsRandom);             break;         }         speed += 50;         await sleep(speed);     } }; spin.addEventListener('click', StartGame); 

checkWin — функция для проверки выигрыша:

const checkWin = (slots) => {     for(let i=0;i<slots.length-1;i++) {         //Если слот не совпадёт со следующим         if(slots[i] !== slots[i+1]) {             alert('GAME OVER');             return;         }     }     alert('WIN!!'); }; 

Теперь это уже играбельно.

Но я хотел бы добавить больше анимаций(например, анимации выигрыша и главного меню)
Это можно сделать, если ввести глобальные переменные, типа:

let playing = false; let win = false; 

и на основе их показывать анимации:

const slotsAnimation = async () => {     playing = true     let speed = 20;     while (playing) {         const slotsRandom = getSlots();         document.title = slotsRandom.join('');         if(speed >= 1000) {             checkWin(slotsRandom);             playing = false         }         speed += 50;         await sleep(speed);     } }; const winAnimation = async () => {     win = true;     while(win && !playing) {         document.title = 'WIN!*';         await sleep(1000);         document.title = '*WIN!';         await sleep(1000);     } }; //Эта анимация будет просто циклично дублировать слоты const mainAnimation = async () => {     playing = false     let i = 0;     while (!playing && !win) {         if(i === slots.length - 1) i = 0;         else i++;         document.title = slots[i].repeat(slots.length);         await sleep(1000);     } }; 

Делаем удобнее

Первое, что я хотел бы сделать — функция, которая заменит логику winAnimation. Она будет выводить сообщение и после запускать другую функцию. В качестве аргумента она будет принимать объект с текстов, кол-вом итераций, скоростью и колбеком.

const message = async ({text = [], count = 'infinity', speed = 1000, callback = null}) =>  {     if(!text[0]) throw new Error('need array for text');     let i = 0;     let textIndex = 0;     while(true) {         if(count <= i && count !== 'infinity') {             // в конце вызовем колбек, если он есть             if(callback) callback();             break;         }         if(textIndex >= text.length) textIndex = 0;         //За каждую итерацию цикла будем показывать 1 сообщение из массива сообщений         document.title = text[textIndex];         i++;         textIndex++;         await sleep(speed)     } }; // Использование message({text: ['️WINNER', 'WINNER️'], count: 3, speed: 800, callback: mainAnimation}) 

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

class Animations {     animations = [];     setAnimation(animation, args = {}) {         // Выключаем все анимации         for(const item of this.animations) {             item.play = false         }         // Находим анимацию, включаем и запускаем функцию         const Animation = this.animations.find(item => item.name === animation);         if(!Animation) return;         Animation.play = true;         Animation.callback(args)     };     isPlay(animation) {         return this.animations.find(item => item.name === animation).play     }     createAnimation(name, callback) {         this.animations.push({             play: false,             name,             callback,         })     } } 

Описание методов:
setAnimation — вызывает анимацию.
isPlay — функция-условие для проверки «играет» ли анимация.
createAnimation — добавляет анимацию в хранилище.

Теперь код будет выглядеть вот так:

const checkWin = (slots) => {     for(let i=0;i<slots.length-1;i++) {         if(slots[i] !== slots[i+1]) {             alert('GAME OVER');             animations.setAnimation('main');             return;         }     }     alert('WIN!!');     animations.setAnimation('message',         {text: ['️WINNER', 'WINNER️'], count: 3, speed: 800, callback: () => animations.setAnimation('main')}) }; const slotsAnimation = async () => {     let speed = 20;     while (animations.isPlay('game')) {         const slotsRandom = getSlots();         document.title = slotsRandom.join('');         if(speed >= 1000) {             checkWin(slotsRandom);         }         speed += 50;         await sleep(speed);     } }; const mainAnimation = async () => {     let i = 0;     while (animations.isPlay('main')) {         if(i === slots.length - 1) i = 0;         else i++;         document.title = slots[i].repeat(slots.length);         await sleep(1000);     } }; // Добавление анимаций animations.createAnimation('main', mainAnimation); animations.createAnimation('game', slotsAnimation); animations.createAnimation('message', message); // Запуск animations.setAnimation('main'); 

Возможность добавить слот

Для разнообразия добавим возможность добавить случайный слот.
(Тут мы просто берём emoji из API, добавляем в массив и включаем главную анимацию)

const AddSlot = async () => {     // Показываем сообщение пока ждём ответа от API     animations.setAnimation('message', {text: ['Getting slot','Getting slot'], speed: 500});     try {         const data = await fetch(`https://emoji-api.com/emojis?access_key=КЛЮЧ`);         const body = await data.json();         const emoji = await body[Math.floor(Math.random() * body.length -1)];         slots.push(emoji.character);     } catch (e) {         // Если не получилось         console.log(e);         animations.setAnimation('none');         document.title = 'Error, try again';         await sleep(800);     } finally {         animations.setAnimation('main');     } }; 

Вот и всё. Надеюсь вы узнали что-нибудь новое.

Репозиторий на GitHub — ссылка.
Сайт-пример — ссылка.

ссылка на оригинал статьи https://habr.com/ru/post/507360/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *