PDA

Просмотр полной версии : Предрасчитанный бой. Способ организации отображения на клиенте.


caseyryan
28.03.2013, 19:37
Делаю игру, на подобие такой каких уже дофига, а именно на прокачку персонажа, с предрачитанными на сервере боями.
Уже несколько раз переписал полностью классы отвечающие за отображения сражения двух персов.
С предрасчетом боя на сервере нет проблем, там все четко рассчитывается и клиенту приходит строка с параметрами сражения. А вот как лучше поступить дальше?
Сейчас у меня происходит так: строка парсится, и из нее выдергиваются данные о каждом ударе, о том кто его нанес, какие повреждения должны быть нанесены жертве и тип удара. Вроде бы все ок, задумка ясна. Но по какой-то причине не получается нормально распределить удары между персонажами. Если бы бились двое, то все просто, но врагов может быть несколько.

Так вот, мне не нужно конкретное решение, меня интересует кто бы как сделал подобный алгоритм? Может быть навеется какой-то более интересный подход, чем у меня. Уже голова трещит с этой игрой. Или может кому-то уже доводилось делать такое

iflamberg
28.03.2013, 19:41
Как это "не получается нормально распределить удары между персонажами", когда у тебя "выдергиваются данные о каждом ударе, о том кто его нанес, какие повреждения должны быть нанесены жертве".
Покажи строку.

caseyryan
28.03.2013, 19:59
Не в строке дело. Я уже сто раз проверял эти сроки. Блин, даже не знаю как объяснить..
Сама строка после первоначального разбиения выглядит так:
h-23:0 или так m-0:0 (m = missed)
в первой h означает hit (удар), далее уровень повреждения, а последнее число это индекс жертвы в массиве жертв. Эта строка передается в конструктор класса Hit, в котором все раскладывается по полочкам. Все эти объекты hit попадают в общий массив ударов для сценария боя. А далее берется первый удар, проверяется кто его нанес, к этому персонажу добавляется слушатель завершения удара и дается команда идти и нанести удар. Он идет, наносит удар, при этом "использованный" объект Hit вырезается из массива простым _hits.shift();
И в качестве действующего удара опять назначается удар, находящийся на нулевом индексе. И все сначала. Вроде бы все должно быть ок. Но вот время от времени объекты hit относящиеся к первому персонажу заканчиваются, а здоровье у него не на нуле. И в этот момент оба перса тупо останавливаются и ничего больше не делают.
Уже и на свой серверный алгоритм грешил, может там что не верно. Брал калькулятор, просчитывал все показатели, по всему должен был бы сдохнуть, а нет, жив.

Пришел к выводу, что подход кривой. А посему нужна какая-то другая идея. Может слегка другая реализация этой же идеи, в общем, не знаю.. Что-то я в тупик зашел

mikhailk
28.03.2013, 20:01
Что-то как-то сложно.

caseyryan
28.03.2013, 20:03
Тоже не очень понял, в чем проблема.
Описал в третьем посте.

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

mikhailk
28.03.2013, 20:08
Я баттл 5х5 далал так:
1. сервер просчитывал весь протокол баттла до самого конца
2. на каждом шаге баттла были данные о юнитах, виде удара и дамадже
3. я выбирал для каждого шага пару юнитов и показывал анимацию, после чего переходил к следующему шагу

все

Да, работал без слушателей - по таймингу.
В смысле - ничего не слушал от юнитов.

caseyryan
28.03.2013, 20:11
Ну, в принципе идея та же, что и у меня.
А где можно глянуть как это работает?
или еще нет проекта?

mikhailk
28.03.2013, 20:15
А где можно глянуть как это работает?

В Землях шаманов на ВК или ОК, там до стадии множественного боя с обоих сторон проект не дошел, но баттл впятером против одного босса работает. В принципе, клиенту все равно сколько было показывать, не случилось решения на уровне геймдизайна.

А в прототипе баттла было как раз 5х5.

iflamberg
28.03.2013, 20:48
У меня небыло расчитанных на сервере боев. Но была пошаговая тактика типа desiples, где бой записывался, а потом законченный бой можно было посмотреть в отдельном окне. Данные тоже приходили строкой, xml-кой, чтобы было проще парсить, но это не важно. Точно так же я брал итем за итемом и отображал на экране, что происходит. Только, я уже говорил, я не очень люблю событийную систему, поэтому у меня просто вызывалась функция nextAction() сцены боя по окончанию нанесения удара.
Честно говоря, мне тяжело представить себе какую-то другой способ запрограммировать подобную боевку.
Пока что выглядит так, как будто у тебя где-то объект "съедает" GC. Ну, исходя из "Но вот время от времени объекты hit относящиеся к первому персонажу заканчиваются, а здоровье у него не на нуле". Локальный объект, который подписан на событие, но на который нет глобальной ссылки? Мы тут недавно доказали, что такие объекты GC таки кушает, несмотря на слушатель.

caseyryan
28.03.2013, 21:48
но на который нет глобальной ссылки?
Глобальная ссылка есть. Он лежит в массиве.
Его сборщик мусора не убивает. У меня стоят проверки на количество объектов.
Но раз уж других идей нет, то пока эту попилю. Возможно где-то простейшая оплошность какая-то, но еще не нашел где и какая

mikhailk
29.03.2013, 14:06
А я чего-то вообще не понимаю проблемы. Допустим, сервер прислал протокол баттла (пишу в xml для нагладности):


