Оптимальный базис для разработки новеллы на Ren'Py
Закончился Anivisual Jam #1, и каждый из нас подвёл для себя свои итоги. Все мы заметили, что среди сотни с лишним новелл слишком много недельных поделок уровня... недельных поделок. Подавляющее большинство новелл было сделано в Ren'Py, и несмотря на то, что движок в некоей степени адаптирован под людей, незнакомых с программированием, он всё равно не предоставляет достаточно "материала по умолчанию", чтобы можно было удовлетворить читателя. Да это просто по той причине, что такие вещи должны быть сделаны в индивидуальном порядке – движок не читает мысли разработчика и не может сразу скомпилировать всё необходимое. Поэтому "достраивать" базис необходимо самому.
Почему "базис"? Я лично прочитал не очень много новелл с Джема, но я изначально целился на те, которые наверняка были бы обделены вниманием. После прохождения новеллы я оставлял развёрнутый отзыв, в котором очень часто давал какие-нибудь советы по скрипту новеллы. В этой статье собраны все подобные советы и дополнены ещё некоторыми простыми вещами, которые помогут вам в короткие сроки превратить "голый" скрипт новеллы в что-то отличающееся от той самой "недельной поделки"

В статье предполагается, что потенциальный читатель уже способен скомпилировать первый проект и знаком со стандартным кодом, который генерируется для новеллы. Сделать первые шаги в разработке вам поможет идущее в комплекте с движком Ren'Py обучение от Тома Ротамеля и документация движка.



Работаем.


Первый этап. Изменяем существующий скрипт.
При чтении новелл с Джема я заметил, что многие даже не сделали простейших вещей, которые делают проект более достойным в глазах игрока. Это при том, что необходимые для этого инструменты (строчки кода) уже есть в генерируемом движком материале – достаточно просто изменить некоторые значения/заменить файлы своими.
1. По иконке встречают
И об этом даже не один раз упоминали читатели в комментариях ко многим новеллам с Джема. По своему опыту хотел бы добавить, что стоит даже быть благодарным, что Том "renpytom" вообще позволяет изменить иконку для вашей игры, будь она коммерческим или некоммерческим проектом. RPGMaker, например, не разрешает менять иконку для своих игр, если это коммерческий проект, это будет нарушение прав разработчика движка. Бывают и такие абсурдные исключения. Так не упускайте свою возможность – это очень легко!
[Предупреждение по дальнейшему коду: проверяйте табуляцию при копировании.]
Код

#options.rpy
define config.window_icon = "gui/window_icon.png" #где "gui/window_icon.png" – путь к вашей иконке для панели задач в формате png

Комментарии к коду идут после "#", они игнорируются движком при работе скрипта и не важны для функционирования. В целом я рекомендую убирать комментарии, которые генерируются в скрипте по умолчанию, как только вы поняли каким образом работает код. Так в дальнейшем будет проще ориентироваться в коде.
Также помните, что все конфигурационные переменные (Configuration Variables в документации) нужно помещать в options.rpy, начиная строчку с "define". Те, что содержат "gui" – помещайте в "gui.rpy"
Итак, файла нам нужно два, а не только один .png, для которого я, например, использую разрешение 250х250, сохраняя достаточное качество изображения и балансируя с его размером. Также вам понадобится файл .ico разрешения 256х256 (оба условия обязательные), который нужно поместить не в папку "game" вашего проекта, а в корневую папку проекта, которая содержит эту самую папку "game". Движок Ren'Py сам обнаружит .ico файл – необходимый скрипт уже в нём прописан. К слову об этом, скрипт самого движка также определённым образом помещается в новеллу при построении дистрибутива, а как в этом найти пользу для себя – чуть дальше.

2. По громкости тоже встречают
Тоже немаловажная деталь. У всех людей звук на компьютере настроен по-своему, это также зависит от используемой гарнитуры. Цель разработчика – угадать не оптимальную максимальную громкость, а оптимальную минимальную. Издеваться над ушами игроков точно не стоит, а если всё-таки громкости не хватит, то нужно позволить игроку увеличить её в настройках. И если по стандарту движок создаёт окно настройки, то ползунки для громкости он выставляет сразу на максимум, создавая риск дискомфорта для игрока.
Парой строчек надо просто задать громкость по умолчанию.
Код

#options.rpy

define config.default_music_volume = 0.5 #где 0.5 = 50% громкости
define config.default_sfx_volume = 0.5

Возможно, что 50% будет слишком мало или даже слишком много. У меня при тестировании игроками статистически оптимальные значения получались следующими: 60% для музыки и 40% для звуков (0.6 и 0.4)
О разнице между числовыми "int" (целочисленные) и "float" (дробные) значениями, где и как они должны применяться, можете узнать в обучении, которое идёт к движку. Разделы "позиционирование" и "ATL".

3. Курсор
Буквально это длань игрока, которой он вершит судьбу персонажей, если у нас не кинетическая новелла. Ну или как минимум управляет ходом игры, так что значение этому моменту нужно придавать соответствующее. В целом, такими мелочами (если их много) вы обязательно создадите положительное впечатление о новелле.
Код

init python: #инициализация, чтобы скрипт применялся сразу при запуске игры
  config.mouse = { }
  config.mouse["default"] = [ #существует не только курсор типа "defualt", но этого типа хватает в большинстве случаев. Подробнее в документации движка
  ("gui/cursor.png", 8, 8), #где первое значение – путь к .png курсора (я использую разрешение не больше 50х50), второе значение – смещение "действующего" пикселя курсора по оси ОХ, а третье – по оси OY. Смещение необходимо, если ваш курсор не соприкасается "впритык" к левой и верхней границам изображения, например, если у него есть полупрозрачное пр-во
  ]
Дополнение к комментарию: помните, что начало координат в Ren'Py находится в левом верхнем углу любого изображения при позиционировании и идёт по диагонали к нижнему правому углу, если вы не задавали другого значения с помощью xanchor или yanchor.
Эти строчки надо поместить в файле gui.rpy между
Код
init offset = -2
и
Код
init python:
  gui.init(1920, 1080)
которые создаются в этом файле вместе с проектом.

4. Голоса в твоей голове
В немалой части новелл в настройках есть ползунок громкости голосов. Которых в новелле нет. Вообще.
Эта настройка актуальна только в том случае, если вы используете для соответствующего звукового канала какие-либо звуки. В случае голосов – это озвучка. Если озвучки нет, то просто измените следующую строчку в options.rpy
Код

define config.has_voice = False #по умолчанию здесь True

И в настройках исчезнет ненужный ползунок.

5. Интерфейс
Конечно же, более чем в половине новелл на Джеме вообще никак не был изменён стандартный интерфейс. Чтобы переписать его с нуля, нужно небольшое понимание языка Ren'Py. Но если вам не хочется в это вникать, то возможен более художественный подход: изучите поведение интерфейса и перерисуйте его, включите в себе дизайнера, если не хотите включать кодера. Обратите внимание на то, как работают стандартные game_menu и main_menu, чтобы правильно перерисовать их текстуры. Помните, что Frame использует паддинг, то есть, свою текстуру он растягивает произвольно – как прописано в коде (то же самое для ползунков). Соответственно, я не рекомендую использовать для frame.png неоднородные текстуры, которые при растягивании выглядят неадекватно. Либо исключите паддинг в стилях фрейма для всех окон в screens.rpy, либо адаптируйте вашу текстуру под паддинг.
В общем, стандартный интерфейс пусть и выглядит минималистичным и приятным глазу, но хоть как-то модифицировать его явно стоит, чтобы новелла не была похожа на проходняк. В конце концов, по обложке не судят, но по одёжке встречают?

6. _game_menu_screen
Или просто тот раздел меню, который появляется при нажатии Esc во время игры. Рекомендую ставить следующее:
Код

_game_menu_screen = "preferences"

Под init python в script.rpy. Игрок чаще всего нажимает на паузу именно для того, чтобы перейти в раздел настроек, а по умолчанию открывается раздел сохранений. Казалось бы, разница в один клик, но на самом деле одна строчка делает процесс намного удобнее для игрока.

7. В прошлое и в будущее
Во время чтения игрок может захотеть промотать историю чутка назад или вперёд. Он тут же обнаружит, что для продвижения назад по истории нужно крутить колёсико вверх, а для продвижения вперёд – вниз. Кто-то уже, возможно, привык, но мои тестеры однажды строго приметили, что такое управление для них интуитивно непривычное. Восприятие в новелле – важная вещь, и нельзя это восприятие чем-то портить.
В начале статьи я отмечал, что движок "встраивается" в новеллу при её компиляции в архив. Дело в том, что стандартное управление для всех новелл содержится не в генерируемом коде, а в скрипте движка. То есть, нам нужны файлы самого движка. Помните, что когда вы меняете что-либо в движке Ren'Py, это применяется ко всем создаваемым и существующим проектам, которые не скомпилированы.
Ваш путь следующий: "...renpy-ваша_версия-sdk\renpy\common\00keymap.rpy"
Там ищите следующие строчки и пишите эти значения:
Код

rollback = [ 'K_PAGEUP', 'repeat_K_PAGEUP', 'K_AC_BACK', 'mousedown_5' ],
...
rollforward = [ 'mousedown_4', 'K_PAGEDOWN', 'repeat_K_PAGEDOWN' ],

Здесь же можно позаботиться об ещё одной детали: по стандарту в новеллах на Ren'Py есть кнопки для скриншотов и скрытия интерфейса ("S" и "H"), но эти кнопки работают только в том случае, если ваша активная раскладка клавиатуры – английская. Так как же заставить работать эти кнопки при русской раскладке?
Следующий код является костылём (более оптимального решения я не придумал).
Тот же 00keymap.rpy
Код

screenshot = [ 's', 'alt_K_s', 'alt_shift_K_s', 'ы' ],
...
toggle_fullscreen = [ 'f', 'alt_K_RETURN', 'alt_K_KP_ENTER', 'K_F11' , 'а'],
...
hide_windows = [ 'mouseup_2', 'h', 'р' ],
и т.д. для остальных кнопок.
Кстати, если вы каким-то образом меняете стандартное управление или добавляете свои функции клавиш – обязательно указывайте это в разделе Помощь или Управление в вашей новелле. Также можно использовать
Код
$ renpy.notify(_("Ваше уведомление"))
во время игрового процесса. Если вы уберёте внешние скобки и нижнее подчёркивание, то для находящегося внутри текста не будет генерироваться строчка для перевода в соответствующих файлах. Помните об этом при создании текстовых кнопок или чего-либо текстового в screens.rpy, например.


Второй этап. Добавляем интересности.
Если в первом этапе мы меняли те фишки, которые предусматриваются движком или дают свой стандартный вариант, то следующее в стандартно сгенерированной новелле вообще отсутствует, хотя в большинстве своём просто необходимо для игры.

1. Presplash
Это изображение, которое появляется на рабочем столе во время запуска игры (инициализации основных функций в скрипте). Кстати, о том же Джеме: у одной из конкурсных новелл появились собственные хейтеры, которые говорили, что эта функция злостно скопирована из всем известной Доки-Доки, и этот аргумент они всерьёз использовали для оправдания своей ненависти к конкурсантам. Функция эта ниоткуда не скопирована, она есть в документации движка, и её использование только приветствуется. А для использования предусмотрена собственная простая строчка. Впрочем, она уже вписана в движок, и всё, что вам нужно сделать, так это поместить в папку "game" своей новеллы изображение .png с разрешением по вашему усмотрению (Самое оптимальное – около 1000х250). И всё же, в options.rpy можно добавить одну строчку:
Код

define config.minimum_presplash_time = 3.0 #где можете поставить своё значение в секундах и милисекундах (после точки)
Это минимальное время, которое будет выделено для отображения presplash'а, например, чтобы изображение не появлялось на мимолётное мгновение у тех, чей компьютер очень быстро инициализирует игру.

2. Splashscreen
Немного похожее название, но это совершенно другая функция.
Splashscreen – это label наподобие тех, которые разработчики используют в script.rpy, чтобы делить историю на разные этапы/ветви. Дело в том, что label splashscreen автоматически обнаруживается движком во время инициализации скрипта, и этот label будет показан до появления главного меню при каждом запуске игры (или не каждом, на что будет пример чуть дальше)
Splashscreen можно использовать, например, для различных предупреждений или чтобы показать лого команды разработчика.
В script.rpy перед label start пишите label splashscreen. Далее пример использования:
Код

label splashscreen:
  scene black with dissolve #отображаем чёрный экран
  play sound splashscreen fadein 0.5 #мелодия, которую мы хотим проиграть перед лого. Помните, что лучше выставить громкость по умолчанию пониже, чтобы для игрока этот звук не оказался слишком громким
  pause 0.5 #пауза перед отображением лого
  show image "gui/logo.png" with dissolve: #отображение лого с позиционированием по экрану
  xalign 0.5 ypos 100

  pause 4
  hide image "gui/logo.png" with dissolve #убираем лого
  stop sound fadeout 2.0 #останавливаем звук, чтобы при пропуске, например, он не продолжал играть в главном меню
  pause 1
  play sound "audio/sounds/HeadphonesWarning.ogg"
  show image "gui/headphones.png":
  xalign 0.5
  ypos 0.1

  show text _("Звук в новелле крайне важен для повествования и восприятия") at truecenter #Ещё один пример использования, в общем
  with dissolve
  pause 4
  stop sound fadeout 2.0
  hide image "gui/headphones.png"
  hide text
  with dissolve
  return #чтобы закончить label splashscreen


3. Языки (не) пламени
Если вы самостоятельно переводите свою новеллу на другой язык, то не будет достаточно просто перевести текст. Вам нужно позволить игроку как-то выбирать язык, и на основе выбора в новелле может или даже должно что-то меняться.
Например, можно использовать тот же splashscreen, чтобы при первом запуске новеллы на компьютере игрок мог выбрать свой язык. Именно при первом, а не каждом последующем. Как это сделать?
Допустим, после предупреждения, пример которого был выше, пишем в том же label splashscreen следующее:
Код

if renpy.seen_label('langchoose'):
  pass #то есть, если у нас уже появлялся выбор языка, то ничего не отображаем и просто идём к return
else: #а если всё-таки не видели
  label langchoose: #оперируем тем же label, чтобы можно было отслеживать – появлялся он уже или нет, а в каком-то случае можно было бы даже вернуться к нему во время игры
  call screen languages_script #вызываем через call экран с выбором языка

