Dusk World – Дневники разработки, часть 7:
Как сделать регенерацию энергии (здоровья/маны) в браузерной MMORPG

Прежде чем переходить к руководству, хочу поделиться своим удивлением. Рунет существует ~20 лет. Браузерные игры – почти столько же. Система регенерации энергии/здоровья/маны используется практически в каждой. И нет ни одной инструкции как её сделать с примером кода (по крайней мере, я найти не смог). Как?! Вопросы как это сделать встречаются, но вот примера реализации – нет. При этом в обсуждениях встречаются какие-то феерические сложные советы – «используй node.js», «используй CRON» и т.д.

К чему такие сложности, когда с задачей справится и обычный PHP с MySQL (+ JS на фронте).

Мой пример, возможно, не идеальный, но предупрежу сразу – за критику буду кидать тапками, потому что Если вы такие умные, почему я первый, кто сделал пример подобного функционала?. Ответ, конечно, напрашивается сам-собой – потому что у нас на одного человека, который что-то делает, приходится десятки экспертов-критиков, которые критикуют того, кто что-то сделал, но вот сами не спешат сделать что-то для окружающих.

Ладно, это были эмоции, надо было высказаться :)

Конструктивная критика, конечно, приветствуется.

 

Для кого это руководство

  • Для тех, кто уже знает основы PHP, ООП, MySQL и хоть чуть-чуть знаком с JS
  • Для тех, кто делает свою браузерную игру (возможно MMORPG) на PHP и MySQL
  • Для тех, кто хочет реализовать механику регенерации энергии/здоровья/маны, но не может сделать её своими силами.

Руководство не для тех, кто работает в веб-разработки 10+ лет – вы сами все сделаете, возможно лучше, чем у меня.

 

О подходе к задаче

При написании данного примера ставил перед собой задачу сделать нужный функционал с минимальным количеством кода. Я мог бы сделать его на Yii2 или Laravel, добавить к этому AJAX, но тогда новичку было бы сложно во всем этом разобраться – понять, где код самого фреймворка, а где код скрипта. Да и тот же фреймворк еще надо уметь поставить.

В итоге вышло ~400 строк кода, с комментариями практически к каждой строчке. Даже новичок должен разобраться. А кто не сможет разобраться даже в этих 400 строк кода с подробными комментариями, должен задуматься – а сможет ли он вообще сделать свою браузерную MMORPG. Это довольно сложная и многогранная задача, которая требует некоторого уровня профессионализма.

 

Демо

Хорошим тоном считаю вначале показать что мы собственно будем делать:

Демо | Код на github

Примечание: в демо все посетители «управляют» энергией одного пользователя, и это может создавать иллюзию «бага»: когда два посетителя будут одновременно увеличивать или уменьшать энергию – будут учитываться все действия (вы нажали «-30 энергии», а кто-то в этот момент нажал «+30 энергии» – в итоге изменения энергии не произойдет вовсе). Можно было бы под каждого уникального посетителя создавать отдельного пользователя, но это излишние навороты. Учитывая крайне низкую вероятность, что два посетителя зайдут на страницу демо одновременно – этим можно пренебречь.

 

Необходимые параметры