<battle>
<!-- unitID бьет по targetUnitID (вид удара - action, ущерб - dmg,
здоровье у targetUnitID спосле удара - hp) -->
<step unitID="1" targetUnitID="2" action="hit" dmg="30" hp="100" />
<step unitID="2" targetUnitID="3" action="miss" dmg="0" hp="200" />
<step unitID="3" targetUnitID="4" action="crit" dmg="120" hp="0" />
<step unitID="2" targetUnitID="1" action="hit" dmg="30" hp="70" />
</battle>


Клиент по шагам отрабатывает этот батл, выбирает unitID и targetUnitID, проигрывает анимацию в зависимости от action, если hp по итогам хода равно нулю, у targetUnitID отыгрывается анимация смерти. Полная свобода действий, клиенту вообще все равно, что там сервер насчитает. Главное, чтобы unitID != targetUnitID. :) Если, конечно, не предусмотрен функционал харакири.

caseyryan
29.03.2013, 14:39
Да это все понятно. Теория ясна как день.
Похоже где-то в коде косяк.

C4Grey
29.03.2013, 17:46
А зачем считать НР еще раз на клиенте, если они на сервере уже были посчитаны? Если удары наносятся в процессе очереди, то почему бы в инфо о каждом удара не писать количество отсавшихся у цели HP? У меня была похожая проблема, когда я часть логики real-time игрушки решил на сервер перенести - иногда юниты не успевали убивать кого-то и "проскакивали" сквозь цель после синхронизации следующего серверного фрейма. Там ошибки появлялись из-за неточности вычислений.

caseyryan
29.03.2013, 22:14
то почему бы в инфо о каждом удара не писать количество отсавшихся у цели HP?
Идея хорошая, но есть одно но. Удары на клиенте перемешиваются в случайном порядке. То есть общее количество урона остается неизменным, но в каком порядке он будет наноситься - уже вопрос. А на сервере расчет происходит общим потоком и для игрока и для врагов. По очереди.

КорДум
29.03.2013, 22:17
Удары на клиенте перемешиваются в случайном порядке.
То есть у тебя может возникнуть такая итуация, что умерший герой будет лупить неумершего?
Возможно тут и стоит искать косяк?

caseyryan
29.03.2013, 22:28
Теоретически нет, потому что при убийстве персонажа отправляется событие onDie и по этому событию либо автоматом ставится следующий враг, если он есть, либо выдается сообщение о проигрыше.

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

maxkar
29.03.2013, 23:33
То есть общее количество урона остается неизменным, но в каком порядке он будет наноситься - уже вопрос
Ну так в этом, скорее всего, и проблема. Есть два врага с 99 и 100 хитпоинтами. Пришел массив [99, 100]. Если сначала поставить врага со 100 хитпоинтами, то второй удар придет на него же и на второго вообще не останется. А если поставить с 99, то все будет нормально.

caseyryan
30.03.2013, 09:34
Не, там не массив уронов приходит. Там приходят строки в которых явно указано кто этот урон должен нанести. Строки изначально такого вида:

enemy0=h-23:0|s-45:0&enemy1=h-20:1|m-0:1&player=h-34:0|m-0:0|s-61:1

Все это разбивается по разным массивам для разных врагов.
После двоеточия указан индекс врага, которому должен наноситься укрон.
Самое странное, что после формирования этих массивов, я их трейсил, просто брал калькулятор и пересчитывал весь урон для каждого персонажа. Все четко. Запускал бой, и результат был другим. Такое впечатление, что где-то не правильно до int округляется.

Сегодня перепишу весь алгоритм с нуля, и отпишусь о результатах )

mikhailk
30.03.2013, 16:52
Теоретически нет, потому что при убийстве персонажа отправляется событие onDie и по этому событию либо автоматом ставится следующий враг, если он есть, либо выдается сообщение о проигрыше.

Мне кажется, основная проблема существующей реализации баттла в том, что на самом деле считают все вместе - и сервер и клиент. Мне кажется, клиент в предрасчитанном баттле вообще ничего думать не должен, только как лучше показать.

И события от юнита I_DIED по своей сути быть не может. Не его дело определять свой статус.
От юнита может только прийти событие ACTION_IS_COMPLETE, которое свидетельствует, что он отыграл положенную ему анимацию, перешел в айдл и ждет чего ему дальше скажут делать. Еще он можен отыграть анимацию смерти при нулевом здоровье, перейти в фазу трупа и ждать, пока его уберут с локации.

caseyryan
30.03.2013, 17:34
Событие onDie нужно исключительно для того, чтобы отключить просчет действий персонажа. Проще говоря есть массив врагов _enemies. В бою всегда участвует враг, который находится на нулевом индексе. При прохождении события onDie вызывается _enemies.shift() И в бой автоматически вступает следующий доступный враг
Возможно да, надо от этого избавиться. Попробую сейчас без этих заморочек сделать.

mikhailk
30.03.2013, 21:42
Событие onDie нужно исключительно для того, чтобы отключить просчет действий персонажа

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

caseyryan
30.03.2013, 22:24
Это не относится к просчету самого боя. Я говорю о просчете движения персонажа пока он идет к цели и совершает удар. По этому событию он сносится из массива врагов, и у него перестает вызываться метод update() по энетрфрейму

А в выборе врага да, можно обойтись и без этого

Добавлено через 16 часов 8 минут
Решил проблему )
Она оказалась глупа, как это чаще всего бывает :D
Тут уже даже о чем-то подобном говорилось.
В общем, числа с сервера приходят верные. Но при перемешке массивов, иногда бывает так, что последний удар остается за уже мертвым врагом. Соответственно, ввел проверку на то, чтобы последний удар всегда оставался за тем, кто должен победить. И теперь все работает.

Вот так вот проблемы возникают на ровном месте :D

Всем спасибо за участие