-
caveman
-
-
Вне сайта
-
Архитектор Миров
-
- Сообщений: 1274
- Спасибо получено: 1307
-
-
|
Очередное колесо, изобретенное для игры Imprizoned.
Тутор очень большой, зато с картинками!
ВНЕЗАПНО понадобилось сделать карту подземелья, по которому бегает герой. Хотелось следующие вещи:
1) Карта динамически обновляется с открытием героем новых комнат.
2) По карте возможно бегать, просматривая описания комнат.
3) Вызывать увеличенную картинку комнаты. В итоге стало лень рисовать, и эту возможность отбросил, но краткое описание, как сделать, тут будет.
4) Открывать карту по какой-нибудь кнопке прямо из игры
Итак, нам понадобятся:
Ресурсы:
- Холст - берем подложку с изображением пергамента, размером с экран
- Мини картинки для глобальной карты. Для каждой мапы - своя картинка. Проще всего каждую карту рисовать в фотошопе или пейнтнете как новый слой на холсте. Это прозрачная картинка - контур комнаты с какими-то пометками. Потом, чтобы экономить место и память игры, картинки обрежем по их контуру.
- Одна картинка - шаблон подсветки комнаты. Это просто полупрозрачный прямоугольник, впрочем, можно сделать, как душе угодно. Как вариант (в случае комнат на глобальной карте непростых форм - делаем подсветок столько же, сколько комнат). Вариант проще - картинку не надо, а просто заливаем прямоугольник полупрозрачным белым цветом.
- Картинка светящейся точки - положения героя на глобальной карте.
- Туча увеличенных карт (отдельная комната) - по желанию. Долго делать, но зато красиво.
Классы:
1) Нам необходимо добавить class Map, содержащий:
- Id - это Id карты в мейкере.
- Название - для печати названия выбранной карты на глобальной.
- Положение верхнего левого угла карты на глобальной (размер берем из файла); x, y.
- Положение отметки игрока; x, y.
- 4 значения - Id карты, соседней с этой. Нужно для управления крестовиной на глобальной карте, для перехода на соседнюю карту. -1, если нет соседней карты.
- Название части карты и увеличенной карты (если не принята какая-нибудь нотация относительно Id)
Пример:
Камеры
ID: 2
Name: Тюремные камеры
a) 430, 28 - верхний левый угол
b) 448, 50 - позиция отметки о герое
c) -1, -1, -1, 1 - id карт слева, справа, сверху и снизу соответственно
Код класса Map:
class Map
#--------------------------------------------------------------------------
attr_reader :id
attr_reader :name
attr_reader :x
attr_reader :y
attr_reader :plx
attr_reader :ply
attr_reader :left
attr_reader :right
attr_reader :up
attr_reader :down
def initialize(id, name, x, y, plx, ply, l, r, u, d)
@id = id
@name = name
@x = x
@y = y
@plx = plx
@ply = ply
@left = l
@right = r
@up = u
@down = d
end
end
2) В Game_Party нужно добавить список, который будет содержать открытые карты и добавить методы по добавлению новой карты, и вспомогательные методы. Под спойлером код с комментариями.
class Game_Party # просто добавлен как доп partial файл
# дописываем в основной класс моменты, связанные с картой
attr_reader :maps # тип Map
def initialize
...
@maps = []
end
# реализуем методы для работы с картой
# добавить карту (этот метод запускаем в autorun событии каждой карты с нужными значениями)
def addmap(id, name, x, y, plx, ply, l, r, u, d)
map = Map.new(id, name, x, y, plx, ply, l, r, u, d)
@maps.push(map)
end
def clearmaps
@maps = []
end
def showmap
# в классе Game_Temp я завел две переменные, тут их проставляю
$game_temp.current_map = $game_map.map_id # текущая выделенная карта (изначально - та, где сейчас герой)
$game_temp.map_showing = true # показать карту
end
def hidemap
$game_temp.map_showing = false # спрятать карту
end
# этот метод принимает значение крестовины (вниз == 2, влево == 4, вправо == 6, вверх == 8 - это стандартные значения BUTTON INPUT в XP) и определяет карту, которую надо подсветить.
def movemap(dir)
for i in 0...@maps.size
if @maps[i].id == $game_temp.current_map
newid = -1
if dir == 4
newid = @maps[i].left
end
if dir == 6
newid = @maps[i].right
end
if dir == 8
newid = @maps[i].up
end
if dir == 2
newid = @maps[i].down
end
if newid != -1
for i in 0...@maps.size
if @maps[i].id == newid
$game_temp.current_map = newid
end
end
end
return
end
end
end
end
Вот так мы из события авторана, при входе на карту, добавляем её в список (не забудьте поставить условие на какой-нибудь Self Switch, чтобы добавить только один раз).
$game_party.addmap(2, 'Тюремные камеры', 430, 28, 448, 50, -1, -1, -1, 1)
Осталась, собственно, отрисовка.
Рисуем в классе Spriteset_Map, как в примере из поста про картинки интерфейса. Я вынес все в отдельный файл Spriteset_Map2:
class Spriteset_Map
# в первой части класса в initialize заводим поверхности для рисования
# снова viewport3, чтобы находилось выше всего
# подложка
@map_surface = Plane.new(@viewport3)
@map_surface.z = 5001
# карта
@maps = Plane.new(@viewport3)
@maps.z = 5002
# тут инициализируем битмапы. Я сначала умудрился делать это в update и словил переполнение памяти через пару минут игры :)
@map_surface.bitmap = Bitmap.new(640, 480)
@maps.bitmap = Bitmap.new(640, 480)
# в методе dispose не забываем удалять поверхности
@map_surface.dispose
@maps.dispose
# наконец, в update, после тумана, я рисую
# очищаем битмапы, иначе при выходе из режима карты у нас могут остаться левые следы
@map_surface.bitmap.clear
@maps.bitmap.clear
# рисуем, если включен режим показа карты (вызван $game_party.showmap)
if $game_temp.map_showing
show_map
end
# в отдельном файле реализация отрисовки
def show_map
# рисуем подложку (в моем случае - пожелтевший пергамент)
map = RPG::Cache.picture("mapsurface")
@map_surface.bitmap.blt(0, 0, map, Rect.new(0, 0, map.width, map.height))
for i in 0..$game_party.maps.size-1
# бежим по всем открытым картам и рисуем. Координаты мы записали заранее для каждой карты
mmap = $game_party.maps[i]
mmappic = RPG::Cache.picture('map' + mmap.id.to_s)
@map_surface.bitmap.blt(mmap.x, mmap.y, mmappic, Rect.new(0, 0, mmappic.width, mmappic.height))
# теперь подсвечиваем выделенную карту и пишем её описание
if $game_temp.current_map == mmap.id
@maps.bitmap.fill_rect(mmap.x+2, mmap.y+2, mmappic.width-4, mmappic.height-4,
Color.new(255, 255, 255, 128))
@map_surface.bitmap.font.size = 24
@map_surface.bitmap.font.color = Color.new(89, 64, 49, 255)
w = @map_surface.bitmap.text_size(mmap.name).width
x = 320 - w / 2 # для центрирования
@map_surface.bitmap.draw_text(x, 4, 400, 24, mmap.name, 0)
end
# а на текущей карте рисуем отметку, что мы находимся здесь
if $game_map.map_id == mmap.id
mhero = RPG::Cache.picture('mhero') # тут у меня фигнюшка, утыренная с какого-то виндовскина
w = mhero.width
h = mhero.height
# положение отметки мы тоже заблаговременно завели в классе карты
@maps.bitmap.blt(mmap.plx - w / 2, mmap.ply - h / 2, mhero, Rect.new(0, 0, w, h))
end
end
end
end
Теперь в игре нужно создать событие просмотра карты, подвесив его на кнопку. В Common Events по свичу добавить отлов нажатия кнопки; свич инициализировать либо в начале игры, либо при наступлении какого-то события, например, герой нашел бумагу и перо, и теперь может рисовать карту. Проще показать картинками:
Тут я создал параллельное общее событие (зависимость поставлена от switch, который я активирую, когда герой находит письменные принадлежности). В событии просто проверяется нажатие кнопки W (в стандартной раскладке; так то это RShift) и дергаем скрипт $game_party.showmap, сопровождая это звуком Book1. Флаг ожидания я убираю, чтобы не мешал в режиме карты (на деле лучше завести еще один), но включаю еще один флаг - для обработки действий игрока на карте. Заодно, от греха подальше, убираю доступ к меню.
Сюда мы попадаем (см. флаг-условие), когда нам показывают карту. Тут тоже обрабатываем нажатия кнопок: по W или X (назад, или B на джойстике) прячем карту, возвращаясь в игру и включаем первый флаг для ожидания нового нажатия вызова карты. На кнопки направления реагируем, двигаясь по карте при помощи вызова метода movemap(dir).
Остался один момент. Так как я вызываю карту не из меню предметов, а по кнопке, то при навигации по карте, герой будет бегать по реальной карте, а может и нарваться на драку или поболтать с кем-то... Решение проблемы у меня довольно варварское, но с ходу ничего лучше не придумал.
В начало метода update класса Scene_Map добавляем следующий код:
if $game_temp.map_showing
@spriteset.update
$game_map.update
$game_system.map_interpreter.update
$game_system.update
$game_screen.update
return
end
тут почти все, что в цикле далее, но убран апдейт героя, так что ходить он не будет, а реакция на кнопки управления и события осталась.
Все, собираем, запускаем, отлавливаем баги и тестируем карту.
Вот как это выглядит в игре (желтая штука - отметка, где находится герой, светленьким выделяется выбранная кнопками направления комната, её название пишется сверху). Художник из меня не очень, но при должной фантазии и прямых руках можно сотворить отличную карту!
Если хочется рисовать еще и доп карты для отдельных помещений, то, разобравшись с этим кодом, думаю, что сложностей не будет.
Вкратце:
Нужна будет еще одна поверхность выше карты, как будто мы держим карту отдельного помещения над основной. Нужно еще одно общее событие, которое активизируется из второго, например, по кнопке "пробел" (C на джойстике). Нужно дописать метод show_map ,чтобы по новому флагу из $game_temp рисовать увеличенную карту. Карты складываем в папку Picture, придумываем нотацию названия, например mapbigX.png и дергаем картинку
RPG::Cache.picture('mapbig' +$game_temp.current_map.to_s) Не забываем в новое общее событие дописать возврат на обычную карту по кнопке B
Демку создавать было лениво, так что лучше я выложу пример на реальном проекте, заодно можно увидеть наработки по изменениям в Imprizoned. Готово пока 8 карт, прохождение до момента подрыва двери. Карты открываются по кнопке W, после того, как в первой же камере герой подберет бумаги и перо с пола.
Ссылка на проект: rusfolder.com/35168717
И бонус - постоянно теряемый и находимый в гугле список кодов кнопок!
2 - down
4 - left
6 - right
8 - up
11 - A (Shift)
12 - B (Esc, Num 0, X)
13 - C (Space, Enter, Z)
14 - X (A)
15 - Y (Y)
16 - Z (Z)
17 - L (L)
18 - R (W)
|