Что же за screen languages_script? И почему именно call, а не show? Разницу между call и show можете узнать в обучении к движку.
Окно с выбором языка нужно писать в том же script.rpy сразу после init python, но не внутри него
Код

screen languages_script():
  hbox:
  at truecenter #располагаем по центру
  spacing 50
  imagebutton auto "gui/button/rulang_%s.png" at left focus_mask True activate_sound "audio/sounds/activate_button.ogg" hover_sound "audio/sounds/buttonhover.ogg": #Изображение-кнопка с российским триколором, например. Параметры можете изучить сами по доукментации
  action [Language(None), Return(True)] #Стандартный язык (в моём случае русский, т.к. RenPy у меня на русском. Здесь, кстати, action выполняет два действия: не меняет язык при первом выборе и выполняет функцию Return(True), аналог того же return в конце label splashscreen
   
  imagebutton auto "gui/button/englang_%s.png" at right focus_mask True activate_sound "audio/sounds/activate_button.ogg" hover_sound "audio/sounds/buttonhover.ogg": #то же самое для английского
  action [Language("english"), Return(True)] #здесь выбирается английский язык, но не устанавливается как дефолтный, чтобы нам дальше было легче оперировать между всеми вариантами

Далее нам нужно дать возможность игроку изменить язык в главном меню. И кнопка для этого должна быть интуитивно понятна для носителя любого языка, ибо игрок может случайно выбрать не тот язык при первом выборе.
Для этого работаем в screens.rpy. После init offset = -1 пишем следующее:
Код

init:
  $ lang_var = "Rus"

Эта переменная нужна нам, чтобы, например, определять, какие кнопки нам нужно показывать в главном меню в зависимости от выбранного языка. К примеру:
Код

imagebutton auto "gui/button/community_%s.png"hover_sound "sounds/sounds/buttonhover.ogg" activate_sound "sounds/sounds/menubutton1.ogg": #кнопка, которая позволяет открыть в браузере страницу группы ВК или Игрового Центра в Steam, если игрок иностранец. Позиционируйте под себя
  if lang_var == "Rus":
  action OpenURL("https://vk.com/вашаГруппаВК") #открываем группу ВК для русскоязычных игрков
   
  elif lang_var == "Eng": #В ином случае, если хотим English). Пишем именно elif на тот случай, если будем интегрировать ещё другие языки
  action OpenURL("https://steamcommunity.com/app/ваш_appID_в_Steam/discussions/") #Или любой другой сайт

[Значения переменной "lang_var" отредактированы по комментариям к статье]. Переменная типа bool тут бы не подошла, ибо у неё возможны только значения True и False, что может вызвать проблемы с двумя языками в игре, а уж тем более если их больше.
А как же мы будем менять значение переменной на "Rus" или "Eng"? Нам нужен свой экран для главного меню. Пишем в screens.rpy (Это отдельный от script.rpy экран, на случай, если вы захотите как-то его видоизменить, чтобы он отличался от предлагаемого в label splashscreen, например, добавить frame или другой фон. Возможно, даже сделать отдельный раздел в меню):
Код

screen languages(): #В моём случае он ничем не отличается от того, что используется в label splashscreen, но если мы хотим использовать его в главном меню, а не только в игровом процессе или label splashscreen, то нам понадобится аналог этого окна в screens.rpy, чтобы переменные и кнопки могли к нему обращаться и использоваться с ним
  hbox:
  at truecenter
  spacing 50
  imagebutton auto "gui/button/rulang_%s.png" at left focus_mask True activate_sound "sounds/sounds/activate_button.ogg" hover_sound "sounds/sounds/buttonhover.ogg":
  action [Language(None), SetVariable('lang_var', "Rus"), Hide("languages", dissolve)] #И всё же здесь есть отличие: параметр Language() остаётся тем же, а дальше с помощью SetVariable мы меняем значение для переменной lang_var, которую объявили в начале для инициализации и далее не просто выполняем Return, а убираем экран с помощью Hide и с определённым переходом
   
  imagebutton auto "gui/button/englang_%s.png" at right focus_mask True activate_sound "sounds/sounds/activate_button.ogg" hover_sound "sounds/sounds/buttonhover.ogg":
  action [Language("english"), SetVariable('lang_var', "Eng"), Hide("languages", dissolve)]

Казалось бы, мы всё сделали и теперь игроки могут свободно менять язык игры и в начале, и в процессе чтения, но нет! Нам нужна кнопка, чтобы вызывать это окно для выбора языка. Всё просто:
Код

imagebutton auto "gui/button/langchanger_%s.png" hover_sound "sounds/sounds/buttonhover.ogg" activate_sound "sounds/sounds/menubutton1.ogg": #Позиционируем под себя!
  action Show("languages", dissolve) #Показываем окно с кнопками для выбора языка и с каким эффектом это делаем

Поздравляю, у вас есть полноценная система выбора языка. Не забывайте, что позиционирование в этом коде нужно менять под себя, как и пути к файлам, их названия и даже названия переменных.

