
04.04.2013, 01:00
|
Интересующийся
|
|
Регистрация: 04.04.2013
Сообщений: 10
|
|
Анимация в real-time приложениях(Нужны советы)
Всем привет! Пришла мысль, сделать dendy игру аналог на jquery+nodejs.
Пишу на jquery не так давно,и играми естественно никогда не занимался.
Встрял на Анимации персонажей и окружающего мира.
1ая реализация была такая:
Подключившись к серверу, клиент получает содержимое текущего мира и "отрисовывает" его в браузере.
На деле было так: на клиент приходил массив, всех игроков которые были в игре, каждый игрок добавлялся на страницу как Дом элемент.
При движении в N сторону, посылался запрос на сервер о движении персонажа, после чего он вносил новые координаты текущего игрока в окружающий мир и после этого, давал команду на анимацию клиенту и всем остальным участникам.
Открыто сказать работало это всё дело коряво, ситуация усугублялась тем ,что сам клиент должен был оставаться на месте, прокручивая окружающий мир в N сторону.От этого происходила путаница кого в какую сторону надо двигать,да еще и начальная координата была "-170"  что еще больше путало.
2ая реализация работает уже так: всё тоже самое, только вместо ДОМ элементов теперь использую Canvas, клиент постоянно спрашивает у сервака "че происходит" и тот ессесно, любезно рассказывает.
Всё явно стало работать лучше,рассинхрону случится просто негде,т.к каждый кадр это Вопрос к серверу "Что на экране".При событии движения сервер меняет координату текущего игрока и в следующем запросе клиента видно, что тот походил.
Проблема встала в том, что так хорошо получается сделать анимацию в 2 кадра,а сейчас надо минимум 2.
Следующая проблема это то, что каждый кадр приходится рисовать заново с нуля.А игрок, может ходить как по горизонтали,так и по вертикали,как следствие - нужно определять кто стоит выше а кто ниже.
В идеале, хотелось бы сделать, что бы при движении в N сторону, игрок делал 2 кадра, после чего возвращался в исходную позицию.
Анимация двух кадров работает так:
1.клиент setInterval(100) посылает серваку "че?"
---
Клиент::Движение: говорим серверу Я иду вправо, тот меняет координату с 0 на 1, текущий спрайт с 0 на 1, после чего опять setInterval(50),который возвращает спрайт с 1 на 0.
На клиенте мы видим анимацию в 1 кадр,да при этом, в зависимости от
время нажатия клавиши движения относительно последнего запроса мы Всё равно попадаем в момент, когда анимация на сервере происходит быстрее, и клиент без кадра движения телепортируется на точку движения
Вообщем, знаю что это бредово, но пока это всё что я придумал, и хотел бы услышать ваши соображения по поводу такой задачи и её грамотной реализации
К слову, думаю засесть ковыряться с исходниками BrowserQuest,но уровень кода+связка с другими технологиями о которых я не знаю,усложняет понимание..
|
|

04.04.2013, 18:07
|
 |
Профессор
|
|
Регистрация: 28.03.2012
Сообщений: 376
|
|
намудрил ты, да)
во-первых, отдели мух от котлет: игровой мир (структуры данных, "физические координаты" и прочее) и визуальный мир (то что видят игроки).
то есть, условно, сервер тебе присылает структуру вида
{
gameState: 'active',
// ...
players: [
{ name: 'First', positionX: 10, positionY: 20 },
{ name: 'Second', positionX: 30, positionY: 35}
]
}
а тебе надо всё это вывести на экран.
для этого напиши функцию, которая будет "физические" координаты переводить в экранные.
грубо говоря, позиция (10;20) в физическом мире это (170;180) в экранном, так что функция будет выглядить типа так:
function Phisic2Screen(player) {
return {
screenX: player.positionX * 1 + 70,
screenY: player.positionY * 9
};
}
во-вторых, если игра в реальном времени, а не пошаговая, то надо немного хитрить.
смотри - "по-хорошему" когда игрок нажимает стрелку вверх, клиент должен послать серверу сообщение об этом, сервер должен его обработать и прислать новый снапшот состояния игры, в котором игрок переместился вверх. на практике это приведёт к задержке и дёрганиям (пока запрос уйдёт, пока сервер ответит..).
поэтому как только запрос ушёл, сразу начинай отрисовывать анимацию (чтобы игрок не видел никаких задержек) - но будь аккуратен, делай доп. проверки - вдруг запрос не дойдёт - и тогда у игрока на экране будет одно, а на сервере совсем другое.
к слову, в интерактивном режиме лучше пользоваться не аяксом, а веб-сокетами.
не знаю, об этом ли ты спрашивал, очень уж всё изложено сумбурно. так что уточняй вопрос, помогу если что.
|
|

04.04.2013, 18:10
|
 |
Профессор
|
|
Регистрация: 28.03.2012
Сообщений: 376
|
|
ну и подумай и протестируй быстродействие, может быть и не нужно хранить на сервере состояние спрайтов (какой шаг анимации сейчас отрисовывается) - мне кажется клиент будет просчитывать всё плавнее (опять же потому что не надо каждый раз ждать пока сервер ответит).
да и при медленном соединении расчёт анимаций на сервере будет приводить к жутким дёрганиям происходящего на экране, в то время как расчёты визуальной части на клиенте будут давать заметно более плавную картинку.
|
|

