Задумал я сделать игрушку...
В этой записи я буду писать много всякого бреда, отрывочных мыслей и бессвязных кусков кода. Это своеобразный дневник разработки игры. А какая разработка, такой и дневник... :[sarcasm] Писать буду стараться каждый день, описывать разные стадии разработки. Не все решения доживут до финала. Постараюсь также выкладывать промежуточные версии исходников и скриншоты текущего состояния программы. День1. Powder Toy. Натолкнулся я недавно вот на такую игрульку The Powder Toy. Это песочница. В буквальном смысле - с песком :о) Смысл в том, что вы можете сыпать песочек, водичку и другие материалы, которые сталкиваются и каким-то образом взаимодействуют друг с другом. Узнал об игре из этой темы. Играть в такие игры я не особо люблю, я предпочитаю шутеры. Но задело меня в этой игре то что она имеет маленький размер, написана в стиле олд-скул-пиксел-арт, и то, что в ней высокое быстродействие при том что считается довольно серьезная (как казалось) физика. При этом, судя по всему, она не использует графических или физических движков, и не использует шейдеры. При этом она моделирует огромное число взаимодействующих частиц. Даже при размере небольшого игрового поля 600*400, на нем может поместиться до 240 тысяч частиц. Если реализовать взаимодействие в лоб (чего конечно в реальности никто не делает, но тем не менее), то это будет O(n^2) и нужно обрабатывать 58 миллиардов(!) попарных взаимодействий. И это нужно делать с FPS не ниже 30. А в Powder Toy FPS под 60! Вы конечно можете сказать, что в современных играх тоже есть огромное число взаимодействующих объектов, но в реальности это совсем не то. Физика в игре моделируется только вокруг игрока и самих взимодействующих объектов не так уж и много. И уж точно меньше чем песчинок в куче песка. Если вы стоите в комнате, то там есть всего лишь десяток объектов, которые могут двигаться, и для которых считается физика. А здесь каждая песчинка на экране обрабатывается как полноценный физический объект. Конечно, в современных играх в таких случаях используются партиклы, и их обычно много. Но партиклы никогда не обрабатывают коллизии и не могут сталкиваться друг с другом. В Powder же ситуация совсем другая. Все песчинки взаимодействуют, сталкиваются. И это удивительно. Удивительно как такая маленькая простенькая програмка может моделировать такую сложную физическую систему. Тут я конечно немного преувеличиваю свое удивление, но все таки меня заинтересовало как это все работает. Вы можете подумать, что я далее буду описывать как сделать игру наподобие Powder Toy, а вот и нет. Потому что делать я буду совсем другую игру :о) Зато Powder Toy натолкнула меня на много интересных методов, которые неплохо было бы использовать в других разработках. И да, простенький аналог Powder я все же сделал. Получилось неплохо :о) Быстродействие достаточное, моделирование физики и вывод графики работало довольно шустро. В процессе разработки придумал некоторые методы оптимизации, с которыми и хочу поделиться. Далее я буду описывать свою версию программы. Как точно работает настоящий Powder Toy - я точно не знаю, я смотрел код лишь поверхностно, но думаю что работает примерно так же. В чем же секрет Powder? Секрет как раз в тех самых пикселах. А точнее в элементарных ячейках, в которых могут находиться песчинки. Работает это так: каждая песчинка имеет свою позицию, задаваемую двумерным вектором. Она также имеет вектора скорости и ускорения. Вектора имеют компоненты типа float. То есть координаты не дискретные, и частица может занимать любое положение в пространстве. Движение частицы моделируется обычным образом - через закон Ньютона силы делятся на массу, получается ускорение. Затем находится скорость и приращивается координата местоположения. Это стандартный набор для любого физического движка. Однако, после того, как координаты песчинок пересчитаны, они помещаются в двумерную сетку. Вектора округляются до целых и находится ячейка сетки, куда должна попасть песчинка. И тут есть главная хитрость: в каждую ячейку сетки помещается ровно одна песчинка. Если ячейка, куда должна попасть песчинка - занята, значит песчинка не может занять эту ячейку и должна "искать" себе другую. В таком случае считается что произошло столкновение частиц. Скорость частицы обнуляется, а координата возвращается к своему значению на предыдущем шаге. Песчинки, которые не поместились в ячейки - не отображаются. Вместо этого им придается случайное ускорение в произвольную сторону. Здесь вторая хитрость. Если бы мы считали столкновения "честно", то нам нужно было бы находить вектора, направления соударения и так далее. Но у нас ведь песок, мелкие частицы. Пользователь все равно не видит форму частицы и не видит точку соударения частиц. Поэтому фактически мы можем присваивать случайный "отскок" без потери правдоподобности. Будет выглядеть так, что песчинки как-то там ударились и одна из них отскочила в случайную сторону. Далее процесс продолжается, снова высчитываются силы, ускорения, скорости и новые местоположения. И снова частицы заносятся в ячейки и так далее. Такой простой алгоритм позволяет нам моделировать огромное число частиц и их столкновений практически мгновенно. Красиво, да? Конечно, метод сеток я знал и раньше, но все таки, обычно он используется немного по-другому. Как это выглядит на практике можно посмотреть на скриншотах и в иcходных кодах. Приложение вышло очень простое, компактное (20 кб exe файл) и быстрое. В режиме моделирования около 100 тысяч частиц выдается производительность 45 fps для физики, и 52 fps - отрисовка. Работа ведется в два потока, занимает два ядра процессора. Класс Particle
Класс Sandbox
Это то, что касается физики. Теперь еще один момент - как это все выводить на экран? В Powder не используется ни DirectX, ни OpenGL. Значит вариант один - обычный GDI. Но тут нужно обеспечить максимальную производительность. Как мы это сделаем? Об этом будет День Второй... День 2. DirectX? Нет, не слышал... В любой игре есть два главных момента - физика и графика. С физикой мы немного разобрались. Теперь разберемся с графикой. Я мог бы взять игровой движок типа Unity или XNA, или хотя бы WPF. И рисовать все в нем. Но это не интересно. Какое удовольствие я получу от того что буду использовать то что кто то сделал уже до меня? К тому же мы будем делать такие вещи, которые сложно сделать в стандартных движках. Вы поймете о чем я в следующие дни. Итак. Рисовать мы будем просто через стандартные методы WinForms/GDI+. Я перепробовал разные варианты, какие-то работали получше, какие-то похуже. Что-то было быстрее, а что-то давало больше возможностей. В итоге я остановился на таком варианте: SandboxPanel
Собственно это весь код графики в PowderToy. Игровое поле имеет фиксированный размер 600x400. В конструкторе панели сразу создается битмап такого же размера. Этот битмап нужен для более быстрой отрисовки графики. Формат пикселей - PArgb 32bpp. Я использую PArgb (premultiplied ARGB) потому что он отрисовывается быстрее чем простой Argb. Также, в конструкторе я задаю стандартные параметры для буферизации отрисовки, предотвращающие мигание:
Далее. Вся отрисовка происходит в методе OnPaint. Сначала задаются параметры графики, увеличивающие скорость вывода изображений:
Далее, используя класс ImageWrapper, дающий unsafe доступ к пикселам буферного битмапа, мы просто пробегаемся по всем ячейкам нашего поля и если там есть песчинка, заносим ее цвет в пиксел буферного битмапа (забегая вперед, скажу, что это не самый быстрый способ, впоследствии я откажусь от него). Далее просто отрисовываем битмап на канве:
Про Powder Toy все. Програмка получилась простая и быстрая. Как я люблю. Дальше я буду использовать ее идеи для собственно другой игры, которая будет намного больше и сложнее. Об этом будет День Третий. Продолжение следует... Исходник: PowderToy_source Exe: PowderToy_bin |
Всего комментариев 3
Комментарии
-
Запись от 8Observer8 размещена 25.02.2017 в 16:57 -
Запись от Storm23 размещена 25.02.2017 в 17:39 -
Запись от 8Observer8 размещена 25.02.2017 в 18:54