Jump to content
Korean Random
SoprachevAK

Сбор статистики о попаданиях

Recommended Posts

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

Встаёт вопрос о том как получить инфу от клиента, как то год назад уже писал модик, крайне неприятный опыт, так что проще всего спросить совета, а не рандомно тыкаться в исходник

 

Собственно мне нужно:

  1. Хук в старт битвы
    1. Получение общей инфы (ник игрока, техника, пушка, её урон и продибие по типу снарядов, разброс с учётом перков и модулей)
  2. Хук в момент выстрела 
    1. Координата маркера прицела 
    2. Текущий разброс (надеюсь он в хук выстрела ещё не разбрасывается)
    3. Танк на который наведён прицел (буду брать хп шотных совков)
    4. Тип снаряда которым произведён выстрел
  3. Хук? в момент попадания снаряда в коллайдер (видел как то на форму такой коллбэк, но сейчас найти не получается)
    1. Координата куда снаряд попал
    2. Инфа о попадание если попал в танк (тип попадания (пробитие/рикошет/не пробил), урон)

 

Буду очень признателен за помощь, потенциально модик интересный, обговорил с некоторыми стримерами и они готовы информационно поддержать, так что данные для анализа будут

 

Share this post


Link to post

Short link
Share on other sites

Этой седой бороде уже 10 лет в обед. Кто только не пробовал собирать и анализировать эту инфу. На раковом были даже профильные темы, я в них сидел раньше. Скажу сразу - ничего там интересного нет. Обычное нормальное распределение. Степень его кривости будет коррелировать с репрезентативностью собранных данных и тем насколько процесс их собирания близок к Пуассоновскому потоку событий.

 

Про моды - можно посмотреть здесь (описание тут) и здесь.

 

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

Edited by StranikS_Scan
  • Upvote 3

Share this post


Link to post

Short link
Share on other sites
5 hours ago, StranikS_Scan said:

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

Ну тут вопрос в том как  эту информацию отображать, я хочу сделать визуальную инфографику, которая будет по списку боёв показывать точку попадания каждого выстрела относительно круга сведения, будет опция переключение на нормализованное отображение, когда круги сведения приводятся к одному размеру, будет отображаться суммарный круг, где на одной картинке будут все точки попаданий выбранных боёв, на ней будет 50% квантиль нормального распределения и 50% квантиль фактического, по их взаимному расположению можно будет однозначно определить везло или нет в выбранных боях
У многих стримеров горит с того как не летят снаряды и им было бы интересно чем то оправдать своё горение, ровно так же, как многие стримеры юзают модик показывающий точки попадания по танку после боя, пару стримеров которых я спросил с аудиторией 100к-150к, подтвердили интерес

За модики спасибо, из них суммарно я смогу взять координату пушки до выстрела и координату попадания как по ландшафу, так и по технике, однако под вопросом остаётся способ получения текущего разброса танка в момент перед выстрелом, тк далеко не все выстрелы производятся с полным сведением

 

И если не сложно, ткни в мануал как запустить исходники твоих модов, через .wotmod работают, через орион и exec, import/reload scripts from file/folder выдаёт ошибки по типу ImportError: No module named hook, тесторый скрипт print(BigWorld.player().name) работает

Share this post


Link to post

Short link
Share on other sites
8 минут назад, SoprachevAK сказал:

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

 

Это теория. А на практике:

- не круг сведения, а круг разброса, это разные круги

- есть клиентский круг разброса и есть серверный, в динамике они не совпадают

- событие выстрела и событие попадания разнесены по времени

- распределение у разброса снарядов в текущей версии игры не совсем "нормальное", оно искусственное и до конца не изучено (патч 0.8.5, патч 0.9.6, патч 1.0.x)

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

- после чего осмысливаем с какой погрешностью это все будет работать в динамике

 

Отсюда приходим к простому выводу, что если стримеру нужно знать "везло ему или не везло", то есть в разы более простые и понятные пути как это посчитать. Например, взять число выстрелов за бой и число попаданий и выдать стримеру %. Это идеальный мод для ютуберов и их хомяков. Легко, просто, понятно. Сей мод будет не только информативен но даже и полезен, так как сподвигнет малолетних танкистов не стрелять в небо по кустам и просто так.

 

1 час назад, SoprachevAK сказал:

через .wotmod работают

 

Они же собраны под пакет. Только в составе пакета и будут работать. Запуск через Орион они не предполагают.

Share this post


Link to post

Short link
Share on other sites
1 hour ago, StranikS_Scan said:

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

Ну в твоём моде с артой есть серверный тангенс разброса

 

1 hour ago, StranikS_Scan said:

распределение у разброса снарядов в текущей версии игры не совсем "нормальное", оно искусственное и до конца не изучено (патч 0.8.5, патч 0.9.6, патч 1.0.x)

Вместо нормального можно брать среднее по всем боям, думаю оно будет весьма точным

 

1 hour ago, StranikS_Scan said:

точка попадания может оказаться за точкой прицеливания

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

 

1 hour ago, StranikS_Scan said:

после чего осмысливаем с какой погрешностью это все будет работать в динамике

 

Спорно, большинство выстрелов делается в стоячем положение с полным или почти полным сведением, что нивелирует рассинхронизацию, и остаётся научиться считать траекторию, либо засчитывать лишь те выстрелы, у которых F(скорость полёта снаряда, расстояние от прицела до попадания) < X, где функцию F и константу X взять с потолка

 

1 hour ago, StranikS_Scan said:

взять число выстрелов за бой и число попаданий и выдать стримеру %

Это вроде бы и так показывается в послебоевой статистике, однако из 20 выстрелов обидно иметь не 5 попаданий по танку, а 15 промахов когда улетело в край разброса, обидны выстрелы, которые могли бы попасть, если бы не рандом

 

 

В заключение остаётся вопрос о вычисление траектории

Share this post


Link to post

Short link
Share on other sites
8 часов назад, SoprachevAK сказал:

В заключение остаётся вопрос о вычисление траектории

 

Ну да, в остальном же все гладко ))))))) 

https://github.com/StranikS-Scan/WorldOfTanks-Decompiled/blob/b92d3a2b1678f04f1ea78aa78ab6e77e51b98844/source/res/scripts/common/projectile_trajectory.py

Share this post


Link to post

Short link
Share on other sites
24 minutes ago, StranikS_Scan said:

Ну если предположить, что ProjectileMover.add получает refVelocity достаточную для вычисления траектории, то да, остальное вроде бы гладко

И судя по всему это то что надо

 

Два выстрела с бт7 артиллерийского

image.thumb.png.9aff4ef46bf726ea1ef3ed9f4c2f8f53.png

image.thumb.png.24d3434b748760baee05720aa00d424e.png

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites

@StranikS_Scan а почему скорость трассера меньше ттх скорости снаряда? Ты в своём моде делишь её на 0.8, что видимо было посчитано эмпирически, тк в движке я никаких упоминаний об этих магических константах я не нашел  

 

ps. Если интересно вот что получилось, пока в альфа версии https://soprachev.com/wot-shoot/

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites
50 минут назад, SoprachevAK сказал:

почему скорость трассера меньше ттх скорости снаряда? Ты в своём моде делишь её на 0.8, что видимо было посчитано эмпирически, тк в движке я никаких упоминаний об этих магических константах я не нашел  

 

Share this post


Link to post

Short link
Share on other sites
2 часа назад, SoprachevAK сказал:

что видимо было посчитано эмпирически

 

Ага, ну прям))) Эмпирика - удел безработных чудиков. У меня нет времени на такое. Смотри чтение параметров из xml - тыц. Ну и ссылку на подробное объяснение выше дал Ктулху.

 

2 часа назад, SoprachevAK сказал:

ps. Если интересно вот что получилось, пока в альфа версии https://soprachev.com/wot-shoot/

 

Нету подписей у кругов. Не понятно, что есть что. Ну и в таких случаях стоит где-то описание подробностей давать ибо иначе сразу возникают вопросы - насколько все это соотносится с физикой игры. Например та же вероятность попадания (квантиль) при каких условиях считается. Когда, например, erfc() или erf() применяете, то нужно учитывать, что в игре распределения не идеальные ,а с обрезкой. ВГ в распределениях разброса, урона, пробития, берет(брал) обрезку по трем сигмам..... Еще бы вероятность попадания в круг показывалось бы. Да и кругов этих много... может какие-то круги с полупрозрачной заливкой сделать.

 

А вообще, если вы такое умеете делать, как этот сайт сейчас, то мои советы вам нафиг не нужны )))))

Edited by StranikS_Scan

Share this post


Link to post

Short link
Share on other sites
On 2/18/2021 at 2:02 PM, StranikS_Scan said:

 

Ага, ну прям))) Эмпирика - удел безработных чудиков. У меня нет времени на такое. Смотри чтение параметров из xml - тыц. Ну и ссылку на подробное объяснение выше дал Ктулху.

 

 

Нету подписей у кругов. Не понятно, что есть что. Ну и в таких случаях стоит где-то описание подробностей давать ибо иначе сразу возникают вопросы - насколько все это соотносится с физикой игры. Например та же вероятность попадания (квантиль) при каких условиях считается. Когда, например, erfc() или erf() применяете, то нужно учитывать, что в игре распределения не идеальные ,а с обрезкой. ВГ в распределениях разброса, урона, пробития, берет(брал) обрезку по трем сигмам..... Еще бы вероятность попадания в круг показывалось бы. Да и кругов этих много... может какие-то круги с полупрозрачной заливкой сделать.

 

А вообще, если вы такое умеете делать, как этот сайт сейчас, то мои советы вам нафиг не нужны )))))

А я да, сама актуальность) 

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

Квантиль считаю не теоретическую, а практическую по текущей выборке. Например выбрано 100 попаданий, сортирую их по расстоянию до центра относительно разброса на момент выстрела, и беру радиус от выстрела с индексом желаемой квантили. Для 60% квантили будет R = shots.sort()[count * 0.6], что означает: 60% выстрелов попали в радиус <= R при нормализации разброса на момент выстрела. 
 

Круги не помню как раньше, а сейчас по умолчанию включен 1 — базовый разброс на момент старта боя. Для группового выбора выстрелов отображается ещё круг выбранной квантили. 
 

Ну и советы с критикой всегда важны. 
 

Сейчас вот появилось время/желание и запилил аналитический раздел, можешь глянуть мб будет интересно. 
Взглянув на график распределения снарядов от класса техники возникает предположение, что я неправильно считаю арту https://wotstat.soprachev.com/analytics/60fa42ff8149f07093332402

 

Собственно внимание вопрос, как к человеку, куда лучше знакомому с игровыми механиками, как расположен конус разброса у САУ? Кажется что не так, как у обычных танков, и если да, то ещё от какого расстояния у них зависит фактический размер круга разброса?

 

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

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites
On 7/23/2021 at 7:26 AM, SoprachevAK said:

Собственно внимание вопрос, как к человеку, куда лучше знакомому с игровыми механиками, как расположен конус разброса у САУ? Кажется что не так, как у обычных танков, и если да, то ещё от какого расстояния у них зависит фактический размер круга разброса?

Ходят слухи, что у арты круг разброса == кругу сведения

 

On 7/23/2021 at 7:26 AM, SoprachevAK said:

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

Не знаю как точно ты считаешь, можешь попробовать апроксимировать сразу после первого сегмента трассера - так у тебя будет почти идеальный круг, соответственно - это тебе даёт плоскость, на которой 2 точки - центр круга и пересечение трассера с этим кругом. Т.е. ты можешь посчитать оклонение

 

ЗЫ Если охота - пободайся с распредением урона на добивании. Бери ХП танка по которому есть выстрел, который теоретически мог его уничтожить, выкидывай фугасы из расчётов и смотри на распределение урона. Там интересно - если урон снаряда ниже текущего ХП танка - почти никогда не будет фрага.

Edited by MoD

Share this post


Link to post

Short link
Share on other sites
38 minutes ago, MoD said:

Ходят слухи, что у арты круг разброса == кругу сведения

Тыкался тут тыкался и уже сам начал сомневаться, новая версия покажет, возможно реально 1 в 1 как у обычных танков, а не работало из-за другого бага
 

 

39 minutes ago, MoD said:

ЗЫ Если охота - пободайся с распредением урона на добивании. Бери ХП танка по которому есть выстрел, который теоретически мог его уничтожить, выкидывай фугасы из расчётов и смотри на распределение урона. Там интересно - если урон снаряда ниже текущего ХП танка - почти никогда не будет фрага.

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

def showTracer(self, attackerID, shotID, isRicochet, effectsIndex, refStartPoint, velocity, gravity, maxShotDist, *a, **k)
def showDamageFromShot(self, attackerID, points, effectsIndex, damageFactor, *a, **k)

showTracer я юзаю для записи выстрела, showDamageFromShot можно было бы использовать для подсчёта дамага, но к сожалению у неё нет shotID и я не нашел как связать по другому.
Так что отложу на неопределённое время, если будет большой интерес к модику, обновлю и допишу 

Share this post


Link to post

Short link
Share on other sites
7 часов назад, SoprachevAK сказал:

Тыкался тут тыкался и уже сам начал сомневаться, новая версия покажет, возможно реально 1 в 1 как у обычных танков, а не работало из-за другого бага
 

 

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


def showTracer(self, attackerID, shotID, isRicochet, effectsIndex, refStartPoint, velocity, gravity, maxShotDist, *a, **k)

def showDamageFromShot(self, attackerID, points, effectsIndex, damageFactor, *a, **k)

showTracer я юзаю для записи выстрела, showDamageFromShot можно было бы использовать для подсчёта дамага, но к сожалению у неё нет shotID и я не нашел как связать по другому.
Так что отложу на неопределённое время, если будет большой интерес к модику, обновлю и допишу 

при каждом showTracer от себя запоминай его в словарь.

при каждом showDamageFromShot от себя, бери последнее значение в словаре и присваивай результат выстрела.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
9 часов назад, SoprachevAK сказал:

Типа список выстрелов и рядом с каждым результат выстрела. 

 

https://github.com/StranikS-Scan/StranikS_Scan-mods/tree/master/.experiments/SPGDispersion - это если ты в треньке один стреляешь для сбора статистки попаданий и изучения разброса, то сойдет и такой мод.

 

Если в бою/реплеи нужно отследить чьи-то трассеры/попадания, то используем shooterID в new_showTracer и используем attackerID в new_showDamageFromShot и в new_showDamageFromExplosion

 

9 часов назад, SoprachevAK сказал:

Тыкался тут тыкался и уже сам начал сомневаться, новая версия покажет, возможно реально 1 в 1 как у обычных танков, а не работало из-за другого бага

 

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

Edited by StranikS_Scan

Share this post


Link to post

Short link
Share on other sites

По поводу конуса, если всё правильно - тебе надо от последнего сегмента трассера получать угол между нормалью в точке прицеливания и трассером, это тебе даст часть данных для вычисления эксцентриситета, второе что тебе надо это угол привершине конуса, который можно получить зная расстояние и макс величину раброса (или там уже тангенс есть для разброса), а длинна конуса - это длинна параболы, которая от точки выстрела до точки прицеливания. Тут прекрасное видео от 3b1b

 

Share this post


Link to post

Short link
Share on other sites

Еще можно покурить вот этот

 

331296323_.thumb.jpg.fbd01c1451b9b46b99a483982fb5d9b6.jpg

 

Нашел древнюю тему по арте - http://forum.worldoftanks.ru/index.php?/topic/92927-эксперимент-с-разбросом-снарядов-у-арты-№2-прод/ Вот оттуда я знаю про эллипс. Можно почитать, что там.

Edited by StranikS_Scan
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

@MoD @StranikS_Scan, за материалы спасибо, однако я ооочень сомневаюсь, что ленивые программисты в 2012 году писали для артиллерии новую уникальную механику, причём весьма сложную с точки зрения математики. Я на выходных тестировал, и у меня появилось предположение, что берётся координата маркера, на ней строится плоскость перпендикулярная вектору от САУ до маркера, на этой плоскости строится круг разброса и в нём рандомится точка, через которую пойдёт траектория снаряда (пока что ровно так же как и у обычных танков), а уже потом, в арт прицеле, этот круг проецируется на рельеф продолжая траекторию трассера. 
Если так, то это даже не конус получается, это получается банан (не знаю как по науке называется конус с параболической осью)

Если так, то на сервере вообще не надо считать ничего, там просто дискретно летит трассер по простой траектории пока не пересечётся с препятствием, причём арта ничем не отличается от других танков в этом случае 

IMG_4A794FA757DB-1.thumb.jpeg.21fb8d8541084ce1bcb793475a0333af.jpeg

 

9 hours ago, spoter said:

при каждом showTracer от себя запоминай его в словарь.

при каждом showDamageFromShot от себя, бери последнее значение в словаре и присваивай результат выстрела.

в таком случае полностью игнорируются пулемётные танки, хотя они у меня и так не работают, потому что корректно считывать разброс для них я не смог 
И надо ещё проверить когда именно вызывается функция, и не считаются ли всякие тараны, пожары или расходники DamageFromShot'ом, потому что я тут недавно узнал, что дымы в спецрежимах являются трассером, который принадлежит игроку который их запустил

Вообще да, решение простое и скорее всего действенное, о словаре я чёт не подумал) 

Share this post


Link to post

Short link
Share on other sites

Не, у тебя не банан. У тебя конус и сечение его.

У тебя есть точка прицеливания, нормаль в точке прицеливания и вектор последнего сегмента трассера - угол между ними. Это тебе задаёт плоскость П 

У тебя есть тангенс угла, это как раз угол при вершине конуса. ты размещаешь центр основания конуса в точке прицеливания, высота консуа - расстояние до цели (длинна дуги параболы), потом типа "продляешь консу вниз", и находишь эллипс, который образовывается при пересечении конса и плоскости П. Это тебе даст эллипс.

Дальше у тебя есть точка попадания снаряда и эллипс -> можно найти насколько сильно оно уехало в сторону

Но это ещё усложняется тем фактом, что эллипс у тебя может вырождаться в параболу и точка попадания - онато на рельефе, и она не факт что совпадёт с эллипсом. Это по сути с определённой точностью можно только вычислить....

Тут https://math.stackexchange.com/questions/2638515/how-to-convert-points-in-a-circle-to-points-in-an-ellipse описано как потом точку принадлежащую эллипсу пересчитать для круга

 

Share this post


Link to post

Short link
Share on other sites

Немного про механику игры, исходя из клиентского кода

 

Разброс в механике игры задается сервером как тангенс половинного угла разброса орудия dispersionAngle. Эту цифру сервер обсчитывает с тиком 0,1 сек и шлет клиенту игры. Вычисляет её он как функцию от динамических характеристик танка, путем применения дополнительных коэффициентов к базовому тангенсу разброса орудия, прописанному в файлах ТТХ для дистанции 100 метров. Все эти коэффициенты и формулу можно посмотреть в коде игры, они дублируют расчеты на сервере чтобы сглаживать движение объектов в клиенте игры. Расчеты в клиенте вынесены в отдельную функцию getOwnVehicleShotDispersionAngle. Ниже её код, а вот тут его разбор в числах, правда он мог немного устареть.

 

    def getOwnVehicleShotDispersionAngle(self, turretRotationSpeed, withShot=0):
        descr = self.__getDetailedVehicleDescriptor()
        aimingStartTime, aimingStartFactor, multFactor, gunShotDispersionFactorsTurretRotation, chassisShotDispersionFactorsMovement, chassisShotDispersionFactorsRotation, aimingTime = self.__aimingInfo
        vehicleSpeed, vehicleRSpeed = self.getOwnVehicleSpeeds(True)
        vehicleMovementFactor = vehicleSpeed * chassisShotDispersionFactorsMovement
        vehicleMovementFactor *= vehicleMovementFactor
        vehicleRotationFactor = vehicleRSpeed * chassisShotDispersionFactorsRotation
        vehicleRotationFactor *= vehicleRotationFactor
        turretRotationFactor = turretRotationSpeed * gunShotDispersionFactorsTurretRotation
        turretRotationFactor *= turretRotationFactor
        if withShot == 0:
            shotFactor = 0.0
        elif withShot == 1:
            shotFactor = descr.gun.shotDispersionFactors['afterShot']
        else:
            shotFactor = descr.gun.shotDispersionFactors['afterShotInBurst']
        shotFactor *= shotFactor
        idealFactor = vehicleMovementFactor + vehicleRotationFactor + turretRotationFactor + shotFactor
        additiveFactor = self.__getAdditiveShotDispersionFactor(descr)
        idealFactor *= additiveFactor ** 2
        idealFactor = multFactor * math.sqrt(1.0 + idealFactor)
        currTime = BigWorld.time()
        aimingFactor = aimingStartFactor * math.exp((aimingStartTime - currTime) / aimingTime)
        isGunReloading = self.guiSessionProvider.shared.ammo.isGunReloading()
        if aimingFactor < idealFactor:
            aimingFactor = idealFactor
            self.__aimingInfo[0] = currTime
            self.__aimingInfo[1] = aimingFactor
            if abs(idealFactor - multFactor) < 0.001:
                if not self.__isAimingEnded and not isGunReloading:
                    self.soundNotifications.play('sight_convergence')
                self.__isAimingEnded = True
            elif idealFactor / multFactor > 1.1:
                self.__isAimingEnded = False
        elif aimingFactor / multFactor > 1.1:
            self.__isAimingEnded = False
        return [descr.gun.shotDispersionAngle * aimingFactor, descr.gun.shotDispersionAngle * idealFactor]

 

В механике игры снаряды летят по параболе, параметры которой зависят от дистанции от танка игрока до точки прицеливания. Сервер в игре вычисляет дистанции по прямой, либо от опорной точки модели танка, либо от точки вылета снаряда. При этом снаряд на всех моделях вылетает из точки крепления пушки, она же опорная точка орудия (трассер кстати рисуется из другой точки, из конца орудия - элемент модели _GunFire). Первое юзается в механике маскировки и в засвета, второе в баллистике и прицеливании.

 

Опорная точка пушки вычисляется, например так:

startPoint = player.getOwnVehiclePosition()
startPoint += player.vehicleTypeDescriptor.hull.turretPositions[0] + player.vehicleTypeDescriptor.turret.gunPosition

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

markerPos = player.gunRotator.markerInfo[0]

Отсюда получается, что дистанция от танка игрока до точки прицеливания в механике стрельбы это всегда:

markerDist = startPoint.distTo(markerPos)

Тогда радиус круга разброса в точке прицеливания для любого танка это:

dispersionRadius = player.gunRotator.dispersionAngle * markerDist

В клиенте игры это все есть в коде, который отвечает за отрисовку сведения. Для этого используются следующие функции:

 

1. Внеклассовая функция расчета баллистических параметров в точке прицеливания getCappedShotTargetInfos

def getCappedShotTargetInfos(shotPos, shotVec, gravity, shotDescr, vehicleID, minBounds, maxBounds, collisionStrategy):
    endPos, direction, collData, usedMaxDistance = BigWorld.wg_getCappedShotTargetInfos(BigWorld.player().spaceID, shotPos, shotVec, gravity, shotDescr.maxDistance, vehicleID, minBounds, maxBounds, collisionStrategy)
    if collData != 0:
        collData = EntityCollisionData(*collData)
    else:
        collData = None
    return (endPos, direction, collData, usedMaxDistance)

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

 

 

 


Модуль projectile_trajectory.py содержит неиспользуемые сегодня, но все еще актуальные, функции расчета траектории, это BigWorld.wg_computeProjectileTrajectory и пайтон аналог её, когда-то давно использовавшийся:

 


def computeProjectileTrajectory(beginPoint, velocity, gravity, time, epsilon):
    checkPoints = []
    endPoint = beginPoint + velocity.scale(time) + gravity.scale(time * time * 0.5)
    stack = [(velocity, beginPoint, endPoint)]
    while len(stack) > 0:
        lastIdx = len(stack) - 1
        v1, p1, p2 = stack[lastIdx]
        del stack[lastIdx]
        delta = p2 - p1
        xzNormal = Math.Vector3(-delta.z, 0.0, delta.x)
        normal = xzNormal * delta
        if abs(normal.y) < epsilon:
            checkPoints.append(p2)
            continue
        normal.normalise()
        extremeTime = normal.dot(v1) / (-gravity.y * normal.y)
        extremePoint = v1.scale(extremeTime) + gravity.scale(extremeTime * extremeTime * 0.5)
        dist = abs(normal.dot(extremePoint))
        if dist > epsilon:
            extremeVelocity = v1 + gravity.scale(extremeTime)
            stack.append((extremeVelocity, p1 + extremePoint, p2))
            stack.append((v1, p1, p1 + extremePoint))
        checkPoints.append(p2)

    return checkPoints



computeProjectileTrajectory.thumb.jpg.d928831c8d87dd6d5a654f22c6951068.jpg
 

 

2. Классовая функция расчета баллистических параметров и разброса в точке прицеливания __getGunMarkerPosition, использующая функцию из п.1

    def __getGunMarkerPosition(self, shotPos, shotVec, dispersionAngles):
        shotDescr = self._avatar.getVehicleDescriptor().shot
        gravity = Math.Vector3(0.0, -shotDescr.gravity, 0.0)
        testVehicleID = self.getAttachedVehicleID()
        collisionStrategy = AimingSystems.CollisionStrategy.COLLIDE_DYNAMIC_AND_STATIC
        minBounds, maxBounds = BigWorld.player().arena.getSpaceBB()
        endPos, direction, collData, usedMaxDistance = AimingSystems.getCappedShotTargetInfos(shotPos, shotVec, gravity, shotDescr, testVehicleID, minBounds, maxBounds, collisionStrategy)
        distance = shotDescr.maxDistance if usedMaxDistance else (endPos - shotPos).length
        markerDiameter = 2.0 * distance * dispersionAngles[0]
        idealMarkerDiameter = 2.0 * distance * dispersionAngles[1]
        replayCtrl = BattleReplay.g_replayCtrl
        if replayCtrl.isPlaying and replayCtrl.isClientReady:
            markerDiameter, endPos, direction = replayCtrl.getGunMarkerParams(endPos, direction)
        return (endPos, direction, markerDiameter, idealMarkerDiameter, collData)

Как видим, диаметр круга разброса в точке прицеливания это удвоенное текущее значение тангенса половинного угла разброса орудия танка 2*dispersionAngles[0] помноженное на дистанцию distance = .... (endPos - shotPos).length между опорной точкой орудия shotPos и точкой прицеливания endPos.

 

3. Далее находим, где используется __getGunMarkerPosition. В функции обновления сведения прицела __updateGunMarker, которая вызывается пока игрок в бою с периодичностью 0,1 сек, что составляет тик сервера:

    def __updateGunMarker(self, forceRelaxTime=None):
        if self._avatar.getVehicleAttached() is None:
            return
        else:
            shotPos, shotVec = self.getCurShotPosition()
            markerPos, markerDir, markerSize, idealMarkerSize, collData = self.__getGunMarkerPosition(shotPos, shotVec, self.__dispersionAngles)
            replayCtrl = BattleReplay.g_replayCtrl
            if replayCtrl.isRecording and not replayCtrl.isServerAim:
                replayCtrl.setGunMarkerParams(markerSize, markerPos, markerDir)
            if not self.__targetLastShotPoint:
                self.__lastShotPoint = markerPos
            replayCtrl = BattleReplay.g_replayCtrl
            if replayCtrl.isPlaying and replayCtrl.isUpdateGunOnTimeWarp:
                self._avatar.inputHandler.updateGunMarker(markerPos, markerDir, (markerSize, idealMarkerSize), 0.001, collData)
            else:
                relaxTime = self.__ROTATION_TICK_LENGTH if forceRelaxTime is None else forceRelaxTime
                self._avatar.inputHandler.updateGunMarker(markerPos, markerDir, (markerSize, idealMarkerSize), relaxTime, collData)
            self.__markerInfo = (markerPos, markerDir, markerSize)
            if self._avatar.inCharge:
                self._updateMultiGunCollisionData()
            return

А еще в дублирующей её setShotPosition, которая обеспечивает автовозвышение орудия танка и обновление, упомянутого выше публичного буфера player.gunRotator.markerInfo.

 

4. Теперь нужно лезть в дебри модулей, отвечающих за управление прицелом и отрисовку сведения через флэшку, именно туда ведет вызов _avatar.inputHandler.updateGunMarker из кода выше. Если опустить подробности то, передача данных идет через объект DataProvider, который по сути является прокладкой для передачи данных в код флэшки прицела. Здесь же стоит отметить, что версии провайдера разные для прицела арты и для остальных прицелов танков.

 

Для прицела арты создается он так:

    def _makeSPGProvider():
        dataProvider = GUI.WGSPGGunMarkerDataProvider(aih_constants.SPG_GUN_MARKER_ELEMENTS_COUNT, aih_constants.SPG_GUN_MARKER_ELEMENTS_RATE)
        dataProvider.positionMatrixProvider = Math.MatrixAnimation()
        dataProvider.maxTime = 5.0
        dataProvider.serverTickLength = constants.SERVER_TICK_LENGTH
        dataProvider.sizeScaleRate = aih_constants.SPG_GUN_MARKER_SCALE_RATE
        dataProvider.sizeConstraint = (aih_constants.SPG_GUN_MARKER_MIN_SIZE, aih_constants.SPG_GUN_MARKER_MAX_SIZE)
        dataProvider.setRelaxTime(constants.SERVER_TICK_LENGTH)
        return dataProvider

А для всех остальных вот так

    def _makeDefaultProvider():
        dataProvider = GUI.WGGunMarkerDataProvider()
        dataProvider.positionMatrixProvider = Math.MatrixAnimation()
        dataProvider.setStartSize(_setupGunMarkerSizeLimits(dataProvider)[0])
        return dataProvider

Что означают доп.константы у прицела арты, думаю не сложно догадаться, их значения кстати вот такие:

GUN_MARKER_MIN_SIZE = 32.0
SPG_GUN_MARKER_ELEMENTS_COUNT = 37
SPG_GUN_MARKER_ELEMENTS_RATE = 10
SPG_GUN_MARKER_MIN_SIZE = 50.0
SPG_GUN_MARKER_MAX_SIZE = 100.0
SPG_GUN_MARKER_SCALE_RATE = 10.0

Обращаем внимание на последний параметр, у сведения арты есть дополнительный коэф. масштаба SPG_GUN_MARKER_SCALE_RATE.

 

Передача данных в провайдер прицела арты выглядит вот так:

    def update(self, markerType, position, direction, size, relaxTime, collData):
        super(_SPGGunMarkerController, self).update(markerType, position, direction, size, relaxTime, collData)
        positionMatrix = Math.Matrix()
        positionMatrix.setTranslate(position)
        self._updateMatrixProvider(positionMatrix, relaxTime)
        self._size = size[0]  #################################################
        self._update()        
        
    def _update(self):
        pos3d, vel3d, gravity3d = self._getCurrentShotInfo() 
        replayCtrl = BattleReplay.g_replayCtrl
        if replayCtrl.isPlaying and replayCtrl.isClientReady:
            self.__updateRelaxTime()
        self._updateDispersionData() 
        self._dataProvider.update(pos3d, vel3d, gravity3d, self._size)

    def _getCurrentShotInfo(self):
        gunMat = AimingSystems.getPlayerGunMat(self._gunRotator.turretYaw, self._gunRotator.gunPitch)
        position = gunMat.translation
        velocity = gunMat.applyVector(Math.Vector3(0, 0, self._shotSpeed))
        return (position, velocity, Math.Vector3(0, -self._shotGravity, 0)) #########################        
        
    def _updateDispersionData(self):
        dispersionAngle = self._gunRotator.dispersionAngle #########################
        isServerAim = self._gunMarkerType == _MARKER_TYPE.SERVER
        replayCtrl = BattleReplay.g_replayCtrl
        if replayCtrl.isPlaying and replayCtrl.isClientReady:
            d, s = replayCtrl.getSPGGunMarkerParams()
            if d != -1.0 and s != -1.0:
                dispersionAngle = d
        elif replayCtrl.isRecording:
            if replayCtrl.isServerAim and isServerAim:
                replayCtrl.setSPGGunMarkerParams(dispersionAngle, 0.0)
            elif not isServerAim:
                replayCtrl.setSPGGunMarkerParams(dispersionAngle, 0.0)
        self._dataProvider.setupConicDispersion(dispersionAngle) 

В то время как для остальных танков вот так:

    def update(self, markerType, pos, direction, sizeVector, relaxTime, collData):
        super(_DefaultGunMarkerController, self).update(markerType, pos, direction, sizeVector, relaxTime, collData)
        positionMatrix = Math.Matrix()
        positionMatrix.setTranslate(pos)
        self._updateMatrixProvider(positionMatrix, relaxTime)
        size = sizeVector[0]
        idealSize = sizeVector[1]
        replayCtrl = BattleReplay.g_replayCtrl
        if replayCtrl.isPlaying and replayCtrl.isClientReady:
            s = replayCtrl.getArcadeGunMarkerSize()
            if s != -1.0:
                size = s
        elif replayCtrl.isRecording:
            if replayCtrl.isServerAim and self._gunMarkerType == _MARKER_TYPE.SERVER:
                replayCtrl.setArcadeGunMarkerSize(size)
            elif self._gunMarkerType == _MARKER_TYPE.CLIENT:
                replayCtrl.setArcadeGunMarkerSize(size)
        positionMatrixForScale = self.__checkAndRecalculateIfPositionInExtremeProjection(positionMatrix)
        worldMatrix = _makeWorldMatrix(positionMatrixForScale)
        currentSize = _calcScale(worldMatrix, size) * self.__screenRatio
        idealSize = _calcScale(worldMatrix, idealSize) * self.__screenRatio
        self.__sizeFilter.update(currentSize, idealSize)
        self.__curSize = self.__sizeFilter.getSize()
        if self.__replSwitchTime > 0.0:
            self.__replSwitchTime -= relaxTime
            self._dataProvider.updateSize(self.__curSize, 0.0)
        else:
            self._dataProvider.updateSize(self.__curSize, relaxTime)

Рассмотрим сначала второй код. Для аркадного и снайперского прицелов размер сведения в пикселях вычисляется непосредственно в функции update() и затем отдается провайдеру. Для этого берется диаметр мгновенного значения круга разброса в текущей точке прицеливания size = sizeVector[0], высчитывается его масштаб для экрана игрока через функцию _calcScale и этот масштаб умножается на половину ширины экрана в пикселях self.__screenRatio. Последняя величина находится вот так self.__screenRatio = GUI.screenResolution()[0] * 0.5. Чтобы сведение в игре не было очень маленьким или очень большим, а оно будет таковыми, имеется фильтр self.__sizeFilter, который ограничивает минимально и максимально возможное значение круга сведения. Лимиты, которые задаются в него, вычисляются вот так:

def _setupGunMarkerSizeLimits(dataProvider, scale=None):
    if scale is None:
        settingsCore = dependency.instance(ISettingsCore)
        scale = settingsCore.interfaceScale.get()
    limits = (aih_constants.GUN_MARKER_MIN_SIZE * scale, min(GUI.screenResolution()))
    dataProvider.sizeConstraint = limits
    return limits

Переходим к арт-прицелу. У арты расчеты в пикселях в функции update() не выполняются, вместо этого данные о точке выстрела, векторах начальной скорости и гравитации и текущем диаметре круга разброса в точке прицеливания - pos3d, vel3d, gravity3d и self._size - передаются прямо в провайдер. А также в него передается текущее значение тангенса угла разброса орудия dispersionAngle. Что со всем этим делает провайдер WGSPGGunMarkerDataProvider сказать трудно, т.к. он нативный.

 

Если глянуть список доступных атрибутов и функций у WGSPGGunMarkerDataProvider, то там можно увидеть:

*** enableSmoothFiltering
*** getPointsInside
*** maxTime
*** positionMatrixProvider
*** relaxTime
*** reset
*** serverTickLength
*** setRelaxTime
*** setStartSize
*** setupConicDispersion
*** setupFlatRadialDispersion
*** sizeConstraint
*** sizeScaleRate
*** update
*** updateSize

Функция setupFlatRadialDispersion тоже встречается в коде клиента игры. Это код, который отвечает за отрисовку новой игровой фичи - подсветки на игровом поле места, куда стреляет или будет стрелять арта. Код класса можно глянуть тут. В качестве аргумента в функцию передается игровая константа areaRadius.

Edited by StranikS_Scan
  • Upvote 5

Share this post


Link to post

Short link
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...