Кажется, я починил DispatchAnimEvents. Досконально не тестировал, но скрипт на карте gruntbattledemo в спирите стал работать значительно лучше. Да и вроде монстры стали меньше тупить. Проблема поднималась на форуме как минимум один раз тут. Как Дядя Миша писал выше, проблема в обнулении m_fSequenceFinished, и последующем проигрывании секвенции n-ное количество раз, пока, наконец, m_fSequenceFinished не станет true.
Я сравнил код из халфы с кодом из hl2beta, а затем из se2007. SourceSDK 2013 не смотрел, т.к. уверен, что принципиальных отличий нет. Хрен его знает зачем, но в первой халфе вальва сделала в вычислении диапазона эвента привязку к gpGlobals->time. А это, как оказалось, имеет зависимость от фпс и даёт нехорошие погрешности. Вот этот код в animating.cpp, void CBaseAnimating :: DispatchAnimEvents ( float flInterval )
C++ Source Code:
1
// FIXME: I have to do this or some events get missed, and this is probably causing the problem below
По комментам понятно, что эта фигня глючит, да ещё и m_fSequenceFinished приходится выставлять в TRUE ещё один раз, если StudioFrameAdvance этого не делает. Если приглядеться, то flStart и flEnd это не время, а кадры в диапазоне от 0 до 256. И вот тут-то доходит, в чём отличие сорсовской версии. Там нет привязки к времени. То есть никаких pev->animtime и gpGlobals->time, только диапазон от 0 до 1
C++ Source Code:
1
float flStart = m_flLastEventCheck;
2
float flEnd = m_flCycle;
3
4
if (!m_bSequenceLoops && m_bSequenceFinished)
5
{
6
flEnd = 1.0f;
7
}
8
m_flLastEventCheck = flEnd;
Я взял, да и зменил исходный код на сорсовский с изменениями:
C++ Source Code:
1
float flStart = m_flLastEventCheck;
2
float flEnd = pev->frame;
3
4
if (!m_fSequenceLoops && m_fSequenceFinished)
5
flEnd = 256.0f;
6
7
m_flLastEventCheck = flEnd;
Ну и ещё в ResetSequenceInfo поменял m_flLastEventCheck = gpGlobals->time; на m_flLastEventCheck = 0; В коде цыклера это надо тоже сделать.
Короче, вроде бы работает лучше, будем посмотреть.
Добавлено 01-12-2020 в 04:10:
Вдогонку видос с доработанным барником. Разобрался, как грамотно чекать врагов, чтобы монстр убегал от них резвее. Когда добавлю ему это - ближнебойные потенциально не смогут его завалить.
Aynekko по сути это не фикс, а дописывание функционала барника с использованием того, что есть в дефолтном AI. Но кое-что я всё-таки поменял. Обкатаю и протестирую, и может быть напишу тутор. Ещё кое-что сделано не совсем правильно, но результат уже хороший: выживаемость у него выросла. Даже боюсь представить, что будет с грантами и ассассинами - они живучее и подвижнее.
Ku2zoff, попозже попробую твои фиксы анимаций тоже. В идеале такие изменения нужно отдельными коммитами выкладывать на какой-нибудь гитхаб. Чтобы заинтересованные могли глянуть, что поменялось в сравнении с оригиналом, и видеть только необходимое множество изменений.
FreeSlave писал: попозже попробую твои фиксы анимаций тоже
Дело не только в анимациях, но ещё и в щедъюлях. В некоторых из них есть TASK_WAIT и TASK_STOP_MOVING там, где они вообще не нужны. Например, SCHED_TAKE_COVER_FROM_ENEMY. Монстр теряет целую секунду на этих двух строчках. Если их закомментить, барник отваливает от врагов почти сразу, как они подойдут. Если отредактировать все щедъюли, то ненужных пауз между действиями не будет.
Ku2zoff писал: Дело не только в анимациях, но ещё и в щедъюлях.
Я про DispatchAnimEvents. Так как проблема двойного срабатывания ивентов действительно существует.
По поводу шедулей я кстати тебе в личку писал свои мысли. Мб ты пропустил сообщение?
Цитата:
Если их закомментить, барник отваливает от врагов почти сразу, как они подойдут. Если отредактировать все щедъюли, то ненужных пауз между действиями не будет.
Мне кажется, ожидания в некоторых местах всё же нужны. Иначе возможна ситуация, когда монстр будет постоянно пытаться построить путь, что может быть трудоёмко для старых компов, особенно если монстров много.
Кстати ошибка построения пути наверно самая частая причина непредусмотренных прерываний (т.е. срабатывающих не по условию в свойствах шедули).
FreeSlave писал: Мне кажется, ожидания в некоторых местах всё же нужны.
Конечно нужны. Иначе выглядит нелепо: на одном из моих видео барник сначала бежит к одной ноде лицом, а потом бежит почти боком к другой. Тут надо сам код FindCover переписывать. В голдсорсе ищется нода, если она подходит по критериям, монстр движется к ней, если достиг её - return TRUE - TaskComplete(). А правильнее надо так: ищется нода, если подходит по критериям, то BuildRoute до ноды, если успешно, монстр движется. Если вдруг не дошёл, пробуем другой маршрут до ноды, или ищем новую ноду. Снова BuildRoute и движение. И вот когда дошёл - TaskComplete(). А то обычным способом (если немного подкрутить функцию на поиск соседних связанных нод) он начинает метаться между нодами не меняя angles, что выглядит по-уродски. А если не подкрутить на поиск, то вообще TaskFailed() и искорки над головой.
Уважаемые админы, вырежьте нам пожалуйста из этой темы посты начиная с этого в отдельную тему. Эта ведь для вопросов, а не обсуждений и демонстрации.
Ku2zoff, не совсем тебя понял. FindCover зовёт MoveToLocation, который суть просто непрямой вызов того же BuildRoute. Т.е. ты описал два одинаковых случая (если не брать в расчёт предложенное перестраивание пути). А непосредственно движение выполняется вообще не в шедулях.
Бег боком это тоже что-то новенькое - наверно в твоих библиотеках. В HL то монстры (кроме контроллеров) всегда поворачиваются по направлению движения.
FreeSlave писал: MoveToLocation, который суть просто непрямой вызов того же BuildRoute
Точно, тупанул. Казалось, BuildRoute вызывается до начала движения, а не из одной функции с ним.
Цитата:
FreeSlave писал: В HL то монстры (кроме контроллеров) всегда поворачиваются по направлению движения.
Здесь наверное причина в отсутствии TASK_WAIT и TASK_STOP_MOVING. Монстр просто не успевает повернуться или проиграть ACT_TURN_LEFT/ACT_TURN_RIGHT если нужно. Ладненько, будем посмотреть. Я сейчас откачусь до предыдущих сорцев, где шаренный код монстров ещё не тронут, и буду ковырять только барника. По-грамотному уже, а то в текущей версии у меня всё на скорую руку.
Добавлено 01-12-2020 в 23:33:
FreeSlave а какие у тебя наработки в этой области?
В TASK_FIND_COVER_FROM_BEST_SOUND я помимо поиска пути до укрытия сделал в качестве альтернативы просто поиск отдаленного нода. Т.к. в случае ухода от звука опасности (гранаты, например) суть даже не в том, чтобы найти укрытие, а в том, чтобы просто убежать подальше. Думаю, ты сделал нечто такое и в коде сбегания барника от врага. Тут ничего сложного, по сути тот же FindCover, но без проверки на то, что ориджин "угрозы" не увидит монстра после того, как тот встанет на ноду.
//ALERT(at_aiconsole, "Using the last resort to run away\n");
27
TaskComplete();
28
}
29
}
30
// no coverwhatsoever. or no sound in list
31
if (!TaskIsComplete())
32
TaskFail();
Если говорить в целом о поведении монстров, то я уже кое-что упоминал в предыдущей теме. Но здесь распишу то, что касается всех передвигающихся монстров, а не отдельных видов.
Добавил Monster Roaming (параметр freeroam) из Sven Co-op, с которым монстры рандомно ходят по нодам даже в idle-состоянии. В принципе может быть полезно для создания иллюзии патрулирования территории без лишних затрат.
Кстати, в HL есть патрулирование по path_track, но в очень урезанном виде - при встрече с препятствием монстр не ищет обходных путей. Это я у себя тоже исправил, плюс добавил возможность использования ожидания выставленного в path_track. И теперь в местах, где достаточно задать только передвижение монстра, использую патрулирование вместо scripted_sequence дабы монстр адекватно реагировал на окружение, ибо во время выполнения скриптов монстры реагируют только на урон.
В качестве экспериментов:
Как известно, FTriangulate в HL используется для поиска промежуточной точки, по которой можно обогнуть встреченное на пути препятствие. Я добавил возможность искать не одну, а две точки (это уже получается не триангуляция, а квадрогуляция ), что даёт неплохие результаты и особенно полезно для NPC, следующих за игроком (вас же тоже задолбали союзники, тупящие на каждом углу? )
Для союзников следующих за игроком реализована попытка подобраться к игроку как можно ближе, если монстр не смог построить путь до игрока (что случается довольно часто, стоит лишь игроку забраться повыше или пониже). В результате на crossfire барник бегает вдоль края поля перед бункером, если игрок находится на втором этаже. Побочным эффектом тут конечно является то, что монстр будет бегать за игроком вдоль стены аки примагниченный, если игрок находится с другой стороны стены. Замечу, что это сделано без использования нодов. В качестве дополнительной или альтернативной меры можно использовать что-то в духе BuildNearestRoute, но я пока этого не делал (почему-то не подумал в своё время)
Способность монстров "забывать" о своём враге, если давно его не видели, пытались-пытались добраться, да не смогли на протяжении некоего промежутка времени. Особенно актуально, если враг - игрок, т.к. ему проще всего убежать от монстра. В HL, если игрок пропустил какого-нибудь хедкраба, тот так и запомнит врага и будет весь остаток игры пытаться до игрока дойти. Монстры в HL вообще не забывают о враге до тех пор пока тот не умрёт либо не исчезнет с карты.
Сейчас в связи с последним (да и не только) думаю, чем можно было бы занять монстра в ALERT состоянии. В HL лишь некоторые монстры занимаются чем-то полезным в этом состоянии - буллсквиды ищут чего покушать, солдаты идут к трупу поверженного врага, чтобы убедиться в том, что тот умер. В остальном же монстры просто стоят и смотрят. Тут напрашиваются такие вещи как переход к последней известной позиции врага (если враг был "утерян"), исследование услышанных звуков боя (типа Investigate Sound, который по умолчанию используется только у ассассинок), и тот же freeroam, описанный выше.
FreeSlave писал: Тут ничего сложного, по сути тот же FindCover, но без проверки на то, что ориджин "угрозы" не увидит монстра после того, как тот встанет на ноду.
Нет, я сделал не так, а добавил поиск связанных нод. Это немного помогло избавиться от тупняков. Но вчера убрал кактус с тестовой карты, и барник перестал убегать, т.к. прятаться стало не за что. Я сразу смекнул что к чему, вчитался внимательнее в код и тоже решил убрать оттуда трассу, чтобы сделать просто RunAway на открытой местности.
Добавлено 02-12-2020 в 20:51:
Цитата:
Next Day писал: А куда этот код добавлять если не секрет ?