Думаю, многие хотели бы сделать так, чтобы действие игры происходило в одном, цельном и бесшовном мире. Но деактивация объектов в огромной комнате не спасает от жутких тормозов, поэтому создавать просто большую комнату - не вариант.
Я предлагаю вам своё решение этой проблемы :)
Управление:- WASD для движения (персонаж двигается в сторону курсора)
- E и Q для изменения скорости передвижения персонажа
Скриншоты:СОДЕРЖАНИЕ:Пример 1: Очень большой мир с занесением координат объектов в массив и генерацией карты заранее;
Пример 2 (добавлено 10.02.2012): Бесконечный мир с хранением координат и id объектов в файлах, карта генерируется по мере надобности (а-ля Minecraft).
ПРИМЕР 1Вкратце, как работает пример:Во-первых, надо сказать, что персонаж, которым мы управляем, не двигается. Вообще. Это мир крутится вокруг него :) Комната имеет фиксированный размер 4096x4096 пикселей, и персонаж всегда находится строго в центре. В принципе, можно было бы сделать на четверть меньше, но я слегка перестраховался. У каждого объекта есть две переменные абсолютных координат world_x и world_y (а обычные x и y - это относительные координаты, т. е. положение объекта в комнате). В зависимости от того, как в абсолютных координатах переместился персонаж, мы двигаем все объекты в относительных координатах. К фону это тоже относится.
Весь мир разбит на локации. Одна локация - квадрат 1024x1024 пикселей (в абсолютных координатах). В любой момент времени активны лишь 9 локаций - та, в которой находится персонаж, и 8 прилегающих. При переходе между локациями мы выгружаем три устаревшие и подгружаем три новые.
Загрузка и выгрузка происходит следующим образом. У нас есть два двухмерных массива (в комментариях к коду я иногда называю их матрицами) - location и locobj. Location содержит координаты всех расположенных в нём деревьев через запятую. Если несколько усложнить код и применить шифрование, можно записать в этот массив координаты любых объектов в достаточно больших количествах, но я пока что этим не занимался. При подгрузке новой локации мы берём координаты объектов из массива location с соответствующими индексами (например, если мы подгружаем локацию 100/100, то берём строку location[100,100]), и заносим id всех созданных объектов через запятую в массив locobj с соответствующими индексами (например, locobj[100,100]). При выгрузке локации мы просто удаляем все объекты с id, записанными в locobj.
Достоинства:- Большой бесшовный мир с минимумом объектов в комнате.
Недостатки на текущий момент:- При большом количестве локаций массив location жрёт много памяти (locobj в разы меньше, ибо зачищается). В перспективе лучше работать с файлом;
- Ограниченное количество объектов, которые можно поместить на локацию, хотя и достаточно большое. Опять же, если использовать файл вместо двухмерного массива, проблема решается;
- Сложность "осмысленного" добавления объектов при создании карты. В перспективе решается несложным скриптом;
-
По непонятной пока что причине деревья дёргаются при перемещении. Вроде всё связанное с координатами записал в End Step. Разберусь попозже. Спасибо Krib за помощь :)
БОНУС:В пример входят пять авторских скриптов:
- Три для перевода целых чисел из 10-ричной системы счисления в 128-ричную (!) и обратно. Может заметно сократить объём занимаемой цифрами памяти. Использовать осторожно, т. к. переход обратно из 128-ричной в 10-ричную систему ест ресурсы;
- Два для шифрования id объектов в более компактный вид. По сути, просто отнимаем 100 000 и остаток переводим в 128-ричную систему, но длина номера уже сокращается до 1-2 символов :)
Ни один из этих скриптов я в примере не использовал, чтобы не запутывать код. Но тем, кто возьмётся за это дело всерьёз, думаю, они помогут очень неплохо :)
ПРИМЕР 2:Как несложно догадаться из содержания, отличие от предыдущего примера по большому счёту лишь в том, что вместо массивов location и localobj используются файлы world.map и locobj.map (обрабатываются они как ini-файлы). Однако это решение не только устранило часть существовавших ранее проблем с массивами, но и добавило новых. Некоторые моменты пришлось переделывать существенно, поэтому я счёл нужным вынести это в отдельный пример.
Начнём с проблем, которые использование файлов решило. В первую очередь, конечно, процесс игры больше не занимает такого бешеного количества памяти :) Во-вторых, ранее неприятным моментом являлось то, что массивы не могут иметь отрицательных индексов и содержать больше 1 000 000 значений. Таким образом, в нашем мире нижней границей была локация 0/0, а верхней - 999/999. Поскольку файлы не имеют подобных ограничений, мир стал практически бесконечным. А раз так, я посчитал нужным несколько изменить формулу расчёта локаций. Старая формула (world_координата div 1024, где 1024 - ширина/высота комнаты) не работает диапазоне от (-1024,-1024) до (1024,1024) - четыре локации слипаются в одну. Новая формула такова:
location_x = round(world_x/1024)Где location_x - номер локации по оси Х, world_x - положение объекта в абсолютных координатах по оси Х.
Такая же формула справедлива и для У-координат, надо только заменить Х на У.
Таким образом, персонаж, появляясь в точке (0,0) оказывается в самом центре локации 0/0
Ну а теперь о тех проблемах, которые использование файлов добавило. Первая и самая главная - они работают медленнее. Я отказался от генерации карты заранее не только потому, что мне так захотелось, но и из-за того, что скорость генерации упала просто невыносимо. Если 10х10 локаций генерируются за ~5 секунд, то 50х50 я так и не дождался (хотя и ждал не слишком долго, около минуты). И, поскольку загрузка локаций тоже происходит из файла, FPS игры несколько упал. Хотя я бы не сказал, что значительно. Во-первых, частично это из-за того, что карта теперь генерируется на лету (на уже сгенерированных локациях FPS больше). А во-вторых, при скорости передвижения 100% (а мы ведь в нормальной игре не будем летать со скоростью 200 пикселов за шаг? ;) ) даже этого не замечается. FPS стабильно держится 58-60.
Но всё-таки я размышлял над тем, как оптимизировать всю эту ерунду. Подумал: "может, если всё-таки переводить координаты в 128-ричную систему, а id объектов шифровать, запись файлов будет идти быстрее - т. к. записывать надо меньше?" Проверил. Не быстрее. Но я знал, что в этом варианте слабое звено - скрипт перевода из 128-ричной системы обратно в 10-ричную, поскольку он жрёт до неприличия много ресурсов - а всё из-за перебора массива с 128 значениями. И я подумал: может, со switch будет работать быстрее? Проверил. И удивился :)
В спринтерском забеге на 250 локаций по диагонали со скоростью 64 пиксела за шаг (примерно полторы минуты по времени) программы показали следующие результаты:
1. Без 128-ричной системы и шифрования средний FPS составил
51.752. Со 128-ричной системой и шифрованием средний FPS составил
53.3Естественно, чистота эксперимента была сохранена, то есть кроме игры ничего не функционировало. Кто там спрашивал, нафига эта система нужна? :)
В обоих примерах скрипты 128-ричной системы обновил на более оптимизированные.
Ну и конечно, это ещё не предел. В планах:
-
Оптимизация использования файлов:
а) Запись в world.map координат объекта на локации, а не в мире (сделано)
б) Выкидывание из записей в файле world.map запятых (сделано)
в) Запись в world.map только положительных чисел (по локации и так понятно, в какой координатной четверти объект) (сделано)
г) Возврат обратно к массиву locobj вместо файла locobj.map (сделано)
- Введение других объектов и сохранение их на локации. А то всё деревья да деревья... :)
- Offline-alive. Рассчитываем поведение активных объектов за пределами комнаты;
- Удобный редактор карты (сейчас создать её самому можно только путём прямого редактирования world.map)
Надеюсь, кому-то это интересно :)