04.04.2013, 21:59
|
Интересующийся
|
|
Регистрация: 04.04.2013
Сообщений: 10
|
|
Сообщение от keen
|
во-первых, отдели мух от котлет: игровой мир (структуры данных, "физические координаты" и прочее) и визуальный мир (то что видят игроки).
то есть, условно, сервер тебе присылает структуру вида
|
Ага, так сейчас и есть! клиент получает приблизительно вот такую шнягу:
var currentgame = {
players: {
playerID: {
nickname: nickname,
x: 1180,
y: 340,
z: 340,
currentframe: 0,
weapon: 0
}
},
weapons: {}};
после на клиенте я сортирую их по Z и начинаю рисовать дальнего от экрана игрока
Сообщение от keen
|
смотри - "по-хорошему" когда игрок нажимает стрелку вверх, клиент должен послать серверу сообщение об этом, сервер должен его обработать и прислать новый снапшот состояния игры, в котором игрок переместился вверх. на практике это приведёт к задержке и дёрганиям (пока запрос уйдёт, пока сервер ответит..).
|
Считаю точно так же,и от этого я отказался(так было в первой версии)
Сообщение от keen
|
к слову, в интерактивном режиме лучше пользоваться не аяксом, а веб-сокетами.
|
ага, аяксом тут не отделаешься - пользуюсь socket.io
Теперь попробую уточнить...
Я согласен, что анимацию надо перенести на клиент,а теперь для наглядности проблемы:
Берем вариант что клиент спрашивает сервер раз в 100мс что рисовать,тогда:
1[00.00]C1:S Что рисовать?
2[00.05]S:C Ответ
3[00.10]C1: Отрисовывает
4[00.50]C1:S -> Иду вправо
(теперь надо отрисовать 3 кадра подряд)
А в этот момент некий C2 тоже начинает движение...
И как быть? Если отрисовка шагов C1 еще не закончилась,а уже пришел пакет на то, что C2 тоже куда то поперся.
Тут я представил, что можно сохранять мир при запросе к серверу "Что рисовать" и при пакете о движении С2 начать менять его координаты, но тогда мы теряем 1 кадр(типа если С1 рисует 3 кадра,а движение С2 началось с отрисовки 2ого кадра С1)
Если мы используем ту же функцию что и для отрисовки С1, тогда вообще не понятно что будет отрисовываться)) т.к в одно время будут рисовать сразу 2 функции(а если клиентов 10?100?)
2ой косяк, будет если анимация начнется с [00.95], тогда отрисуется 1 кадр анимации, и в [01.00] придет пакет который заменит весь сохраненный мир, и 2ым кадром Игрок будет на "новой координате", т.е на кадре "0" потеряв кадр 2 и 3.
Тут уже начинаю думать о неком Стеке анимаций,но как сделать что бы не случился рассинхрон накопленных анимаций с действиями в игре?  Или выполнять анимацию из стека пока не получили новый пакет "Что рисовать" и забить на потерю остатка анимаций?
Надеюсь суть проблемы понял, и спасибо что ответил!
Надеюсь услышать еще рассуждений на эту тему.
|
|

04.04.2013, 22:16
|
Интересующийся
|
|
Регистрация: 04.04.2013
Сообщений: 10
|
|
хм, подумал, что может отрисовывать скажем раз в 100мс, спрашивать раз в 1000 или в 10.000 для сверки и запускать функцию которая будет смещать игроков,ведь отрисовываем постоянно а не только при пакетах "что рисовать" и "движение".может прокатит 
|
|

05.04.2013, 11:34
|
 |
Профессор
|
|
Регистрация: 28.03.2012
Сообщений: 376
|
|
Сообщение от ak-o
|
Берем вариант что клиент спрашивает сервер раз в 100мс что рисовать
|
погоди, раз сокеты - то клиент сам ничего спрашивать не должен, зачем эти лишние действия и нагрузка сети.
клиент информирует сервер только об измененях (например если нажата клавиша), а сервер отвечает, типа "ок, информацию принял", плюс сам же, не дожидаясь никаких запросов, периодически рассылает всем клиентам информацию о состоянии игрового мира (дабы не было рассинхрона).
теперь касаемо визуализации.
рекомендую вот эту библиотеку - http://www.greensock.com/ , в некоторых тестах показывает производительность раз в 10 больше чем jquery.
вообще говоря, сам процесс отрисовки должен выглядеть примерно так:
засекаем время t1; рисуем игровой мир; засекаем время t2; считаем разницу (deltaT=t2-t1); отрисовываем игровой мир; переходим к началу.
то есть процесс отрисовки идёт с учётом дельты для того чтобы просчитать какие кадры спрайтов рисовать:
например есть у нас.. м.. спрайт бегущего игрока, частота кадров которого 10 герц (10 кадров в секунду, значит каждый новый кадр должен отрисовыватся каждые 100 мс).
допустим, у нас мощный комп и процесс отрисовки выдал дельту 50 мс.
тогда следующий цикл отрисовки по-прежнему должен выводить 1й кадр анимации бегущего игрока.
а если комп тормозной и отрисовал игровой мир за полсекунды (500 мс), то следущий цикл отрисовки начнёт сразу с 6го кадра.
зная дельту легко расчитать fps. и при высоких значениях все движения будут отрисовываться плавно, а при низких - резко, но - самое важное - от fps не должны зависеть скорости передвижения игроков в игровом мире.
ну вот например глянь на небольшую демку на webgl, которую я делал - там в исходном коде как раз хорошо виден тот принцип что я выше описал.
если бы всё отрисовывалось без учёта дельты времени, на слабых компах модель бы вращалась медленно, а на шустрых - быстро.
|
|
|
|