4. Слоты сохранений и их удаление
Для многих наверняка знакомая проблема: вы заходите в экран сохранений/загрузки и, допустим, хотите удалить это сохранение. Как же это сделать? В управлении по стандарту не указывается, что на самом деле слот можно удалить, наведя на него курсор мыши и нажав кнопку Del. Но даже такой способ (который далеко не всем интуитивно понятен) не является удобным. Нужно добавить кнопку для удаления, а лучше к каждому слоту. Простейший крестик.
Работаем в screens.rpy. Ищем screen file_slots(title) и добавляем немного кода:
Код

text FileSaveName(slot) #У вас по стандарту есть эта строчка. Нам нужно найти её и прямо под ней добавить кое-что своё:

if FileLoadable(slot):
  imagebutton auto "gui/deleteslot_%s.png" ypos -50 action FileDelete(slot) #Это оптимальное позиционирование крестика в слоте, если у вас кнопка является изображением 30х30. Возможно, вам понадобится иное – экспериментируйте

key "save_delete" action FileDelete(slot) #Эта строчка у вас тоже уже есть. Оставил просто для наглядности – куда именно нужно вставить кнопку с крестиком для удаления слота

Теперь игрок чётко видит, как можно удалить сохранение. И делается это теперь удобно.

5. Звуковые каналы и зачем они нужны
В RenPy помимо звуковых каналов музыки, звуков и голосов можно создавать свои собственные. Кратко об это написано в уже несколько раз упомянутой документации RenPy, но всё же, разберёмся быстренько здесь.
В новеллах часто используются одиночные звуки, которые проигрываются при каком-то действии персонажа (шаг, удар, взял что-то в руки) или если просто ворона каркнула за окном. При этом должны быть и звуки окружения, которые на повторе играются на определённой локации или ситуации – это называется BGS или background sound, звуки окружения. Они не могут быть проиграны одновременно на одном канале. Для реализации нормального проигрывания нам нужно зарегистрировать новый канал.
Кстати, каналы объединяются в миксеры для того, чтобы их звуками можно было оперировать независимо друг от друга, но чтобы, например, их можно было настроить одним ползунком в настройках. В нашем случае одиночные звуки и звуки окружения должны настраиваться одним ползунком и гармонировать друг с другом. Игрок может поначалу просто не понимать разницы между одиночными звуками и звуками окружения, если в настройках мы сделаем два разных ползунка. Да и как мы сами знаем на практике в роли игроков, для звука в настройках всегда ползунок один. А далее громкость интерфейса, музыки и т.д.
Нам нужен script.rpy, первая же строчка init python, где мы уже написали _game_menu_screen = "preferences". Над ним же/под ним же пишем следующее:
Код

renpy.music.register_channel("bgs_channel", "sfx", True, True)

где bgs_channel – название канала, sfx – название миксера, у нас это единый миксер для всех звуков, есть ещё миксеры music и voice, можно регистрировать свои. True мы задаём двум параметрам: loop и stop_on_mute, то есть, когда мы будем начинать проигрывание bgs, нам не нужно будет отдельно для всех указывать значение параметра loop – нам всегда нужен повторяющийся звук. stop_on_mute просто прекращает проигрывание звука в том случае, если мы выкрутили ползунок в настройках на минимум во время игры или отключили все звуки. Это опционально
Чтобы проигрывать звуки на новом канале, мы должны писать не play sound, а play bgs_channel, как с примером предоставленного кода. Также не забывайте указывать каждому звуку fadein и/или fadeout, таких параметров у миксеров или каналов нет, а указывать их надо и иногда индивидуально каждому звуку. Или не надо, если нужно проиграть звук неожиданно.

6. Прячем курсор
Когда игрок читает новеллу, он, возможно, не клацает мышкой, а жмёт на пробел. При этом курсор нужно увести в сторону, если он мешает. А можно просто задать таймер, который при бездействии игрока с мышью через определённое время сам прячет курсор. По такому же принципу работает таймер afm, про который можно почитать в документации. В играх встречается такая фича как idle анимация: это когда при долгом бездействии игрока и только в этом случае начинает происходить что-то особенное. Но сейчас о мышке. В options.rpy пишем:
Код

define config.mouse_hide_time = 10 #Где вместо "10" можно указать своё время в секундах


7. Удобства для разработки
Это не влияет на геймплей никаким образом, но делает процесс разработки значительно удобнее. Да и в целом вам стоит знать, что можно создавать свои .rpy файлы помимо использования существующих. Самый простой из них, который можно реализовать – это файл для объявления персонажей, звуков, картинок и другого. Я, обычно, называю его defines.rpy. Просто создаём соответствующий файл в папке игры наряду с остальными и открываем его в своём редакторе. Движок его сам увидит и сделает с ним что надо.
Однако, нельзя забывать, что для отдельного файла, где мы объявляем персонажей и прочее, нужно всё делать через инициализацию – указать её. То есть:
Код