Вначале пройдемся по параметрам, которые нужны для работы (использую теже названия, что и в коде):

  1. $cost – базовый параметр, который будет храниться отдельно (один для всех) – необходимое количество секунд, для восстановления одной энергии. В примере скрипта, указанного ниже он равен 20. Т.е. за 20 секунд пользователь восстанавливает 1 энергию
    Примечание: если в вашей игре вы планируете, к примеру, добавить предметы, которые будут уменьшать требуемое количество секунд на восстановление одной энергии – то данный параметр надо хранить у каждого пользователя отдельно;
  2. id – id пользователя. Я буду работать с одним пользователем, и id, в общем-то, не нужен, но закладывать его нужно сразу;
  3. name – имя пользователя, чтобы мы определяли энергию не абстактного нечто, а конкретного пользователя с именем;
  4. energy – текущая энергия;
  5. energy_max – максимальное количество энергии;
  6. time – время последнего обновления энергии - именно по разнице между этим временем и текущим мы будем считать, нужно ли пользователю пополнить энергию и на сколько;
  7. residue – остаток времени - необходимость этого параметра опишу ниже;
  8. eweight – длина полоски энергии – мы же хотим видеть полоску энергии, и наблюдать как она заполняется со временем, верно?;
  9. sweight – длина полоски секунд
  10. time_actual – текущее время;
  11. efull – параметр отвечающий за то, полная ли энергия у пользователя или нет. Если энергия полная – то никаких дополнительных расчетов делать не нужно;
  12. difference – разница между текущим временем и временем последнего обновления энергии;
  13. addenergy – количество энергии которое мы должны добавить пользователю (может быть 0, 1 или больше);
  14. residue_new – новый остаток времени;

Для реализации нам нужно будет реализовать бек-энд и фронт-энд. Задача бек-энда – по запросу выдавать количество текущей энергии пользователя (и дополнительные параметры). Задача фронт-энда – визуально эмулировать восстановление энергии (здоровья/маны) в браузере пользователя. Дергать постоянно сервер мы не будем – это не нужно.

 

Стркуткра БД

Базовых параметров, которые требуются для рассчета энергии и которые нужно хранить в базе всего 4: текущая энергия, максимум энергии, время последнего обновления энергии и остаток секунд. Чтобы пример был более приближен к реальности – добавил еще два: id пользователя и его имя

MySQL-таблица будет выглядеть так:

SQL код для её создания наполнения

CREATE TABLE `energy` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`uname` varchar(255) NOT NULL,
`energy` INT(11) DEFAULT '100',
`energy_max` INT(11) DEFAULT '100',
`time` decimal(20,4) NOT NULL,
`residue` INT(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `energy` (`id`, `uname`, `energy`, `energy_max`, `time`, `residue`) VALUES
(1, 'Andariel', 94, 120, '1517230970.1444', 12);
 

Логика работы бек-энда

Ну что ж, давайте начнем. Что такое «энергия» в браузерных играх объяснять думаю не нужно – все с ней сталкивались. Наша задача – создать её, и сделать механику её «регенерации».

Вариант №1: Энергия полная

  1. Получаем данные о пользователе из БД
  2. Узнаем, полная ли у пользователя энергия
  3. Если да – ничего дополнительно делать не нужно

Вариант №2: Энергия не полная

Вначале опишу на примере конкретных цифр, чтобы было проще понять, затем – логикой самого скрипта.

К примеру, у нас есть пользователь с 50 энергии из 100. Последнее обновление было 30 секунд назад, а на восстановление одной энергии требуется 20 секунд.

  1. Узнав, что энергия не полная, нам нужно узнать разницу между текущим временем и временем последнего обновления – получаем 30 секунд;
  2. Считаем, какое количество энергии можем восстановить, на основе прошедшего времени – 30 делим на 20 и округляем до меньшего. Получаем 1;
  3. Дальше нам нужно рассчитать количество секунд, которые остались после расчетов:
    остаток секунд = разница времени – (количество восстанавливаемой энергии * количество секунд на восстановление)
    В нашем случае остаток будет 10 секунд;
  4. Итак, мы рассчитали, что игроку надо восстановить 1 энергию, и записать остаток секунд – 10, чтобы учитывать их при следующем обновлении страницы (и, соответственно, следующем рассчете текущего количества энергии). Записываем все это в бд: новое количество энергии, остаток, и время обновления энергии обновится само;

Теперь перейдем к реальной логике скрипта

  1. Получаем базовые данные о пользователе из БД (id пользователя, имя, количество энергии, максимальное количество энергии, время последнего обновления и остаток секунд)
  2. Рассчитываем дополнительные параметры пользователя. Для этого первым делом узнаем, полная ли у пользователя энергия.
  3. Если энергия не полная, необходимо рассчитать дополнительные параметры:
  4. Разница времени в сек. = (текущее время - время последнего обновления энергии) + остаток секунд
  5. Добавляемая энергия = разница времени в сек. / количество времени на восстановление одной энергии
    Полученное значение округляем в меньшую сторону
  6. Новый остаток в сек. = разница времени в сек. - (добавляемая энергия * количество времени на восстановление одной энергии)
  7. Далее проверяем, можем ли мы добавить одну или более энергии пользователю
  8. Если да – добавляем энергию (попутно проверяя, не стала ли она выше максимальной) и записываем новые параметры
  9. Так как базовые параметры изменились – по новой рассчитываем дополнительные параметры
  10. Выводим информацию на страницу
 

Логика работы фронт-энда

Теперь переходим к фронт-энду. Так как у нас не реал-тайм геймплей (PHP для данной цели вообще плохо подходит, я бы смотрел в сторону NodeJS), то знать количество энергии пользователя нужно только в момент отображения (обновления) страницы и каких-либо действий – например пользователь хочет войти в локацию, вход в которую стоит 20 энергии, и только в этот момент нам нужно узнать реальное количество энергии пользователя.

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

Для этого пишется довольно простой JS-скрипт-таймер, который каждую секунду (в моем примере это секунды, хотя можно сделать и другие промежутки времени) добавлять «по секунде», а когда накопится нужное количество секунд для восстановления одной энергии – увеличивает количество энергии на 1, а счетчик секунд сбрасывает.

Когда энергия полная (или если она полная изначально) – таймер останавливается.

 

Как бек-энд и фронт-энд работают в паре

При запросе к серверу (открытии страницы, или изменении энергии) бек-энд считает все данные, проверяет возможность изменения энергии и формирует html страницу вместе с нужными параметрами для js скрипта.

Когда страница загрузилась – начинает работать js, который запускает функцию timer(). Он в свою очередь запускает функцию step(), которая обрабатывает логику счетчика, а он в свою очередь запускает функцию view(), которая обновляет информацию на странице.

 

Примечания

  • Точность: При расчетах остаток секунд округляются до целых, соответственно может теряться до 0.5 секунд. Если игра не подразумевает частых расходов энергии, и частого обновления страницы – то это нормально. Иначе нужно будет дорабатывать точность: окгуллять не до целых, а, например, до десятых или сотых.
  • AJAX: Сейчас при изменении энергии обновляется страница, и, соответственно, перезапускается js-скрипт. Если вы будете изменять энергию AJAX-ом, то js-таймер нужно будет запускать отдельно.
  • Безопасность: В текущей версии все реальные расчеты происходят на бек-энде. Пользователь может со своей стороны что угодно делать с html/js (рисовать какие угодно цифры) – это никак не повлияет на реальные значения энергии пользователя.



Комментарии

bodnar 2018-03-18 21:35:01
БОльшое спасибо, у меня голова раскалывалась как это сделать(пару месяцев учу веб-разроботку, а тут курсач с программирования). Решил значит браузерку сделать в стиле ММОРПГ. На листе вс' біло отлично, до того момента, как за пару дней до сдачи у меня спросили, а резон проходить игру одним дыханием, если нету никакой энергии(здоровья). Я побледнел просто, как я мог забыть. Всё делал сам, а тут никак, загуглил - ничего путнего, но тут наткнулся на Вашу статью, огрооомное спасибо. Я код не смотрел, только дочитывал статейку, всё в голове сошлось и за часик сделал всё как нужно)

Ответить
? +6 ?
Rubak 2018-03-24 15:22:30
Сразу видно проплаченный комментарий

Ответить
? +3 ?
Страницы:

Ваше имя
Комментарий



Реклама:

Наша командаРазработка глобального обновления

При поддержке Сил Преисподней
2008-2024