Войти на сайт
×
ТЕМА: Урок по созданию безграничного пространства
Урок по созданию безграничного пространства 12 года 5 мес. назад #52828
|
Урок по созданию безграничного пространства.
Он же — расширенный урок по телепортации. Автор: DeadElf79. Подготовлено специально для сайта http://www.rpg-maker.info/ Скачать статью, чтобы читать в оффлайне можно здесь: ссылка. Файл немного устарел в плане решения задачи о "гуляющих персонажах", вы можете найти правильное решение ниже. Порывшись на форуме и пересмотрев десяток — другой демонстрационных версий игр, а также полных версий от разных разработчиков с разным уровнем знаний по гейм-дизайну, левел-дизайну, написанию скриптов я не обнаружил той вещи, к которой так привык. А именно — бесконечного, безграничного пространства локаций. Посему и стал писать сей урок. Урок состоит из трех основных частей:
Урок был написан отдельно для разных версий RPG Maker - XP и VX ACE (RPG Maker VX запуститься не пожелал) ввиду их отличий и особенностей работы. Небольшое предупреждение — у меня есть только английская версия этих программ, если у кого есть русская версия и время — прошу, сделайте скриншоты для дополнения этой статьи. RPG MAKER XP ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ][ Нажмите, чтобы скрыть ] Часть первая. Подготовка локации А. Для начала создадим новый проект и в нем — новую карту. Откроем свойства карты. 1: Далее, посмотрим на свойства Ширина и Высота (Width и Height). 2: По умолчанию карта создается с размерами 20х15 тайлов (квадратов 32х32 пикселя). Такая локация полностью помещается на экране. Если же ее увеличить, сделать, скажем 30х20 тайлов, то она «растянется» в высоту и при запуске игры мы будем видеть лишь часть карты. Заметили что-нибудь во время перемещения? Конечно же! Когда персонаж уходит на какое-то расстояние вниз, карта смещается наверх, что создает иллюзию движения персонажа вниз по локации. Когда же персонаж приближается к границам карты, то мы видим, как перемещается именно он, а не карта. Выводим из этого правило, которым воспользуемся позже: Пока персонаж не подходит к краям карты, у игрока создается ощущения, что он перемещается по большой местности. Соответственно, чем дольше персонаж находиться в центре экрана, тем больше размеры карты по мнению воображения игрока. Стандартные размеры карты — 20х15 тайлов и по умолчанию в новом проекта место старта находиться в центре локации, соответственно и в центре экрана. Включаем логическое мышление, считаем, складываем, решаем дифференциальные уравнения методом Лагранжа... И вот, что получаем — чтобы создать иллюзию «большой» карты и персонаж все время находился в центре экрана, достаточно лишь не подпускать его близко к краям карты, которые располгаются по 10 тайлов слева и справа(20/2 по ширине), по 8 сверху и снизу (15/2 округляем — по высоте). Для примера возьмем карту, которую я рисовал для одного из своих тестовых проектов. Рисовать новую хорошую карту, которая будет использована только как скриншот для урока мне лень, поэтому смотрим: 3: Как мы видим, из города имеется три выхода. Также, прошу присмотреться внимательным взглядом и заметить, насколько естественными выглядят ограничения, мешающие персонажу пройти к краям локации. Кто еще не понял — игрок может свободно передвигаться только в пространстве, обведенном красным прямоугольником на рисунке: 4: Препятствиями являются стены крепости, вода, деревья, заборы. А теперь вспомним правило, которые вы вывели выше. Игрок ни с одной стороны карты не подходит к краям локации ближе, чем 10 тайлов по ширине и/или 8 тайлов по высоте. Правило соблюдено, эффект большого пространства уже создан. Большого, но, увы, еще не бесконечного. Поэтому продолжим. Часть вторая. Подготовка локации Б. Перед тем, как подходить к этому этапу, убедитесь, что созданная вами локация А удовлетворяет вашим желаниям, потому что иначе у вас появятся тонны работы на следующем этапе. Если вас все утраивает — читаем и смотрим далее. Для этого этапа я также использовал карту из того же проекта, в котором успешно использовал эффект «безграничного мира». Вот скриншот: 5: Здесь размер мест, по которым может спокойно пройти наш персонаж еще меньше. Заметьте, ширина этой карты не соответствует ширине прошлой. То есть строить карты одинаковых размеров совершенно необязательно, главное соблюдать правило, которые мы вывели выше. Также, присмотритесь... Да это же... Нижняя часть этой локации полностью аналогична с верхней частью прошлой! Как же так? Дело в том, что для создания плавного мгновенного перемещения необходимо, чтобы видимая часть обеих локаций во время перемещения было максимальной схожей. В идеале — одинаковой во всех отношениях. Важно, чтобы при этом бродящие по карте ивенты не попались в момент перемещения нам на глаза, иначе эффект будет безнадежно испорчен. Как не допустить этого — смотрите в разделе тонкости, как только дочитаете этот урок до конца. Так как некоторая часть обеих карт одинакова, то можно сделать такую вот схему: 6: Здесь я всего лишь совместил похожие части двух карт и объединил их в одну. Именно такой карта будет, по мнению игрока. Так как выходов из города несколько, то величина локации в воображении игрока будет расти и расширяться. Теперь пояснение — что мы сделали и зачем это нужно. Во-первых, созданием двух карт мы сделали иллюзию бесконечности мира - его можно продолжать расширять, не забывая в каждой условной локации Б (куда) делать видимую часть карты в месте перемещения точно такой же, как в условной локации А (откуда). Во-вторых, этот метод полезен для оптимизации игры. Так как иногда разработчику игры хочется показать местность побольше, вложить какой-то сюжет, расставить побольше врагов и привнести на карту параллельные процессы для оживления локации, то необходимо как-то эту деятельность контролировать. Иначе игрок будет наблюдать многочисленные навязчивые лаги или даже, в худшем случае и, к сожалению, нередком, — слайдшоу. Так как карт в итоге получается больше, то и распределить ресурсы можно на всех поровну. А то, что персонаж из раза в раз перемещается игроку знать совершенно необязательно. Его дело — играть и наслаждаться (с). По первому пункту приведу пример, чтобы было полегче — берем инструмент Select (Выделение) в режиме рисования, выделяем необходимую область (ту самую, которая видимая часть) и жмем Ctrl+C или же в главном меню Edit (Правка), затем Copy (Копировать). Кому как удобнее. После этого переходим на локацию Б и вставляем у нужного края. Смотрим скриншоты, хотя я и объяснял как можно проще: 7: 8: Кусок не обязательно может вместиться весь, главное, чтобы было соблюдено правило, приведенное выше. Поскольку персонаж зафиксирован в центре экрана и видимая местность в окрестностях телепортов на обеих картах совпадает, то во время игры мы будем наблюдать эффект, который можно назвать красиво: «Плавная мгновенная телепортация». Теперь же приступим к осуществлению этого эффекта. Часть третья. Установка и настройка телепортов. Делаются они с помощью ивента, реагирующего на прикосновение персонажа. Выбираем Player Touch. Чтобы не нарушать эффекта незаметного (плавного) перемещения из карты в карту, лучше не ставить никакой видимой графики. Мои настройки выглядят так: 9: Далее обязательно запоминаем расположение ивента, задаем ему команду Transfer Player на координаты тайла на следующей карте, который расположен там же, где и наш. Это делается только на глаз, никакими числами не поможешь (карта-то другая). Вот тут нам еще раз помогает подготовка обеих карт, описанная в двух предыдущих частях урока. Далее, смотрим скриншот: 10: Выставляем Direction (Направление) в Retain, чтобы персонаж на следующей карте смотрел в ту же сторону, в какую смотрел, когда наступил на телепорт. Выбираем в Fading (Переход) значение No, чтобы не происходило Transition (плавного замещения одного изображения, в данном случае — видимой части карты А, другим — видимой частью карты Б). Так как обе части выглядят аналогично, то Fading со значением Yes будет подозрительно похож на зависание. При значении No, если карты не слишком велики и загружены процессами, переход будет незаметен и зависания не произойдет. Чего мы и добиваемся. Если вам нужно, чтобы игрок мог телепортироваться туда и обратно, ставим второй телепорт на карте Б, точно там же, куда перемещаемся с карты А. Координаты, в которые персонаж попадет на карту А — это место, где стоит телепорт, отправляющий с Б на А. Если предыдущий абзац показался вам слишком запутанным, не волнуйтесь - это только кажется. Смотрим скриншоты, для разнообразия сделанные с двух других карт: 11: 12: Теперь вам должно быть понятно то, что было сказано выше. Как видите, здесь я сделал даже несколько телепортов, каждый из которых перемещает персонажа в свои координаты. В этом имеется свой плюс — игрок и не заподозрит о том, что его нагло обманули, подменив пироги с А на Б. Перебарщивать не стоит, потому что вам еще настраивать, но эффект хороший. На этом урок окончен. Запустите полученную вами демку и проверьте, все ли правильно проходит. Если нет — перепроверьте координаты перемещения. Теперь можете со спокойной душой посмотреть некоторые тонкости при работе с этим методом (раздел Тонкости). RPG MAKER VX ACE ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ][ Нажмите, чтобы скрыть ] Полный урок можно просмотреть в разделе XP, здесь я приведу некоторые оговорки, связанные с VX ACE. Во-первых, размер экрана, используемого игрой был уменьшен компанией Enterbrain (негодяи какие). Поэтому и размеры карты по умолчанию не 20х15 тайлов, а 17х13. Теперь справа и слева можно оставлять по 9 тайлов, а сверху и снизу — 7. Во-вторых, появилась возможность с помощью скрипта изменять разрешение игрового экрана на любое. В XP было 640х480 и менялось специальными скриптами, в VX ACE – по умолчанию 544х416, но можно легко расширить хоть до 1900х1080. Сложность заключается в том, что при увеличении разрешения и видимая область карты увеличивается, соответственно и картографам (мапперам) работы прибавится - для создания иллюзии «бесконечности» игрового мира для каждого разрешения нужно посчитать отступы от границ карты, чтобы персонаж находился всегда в центре. Это не сложно, но, по-моему, это просто лишняя работа. В-третьих, кнопку Select (Выделение) убрали с панели инструментов. Поэтому для копирования части одной карты в другую используем выделение с помощью зажатия правой клавиши. Так как, в отличие от XP, в VX ACE нет слоев, то и скопировано будет все и сразу. Кстати, для VX ACE имеется интересный скрипт, позволяющий использовать тени (как статические, так и динамические). Поскольку обработка сразу большого количества источников света на большом пространстве отнимает значительную часть процессорного времени (читай — сил компьютера), то использование «безграничного мира» выручает, как ничто иное. Тонкости работы ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ][ Нажмите, чтобы скрыть ] Проблем у представленного метода всего три:
Разберем по порядку проблемы и их решение. Первая проблема. Спутники в XP появляются благодаря скриптам, в VX ACE с помощью галочки Show Player Followers (показывать спутников). И в том, и в другом случае после перемещения главного героя с карты на карту, они становятся на одно место с ним. Проблема как раз в том, что это разрушает иллюзию «безграничности», ведь игрок замечает поведение спутников и начинает подозревать, что это либо баг, либо... Тут обман и раскрывается. Обидно, особенно, если вы очень старались. Решений этой проблемы два:
Первое делается просто, второе — посложнее. Скрипт нужно либо искать, либо писать самому. У меня его нет. Вторая проблема — праздношатающиеся прохожие, ведомые волей случая. Как показало время и опыт, данный метод не поможет и Вам стоит попробовать использовать вот этот замечательный скрипт, переведенный на русский язык недавно: Проходимость, основанная на регионах Yanfly. Все настройки даны в самом скрипте, довольно понятно объяснено. Третья проблема — враги. Тут решений два — либо использовать приведенный выше пример с Event Touch, либо ставить срабатывание телепорта только если персонаж в безопасности и никто его не трогает. То есть пока не добьет врага — никуда не уйдет. Первое решение может сказаться на логике игры, так что лучше представить все так, как будто действует какое-то защитное поле или же стоит забор. Второе может сказаться на сложности игры — побитый персонаж, мчащийся галопом от толпы врагов к спасительному телепорту, где его ждут друзья с автоматами и море аптечек (Potion) вдруг обнаруживает, что никакого спасения и нет, перед ним тупик... Безысходность пугает. Впрочем, это тоже можно представить как особенность игры, надо только постараться. На этой радостной ноте статья и заканчивается. Она получилась объемной, но при этом полной. Надеюсь, я ничего не забыл. Смотри также другие уроки автора: Генератор имен Основные ошибки картостроения (совместно с Kolhe и AnnTenna) |
Последнее редактирование: 11 года 3 мес. назад от DeadElf79.
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 12 года 5 мес. назад #52829
|
Занятный урок, хорошо написан. Где-то даже видится попытка юмора. Ничего нового не открыл, да и карты такие не люблю, но все же урок хорош.
|
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: RastaManGames
|
Re: Урок по созданию безграничного пространства 12 года 5 мес. назад #52830
|
Kolhe, статья рассчитана, в основном, на тех, кто любит строить большие и очень большие карты. Поможет оптимизировать))
|
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 12 года 5 мес. назад #52831
|
В VXA, в принципе, этого можно добится прокруткой, которая в настройках карты. А хрюнделям пригодится, да.
|
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 12 года 5 мес. назад #52834
|
DeadElf чёт не могу врубиться, но это почти тоже о чём я спрашивал когда то?
rpg-maker.info/forum/pomoshh/48362-plavn...taciya-edinaya-karta |
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 12 года 5 мес. назад #52842
|
LarryCS, о, такая тема действительно есть)) Но тема затерялась в глубинах форума, так что я о ней и не знал. Ну да, это почти тоже самое, только лежит в качестве урока, что, наверное, все же удобнее для поиска)))
Тем более, судя по прочитанной теме, кто-то пытался реализовать то же самое с помощью скриптов. Здесь же - только маппинг и пара ивентов, без переходов, без затемнений, сам пробовал на представленной в качестве скрина в уроке карте 80х80 с несколькими параллельными процессами - проходит на ура, все плавно и перемещение незаметно)) |
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 11 года 6 мес. назад #60131
|
Каким-то образом случайно нашёл эту тему, заинтересовало, прочёл. Вот одна деталь обратила на себя моё внимание:
Во-вторых, появилась возможность с помощью скрипта изменять разрешение игрового экрана на любое. В XP было строго и неизменно 640х480, в VX ACE – по умолчанию 544х416, но можно расширить хоть до 1024х768. Можно ли уточнить сию информацию? Такой скрипт мне бы пригодился в моей идее, но, видимо, я не умею пользоваться гуглом, ибо найденные скрипты не дают тех эффектов, которые я ищу. Таких, чтобы менялась и видимая область карты. |
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 11 года 6 мес. назад #60136
|
Джеймс, тебе под ХР нужен? Рекомендую вот этот - сам проверял, настраивал, тестировал. Работает на ура даже на моей древней машинке, жрёт мало, единственная проблема - тебе придётся переписывать абсолютно все меню и скриптовые сцены под новое разрешение, а также отказаться от стандартной функции RMXP - "переходов", которые основаны на картинках.
|
Огромный любитель среброволосых или пепельноволосых 2D-девушек с хорошим характером или со скрытыми привлекательными чертами.
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 11 года 6 мес. назад #60149
|
Благодарю, для XP тоже пригодится. Но всё же в первую очередь мне сейчас интересен аналог для VXA.
И ещё было бы здорово, если бы можно было как-то менять размер самих тайлов, например, вдвое. И чтобы это сохранялось при переходе в фуллскрин. Если кто-то знает, где такое можно найти, заранее благодарен. |
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 11 года 6 мес. назад #60152
|
Менять размер тайлов? Если стандартно, то можно написать скрипт, который увеличивает картинку в Spriteset_Map в нужное количество раз, но качество это убьет.
|
Администратор запретил публиковать записи гостям.
|
Урок по созданию безграничного пространства 11 года 6 мес. назад #60155
|
Почему? На эмуляторе же пиксели крупные, и при этом качество ни капли не страдает. Ну, по меньшей мере мне нравится.
|
vk.com/sando_rpgmaker - пустая болтовня (приходите)
Администратор запретил публиковать записи гостям.
|
Урок по созданию безграничного пространства 11 года 6 мес. назад #60159
|
1. Какое разрешение экрана?
2. На весь ли экран растянута картинка? 3. Какой коэффициент увеличения? (2х, 3х, иное) Если увеличивать картинку, то половину всего просто будет невидно. По-моему, играть, к примеру, на экране 10х7 тайлов (в два раза меньше, чем стандартный экран ХР), растянутом на весь экран, будет несколько тяжело. Хотя так можно скрывать свой убогий маппинг, но его стоит улучшать, а не всячески прятать) |
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 11 года 6 мес. назад #60169
|
Но всё же в первую очередь мне сейчас интересен аналог для VXA. Есть такая буква. Здесь дофига свистелок, но жрёт оперативную он... прилично. ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ][ Нажмите, чтобы скрыть ] #============================================================================
# ** Tilemap Enchance v.0.75 (RGSS3 Version)
#============================================================================
#
# Dieses Script ersetzt die Standard Tilemap-Klasse komplett und ermöglicht
# Zugriff auf die einzelnen Layer, bietet aber auch einige spezielle Features.
# Das Script funktioniert grundsätzlich einwandfrei, anders als die meisten
# Tilemap-Rewrites.
#
# 1. Benutzung
#
# Fügt das Script über main und unter den Standardscripten ein.
#
# Falls ihr den RGSS3-Player für den XP verwendet, sucht nach "RGSS3", kommentiert
# die 3 Zeilen aus (# entfernen) und fügt ein "#" vor die jeweils darüberliegende
# Zeile.
#
# 2. Funktionsweise
#
# Das Script lädt beim Betreten einer Map statt der Klasse Tilemap die neue Klasse
# Tilemap_New. Es wird für jede Zeile der Map, als in Y-Richtung von 0 bis
# zum unteren Ende der Map, ein Sprite mit Bitmap erstellt. Dabei wird noch
# zwischen den einzelnen Priorty-Settings und Tiles/Autotiles unterschieden.
# Die Tiles werden also im vorhinein geladen und nicht während dem Spiel
# neu gezeichnet. Das verbraucht eine Menge RAM und erhöht die Ladezeit pro Map
# etwas, erhöht aber gleichzeitig die Performance!
#
# 3. Nutzen
#
# Man kann jetzt aktiv in den Darstellungsprozess der Tiles eingreifen, bzw.
# die Tile-Grafiken selber manipulieren. Drei eingebaute Features sind :
# Blend-Types für Autotiles (zu Normal kommt noch Add und Sub dazu; nützlich z.B
# für bestimmte Wasser/Licht-Effekte), variable Animationsgeschwindigkeit
# für Autotiles sowie ein Color-Tone für die Tilemap alleine. Andere
# Verwendungszwecke wären z.B. für ein Screen-Size-Script. Meine Tilemap-Klasse
# eignet sich dafür besonders, da die Performance mit zunehmender
# Bildgröße nur geringfügig abnehmen würde,wenn überhaupt.
#
# 4. Verwendung
#
# Das Script wird automatisch nach Einbau verwendet, jedoch muss man die
# besonderen Features extra aktivieren:
#
# - Autotile blend_type/speed:
# Wird über das module Autotile direkt unter der
# Einleitung konfiguriert. Mögliche Werte für blend_type sind 0 (Normal),
# 1 (Add), 2 (Sub), für speed kann jede Ganzzahl verwendet werden. Kleinere
# Zahlen bedeuten schnellere Animation (0 = jedes Frame).
# Konfiguriert wird: MAP_ID=>{AUTOTILE_ID=>BLEND_TYPE/BLEND_SPEED} im jeweiligen
# Abschnitt BLEND_TYPE bzw. SPEED, Beispiele vorhanden. ID geht von 0 bis 6
# (links nach rechts im Tileset)
#
# DEFAULT_SPEED ist die Geschwindgkeit die verwendet wird, wenn in SPEED für
# eine Map oder ein Autotile keine Angabe gemacht wurde.
#
# - Tilemap-Tone:
# Kann per Callscript oder in einem Script selbst mit dem Befehl
#
# $game_map.start_tone_change(Tone,Dauer)
#
# aufgerufen werden, wobei Tone = Tone.new(r,g,b,gr). Dieser Tone wirkt sich
# dann nur auf die Tiles und Autotiles aus, der Screen-Color-Tone kann immer
# noch über alles gelegt werden.
#
# 5. Beschränkungen
#
# - Nachträgliche Änderung von Tile-Daten im Script hat in der jetzigen Version
# noch keine Auswirkungen (z.B. über $game_map.data)
#
# - Höherer RAM-Verbrauch. Je nach Größe der Map und Verwendung von Autotiles
# kann der RAM-Verbrauch an die 800 Mb betragen. (200*200 Map mit 1 Layer voll
# normalen Tiles und 1 Layer voll 4 Frames Autotile)
# Realistischer Verbrauch liegt bei max. 500 MB, 512 MB RAM sollten bei Maps größer
# als 100*100 vorhanden sein!
#
# - Längere Ladezeit. Abhängig von der Map-Größe und Verwendung von Autotiles
# pendelt die Ladezeit zwischen 0 und 6 Sekunden. Kleine Maps (30*30) Instant-
# Load, 60*60 mit Autotiles ca. 1 Sekunde, 100*100 mit Autotiles 2 Sekunden,
# 200*200 Map mit 1 Layer voll normaler Tiles und 1 Layer voll animierter
# Autotiles 6 Sekunden
#
# - Ein Teleport auf derselben Map führt zu einem Grafikfehler.
#
# 6. Vorteile
#
# - Voller Zugriff auf die Sprites der Map
# - Autotile-Blend_types und variable Geschwindigkeit
# - höhere Performance zu Lasten von RAM
# - Volle Funktionalität (Ausnahme siehe Beschränkungen) gegeben
# - Lag skaliert nicht mit Fenster-Größe (Verwendung für Screen-Script+)
#
#
# 7. Credits
#
# Credits an GodLike für die RPG::Cache-Erweiterung
# Verwendung/Modifizierung steht frei, bei Nutzung jedoch bitte Credit-Vergabe.
#
# 8. Changelog
#
# V 0.75:
#
# - Kompatibilität mit dem RGSS3-Mod für den XP
# - Ladgeschwindigkeit leicht verbessert
# - Allgemeine Performance verbessert
# - Kleine Darstellungsfehler bei bestimmten Autotile-Kombinationen behoben
#
# 9. Kontakt
#
# Bei Fragen, Anregungen, Bugmeldungen, bitt eine E-Mail an [email protected]
#
#
#============================================================================
#==============================================================================
# ** Module Autotile
#------------------------------------------------------------------------------
#
# Mit diesem Modul werden die Autotile-Eigenschaften konfiguriert.
#
#==============================================================================
module AUTOTILE
#MAP_ID=>{AUTOTILE_ID=>BLEND_TYPE,..}
BLEND_TYPE = {
4=>{5=>1,6=>2},
}
#MAP_ID=>{AUTOTILE_ID=>SPEED,..}
SPEED = {
4=>{4=>2},
}
#DEFAULT_SPEED
DEFAULT_SPEED = 4
end
#==============================================================================
# ** Sprite_Auto NEW
#------------------------------------------------------------------------------
#
# Die Klasse zum Verwalten der Autotile-Layer. Jeder Layer enthält mehrere
# Sprites, eines für jede Autotile-Spalte. Erzeugt auch die jeweiligen Bitmaps
# für den Layer.
#
#==============================================================================
class Sprite_Auto
attr_reader :cache2
#--------------------------------------------------------------------------
# * Initialize (Tiles auslesen, Sprites & Bitmaps erzeugen)
#--------------------------------------------------------------------------
def initialize(viewport,tiles,speed,autotile_frames,priority,blend_type)
# Store autotile data
@tiles = tiles
@tileset_id = $game_map.map.tileset_id
@tileset = RPG::Cache.tileset($game_map.tileset_name)
@speed = speed
@priority = priority
@autotile_frames = autotile_frames
@frames = autotile_frames[tiles[0]]
@priorities = $game_map.priorities
@blend_type = blend_type
@viewport = viewport
# Animation variables
@time = 0
@state = -1
# Get number of tiles on x/y axis
@width = $game_map.width
@height = $game_map.height
# Set layer border variables to l<->r and u<->d (changed max/min values)
@border_lx = @width
@border_ty = @height
@border_rx = 0
@border_by = 0
# Set row borders
@border_lx_row = @width
@border_rx_row = 0
# Create table for row borders
@rows = Table.new(@height,2)
# Hash for sprites & cache
@sprites = {}
@cache = {}
@cache2 = {}
# If special blend_type
if @blend_type != 0
# Setup tiles accordingly
setup_tiles_blend
# Else: normal blendig
else
# Setup tiles
setup_tiles
end
# Return if not valid
return if !valid?
# Otherwise, create sprites & bitmaps
create_sprites
# If special blend_type
if @blend_type != 0
# Draw blended tiles
draw_tiles_blend
else
# Draw tiles
draw_tiles
end
@tileset = nil
end
#--------------------------------------------------------------------------
# * Valid? (Überprüft ob in Tiles für diesen Layer gefunden wurden)
#--------------------------------------------------------------------------
def valid?
# If right border greater or equal to left border and
# top border greater or equal to bottom border then true
return @border_rx >= @border_lx && @border_by >= @border_ty
end
#--------------------------------------------------------------------------
# * Row_valid? (Überprüft ob in Tiles in Spalte Y gefunden wurden)
#--------------------------------------------------------------------------
def row_valid?(y)
# If right border of row equal or greater to left border of row
# then true
return @rows[y,1] > @rows[y,0]
end
#--------------------------------------------------------------------------
# * Setup_tiles_blend (Liest Tiles für diesen Layer aus und bestimmt die
# Grenzen. Verwendet spezielle Regeln beim auslesen)
#--------------------------------------------------------------------------
def setup_tiles_blend
# Get pre-sorted tile data with priority of the layer
@data_link = $game_map.priority_data(@priority)
# Clone hashs, needed for functionally interation while deleting stuff
# from the data
@data = @data_link.dup
@data_link.each_pair do |dx,data_x|
dat_x = @data[dx]
dat_x = data_x.dup
data_x.each_pair do |dz,data_z|
dat_x[dz] = data_z.dup
end
end
tiles_found = false
layer = 0
# Iterate through sorted tiles on y-axis
@data.each_pair do |y,data_y|
# Iterate through sorted tiles on x-axis
data_y.each_pair do |x,data_x|
# Iterate through map editor layers downwards
data_x.each_pair do |z,t|
# Get Tile ID
# Skip to next if there no tile
next if t == nil
# If tile fits properties of layer, only true if t is a autotile
if @tiles.include?(t/48-1)
# Add tile id to cache
@cache[y] = {} if @cache[y] == nil
@cache[y][x] = {} if @cache[y][x] == nil
@cache[y][x][z] = t
# Extend borders
border(x,y)
# Delete from map data list
@data_link[y][x].delete(z)
@data_link[y].delete(x) if @data_link[y][x].size == 0
@data_link.delete(y) if @data_link[y].size == 0
tiles_found = true
layer = z
end
end
if tiles_found == true
if layer == 0
for z in [1,2]
t = data_x[z]
next if t == nil || t < 384 || @priorities[t] != @priority
@cache2[y] = {} if @cache2[y] == nil
@cache2[y][x] = {} if @cache2[y][x] == nil
@cache2[y][x][z] = t
@data_link[y][x].delete(z)
@data_link[y].delete(x) if @data_link[y][x].size == 0
@data_link.delete(y) if @data_link[y].size == 0
end
elsif layer == 1
t = data_x[2]
next if t == nil || t < 384 || @priorities[t] != @priority
@cache2[y] = {} if @cache2[y] == nil
@cache2[y][x] = {} if @cache2[y][x] == nil
@cache2[y][x][z] = t
@data_link[y][x].delete(z)
@data_link[y].delete(x) if @data_link[y][x].size == 0
@data_link.delete(y) if @data_link[y].size == 0
end
end
end
# Set row borders
@rows[y,0] = @border_lx_row
@rows[y,1] = @border_rx_row
# Re-Initialize row border temporaries for next iteraton
@border_lx_row = @width/32
@border_rx_row = 0
end
end
#--------------------------------------------------------------------------
# * Setup_tiles (Liest Tiles für diesen Layer aus und bestimmt die
# Grenzen. Verwendet spezielle Regeln beim auslesen)
#--------------------------------------------------------------------------
def setup_tiles
# Get pre-sorted tile data with priority of the layer
@data_link = $game_map.priority_data(@priority)
# Clone hashs, needed for functionally interation while deleting stuff
# from the data
@data = @data_link.dup
@data_link.each_pair do |dx,data_x|
dat_x = @data[dx]
dat_x = data_x.dup
data_x.each_pair do |dz,data_z|
dat_x[dz] = data_z.dup
end
end
# Iterate through sorted tiles on y-axis
@data.each_pair do |y,data_y|
# Iterate through sorted tiles on x-axis
data_y.each_pair do |x,data_x|
# Initialize autotile found flag to false
autotile_found = false
# Iterate through map editor layers downwards
data_x.each_pair do |z,t|
# Iterate through map editor layers
# Get tile id
# Store whether tile fits layer properties or not
# only true if t is a autotile
tiles_include = @tiles.include?(t/48-1)
# If tile is included or tile is from tileset or tile is autotile
# with 1 frame
if tiles_include || (t >= 384 || @autotile_frames[t/48-1] <= 1)
# Store in cache
@cache[y] = {} if @cache[y] == nil
@cache[y][x] = {} if @cache[y][x] == nil
@cache[y][x][z] = t
# If tile is a autotile and is included
if t < 384 && tiles_include
# Delete from map data list
@data_link[y][x].delete(z)
@data_link[y].delete(x) if @data_link[y][x].size == 0
@data_link.delete(y) if @data_link[y].size == 0
# Extend borders
border(x,y)
# Set autotile found flag to true
autotile_found = true
end
end
end
# If no autotile was found & row at y is not empty and x-position is not empty
if autotile_found == false && @cache[y] != nil && @cache[y][x] != nil
# Delete all tiles on x-position from cache
@cache[y].delete(x)
@cache.delete(y) if @data_link[y].size == 0
end
end
# Set row borders
@rows[y,0] = @border_lx_row
@rows[y,1] = @border_rx_row
# Re-Initialize row border temporaries for next iteration
@border_lx_row = @width/32
@border_rx_row = 0
end
end
#--------------------------------------------------------------------------
# * Create_Sprites (Erzeugt Sprites & Bitmaps für jeden Zeile)
#--------------------------------------------------------------------------
def create_sprites
# Iterate through tiles on y-axis
for y in @cache.keys
# If row is not valid
if !row_valid?(y)
# skip to next
next
end
# Calculate row width
distx = @rows[y,1] - @rows[y,0]
# Create sprite
sprite = Sprite.new(@viewport)
# Create bitmap with row-width*count of frames
sprite.bitmap = Bitmap.new(distx*32*@frames,32)
sprite.x = @rows[y,0]*32-$game_map.display_x/4
sprite.y = y*32-$game_map.display_y/4
# Set source rect to frame 0
sprite.src_rect = Rect.new(0,0,distx*32,32)
sprite.blend_type = @blend_type
# If priority equals 0
if @priority == 0
# Set z to 0, always beyond later created layers
sprite.z = 0
else
# Set z to base + priority + dosition - display-Position
sprite.z = 32 + 32*@priority + 32*y - $game_map.display_y/4
end
# Add sprite to hash
@sprites[y] = sprite
end
end
#--------------------------------------------------------------------------
# * Border (Erweitert die Grenzen des Layers und der aktuellen Zeile
#--------------------------------------------------------------------------
def border(x,y)
# If X is outside left border
if x < @border_lx
# Extend left border to x
@border_lx = x
end
# If X is outside right border
if x > @border_rx
# Extend right border to x+1
@border_rx = x+1
end
# If Y is outside top border
if y < @border_ty
# Extend top border to y
@border_ty = y
end
# If Y is outside bottom border
if y > @border_by
# Extend bottom_border to y
@border_by = y
end
# If X is outside left row border
if x < @border_lx_row
# Extend left row border to x
@border_lx_row = x
end
# If X is outside right row border
if x+1 > @border_rx_row
# Extend right row border to y
@border_rx_row = x+1
end
end
#--------------------------------------------------------------------------
# * Update (Aktualisiert die Position der Zeilen im Bild sowie die
# Animation)
#--------------------------------------------------------------------------
def update(ox,oy)
# Get first and last row 1 outside screen
dy = oy/32.0
l = [dy.to_i-1,@border_ty].max
r = [dy.to_i+17,@border_by].min
# If time exceedes speed
if @time > @speed
# Reinit time, advance state
@time = 0
@state += 1
# Iterate through every row on screen
for i in l..r
# Skip to next If row has no sprite
sprite = @sprites[i]
next if sprite == nil
# Set sprite properties
sprite.tone = $game_map.tone# if @blend_type == 0
sprite.x = @rows[i,0]*32-ox
sprite.y = i*32-oy
# If priority equals 0
if @priority == 0
# Set Z to 0, always beyond later created tiles
sprite.z = 0
else
# Set Z to Base + Priority + Position - Tilemap position
sprite.z = 32 + 32*@priority + i*32 - oy
end
# Get row width
lx = @rows[i,0]
w = @rows[i,1] - lx
# Set animation frame
sprite.src_rect = Rect.new(@state*w*32,0,w*32,32)
end
# If state exceeded frame count
if @state >= @frames-1
# Reset state
@state = -1
end
# Else: just update position
else
# Iterate through every row on screen
for i in l..r
# Skip to next if row has no sprite
sprite = @sprites[i]
next if sprite == nil
# Set sprite properties
sprite.tone = $game_map.tone# if @blend_type == 0
sprite.x = @rows[i,0]*32-ox
sprite.y = i*32-oy
# If priority equals 0
if @priority == 0
# Set Z to 0, always beyond later created tiles
sprite.z = 0
else
# Set Z to Base + Priority + Position - Tilemap position
sprite.z = 32 + 32*@priority + i*32 - oy
end
end
end
# Advance time
@time += 1
end
#--------------------------------------------------------------------------
# * Refresh (Erneuert die Eigenschaften aller Reihen)
#--------------------------------------------------------------------------
def refresh
# Iterate through all sprites
for y in @cache.keys
# Skip if sprite doesn't exist
sprite = @sprites[y]
next if sprite == nil
sprite.tone = $game_map.tone
# If priority equals 0
sprite.x = @rows[y,0]*32-$game_map.display_x/4
sprite.y = y*32-$game_map.display_y/4
if @priority == 0
# Set z to 0, always beyond
sprite.z = 0
else
# Set Z to Base + Priority + Position - Screen Position
sprite.z = 32+32*@priority+y*32-$game_map.display_y/4
end
end
end
#--------------------------------------------------------------------------
# * Draw_tiles_blend (Zeichnet den kompletten Layer in die einzelnen Zeilen.
# Wendet spezielle Regeln für 1/2-Blended Autotiles an.)
#--------------------------------------------------------------------------
def draw_tiles_blend
# Iterate through cached tiles on y axis
@cache.each_pair do |y,cache_y|
sprite = @sprites[y]
next if cache_y == nil
lx = @rows[y,0]
w = @rows[y,1]-lx
cache_y.each_pair do |x,cache_x|
cache_x.values.each do |t|
#cache_x.values.reverse.each do |t| #RGSS3
file = (t / 48) - 1
a = t - (file+1)*48
for f in 0...@frames
autotile = RPG::Cache.atile($game_map.autotile_names[file],a,f)
sprite.bitmap.blt(-lx*32+x*32+f*w*32,0,autotile,autotile.rect)
end
end
end
end
RPG::Cache.clear_auto
end
def draw_tiles
@cache.each_pair do |y,cache_y|
sprite = @sprites[y]
lx = @rows[y,0]
w = @rows[y,1]-lx
cache_y.each_pair do |x,cache_x|
cache_x.keys.each do |z|
#cache_x.keys.reverse.each do |z| #RGSS3
t = cache_x[z]
if t < 384
file = (t / 48) - 1
a = t - (file+1)*48
for f in 0...@frames
autotile = RPG::Cache.atile($game_map.autotile_names[file],a,f)
sprite.bitmap.blt(-lx*32+x*32+f*w*32,0,autotile,autotile.rect)
end
if @autotile_frames[file] <= 1
@data_link[y][x].delete(z)
@data_link[y].delete(x) if @data_link[y][x].size == 0
@data_link.delete(y) if @data_link[y].size == 0
end
else
t -=384
xt = t%8
yt = t/8
r = Rect.new(xt*32,yt*32,32,32)
for f in 0...@frames
sprite.bitmap.blt(-lx*32+x*32+f*w*32,0,@tileset,r)
end
@data_link[y][x].delete(z)
@data_link[y].delete(x) if @data_link[y][x].size == 0
@data_link.delete(y) if @data_link[y].size == 0
end
end
end
end
RPG::Cache.clear_auto
end
def dispose
for sprite in @sprites.values
sprite.dispose
end
end
end
module RPG
module Cache
@auto_cache = {}
[[27,28,33,34],[5,28,33,34],[27,6,33,34],[5,6,33,34],
[27,28,33,12],[5,28,33,12],[27,6,33,12],[5,6,33,12],[27,28,11,34],
[5,28,11,34],[27,6,11,34],[5,6,11,34],[27,28,11,12],[5,28,11,12],
[27,6,11,12],[5,6,11,12],[25,26,31,32],[25,6,31,32],[25,26,31,12],
[25,6,31,12],[15,16,21,22],[15,16,21,12],[15,16,11,22],[15,16,11,12],
[29,30,35,36],[29,30,11,36],[5,30,35,36],[5,30,11,36],[39,40,45,46],
[5,40,45,46],[39,6,45,46],[5,6,45,46],[25,30,31,36],[15,16,45,46],
[13,14,19,20],[13,14,19,12],[17,18,23,24],[17,18,11,24],[41,42,47,48],
[5,42,47,48],[37,38,43,44],[37,6,43,44],[13,18,19,24],[13,14,43,44],
[37,42,43,48],[17,18,47,48],[13,18,43,48],[13,18,43,48]]
AUTOTILES = [26, 27, 32, 33, 4, 27, 32, 33, # 1
26, 5, 32, 33, 4, 5, 32, 33, # 3
26, 27, 32, 11, 4, 27, 32, 11, # 5
26, 5, 32, 11, 4, 5, 32, 11, # 7
26, 27, 10, 33, 4, 27, 10, 33, # 9
26, 5, 10, 33, 4, 5, 10, 33, # 11
26, 27, 10, 11, 4, 27, 10, 11, # 13
26, 5, 10, 11, 4, 5, 10, 11, # 15
24, 25, 30, 31, 24, 5, 30, 31, # 17
24, 25, 30, 11, 24, 5, 30, 11, # 19
14, 15, 20, 21, 14, 15, 20, 11, # 21
14, 15, 10, 21, 14, 15, 10, 11, # 23
28, 29, 34, 35, 28, 29, 10, 35, # 25
4, 29, 34, 35, 4, 29, 10, 35, # 27
38, 39, 44, 45, 4, 39, 44, 45, # 29
38, 5, 44, 45, 4, 5, 44, 45, # 31
24, 29, 30, 35, 14, 15, 44, 45, # 33
12, 13, 18, 19, 12, 13, 18, 11, # 35
16, 17, 22, 23, 16, 17, 10, 23, # 37
40, 41, 46, 47, 40, 41, 46, 47, # 39
36, 37, 42, 43, 36, 5, 42, 43, # 41
12, 17, 18, 23, 12, 13, 42, 43, # 43
36, 41, 42, 47, 16, 17, 46, 47, # 45
12, 17, 42, 47, 0, 1, 6, 7] # 47
module_function
def atile(file, id , offset)
if @auto_cache[[file,id,offset]] == nil
autotile_bitmap = Bitmap.new(32, 32)
autotiles_bitmap = autotile(file)
if autotiles_bitmap.height == 32
if offset*32 >= autotiles_bitmap.width
autotile_bitmap.blt(0,0,autotiles_bitmap,Rect.new(0,0,32,32))
else
autotile_bitmap.blt(0,0,autotiles_bitmap,Rect.new(offset*32,0,32,32))
end
@auto_cache[[file,id,offset]] = autotile_bitmap
return autotile_bitmap
end
real_id = id*4
off = offset*96
if off>=autotiles_bitmap.width
off = 0
end
auto1 = AUTOTILES[real_id + 0]
auto2 = AUTOTILES[real_id + 1]
auto3 = AUTOTILES[real_id + 2]
auto4 = AUTOTILES[real_id + 3]
rect1 = Rect.new(auto1 % 6 * 16 + off, auto1 / 6 * 16, 16, 16)
rect2 = Rect.new(auto2 % 6 * 16 + off, auto2 / 6 * 16, 16, 16)
rect3 = Rect.new(auto3 % 6 * 16 + off, auto3 / 6 * 16, 16, 16)
rect4 = Rect.new(auto4 % 6 * 16 + off, auto4 / 6 * 16, 16, 16)
autotile_bitmap.blt(0, 0, autotiles_bitmap, rect1)
autotile_bitmap.blt(16, 0, autotiles_bitmap, rect2)
autotile_bitmap.blt(0, 16, autotiles_bitmap, rect3)
autotile_bitmap.blt(16, 16, autotiles_bitmap, rect4)
@auto_cache[[file,id,offset]] = autotile_bitmap
return autotile_bitmap
else
return @auto_cache[[file,id,offset]]
end
end
def clear_auto
for bitmap in @auto_cache.values
bitmap.dispose
end
@auto_cache.clear
#GC.start
end
end
end
#==============================================================================
# ** Sprite_Layer NEW
#------------------------------------------------------------------------------
#
# Die Klasse zum Verwalten der Tile-Layer. Jeder Layer enthält mehrere
# Sprites, eines für jede Tile-Spalte. Erzeugt auch die jeweiligen Bitmaps
# für den Layer.
#
#==============================================================================
class Sprite_Layer
#--------------------------------------------------------------------------
# * Initialize (Tiles auslesen, Sprites & Bitmaps erzeugen)
#--------------------------------------------------------------------------
def initialize(viewport,priority,autotile_frames,cache=nil)
# Get Tileset ID
@tileset_id = $game_map.map.tileset_id
# Class variables
@priority = priority
@autotile_frames = autotile_frames
@viewport = viewport
# Calculate number of tiles on x and y axis
@width = $game_map.width
@height = $game_map.height
# Cache for the tiles
if cache == nil
@cache = {}
else
@cache = cache
end
# Set layer border variables to l<->r and u<->d (changed max/min values)
@border_lx = @width
@border_ty = @height
@border_rx = 0
@border_by = 0
# Row border
@border_lx_row = @width
@border_rx_row = 0
# Table to store left and right border of every row
@rows = Table.new(@height,2)
# Hash for the sprites
@sprites = {}
# Setup tiles
if @cache.size == 0
setup_tiles
else
setup_tiles_cache
end
# If not valid, return
return if !valid?
# Otherwise, create sprites
create_sprites
# Get tileset graphic
@tileset = RPG::Cache.tileset($game_map.tileset_name)
# Draw tiles
draw_tiles
@tileset = nil
end
#--------------------------------------------------------------------------
# * Valid? (Überprüft ob in Tiles für diesen Layer gefunden wurden)
#--------------------------------------------------------------------------
def valid?
# If right border greater or equal to left border and
# top border greater or equal to bottom border then true
return @border_rx >= @border_lx && @border_by >= @border_ty
end
#--------------------------------------------------------------------------
# * Row_valid? (Überprüft ob in Tiles in Spalte Y gefunden wurden)
#--------------------------------------------------------------------------
def row_valid?(y)
# If right row border is greater than left row border true
return @rows[y,1]>@rows[y,0]
end
#--------------------------------------------------------------------------
# * Setup_tiles (Liest Tiles für diesen Layer aus und bestimmt die Grenzen)
#--------------------------------------------------------------------------
def setup_tiles
# Get pre-sorted Tile data with priority of the layer
@data_link = $game_map.priority_data(@priority)
# Clone hashs, needed for functionally interation while deleting stuff
# from the data
@data = @data_link.clone
@data_link.each_pair do |dx,data_x|
dat_x = @data[dx]
dat_x = data_x.dup
data_x.each_pair do |dz,data_z|
dat_x[dz] = data_z.dup
end
end
# Iterate through sorted tiles on y-axis
@data.each_pair do |y,data_y|
# Iterate through sorted tiles on x-axis
data_y.each_pair do |x,data_x|
# Iterate through map editor layers downwards
data_x.each_pair do |z,t|
# Get Tile ID
# If tile is from tileset or autotile with 1 frame
if t >= 384 || @autotile_frames[t/48-1] <= 1
# Store in cache
@cache[y] = {} if @cache[y] == nil
@cache[y][x] = {} if @cache[y][x] == nil
@cache[y][x][z] = t
# Extend border
border(x,y)
# Delete from sorted tile list
#@data_link[y][x].delete(z)
#@data_link[y].delete(x) if @data_link[y][x].size == 0
#@data_link.delete(y) if @data_link[y].size == 0
end
end
end
# Set row borders
@rows[y,0] = @border_lx_row
@rows[y,1] = @border_rx_row
# Re-Initialize row borders for next iteration
@border_lx_row = @width/32
@border_rx_row = 0
end
end
def setup_tiles_cache
# Get pre-sorted Tile data with priority of the layer
@data_link = $game_map.priority_data(@priority)
# Clone hashs, needed for functionally interation while deleting stuff
# from the data
@data = @data_link.clone
@data_link.each_pair do |dx,data_x|
dat_x = @data[dx]
dat_x = data_x.dup
data_x.each_pair do |dz,data_z|
dat_x[dz] = data_z.dup
end
end
# Iterate through sorted tiles on y-axis
@cache.each_pair do |y,cache_y|
# Iterate through sorted tiles on x-axis
cache_y.each_pair do |x,cache_x|
# Iterate through map editor layers
border(x,y) if cache_x.size > 0
end
# Set row borders
@rows[y,0] = @border_lx_row
@rows[y,1] = @border_rx_row
# Re-Initialize row borders for next iteration
@border_lx_row = @width/32
@border_rx_row = 0
end
end
#--------------------------------------------------------------------------
# * Create_Sprites (Erzeugt Sprites & Bitmaps für jeden Zeile)
#--------------------------------------------------------------------------
def create_sprites
# Iterate from upper border to lower border
for y in @border_ty..@border_by
# If row is not valid
if !row_valid?(y)
# Skip to next
next
end
# Otherwise, calculate distance from left to right border
distx = @rows[y,1] - @rows[y,0]
# Create sprite
sprite = Sprite.new(@viewport)
# Create bitmap in row-size
sprite.bitmap = Bitmap.new(distx*32,32)
# Set x-coordinate to left border
sprite.x = @rows[y,0]*32-$game_map.display_x/4
# Set y-coordinate to row y
sprite.y = y*32-$game_map.display_y/4
sprite.blend_type = 0
# If priority equals 0
if @priority == 0
# Set Z to 0, Sprite always beyond later created
sprite.z = 0
else
# Set Z to Base + Priority + Position - Screen Position
sprite.z = 32 + 32*@priority + y*32 - $game_map.display_y/4
end
# Add sprite to hash
@sprites[y] = sprite
end
end
#--------------------------------------------------------------------------
# * Border (Erweitert die Grenzen des Layers und der aktuellen Zeile
#--------------------------------------------------------------------------
def border(x,y)
# If X is outside left border
if x < @border_lx
# Extend left border to x
@border_lx = x
end
# If X is outside right border
if x > @border_rx
# Extend right border to x+1
@border_rx = x+1
end
# If Y is outside top border
if y < @border_ty
# Extend top border to y
@border_ty = y
end
# If Y is outside bottom border
if y > @border_by
# Extend bottom_border to y
@border_by = y
end
# If X is outside left row border
if x < @border_lx_row
# Extend left row border to x
@border_lx_row = x
end
# If X is outside right row border
if x+1 > @border_rx_row
# Extend right row border to y
@border_rx_row = x+1
end
end
#--------------------------------------------------------------------------
# * Update (Aktualisiert die Position der Zeilen im Bild)
#--------------------------------------------------------------------------
def update(ox,oy)
# Get uppermost row 1 block outside of screen
y = [((oy*4-224)/128).to_i,@border_ty].max
# Get lowermost row 1 block outside of screen
y2 = [y+17,@border_by].min
# Iterate from uppermost to lowermost row
for i in y..y2
# Skip if row has no sprite
sprite = @sprites[i]
next if sprite == nil
sprite.tone = $game_map.tone
# If priority equals 0
sprite.x = @rows[i,0]*32-ox
sprite.y = i*32-oy
if @priority == 0
# Set z to 0, always beyond
sprite.z = 0
else
# Set Z to Base + Priority + Position - Screen Position
sprite.z = 32+32*@priority+i*32-oy
end
end
end
#--------------------------------------------------------------------------
# * Refresh (Erneuert die Eigenschaften aller Sprites)
#--------------------------------------------------------------------------
def refresh
# Iterate through all sprites
for y in @cache.keys
sprite = @sprites[y]
# Skip if sprite doesn't exist
next if sprite == nil
sprite.tone = $game_map.tone
# If priority equals 0
sprite.x = @rows[y,0]*32-$game_map.display_x/4
sprite.y = y*32-$game_map.display_y/4
if @priority == 0
# Set z to 0, always beyond
sprite.z = 0
else
# Set Z to Base + Priority + Position - Screen Position
sprite.z = 32+32*@priority+y*32-$game_map.display_y/4
end
end
end
#--------------------------------------------------------------------------
# * Draw_tiles (Zeichnet den kompletten Layer in die einzelnen Zeilen)
#--------------------------------------------------------------------------
def draw_tiles
# Iterate through all cached tiles on y-axis
@cache.each_pair do |y,cache_y|
# Iterate through all cached tiles on x-axis
sprite = @sprites[y]
cache_y.each_pair do |x,cache_x|
# Iterate through all cached tile layers (map-editor)
# ! Lowest z first !
cache_x.values.each do |t|
#cache_x.values.reverse.each do |t| #RGSS3
# Get tile ID from cache
# Get left border of row
lx = @rows[y,0]
# If tile is from tileset
if t >= 384
# Calculate x/y-position on tileset
t -= 384
xt = t%8
yt = t/8
# Source rect
r = Rect.new(xt*32,yt*32,32,32)
# Blt tile from tileset to row on -border+x
sprite.bitmap.blt(-lx*32+x*32,0,@tileset,r)
# Else: Tile is an autotile (with 1 frame)
else
# Calculate file id and autotile id
file = (t / 48) - 1
a = t - (file+1)*48
# Get autotile bitmap from RPG::Cache
autotile = RPG::Cache.atile($game_map.autotile_names[file],a,0)
# Blt autotile to row on -border+x
sprite.bitmap.blt(-lx*32+x*32,0,autotile,autotile.rect)
end
end
end
end
# Clear autotile cache
RPG::Cache.clear_auto
end
#--------------------------------------------------------------------------
# * Dispose (Zeilen-Sprites löschen)
#--------------------------------------------------------------------------
def dispose
# Dispose every sprite
for sprite in @sprites.values
sprite.dispose
end
end
end
#==============================================================================
# ** Tilemap_New NEW
#------------------------------------------------------------------------------
#
# Das Kernstück des Scripts, die neue Tilemap-Klasse. Besitzt im Moment
# Methoden zum Updaten und Löschen, verwaltet sich großteils selbst.
# Erzeugt bis zu 5 Layer und 6 Autotile-Layer. Layer werden abhängig von der
# Priority und dem Mapping erzeugt, Autotile-Layer je nach Eigenschaften der
# Autotiles (Speed,Frames,Blend-Mode)
#
#==============================================================================
class Tilemap_New
attr_accessor :tileset,:autotiles,:viewport
attr_reader :autotile_frames
#--------------------------------------------------------------------------
# * Initialize (Layer erzeugen)
#--------------------------------------------------------------------------
def initialize(viewport,tileset,autotiles)
# Save tileset & autotiles
@viewport = viewport
@tileset = tileset
@autotiles = autotiles
# Get number of frames per autotile
@autotile_frames = []
for autotile in @autotiles
# If autotiles height exceeds 32
if autotile.height > 32
@autotile_frames << autotile.width/96
# Else: autotiles in X*32 format
else
@autotile_frames << autotile.width/32
end
end
# Tileset ID
id = $game_map.map.tileset_id
# Temporaries
autotiles = []
done = []
autotiles_later = {}
# Class variables
@autotile_layers = []
@layers = []
@ox = 0
@oy = 0
# Iterate through autotiles
for i in 0..6
# Get frames
frames = @autotile_frames[i]
# If onty one frame => static, skip
next if frames <= 1 || done.include?(i)
# Get properties
speed = AUTOTILE::SPEED[id][i] if AUTOTILE::SPEED[id] != nil
speed = AUTOTILE::DEFAULT_SPEED if speed == nil
blend_type = AUTOTILE::BLEND_TYPE[id][i] if AUTOTILE::BLEND_TYPE[id] != nil
blend_type = 0 if blend_type == nil
priority = $game_map.priorities[(i+1)*48]
# Iterate through all autotiles >= current one
# Batches similar autotiles in the same layer -> less memody/lag
for j in i..6
# Get 2nd autotile properties
speed2 = AUTOTILE::SPEED[id][j] if AUTOTILE::SPEED[id] != nil
speed2 = AUTOTILE::DEFAULT_SPEED if speed2 == nil
frames2 = @autotile_frames[j]
blend_type2 = AUTOTILE::BLEND_TYPE[id][j] if AUTOTILE::BLEND_TYPE[id] != nil
blend_type2 = 0 if blend_type2 == nil
priority2 = $game_map.priorities[(j+1)*48]
# If properties of both autotiles match
if speed2 == speed && frames2 == frames && blend_type2 == blend_type &&
priority2 == priority
# Store autotile id
autotiles << j
# Store properties
speed3 = speed
frames3 = frames
priority3 = priority
blend_type3 = blend_type
# Autotile id is done
done << j
end
end
# If special blended autotiles
if blend_type3 >= 1
# Store autotile ids & properties for later creation (z-priority)
autotiles_later[priority3] = [] if autotiles_later[priority3] == nil
autotiles_later[priority3] << Command_Autotile.new(autotiles,speed3,@autotile_frames,priority3,blend_type3)
# Else : Normal blended tile
else
# Create autotile-Layer
@autotile_layers << Sprite_Auto.new(@viewport,autotiles,speed3,@autotile_frames,priority3,blend_type3)
# Delete if not valid
@autotile_layers.delete_at(@autotile_layers.size-1) if !@autotile_layers[@autotile_layers.size-1].valid?
end
autotiles.clear
end
# Iterate through all priority layers
for i in 0..5
# Create layer sprite
sprite = Sprite_Layer.new(@viewport,i,@autotile_frames)
# Keep sprite onty if valid?
@layers << sprite if sprite.valid?
# If blended autotile waits for creation at current priority
if autotiles_later[i] != nil
# Iterate through autotile commands
for c in autotiles_later[i]
# Create autotile-layer
@autotile_layers << Sprite_Auto.new(@viewport,c.autotiles,c.speed,c.frames,c.priority,c.blend_type)
# Delete if not valid
@autotile_layers.delete_at(@autotile_layers.size-1) if !@autotile_layers[@autotile_layers.size-1].valid?
end
# Delete command layer
autotiles_later.delete(i)
end
end
end
#--------------------------------------------------------------------------
# * Ox (Gibt Tilemap-X-Position zurück)
#--------------------------------------------------------------------------
def ox
return @ox
end
#--------------------------------------------------------------------------
# * Oy (Gibt Tilemap-Y-Position zurück)
#--------------------------------------------------------------------------
def oy
return @oy
end
#--------------------------------------------------------------------------
# * Ox= (Setzt Tilemap-X-Position)
#--------------------------------------------------------------------------
def ox=(ox)
# Return if position is equal to new position
return if @ox == ox
@ox = ox
end
#--------------------------------------------------------------------------
# * Oy= (Setzt Tilemap-Y-Position)
#--------------------------------------------------------------------------
def oy=(oy)
# Return if position is equal to new position
return if @oy == oy
@oy = oy
end
#--------------------------------------------------------------------------
# * Initialize (Position,Frames,Z-Position)
#--------------------------------------------------------------------------
def update
# Update viewport. Corrupts standard x/y positions of fog,characters,weather,
# panorama, corrected in the corresponding method
#@viewport.ox = $game_map.display_x/4
#@viewport.oy = $game_map.display_y/4
# Update autotile_layers
for autotile_layer in @autotile_layers
autotile_layer.update(@ox,@oy)
end
# Update_layers
for layer in @layers
layer.update(@ox,@oy)
end
end
#--------------------------------------------------------------------------
# * Dispose (Layer&Autotile-Layer löschen)
#--------------------------------------------------------------------------
def dispose
# Dispose autotile layers
for layer in @autotile_layers
layer.dispose
end
@autotile_layers = nil
# Dispose layers
for layer in @layers
layer.dispose
end
@layers = nil
end
#--------------------------------------------------------------------------
# * Initialize (Updated alle Reihen aller Layer)
#--------------------------------------------------------------------------
def refresh
for layer in @autotile_layers
layer.refresh
end
for layer in @layers
layer.refresh
end
end
end
#==============================================================================
# ** Command_Autotile NEW
#------------------------------------------------------------------------------
# Klasse zum Speichern von Autotile-Daten. Benutzt um Autotiles mit Blend-Mode
# 1/2 erst nach den normalen Tile-Layer zu erzeugen.
#==============================================================================
class Command_Autotile
attr_reader :autotiles,:speed,:frames,:blend_type,:priority
#--------------------------------------------------------------------------
# * Initialize
#--------------------------------------------------------------------------
def initialize(autotiles,speed,frames,priority,blend_type)
@autotiles = autotiles.clone #Autotile list
@speed = speed #Animation speed
@frames = frames #Nr. of frames
@blend_type = blend_type #Blend type
@priority = priority #Priority
end
end
#==============================================================================
# ** Game_Temp EDIT
#------------------------------------------------------------------------------
# Erweiterung zum Speichern der Tilemap beim Szenenwechseln.
#==============================================================================
class Game_Temp
attr_accessor :store_tilemap
attr_accessor :tilemap
#--------------------------------------------------------------------------
# * Alias Initialize EDIT (Flag & Slot zum temporären Speichern der Tilemap)
#--------------------------------------------------------------------------
alias init_later initialize
def initialize
init_later
@store_tilemap = false # Storage flag
@tilemap = nil # Storage slot
end
end
#==============================================================================
# ** Spriteset_Map EDIT
#------------------------------------------------------------------------------
# Erweitert die Klasse Spriteset_Map. Erzeugt ein Objekt der Klasse
# Tilemap new. Überschreibt die Methoden Initialize, Dispose und Update
#
#==============================================================================
class Spriteset_Map
attr_reader :tilemap
attr_reader :viewport1
#--------------------------------------------------------------------------
# * Initialize EDIT (neue Tilemap erzeugen/laden,funktioniert)
#--------------------------------------------------------------------------
def initialize
@viewport2 = Viewport.new(0, 0, 640, 480)
@viewport3 = Viewport.new(0, 0, 640, 480)
@viewport2.z = 200
@viewport3.z = 5000
# If no tilemap is stored
if $game_temp.tilemap == nil
# Make Viewport1
@viewport1 = Viewport.new(0, 0, 640, 480)
autotiles = []
for i in 0..6
autotile_name = $game_map.autotile_names[i]
autotiles << RPG::Cache.autotile(autotile_name)
end
tileset = RPG::Cache.tileset($game_map.tileset_name)
# Make Tilemap
@tilemap = Tilemap_New.new(@viewport1,tileset,autotiles)
# Else: Tilemap is stored
else
# Load Tilemap & Viewport
@tilemap = $game_temp.tilemap
@viewport1 = @tilemap.viewport
# Remove tilemap from storage
$game_temp.tilemap = nil
end
@panorama = Plane.new(@viewport1)
@panorama.z = -1000
@fog = Plane.new(@viewport1)
@fog.z = 3000
@character_sprites = []
for i in $game_map.events.keys.sort
sprite = Sprite_Character.new(@viewport1, $game_map.events[i])
@character_sprites.push(sprite)
end
@character_sprites.push(Sprite_Character.new(@viewport1, $game_player))
@weather = RPG::Weather.new(@viewport1)
@picture_sprites = []
for i in 1..50
@picture_sprites.push(Sprite_Picture.new(@viewport2,
$game_screen.pictures[i]))
end
@timer_sprite = Sprite_Timer.new
update
end
#--------------------------------------------------------------------------
# * Dispose EDIT (Tilemap speichern wenn nötig, sonst dispose)
#--------------------------------------------------------------------------
def dispose
@tilemap.tileset.dispose
for i in 0..6
@tilemap.autotiles[i].dispose
end
# If tilemap should be stored
if $game_temp.store_tilemap == true
# Store tilemap
$game_temp.tilemap = @tilemap
# Disable storage flag
$game_temp.store_tilemap = false
# Else
else
# Dispose tilemap & viewport
@tilemap.dispose
@viewport1.dispose
end
@panorama.dispose
@fog.dispose
for sprite in @character_sprites
sprite.dispose
end
@weather.dispose
for sprite in @picture_sprites
sprite.dispose
end
# Dispose of viewports except 1
@viewport2.dispose
@viewport3.dispose
end
end
#==============================================================================
# ** Game_Map EDIT
#------------------------------------------------------------------------------
# Erweitert die Klasse Game_Map um Attribute und Methoden zum Vorsortieren von
# Tiles nach Priority, außerdem für den Tilemap-Tone.
#==============================================================================
class Game_Map
attr_reader :map #Map-Daten
attr_reader :tone #Color-Tone
#--------------------------------------------------------------------------
# * Alias initialize (Variablen für Tilemap-Tone)
#--------------------------------------------------------------------------
alias init_later initialize
def initialize
init_later
@tone = Tone.new(0,0,0,0) # Tilemap-tone
@tone_duration = 0 # Tone-duration
end
alias setup_later setup
def setup(map_id)
setup_later(map_id)
# Sort tiles after setup
sort
end
#--------------------------------------------------------------------------
# * Sort (sortiert Tile-IDs nach Priority zum späteren Auslesen
#--------------------------------------------------------------------------
def sort
# One hash for every priority
@priority0 = {}
@priority1 = {}
@priority2 = {}
@priority3 = {}
@priority4 = {}
@priority5 = {}
# Iterate through tiles on x-axis
for x in 0...width
# Iterate through tiles on y-axis
for y in 0...height
# Check layers from map editor top->bottom
for z in [2,1,0]
# Tile ID
tile = data[x,y,z]
# If tile is not empty
if tile != 0
# Get & check priority for tile, set in according hash
case @priorities[tile]
when 0
@priority0[y] = {} if @priority0[y] == nil
@priority0[y][x] = {} if @priority0[y][x] == nil
@priority0[y][x][z] = tile
when 1
@priority1[y] = {} if @priority1[y] == nil
@priority1[y][x] = {} if @priority1[y][x] == nil
@priority1[y][x][z] = tile
when 2
@priority2[y] = {} if @priority2[y] == nil
@priority2[y][x] = {} if @priority2[y][x] == nil
@priority2[y][x][z] = tile
when 3
@priority3[y] = {} if @priority3[y] == nil
@priority3[y][x] = {} if @priority3[y][x] == nil
@priority3[y][x][z] = tile
when 4
@priority4[y] = {} if @priority4[y] == nil
@priority4[y][x] = {} if @priority4[y][x] == nil
@priority4[y][x][z] = tile
when 5
@priority5[y] = {} if @priority5[y] == nil
@priority5[y][x] = {} if @priority5[y][x] == nil
@priority5[y][x][z] = tile
end
end
end
end
end
end
#--------------------------------------------------------------------------
# * Priority-Data (gibt die vorher sortierten Tiles zurück)
#--------------------------------------------------------------------------
def priority_data(priority)
# Check priority and return appropriate sorted hash
case priority
when 0
return @priority0
when 1
return @priority1
when 2
return @priority2
when 3
return @priority3
when 4
return @priority4
when 5
return @priority5
else
# Invalid priority: return hash 0
return @priority0
end
end
#--------------------------------------------------------------------------
# * Start tone change (wie in Game_Screen, aber für die Tilemap!)
#--------------------------------------------------------------------------
def start_tone_change(tone, duration)
@tone_target = tone.clone
@tone_duration = duration
if @tone_duration == 0
@tone = @tone_target.clone
end
end
alias upd_later update
#--------------------------------------------------------------------------
# * Alias update (führt den Tone-Wechsel aus)
#--------------------------------------------------------------------------
def update
if @tone_duration >= 1
d = @tone_duration
@tone.red = (@tone.red * (d - 1) + @tone_target.red) / d
@tone.green = (@tone.green * (d - 1) + @tone_target.green) / d
@tone.blue = (@tone.blue * (d - 1) + @tone_target.blue) / d
@tone.gray = (@tone.gray * (d - 1) + @tone_target.gray) / d
@tone_duration -= 1
end
upd_later
end
end
#==============================================================================
# ** Scene_Map EDIT
#------------------------------------------------------------------------------
# Überschreibt die Main-Methode, um das Spriteset im Fall eines Szenewechsels
# zu speichern.
#==============================================================================
class Scene_Map
#--------------------------------------------------------------------------
# * Main overwrite (setzt die store-tileset-flag im Fall eines Szenenwechsels)
#--------------------------------------------------------------------------
def main
@spriteset = Spriteset_Map.new
@message_window = Window_Message.new
Graphics.transition
loop do
Graphics.update
Input.update
update
if $scene != self
break
end
end
Graphics.freeze
# Set store tilemap flag to true
$game_temp.store_tilemap = true
@spriteset.dispose
@message_window.dispose
if $scene.is_a?(Scene_Title)
Graphics.transition
Graphics.freeze
end
end
#--------------------------------------------------------------------------
# * Alias transfer_player (sorgt für fehlerfreien Teleport auf derselben Map)
#--------------------------------------------------------------------------
alias transfer_later transfer_player
def transfer_player
# If old and new_map are the same
if $game_map.map_id == $game_temp.player_new_map_id
# Set store tilemap flag
$game_temp.store_tilemap = true
end
transfer_later
# Refresh tilemap
@spriteset.tilemap.refresh
end
end И ещё было бы здорово, если бы можно было как-то менять размер самих тайлов, например, вдвое. О таком лучше не думать, если ты только не хочешь убить себе мэйкер. |
Огромный любитель среброволосых или пепельноволосых 2D-девушек с хорошим характером или со скрытыми привлекательными чертами.
Последнее редактирование: 11 года 6 мес. назад от Agckuu_Coceg.
Администратор запретил публиковать записи гостям.
|
Re: Урок по созданию безграничного пространства 11 года 3 мес. назад #63657
|
|
Последнее редактирование: 11 года 3 мес. назад от DeadElf79.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: strelokhalfer
|
Re: Урок по созданию безграничного пространства 11 года 3 мес. назад #63680
|
Тут Джеймс просил возможность изменять размер тайлов... Как ни странно, но подобное существует. Только одна проблема - он не увеличивает размеры тайла, а лишь уменьшает. Но если желаете...
|
Огромный любитель среброволосых или пепельноволосых 2D-девушек с хорошим характером или со скрытыми привлекательными чертами.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: DeadElf79
|
Урок по созданию безграничного пространства 10 года 7 мес. назад #68679
|
DeadElf79 пишет:
Во-первых, размер экрана, используемого игрой был уменьшен компанией Enterbrain (негодяи какие). Поэтому и размеры карты по умолчанию не 20х15 тайлов, а 17х13. Теперь справа и слева можно оставлять по 9 тайлов, а сверху и снизу — 7. По 8 и 6 соответственно. |
Администратор запретил публиковать записи гостям.
|
Время создания страницы: 0.409 секунд