Катсцена с субтитрами на Ren'Py
Содержание
- Предисловие
- Подготовка проекта
- Лейблы
- Экран
- Функции
- Нюансы:
- Использование музыки вместо видео
- Выход в главное меню
- Текстовые теги
- Полный код
- Видео-туториал
1. Предисловие
Всем привет! Я начинаю свою небольшую рубрику «wiwts что-то выдумал». Буду писать в ней туториалы по некоторым плюшкам, которые написал для себя или кому-то в помощь. Сегодня затронем простые субтитры. То, как они могут выглядеть, можете посмотреть здесь:
В качестве подопытного кролика видео-титры из новеллы "Ошибки Первого Рода". К слову, я уже пытался в эту новеллу (т.к. в её разработке я участвовал) внедрить субтитры, но вышло из рук вон плохо. В статье представлена допиленная версия.
И заранее скажу, что меня не волнует, если кто-то где-то когда-то уже такую штуку делал. Я захотел сделать это сам и своим методом — я сделал. Адекватную критику принимаю.
Спасибо за понимание и приятного прочтения!
(P.S. хочу записать видео-туториал по субтитрам в ближайшее время).
2. Подготовка проекта
Прежде всего нам надо обозначить нашу видео-катсцену и сами субтитры.
Тут всё просто: обозначаем видео через Movie, а субтитры — словарём.
Указываем свой канал, я назвал его subtitled_cutscene, вы называйте как хотите, но учтите, что он ещё пригодится дальше по коду.
size=(config.screen_width, config.screen_height) — видео будет тем же разрешением, что и разрешение новеллы.
image song_video = Movie(play='your_video.webm', loop=False, channel='subtitled_cutscene', size=(config.screen_width, config.screen_height))
Субтитры пишем по такому принципу:
- Создаём словарь {}
- В ключ пишем время в секундах, когда начинается нужная нам строка {«1»: }
- В значение пишем саму строку {«1»: «Строка»}
Если хотите в будущем переводить субтитры на другие языки, то оборачивайте строки в такую конструкцию: _(«Строка»).
define cutscene_song_lyrics = { '1': _('Расстрел во тьме дурного сна'), '4': _('Кладёт плашмя'), '5': _('И, прячась, в венах ждёт'), '8': _(''), '10': _('Война летит в лицо'), '12.5': _('Болит и всё про всё'), '15': _('Скулит, а совесть жрёт')} default cutscene_seen_text = [] # Список с просмотренными строками субтитров
Далее можно задать стиль для субтитров, чтобы определить, как и где будут показаны строки. Тут чисто вкусовщина.
style cutscene_text_style: xmaximum 1840 ymaximum 60 xalign .5 yalign .99 color 'dddddd' size 22
3. Лейблы
Для перехода к лейблу катсцены используем старый добрый jump.
label start: jump cutscene_start
Лейбл, где показываем катсцену и субтитры.
label cutscene_start: # Прячем менюшку снизу $ quick_menu = False # Черный фон, чтобы не было возможных просветов между краями экрана и видео scene black # Показываем заданное ранее видео show image 'song_video' # Показываем экран через python, чтобы передать аргумент, который обозначает продолжительность видео $ renpy.show_screen('cutscene_screen', 18) pause
Лейбл, в который мы попадём по окончании субтитров.
label cutscene_end: # Возвращаем менюшку $ quick_menu = True 'КОНЕЦ КАТСЦЕНЫ'
4. Экран
Экран нам нужен, чтобы не давать пропустить катсцену и показывать строки субтитров.
screen cutscene_screen(dur): # Запрещаем скипать катсцену через ctrl и tab $ config.allow_skipping = False # Не даём прокликать катсцену key "dismiss" action NullAction() # При выходе в игровое меню останавливаем видео и показываем экран 'save' key "game_menu" action [PauseAudio('subtitled_cutscene', True), ShowMenu('save')] # Каждую сотую долю секунду проверяем, если видео на паузе, то запускаем его обратно, если нет, то выполняем функцию (см. далее) timer .01 repeat True action If(renpy.music.get_pause('subtitled_cutscene'), PauseAudio('subtitled_cutscene', False), Function(cutscene_update_text, renpy.music.get_pos('subtitled_cutscene'), dur, cutscene_song_lyrics))
5. Функции
Теперь самое главное.
То, что порождает наши субтитры.
Создаём функцию для обновления текста в соответствии с временем видео.
pos — тип float, получаем нашу позицию на временной шкале видео через renpy.music.get_pos('subtitled_cutscene')
lyrics — тип dict, это наш cutscene_song_lyrics
dur — продолжительность катсцены, задавать в int или float
slow_cps=25 — Указываем свой cps
style='cutscene_text_style') — Указываем наш стиль для строк.
init python: def cutscene_update_text(pos:float, dur:float, lyrics:dict): # Берём переменную с просмотренными строками global cutscene_seen_text # Первые секунды Ren'Py на renpy.music.get_pos() возвращает None, поэтому необходимо это условие if pos == None: return # Если катсцена закончилась if pos >= dur: renpy.music.stop('subtitled_cutscene') # Обнуляем катсцену, функцию эту опишу ниже reset_cutscene() config.allow_skipping = True renpy.hide_screen('cutscene_screen') renpy.jump('cutscene_end') # Запускаем перебор словаря с субтитрами for t in lyrics: # Проверяем, видели ли мы текст на этом времени, и какую строку показывать в зависимости от времени if t not in cutscene_seen_text and pos >= float(t): # Добавляем время строки в увиденный текст cutscene_seen_text.append(t) # Показываем строку через show text renpy.show('cutscene_subtitles_text', what=Text(lyrics[t], slow_cps=25, style='cutscene_text_style')) return return
Создаём функцию для сброса катсцены. Она понадобится для того, чтобы по окончанию или прерыванию катсцены она смогла снова исправно запуститься без перезапуска.
def reset_cutscene(): global cutscene_seen_text cutscene_seen_text = [] renpy.hide('cutscene_subtitles_text')
6. Нюансы
Подробнее о них будет в видео-туториале.
1. Использование музыки вместо видео
Если вас интересует использование аудио, вместо видео, то необходимо поменять show image на play music
В renpy.music.stop() поменять канал на music, в таймере так же с get_pause, get_pos, PauseAudio
2. Выход в главное меню
Чтобы катсцена не продолжила играть в уши при выходе в главное меню и при повторном запуске снова воспроизвелась, надо её остановить и «сбросить». Для этого добавим ниже приведённую строку.
В screens.rpy, screen main_menu()
$ renpy.music.stop('subtitled_cutscene'); reset_cutscene()
3. Текстовые теги
В субтитрах можно указывать все ренпаевские теги. Все они будут работать, за исключением {w}, {nw}.
Если кому-то интересно, сработает ли с kinetic-tags
Да, сработает. Проверял.
7. Полный код
Если возникнут вопросы или какие-то проблемы, то пишите в комментарии к статье или в личные сообщения ВК https://vk.com/da_wiwts. Советую написать сюда в комментарии, чтобы в случае чего было удобно всю информацию по этой теме хранить в одном месте.
image song_video = Movie(play='your_video.webm', loop=False, channel='subtitled_cutscene', size=(config.screen_width, config.screen_height)) define cutscene_song_lyrics = { '1': _('Расстрел во тьме дурного сна'), '4': _('Кладёт плашмя'), '5': _('И, прячась, в венах ждёт'), '8': '', '10': _('Война летит в лицо'), '12.5': _('Болит и всё про всё'), '15': _('Скулит, а совесть жрёт')} default cutscene_seen_text = [] # Список с просмотренными строками субтитров style cutscene_text_style: xmaximum 1840 ymaximum 60 xalign .5 yalign .99 color 'dddddd' size 22 init python: def reset_cutscene(): global cutscene_seen_text cutscene_seen_text = [] renpy.hide('cutscene_subtitles_text') def cutscene_update_text(pos:float, dur:float, lyrics:dict): # Берём переменную с просмотренными строками global cutscene_seen_text # Первые секунды Ren'Py на renpy.music.get_pos() возвращает None, поэтому необходимо это условие if pos == None: return # Если катсцена закончилась if pos >= dur: renpy.music.stop('subtitled_cutscene') # Обнуляем катсцену, функцию эту опишу ниже reset_cutscene() config.allow_skipping = True renpy.hide_screen('cutscene_screen') renpy.jump('cutscene_end') # Запускаем перебор словаря с субтитрами for t in lyrics: # Проверяем, видели ли мы текст на этом времени, и какую строку показывать в зависимости от времени if t not in cutscene_seen_text and pos >= float(t): # Добавляем время строки в увиденный текст cutscene_seen_text.append(t) # Показываем строку через show text renpy.show('cutscene_subtitles_text', what=Text(lyrics[t], slow_cps=25, style='cutscene_text_style')) return return screen cutscene_screen(dur): # Запрещаем скипать катсцену через ctrl и tab $ config.allow_skipping = False # Не даём прокликать катсцену key "dismiss" action NullAction() # При выходе в игровое меню останавливаем видео и показываем экран 'save' key "game_menu" action [PauseAudio('subtitled_cutscene', True), ShowMenu('save')] # Каждую сотую долю секунду проверяем, если видео на паузе, то запускаем его обратно, если нет, то выполняем функцию (см. далее) timer .01 repeat True action If(renpy.music.get_pause('subtitled_cutscene'), PauseAudio('subtitled_cutscene', False), Function(cutscene_update_text, renpy.music.get_pos('subtitled_cutscene'), dur, cutscene_song_lyrics)) label cutscene_start: # Прячем менюшку снизу $ quick_menu = False # Черный фон, чтобы не было возможных просветов между краями экрана и видео scene black # Показываем заданное ранее видео show image 'song_video' # Показываем экран через python, чтобы передать аргумент, который обозначает продолжительность видео $ renpy.show_screen('cutscene_screen', 18) pause label cutscene_end: # Возвращаем менюшку $ quick_menu = True 'КОНЕЦ КАТСЦЕНЫ'
8. Видео-туториал
---{-={ЗАПОЛНИТЕЛЬ}=-}---

26.01.2025: Замечен баг с тем, что текст не воспроизводится при повторном прохождении. Внесены правки в код и в статью с целью решения бага.
Нет ответов