Пример простейшего визуального редактора
Если вам надоело писать код, чтобы построить уровень для мини-игры, то вам захочется сделать себе визуальную среду, перетаскивать объекты по экрану и сохранять результаты в файл. Здесь вы увидите пример и сможете на его основе создать собственные редакторы уровней, персонажей, чего угодно. Чтобы не заморачиваться с форматами для сохранения данных, я создал модуль, который записывает любые классы, их списки, словари или просто переменные в виде кода на питоне в файл с расширением RPY. Нужно указать имя файла (можно без расширения), имя функции, которая будет отвечать за "загрузку", ее "уровень" (порядок выполнения блока init) и через запятую имена всех переменных, списков и т.д., которые нужно сохранить, то есть превратить в ренпаевский код.
# в папку game проекта нужно закинуть файл rpy_save.rpy
# он отвечает за сохранения любых данных (в виде кода на питоне)
# уровень этого блока должен быть ниже уровня сохраняемого
init python:
# создадим для примера какой-нибудь класс для объектов на экране
# у него может быть любая структура, параметры, функции
# классов может быть несколько
class TUnit:
# имена параметров на входе должны совпадать с именами параметров класса!
def __init__(self, id, sprite=None, x=0, y=0, flip=False):
# уникальный идентификатор объекта
self.id = id
# его изображение (объекты могут выглядеть одинаково)
if not sprite:
sprite = id
self.sprite = sprite
# координаты объекта
self.x = x
self.y = y
# отзеркаливание
self.flip = flip
# функция возвращает зум объекта по горизонтали
def xzoom(self):
if self.flip:
return -1.0
return 1.0
# перечень всех объектов на экране
units = []
# если флаг установлен, то режим редактора, а не игры
edit_mode = False
# выбранный юнит
current_unit = None
# список доступных спрайтов
sprites_list = ["guy", "kolonna", "lenin", "bg"]
# последний выбранный спрайт
last_sprite = sprites_list[0]
# функция возвращает объект с нужным id
def get_unit(id):
# перебираем все юниты в поисках нужного
for i in units:
if i.id == id:
return i
return None
# функция для перетаскивания объекта мышкой
def draggetxy(drags, drop):
global units, current_unit
# сбрасываем старые координаты объекта
drags[0].oldposition = None
# получаем доступ к нужному
i = get_unit(drags[0].drag_name)
# если он существует, то
if i:
# меняем координаты объекта на текущие
i.x, i.y = drags[0].x, drags[0].y
# выбираем текущий объект
current_unit = i
# перерисовываем экран
renpy.restart_interaction()
# сохраняем уровень и закрываем программу,
# чтобы при следующем запуске ренпай скомпилировал
# сохраненный код на питоне в файл типа *.rpyc
def save_quit():
# сохраняем в файл TEST_DATA_SAVE.rpy
# сгенерированный блок test_data_init
# с уровнем 1 (init 1 python:)
# через запятую указать имена всех переменных,
# объектов, списков, кортежей и т.д.,
# текущее состояние которых нужно запомнить
# в данном случае только список объектов на экране
# и список доступных спрайтов
rpy_save("TEST_DATA_SAVE", "test_data_init", 1, "units", "sprites_list")
# закрываем программу
Quit(confirm=False)()
# создаем action для кнопки редактора
SaveQuit = renpy.curry(save_quit)
# создать новый уникальный id
def new_id():
i = 0
name = None
# перебираем номера id, пока не попадется еще несуществующий
while not name:
if get_unit("id" + str(i)):
i += 1
else:
name = "id" + str(i)
return name
# добавление нового объекта на экран
def add_unit():
global units, current_unit
current_unit = TUnit("id", last_sprite)
current_unit.id = new_id()
# добавляем созданный объект в список
units.append(current_unit)
renpy.restart_interaction()
AddUnit = renpy.curry(add_unit)
# удаление объекта с экрана
def del_unit():
global units, current_unit
if current_unit:
units.remove(current_unit)
current_unit = None
renpy.restart_interaction()
DelUnit = renpy.curry(del_unit)
# смена слоя объекта
def up_unit():
global units, current_unit
if current_unit:
oldindex = units.index(current_unit)
newindex = oldindex + 1
if newindex >= len(units):
newindex = 0
units.insert(newindex, units.pop(oldindex))
renpy.restart_interaction()
UpUnit = renpy.curry(up_unit)
# отзеркаливание объекта
def flip_unit():
global units, current_unit
if current_unit:
current_unit.flip = not current_unit.flip
renpy.restart_interaction()
FlipUnit = renpy.curry(flip_unit)
# смена изображения объекта
def spr_unit():
global units, current_unit, last_sprite
if current_unit:
index = sprites_list.index(current_unit.sprite) + 1
if index >= len(sprites_list):
index = 0
current_unit.sprite = sprites_list[index]
last_sprite = sprites_list[index]
renpy.restart_interaction()
SprUnit = renpy.curry(spr_unit)
# экран редактора (он же экран игры)
screen game:
modal edit_mode
# в режиме игры просто выводим объекты на экран
if not edit_mode:
for i in units:
add i.sprite pos (i.x, i.y)
# в режиме редактора позволяем перемещать и менять объект
else:
for i in units:
drag:
# имя объекта для функции смены координат
drag_name i.id
# координаты объекта
pos (int(i.x), int(i.y))
# сам объект
button:
# кнопка без фона и отступов
background None
xpadding 0 ypadding 0
xmargin 0 ymargin 0
# изображение объекта
add i.sprite xzoom i.xzoom()
# чтобы можно было перетаскивать, отключаем действия
action []
# чтобы перетаскивать только за рисунок
focus_mask True
# что делать при перетаскивании (функция выше)
dragged draggetxy
# выбор предмета кликом, а не перетаскиванием
clicked SetVariable("current_unit", i)
if edit_mode:
# меню редактора
frame:
background "#0128"
align (1.0, 1.0)
vbox:
textbutton _("Добавить объект") action AddUnit()
textbutton _("Сменить спрайт") action [SensitiveIf(current_unit), SprUnit()]
textbutton _("Поменять слой") action [SensitiveIf(current_unit), UpUnit()]
textbutton _("Отзеркалить объект") action [SensitiveIf(current_unit), FlipUnit()]
textbutton _("Удалить объект") action [SensitiveIf(current_unit), DelUnit()]
textbutton _("Выйти") action Return()
textbutton _("Сохранить и выйти") action SaveQuit()
# параметры спрайта
if current_unit:
frame:
background "#0128"
align (0.0, 1.0)
vbox:
text "id: " + str(current_unit.id)
text "sprite: " + str(current_unit.sprite)
text "x, y: " + str(current_unit.x) + ", " + str(current_unit.y)
text "flip: " + str(current_unit.flip)
text "order: " + str(units.index(current_unit))
# выбираем режим
label start:
$ quick_menu = False
menu:
"Режим редактора":
call edit_mode
"Режим игры":
call game_mode
$ quick_menu = True
return
# показываем экран в игровом режиме
label game_mode:
# если сохранения есть, то запускаем загрузку из них
if "test_data_init" in globals().keys():
$ test_data_init()
$ edit_mode = False
show screen game
"Показываем результат..."
return
# показываем экран в режиме редактора
label edit_mode:
# если сохранения есть, то запускаем загрузку из них
if "test_data_init" in globals().keys():
$ test_data_init()
$ edit_mode = True
$ current_unit = None
$ last_sprite = sprites_list[0]
call screen game
return
- Ссылка на архив с проектом: save_data_test.zip
- Источник: Renpy для чайников: Пример простейшего визуального редактора
На главную страницу | Книга рецептов Энциклопедии Ren'Py | Renpy для чайников | Вернуться к началу статьи |