init: #чтобы у нас всё инициализировалось на моменте запуска игры и было готово к моменту начала повествования
  define human = Character(_("Человек")) #Можем объявлять здесь персонажей
  define image cg art1 variant1 = "images/cg/1 1.png" #Свои CG-арты, по тому же принципу спрайты и фоны
  define define audio.activate_button = "audio/sounds/activate_button.ogg" #Даже свои звуки. Кстати, этот и другие я использую в коде для кнопок, так что если вы не добавили его или не заменили, то не удивляйтесь, что вам не даёт нажать на кнопку какая-то ошибка
  define fast_move = MoveTransition(0.2) #И даже переходы и т.д. и т.п.



Третий этап. Убираем лишнее.
Если что, это Сергей Григорович за ножницами. Кто не понял – тот поймёт (цэ) Рамзан Ахматович
Пауза на мемесы


1. Сторона отката
Для начинающих разработчиков это самая настоящая загадка. Что же эта сторона делает, как она работает и зачем её настраивать или выключать? Для игроков это ещё более непонятная функция, поверьте мне и моим тестерам.
Сторона отката позволяет с помощью клика по определённой стороне экрана пройти назад по истории на одну строчку диалога. Ну и всё. Казалось бы, это же не будет лишним, почему бы и нет? А вот мой ответ, основанный на статистике тестеров и даже простых игроков (Я смотрел прохождения своей новеллы на ютубе, там игроки случайно кликали на сторону отката и не понимали, почему предыдущая строчка диалога повторяется). Дело в том, что недостаточно просто убрать из настроек выбор стороны отката – он по умолчанию определён как левая сторона. И если мы просто уберём настройку, то сама функция останется в игре и будет благополучно вводить игроков в непонимание: почему они кликают, чтобы продолжить историю, а она неожиданно идёт назад?
Просто определяем сторону отката как отключённую и убираем соответствующую настройку. Я считаю эту функцию совершенно лишней, и это подтверждается моим опытом. В options.rpy
Код

default preferences.desktop_rollback_side = "disable"

Кстати, не забудьте там же для config.default_fullscreen задать значение True. Просто подавляющее большинство игроков сразу идут включать полный экран для игры при запуске, а подчиняться нам, как разработчикам, нужно именно большинству.

2. История
Опять же, аргументирую своим опытом. По стандарту в RenPy есть раздел с историей диалогов. Однако, практически никто им не пользуется, ибо историю можно банально промотать до нужного момента, а весь ход повествования вам этот раздел не покажет – только в том количестве, которое вы указали максимумом (до этого же момента, кстати, и проматывается история во время игрового процесса). Сам экран убирать нельзя – это приводит к ошибкам компиляции и даже не даёт запустить проект. Поэтому нам нужно просто убрать кнопку истории в главном меню, и всё.
В screens.rpy ищем screen navigation() и просто убираем textbutton _("История")
Почему же стоит убирать? Просто потому что пространство в главном меню очень важно, особенно, когда вы добавляете туда ещё что-то своё. Лишние кнопки в главном меню, по которым подавляющее большинство игроков даже не будет нажимать, очень мешает, создаёт неудобство, а нам это не нужно. Допустим, Игрок поставил паузу, а в меню у него и кнопка раздела и Истории, и Галереи, и Инвентаря, и всякого-всякого прочего. Слишком много.

Усё
Поздравляю, начинающий разработчик, теперь у тебя есть необходимый базис для создания достойного проекта RenPy, который не будет выглядить голым и как очередной проходняк. Ну а как бы он ни выглядел, твоя дальнейшая задача – позаботиться о том, чтобы само содержание не было как у проходняка. Достойное блюдо должно подаваться на красивой тарелке, и у всех такая тарелка должна быть с самого первого проекта в RenPy.

Проходите обучение, изучайте документацию (пусть она и выглядит как непонятная карта сокровищ, но в этом-то и дело, что там спрятаны сокровища). То, что вам сначала может показаться бесполезным, окажется в будущем крайне важным, и не придётся спрашивать на форуме: есть ли у кого код, написано ли это в документации? Читайте сами и будет вам счастье. И игрокам вашим.

Достаточно ли всего вышеописанного, чтобы гарантированно создать хорошую новеллу? Нет, конечно. Для хорошей новеллы вам нужна не только хорошая презентация истории, но и хорошее качество самой этой истории, а также собственные – оригинальные – идеи. Почему здесь не описаны, например, способы создать Галерею, Аудиотеку для музыки, настройку шрифтов, интерфейса или, например, способ автоматического выделения говорящего персонажа? Просто потому что это не является необходимым базисом для создания положительного впечатления у читателя. Гайды на подобные вещи можно найти в пару кликов – это доступно всем, да и в конце концов, тут бы места просто не хватило, а я не хочу делить такой сборник на несколько частей. Всё должно быть в одном месте и в одной статье.

Понятное дело, это статья для начинающих разработчиков, которых я в огромном количестве увидел на Anivisual Jam #1, и в первую очередь этой статьёй я хотел помочь им. Возможно, кто-то из опытных товарищей даже найдёт для себя здесь что-то новенькое, но это крайне маловероятно и понятно уже по названию. Я пытался помогать отдельным людям, давая им советы по скрипту в индивидуальном порядке в отзывах, но здесь я собрал весь свой опыт и делюсь им со всеми, кому это может быть интересно.
Я надеюсь, что более опытные разработчики поделятся в комментариях чем-нибудь своим, что они считают также необходимым базисом для разработки новелл на Ren'Py.

За внимание крайне благодарен,
Александр "AlexNumbers" Павлятенко,
Лидер команды "FlyingFantasy Team".
Удачи в разработке


Автор материала: AlexNumbers
Материал от пользователя сайта.

Разработка новелл 18.08.2020 2403 AlexNumbers разработка новелл, ren'py, Основа, RenPy, для новичков, для чайников, разработка 4.8/17

Комментарии (23):
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
4
1 Lisper  
87222
Ох, очень полезная статья.
Особенно для тех, кто впервые открыл Ren'Py и не знает, за что хвататься.

1
2 Хемуль  
87473
Интересная и полезная статья!

6
3 ✟Agnus_Ater✟  
88663

Цитата
Конечно же, более чем в половине новелл на Джеме вообще никак не был изменён стандартный интерфейс

Данное явление связано не с тем, что авторам было лень/не могут, а то что за неделю всего не успеть, приходиться чем-то жертвовать.
Поскольку интерфейс не столь важен, как сам сюжет, то логично, что основные силы были потрачены именно на это.
Нет, конечно, кто-то успел и сделал какие-то свои примочки, но у кого-то есть для этого команда, а кто-то просто не владеет скоростными навыками, чтобы всё успевать.
Я это говорю, как разработчик, что не спал ночами, но всё равно всего не успел.
Статья конечно полезная и советы здесь весьма нужные для разработчиков, но ставить в качестве плохого примера какие-то недельные поделки такое себе. Разработчики вполне могут сделать всё это вне джема.

Необходимый базис для создания новелл на любом движке - это сценарий. Хорошему сценарию можно простить простое оформление, но плохому сценарию/сюжету совершенное оформление не принесёт никаких плюсов в конкурсе.
Знаем, проходили, скорбим.

0
4 Хемуль  
87473
Ну, в некоторых новеллах на Джем даже картинку в главное меню не добавили. Не знаю, заняло бы пять минут, а так очень некрасиво смотрится.

2
5 AlexNumbers  
215517
Могу только согласиться. Особенно, про бессонные ночи.
Однако, в любом случае, хорошую конфету не помешает завернуть в хороший фантик. Или, опять же, фраза с блюдцем применима.
Недельными поделками огорчить никого не хотел, но в обсуждениях не только от моего имени звучали такие слова

1
19 ✟Agnus_Ater✟  
88663
К хорошей конфете конечно неплохо сделать хороший фантик. 
Этот фантик можно всегда успеть сделать и вне рамок всяких конкурсов. Был бы это контекст на 2-3 месяца, то там уже за каждый грех можно по пальцам бить)) Утрирую конечно.
У меня уже просто был опыт, когда силы были брошены на фантик, внутри которого была конфета из смеси овсяной каши с перцем...

Статья безусловно хорошая, сама сейчас дорабатываю и фантик, и саму конфету, и некоторых советов не знала, пригодятся. Так что спасибо за неё nshy

3
6 moonfork  
5668
Хорошая огромная статья!
Я бы ее посоветовала большинству наших разработчиков. nlove 
(да даже я пару моментов отметила для себя)


Цитата
Игрок чаще всего нажимает на паузу именно для того, чтобы перейти в раздел настроек, а по умолчанию открывается раздел сохранений.
С этим я категорически не согласна! Захожу именно за сохранениями :) А в настройки - лишь один раз после запуска игры.

Про проматывание колесиком мышки (верх-вниз), тоже. Мне интуитивно понятно, что текст читается сверху-вниз, поэтому "вниз" - это дальше.

1
7 AlexNumbers  
215517
Поэтому нам и нужна статистика тестеров. У меня чаще были ребята, которые незнакомы с принципами стандартного управления в новеллах на Ren'Py, отсюда и такие же выводы.

А вообще, чтобы не зависеть от статистики, всегда можно сделать настройку, хе-хе. Но это уже чуть-чуть больше, чем уровень новичка

3
8 Deth  
35358
Как нечто среднее между двумя вариантами, можно вставлять вот такое меню (по моему опыту, в японских вн как раз чаще всего используют такой вариант). 

1
9 moonfork  
5668
Кстати. У меня такая статистика есть! Проводила опрос среди подписчиков (8к аудитория). Вопрос был "Заходите ли вы в первую очередь при запуске игры в настройки", большинство написало, что да. (на данный момент опрос уже удален, к сожалению, но в логах чата разработчиков сохранены выводы)
Полагаю, что заход в настройки нужен 1-2 раза за игру. Тогда как сейв-лоад если не на каждом варианте, то раз за сессию. 
Естественно, что статистика верна в основном для длинных проектов (зачем сохраняться в игре на полчаса?).

1
10 AlexNumbers  
215517
Значит, в случае Джема верен вариант с настройками. С более длинным хронометражем повествования – сохранения

1
11 moonfork  
5668
Я думаю, зависит от новеллы. В некоторых джемовских было более 3х выборов - думаю, тут уже уместно подавать экран сейвов.

1
12 AlexNumbers  
215517
А ещё могу предложить сделать один раздел меню, где будут разложены все функции новеллы в минимизированном виде


Не было такого нигде, случаем?

1
13 moonfork  
5668
в БЛ?

1
14 AlexNumbers  
215517
Не, именно когда на всю страницу (в одном окне/разделе) открываются и настройки, и сохранения, и т.д.
Да, я сумасшедший

3
15 Хемуль  
87473
Тогда в подарок к новелле должна идти лупа:-)

2
16 moonfork  
5668
Как это все уместить?..

1
17 AlexNumbers  
215517
А это уже тема для экспериментов

2
20 makazaki_tohoya  
88228
Вроде в Канон и иных стареньких новеллах есть обширное контекстное меню по правой кнопке.

2
18 Ultra_Scream  
341873
Отлично написано! Вот читаешь каждый пункт и думаешь, какая же полезная мелочь, да и сделать совсем нетрудно. Но чем больше читаешь, тем больше видишь мелочей и уже даже сложно их все запомнить - надо выписать каждый момент на листик в список, чтобы ничего не пропустить. Хоть они мелочи, но их столько складывается, что вкупе очень заметно влияют на первые впечатления, когда игрок всего лишь загрузился до главного меню и еще не начал прохождение. Само собой, некоторые советы я тоже счел новыми для себя, о которых не знал ранее. И спасибо за статью, будет удобным время от времени заглядывать сюда)
P.S. За Григоровича респект :D

1
21 Malkovichi  
182589
Очень полезный материал

2
22 Mawrak  
234
Очень много полезных советов и практических решений, спасибо за материал. Я думаю это будет очень полезно начинающим авторам, работающим на Renpy. Все же, с паром моментов я не совсем согласен:

1) Про громкость - я считаю, громкость нужно ставить на максимум, потому что в современных играх она практически всегда по умолчанию на максимуме. Из-за этого игроки ожидают, что громкость в игре максимальная, и в настройки лезут только если им некомфортно из-за слишком высокой громкости. Таким образом людям может показаться, что звук в новелле слишком тихий, хотя на самом деле он мог быть и больше. Но большая часть игроков об этом даже не узнает.

2)
Цитата
В общем, стандартный интерфейс пусть и выглядит минималистичным и приятным глазу, но хоть как-то модифицировать его явно стоит, чтобы новелла не была похожа на проходняк.
 
Пытаться менять интерфейс просто чтобы было не как стандартный это ставить себе палки в колеса. Стандартный интерфейс хорошо работает (без багов) и интуитивно понятен. Если есть понимание дела и какое-то целостное видение того, каким он должен быть, то, конечно, стоит делать свой. Но неоднократно в новеллах на конкурсе были ситуации, когда люди просто ломали интерфейс и он не работал как надо. Например, просто не работало меню загрузки. Или на главном меню творились странные визуальные ошибки. Также часто бывает, что авторский интерфейс выходит очень мудреный, и он просто менее удобен чем обычный. А главный смысл интерфейса во взаимодействии, а не в картинке. 

Я бы глобально трогал интерфейс только если есть четкая уверенность в работе с движком, и четкое предстваление того, что и зачем хочется в нем поменять.

1
23 AlexNumbers  
215517
Всегда рад развёрнутым отзывам


Цитата
игроки ожидают, что громкость в игре максимальная, и в настройки лезут только если им некомфортно из-за слишком высокой громкости
Так разве не лучше, если звук для игрока окажется слишком тихим, и он его сделает погромче? В ином случае разработчик рискует доставить дискомфорт слишком громкой музыкой в меню при первом запуске.
Здесь речь именно о первом впечатлении, о том, как игра встречает игрока. Как правило, человек перед игрой довольно часто заходит в настройки. Также я вижу в этом следующее преимущество: оригинальные файлы звуков могут быть довольно громкими. Мы делаем их таковыми, чтобы игроки, у которых по микшеру низкая общая громкость, могли сразу сделать звуки погромче в игре, а не лезть в этот же микшер. То есть, если 50% по громкости будет оптимальным решением для большинства, то даже для других меньшинств будет оставаться возможность подогнать громкость под себя сразу в игре.
Если короче: появляется универсальность, оптимальная практически для любого игрока.

По второму пункту соглашусь. Но думаю, это актуально только для скоротечных разработок на те же конкурсы