Приветствую, в этой статье мы сделаем игру «Слоты» внутри названия сайта(пример, TitleRun). Механика игры будет очень простая. нажимаешь на кнопку — получаешь случайные слоты, если все слоты совпадают — выигрываешь.
Да, возможно это и звучит как нечто совсем странное, но, всё же, это довольно интересно. Во всяком случае, интереснее, чем делать игру обычным способом.
Анимации можно реализовать разными способами:
Как слоты я буду использовать эмодзи(смайлики, emoji, как вам удобнее):
Вот так я смогу получать случайные слоты:
При нажатии на кнопку, будет анимация разных слотов, скорость которой будет постепенно увеличиваться:
checkWin — функция для проверки выигрыша:
Теперь это уже играбельно.
Но я хотел бы добавить больше анимаций(например, анимации выигрыша и главного меню)
Это можно сделать, если ввести глобальные переменные, типа:
и на основе их показывать анимации:
Первое, что я хотел бы сделать — функция, которая заменит логику winAnimation. Она будет выводить сообщение и после запускать другую функцию. В качестве аргумента она будет принимать объект с текстов, кол-вом итераций, скоростью и колбеком.
Ещё я хотел бы сделать менеджер анимаций, с помощью которого можно будет их запускать и контролировать. Это будет класс с хранилищем анимаций и несколькими методами.
Описание методов:
setAnimation — вызывает анимацию.
isPlay — функция-условие для проверки «играет» ли анимация.
createAnimation — добавляет анимацию в хранилище.
Теперь код будет выглядеть вот так:
Для разнообразия добавим возможность добавить случайный слот.
(Тут мы просто берём emoji из API, добавляем в массив и включаем главную анимацию)
Вот и всё. Надеюсь вы узнали что-нибудь новое.
Репозиторий на GitHub — ссылка.
Сайт-пример — ссылка.
Зачем вообще делать игры в названии сайта?
Да, возможно это и звучит как нечто совсем странное, но, всё же, это довольно интересно. Во всяком случае, интереснее, чем делать игру обычным способом.
Анимации
Анимации можно реализовать разными способами:
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
исходниках 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, как вам удобнее):
Вот так я смогу получать случайные слоты:
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 — ссылка.
Сайт-пример — ссылка.
TheShock
Нет, он здесь нужен чтобы повыпендриваться. Нормальные программисты использовали бы здесь джоин: