webportal
@webportal

Как можно реализовать таймеры для браузерной игры?

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

Например имеем параметр энергия у юзера, при текущих его скилах энергия восстанавливается со скоростью x, а когда, например, скилл выносливость прокачаем, то скорость восстановления становится x*2. Как быть, если в именно этот момент у нас прошла, скажем, только половина времени восстановления? И как вообще реализовать такое? Очередь и демон? Крон скрип или что-то другое?
  • Вопрос задан
  • 3379 просмотров
Пригласить эксперта
Ответы на вопрос 6
@Vampiro
я бы делал так.
1. берем текущее состояние и время когда последний раз отдавали значение ($a, $t)
2. считаем дельту по времени $dt = time()-$t;
3 рассчитываем новое значение $new = $a+ ($dt*1.2); где 1.2 - это коэффициент увеличения в секунду, скажем.
4. сохраняем время и новое значение

при обновлении статов пересчитываю все параметры от этого стата зависящие.
Ответ написан
Комментировать
@kaasius
Мало данных. Можно сделать так и эдак, зависит от того, что вам надо. По описанию этого не понять.
Ответ написан
Комментировать
dabich
@dabich
Web Developer
Можно запоминать текущее состояние энергии для продолжения в другом темпе. При прокачке просто ускорять таймер. А про реализацию смотря как вообще реализовывать. Какого вида игра. С помощью php я так понял игра статическая, без анимаций? Побольше бы информации про то как вообще реализовывается игра и на что она похожа.
Ответ написан
И как вообще реализовать такое? Очередь и демон? Крон скрип или что то другое?


Например у нас 0 энергии из 10. Мы знаем, что скорость восстановления 1 энергии = 2 секунды. Получается восстанавливается 0,5 э/с.
Перед началом восстановления энергии мы записываем таймштамп начала восстановления.
Сам таймер делаем на клиенте, вида примерно такого:
var energy = {$energy};
var coef = {$coef};
var timer = setInterval(function(){
 if(energy!=totalenergy){
  energy = energy + coef;
 }else{
  clearTimer(timer);
}
}, 1000);

Вместо $coef подставляем 0.5 э/с. Вместо {$energy} подставляем текущее количество энергии, в данном случае 0;

Что происходит, если пользователь обновил страницу, например через 8 секунд?
В php смотрим таймштамп начала восстановления энергии и отнимаем его от текущего таймштампа - узнаем сколько прошло секунд с момента начала восстановления - 8 секунд. Умножаем на коэффициент 8*0.5, узнаем что с момента посленего обновления страницы восстановилось 4 энергии. Отправляем эти данные в клиент. Снова записываем таймштамп.

var energy = 4;
var coef = 0.5;
var timer = setInterval(function(){
 if(energy!=totalenergy){
  energy = energy + coef;
 }else{
  clearTimer(timer);
}
}, 1000);


Пользовался подобным способом в проекте, там был таймер отсчета, но суть та-же.
Ответ написан
Комментировать
arutyunov
@arutyunov
Mooza.ru — Делаем сайты
Каким образом меняется скорость восстановления (т.е. "скилл выносливость увеличился")?
Можно в момент изменения скорости (т.е. скилла) слать запрос на обновление всех таймеров.
Ответ написан
Комментировать
Такого рода вещи следует безусловно считать на сервере для гарантии от жульничества, обрывов связи и т.п.
В сущности вам нужен неблокируемый таймер. Есть несколько способов реализации этого, но почти все костыльные в силу синхронности парадигмы программирования под php.

Однако, есть один способ, который позволит сделать полностью асинхронную обработку, только это потребует кардинального пересмотра всего серверного приложения.Есть такая библиотека libevent и обертка под названием reactPHP.

Я недавно решал проблему в своем проекте с отложенным отключением пользователей при обрыве websocket-соединения и мне данное решение помогло.

Суть в том, что приложение строится как цепочка callback-ов типа onOpen, OnMessage, onClose, которые вешаются на event loop. Сооветственно, на него же можно повесить и таймеры при обработке этих глобальных callback-ов.
Например:
...
public function onClose(Connection $connection)
{
	...
	$loop = MightyLoop::get()->fetch();

	$detacher = function() use ($user) {
		$clients = UserCollection::get();
		$clients->detach($user);

		$this->notifyOnClose($user, $clients);
	};

	if ($user->getAsyncDetach()) {
		$timer = $loop->addTimer(30, $detacher);
		$user->setTimer($timer);
	} else {
		$detacher($user);
		$user->getConnection()->close();
	}
	...
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы