Трудности и их решения, в начинающей группе разработчиков игр:
1. Зачем нам авторитарный сервер, если метод синхронизации в разы проще дешевле?!
2. Создание редактора зон, подготовка карт для сервера и клиента.
3. Как мы ускоряли симуляцию физики на сервере.
4. Быстрый обмен данными, экономия оперативной памяти, кластера и другое…
How NOT to do showcase events: Behind the scenes of Midnight Show / Andrew Ko...
Tomato Engine: Как мы создавали онлайн шутер с авторитарным сервером
1. Как мы создавали онлайн шутер с
авторитарным сервером
Зачем нам авторитарный сервер, если метод синхронизации
в разы проще дешевле?
Создание редактора зон, подготовка карт для сервера и
клиента.
Как мы ускоряли симуляцию физики на сервере.
Быстрый обмен данными, экономия оперативной памяти,
кластера и другое…
2. Что такое авторитарный сервер?!
клиент клиент
клиент
клиент клиент
клиент
Некоторые расчеты игрового мира (в основном
физика), ведутся на компьютерах игроков, а
сервер лишь рассылает уже рассчитанные данные.
Все расчеты, в том числе физика, ведутся на
сервере, а клиенты лишь отображают игровой мир
и принимают от игроков команды.
дешево
дорого
3. Синхронизация
Это видел Вася Это видел Петя когда Вася бросал в него снежок когда бросал снежок в Петю
Этого не будет
с авторитарным
сервером
Петя думал что в него не попали Вася думал что попал в Петю
Одна из проблем метода синхронизации в самой синхронизации. Клиент может не успеть, или не
иметь возможности, или специально не передавать информацию о себе, или же не принимать
информацию о других, что приводит к тому, что разные игроки в один и тот же момент видят мир по
разному.
4. Игрок отключился…
Этого не будет
с авторитарным
сервером
Если один из игроков отключился или у него пропало соединение, кто должен
рассчитывать его поведение в сцене? Откуда брать информацию о нем? Часто он
просто исчезает…
5. Это дает возможность мухлевать
Этого не будет
с авторитарным
сервером
Метод синхронизации дает возможность проходить в нужных местах незамеченным отключая, или
блокируя связь, другие игроки не могут подловить мухлюющего, так как он в этот момент не
передавал информацию о себе, что исключено с авторитарным сервером.
6. Нечестные возможности
Этого не будет
с авторитарным
сервером
Взломав клиент, игроки могут давать себе незапланированные механикой игры
преимущества. Например, ускорять бег, что расстроит честных игроков.
7. Бросать сквозь стены
Этого не будет
с авторитарным
сервером
На самом деле для этого даже не всегда нужно взламывать клиент,
достаточно послать информацию на сервер о том, что ты бросил снежок и
попал в соперника, если физика не рассчитывается на сервере – сервер
не может проверить подлинность данной информации.
8. Ставать бессмертным
Этого не будет
с авторитарным
сервером
Даже так, блокируя или искажая информацию о том сколько урона ты
получаешь.
9. Античиты???
Кажется, можно всего этого избежать, анализируя поведение игрока
на предмет жульничества, однако все многопользовательские игры,
использующие метод синхронизации, переполнены длинными
списками пожизненно забаненных игроков именно за жульничество.
10. Делаем авторитарный сервер
К примеру, по заказу один наш сервер должен:
1. Выдерживать 10000 игроков одновременно.
2. Поддерживать абсолютно разные по своей форме 3D карты.
3. Рассчитывать физику персонажей.
4. Рассчитывать разные пересечения лучей и окружающей среды.
5. Механика игры.
6. Учет баз данных.
11. Как сделать физику персонажей?!
Обычно советуют взять уже готовую клиент-серверную систему, а для расчётов физики
использовать свободные физические симуляторы как bullet physics, но они могут
просчитать всего около 300-600 капсул, чего недостаточно.
12. Бывает еще воксельная физика
Действительно, если записать в воксели информацию о том на каком максимальном
расстоянии он находится до ближайшего препятствия, то можно невероятно быстро узнавать
произошло-ли столкновение сферы со статичным объектом, но такие 3D карты занимают
слишком много оперативной памяти, оптимизации наподобие octree замедляют и не
достаточно уменьшают расход памяти.
13. Карта высот
Еще используют для расчёта физики 2D карты высот, в основном для создания
ландшафтов. Такие карты в отличии от воксельных не занимают много
оперативной памяти
14. Карта высот с кешем под полусферу
Можно дополнительно рассчитать карту где каждый пиксел будет соответствовать
месту столкновения со сферой или капсулой нужного радиуса, что позволит
рассчитывать столкновения окружающей среды с миллионами сфер и капсул
15. Потолок из карты высот
Картой высот можно создать не только ландшафт и
пол, но и потолок, таким образом оставляя
персонажу для перемещения только пространство
между полом и потолком, получать закрытые
пространства.
16. Несколько пар карт высот
Что если использовать несколько пар карт высот, пола и потолка, а места где они
пересекаются будут проходимыми?! Так можно создавать много-этажности,
пещеры и тд.
17. И… вообще, что угодно
У нас получилась вексельная карта с такой-себе «png упаковкой», которая не тормозит. Таким
образом можно упаковывать абсолютно любые 3D сцены. Типичный сервер может
рассчитывать столкновение с такой картой около 10 000 000 капсул или сфер, 30 раз в секунду.
Тест этого метода можно скачать на сайте http://tomato.co.nl/examples.php
18. Автоматическое размещение LightProbes
Данная методика позволяет не только быстро просчитывать коллизию, но еще и автоматически
расставлять LightProbes или делать функции поиска оптимального пути и многое другое
19. Автоматическое размещение LightProbes
Создавать и сохранять вышеописанные карты высот, автоматически создавать LightProbes вы
можете с помощью бесплатного плагина TomatoEngine для Unity3d. Скачать плагин можно с
официального сайта http://tomato.co.nl/
21. Какой метод лучше?!
Функция пересечения луча и треугольника
в bullet physics.
Это довольно большой кусок кода с
большим количеством внешних вызовов,
не настолько быстрый как хочу….
float l1x = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r0.v, v1.v, 0x77));
float l1y = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r1.v, v1.v, 0x77));
float l1z = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r2.v, v1.v, 0x77));
l1x -= m->XShift*l1y;
float p2e = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r0.v, v2.v, 0x77));
float p3e = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r1.v, v2.v, 0x77));
float l2z = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r2.v, v2.v, 0x77));
p2e -= m->XShift*p3e;
float e = l2z/(l1z-l2z);
p2e = p2e+(p2e-l1x)*e;
p3e = p3e+(p3e-l1y)*e;
float p1e = 1 - (p2e+p3e);
if((p2e >= 0) && (p3e >= 0) && (p1e > 0)){ … пересеклось …. }
Давайте сделаем такой!
без внешних вызовов, быстрый и короткий!....
22. Матрицы преобразования
Прежде всего еще при экспорте или загрузке модели, мы создаем матрицы
Преобразования для каждого треугольника..
Перемещаем треугольник
первой точкой в нулевую
координату и матрица в
1,0,0
0,1,0
0,0,1
Поворачиваем треугольник
и матрицу так, чтоб он лег
на плоскости XY и Y второй
точки совпадал с Y первой.
Скейлим матрицу и
треугольник так чтоб
X второй точки
равнялся 1 и Y
третьей равнялся 1
Сами треугольники можно в оперативной памяти вообще не держать,
для всех расчетов можно использовать только матрицы созданные по ним
Сохраняем в отдельную
переменную X третьей
точки, чтоб потом
сделать наш треугольник
прямым отняв ее
Мы должны повернуть треугольник так чтоб прямым он делался именно
скаляром X, так как это первый скаляр xmm регистров процессора с которыми
ми можем работать на прямую, отдельно от остальных
23. Как должна выглядеть матрица
Матрицу записываем в вертикальном порядке, это даст нам возможность
использовать DPPS команды из SSE 4.1,
преобразование одного вектора будет
занимать всего три команды процессора
0.002
-0.925
0.381
0.863
-0.191
-0.468
0.505
0.330
0.797
DPPS XMM1 XMM4 0x77
DPPS XMM2 XMM4 0x77
DPPS XMM3 XMM4 0x77
Естественно данный способ еще и очень быстрый.
24. Как работает функция
1. Преобразовуем нашей матрицей преобразования обе точки линии(луча)
Vector3 v1 = LinePoint1-TrianglePoint1Pos;
Vector3 v2 = LinePoint1-TrianglePoint1Pos;
float l1x = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r0.v, v1.v, 0x77));
float l1y = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r1.v, v1.v, 0x77));
float l1z = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r2.v, v1.v, 0x77));
l1x -= m->XShift*l1y;
float p2e = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r0.v, v2.v, 0x77));
float p3e = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r1.v, v2.v, 0x77));
float l2z = _mm_cvtss_f32(_mm_dp_ps(m->matrix.r2.v, v2.v, 0x77));
p2e -= m->XShift*p3e;
2. Находим позицию XY на точке пересечения, они-же эквиваленты переливов между точками.
float e = l2z/(l1z-l2z);
p2e = p2e+(p2e-l1x)*e;
p3e = p3e+(p3e-l1y)*e;
float p1e = 1 - (p2e+p3e);
if((p2e >= 0) && (p3e >= 0) && (p1e > 0)){
3. Все пересечение произошло, теперь можно высчитать точку пересечения, нормаль, UV…
Vector3 IntersectionPoint = p1Pos*p1e+ p2Pos*p2e+ p3Pos*p3e;
Vector3 Nomal = p1Norm*p1e+ p2norm*p2e+ p3norm*p3e;
Vector3 UV = p1UV*p1e+ p2UV*p2e+ p3UV*p3e;
}
25. Тест метода
Для проверки данного метода был создан рендер, который в реальном времени
обрисовывает модель в HD формате. Этот пример с исходным кодом можно
скачать на странице http://tomato.co.nl/examples.php
27. octree
Традиционный и проверенный метод, но в случае с MMOG сервером имеет ряд
недостатков – вложенные циклы, что не быстро и много вложенных массивов что
сложно передать в shared memory.
28. Воксели
Что если сцену разбить на равные кубики, каждый из которых будет содержать
информацию сколько кубиков осталось к ближайшей преграды и какие треугольники в
них входят?... Да-да, это те-же воксели.., но на этот раз они большие, так что много места
занимать не будут, за то мы будем достигать ближайших треугольников для их анализа за
минимальное число циклов и все легко держать в одном блоке памяти.
30. Спасибо за внимание!
Есть вопросы?
Пишите мне на carrots@v7.net.ua
Более подробную и обновленную информацию смотрите на сайте http://tomato.co.nl/