Как за 3 дня создать игровой движок для новелл

Одним дождливым питерским днем мой проект в UE4 перестал загружаться, и из-за этого я захотел сделать свой собственный движок. И одна новелла подтолкнула меня сделать движок именно для новелл. Если вам хочется узнать побольше и вы не боитесь goto, gosub и других ужасов, добро пожаловать под кат.
image

Оглавление

Акт 1. Введение.

Акт 2. Разработка.

1. Текст.
2. Графика.
3. Звуки и эффекты.

Акт 3. Редактор.

Акт 4. Заключение.

Акт 1. Введение.

image
Все началось с того, как после прохождения N-ой новеллы друг порекомендовал мне Katawa Shoujo. Как оказалось официальной версии в App Store нет и не предвидится, только .ipa на 4pda.

Этот же друг зная, что я пытаюсь создать игру уже третий месяц предложил мне портировать Катаву на iPad без Jailbreak. Выбор пал на smart BASIC, так как я пользуюсь этой программой уже полтора года и знаю синтаксис её внутреннего языка программирования (также у smart BASIC есть SDK для XCode).

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

Акт 2. Разработка

image

1. Текст

Для начала я создал папки (спойлер).

Папки

/Sprites
/Event
/BGs
/Music
/Scenario
/Scripts
/Scripts/Ren_sB
/Scripts/Ren_sB/Functions
/UI
/UI/main
/VFX

Файл Katawa Shoujo Port.txt будет подгружать все скрипты.

scenario$ = “Habr” {Scripts/colorcodes.txt} {Scripts/Ren_sB/render.txt} 

Файл colorcodes.txt будет содержать RGB цвета имен для рендера, для примера возьму Сидзунэ и ее синее выделение:

data "Сидзунэ","107","174","239" dim colors$(1,2) read colors$(color,0) 

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

За это будет отвечать скрипт speak.txt.

Для начала в файл load.txt я прописал загрузку спрайта bg-say.png (поле для реплик) и шрифта playtime_cyrillic:

font "Font/playtime_cyrillic2.ttf" load a$ sprite "bg-say" load "UI/bg-say.png" sprite "bg-say" resize 1024,205 sprite "bg-say" at 0,screen_height()-205 sprite "bg-doublespeak" load "UI/bg-doublespeak.png" sprite "bg-doublespeak" resize 1024,205 sprite "bg-doublespeak" at 0,screen_height()-205 sprite "ctc-strip" load "UI/ctc-strip.png" get sprite "ctc_strip" size w,h sprite "ctc_strip" resize w*1.28,h*1.28 sprite "ctc_strip" at screen_width()-w*1.28-15,screen_height()-52 sprite "ctc_strip" delay 0.05 

После я решил использовать в скрипте сценария знак “|” как разделитель команды, цели команды, вторичной цели, действия и аттрибута.

Показ текста: “1|перс|текст”

1|Сейдзунэ|[Привет, Хабр] 

Файл render.txt считывает эту команду и перенаправляет на метку alone

render.txt

graphics set toolbar off set orientation landscape  file "Scenario/"&scenario$&".txt" readline hmm$ count = count+1 end = file_end("Scenario/"&scenario$&".txt") if end = 0 then goto count  file "Scenario/"&scenario$&".txt" setpos 0  dim info$(count,5)  for l=0 to count-1 file "Scenario/"&scenario$&".txt" readline line$  splite line$ to temp$,n with "|" for m = 0 to n-1 info$(l,m) = temp$(m) next m next l  'Разбор массива текста, перенаправления на подпрограммы ' show: for y = 0 to count-1 if info$(y,0) = "1" then gosub alone ... alone: {Scripts/Ren_sB/Functions/Speak.txt} return 

speak.txt

sprite "bg-say" show name$ = info$(y,1) text$ = info$(y,2)  'Поиск кода цвета данного персонажа ' for color = 0 to 6 if name$ = colors$(color,0) then r = colors$(color,1) g = colors$(color,2) b = colors$(color,3) endif next color  'Создание двух полей ' field 1 text name$ at 20,575 size 600,40 ro field 1 back alpha 0 field 1 font color r/255,g/255,b/255 field 1 font name a$ field 1 font size 28  field 2 text render$ at 20,615 size screen_width()-20,130 ro ml field 2 back alpha 0 field 2 font color 1,1,1 field 2 font size 28 field 2 font name a$  'Анимация текста ' for k = 0 to len(text$)-1 'Использование касания для пропуска анимации ' gosub 3 render$ = substr$(text$,0,k) field 2 text render$ pause 0.025 next k  'Ожидание касания для перехода ' sprite "ctc_strip" show ! sprite "ctc_strip" loop gosub 1 sprite "ctc_strip" hide 

1

1 x1 = touch_x(0) y1 = touch_y(0) if x1 > -1 or y1 > -1 then goto 2 slowdown goto 1  2 x1 = touch_x(0) y1 = touch_y(0) if x1 = -1 or y1 = -1 then return endif slowdown goto 2 

3

3 x1 = touch_x(0) y1 = touch_y(0) if x1 > -1 or y1 > -1 then tapped = 1 endif  if tapped = 1 then if x1 = -1 or y1 = -1 then tapped = 0 if len(text$) = 0 then k = lengthmax - 1 else k = len(text$) - 1 endif return else return endif endif return 

Результат:

image

“Но ведь в Катаве может говорить два персонажа одновременно!” Для этого есть отдельная команда: “2|перс1&перс2|текст1&текст2”:
2|Хисао&Лилли|«Привет!»&”Здравствуйте!"

Добавим в render.txt строки:

render.txt

... show: ... if info$(y,0) = 2 then gosub together ... together: {Scripts/Ren_sB/Functions/Double_Speak.txt} return 

Double_Speak.txt выполняет данные команды:

Double_Speak.txt

sprite "bg-doublespeak" show names$ = info$(y,1) 'Имена и тексты ' nd = instr(names$,"&") name1$ = substr$(names$,0,nd-1) name2$ = substr$(names$,nd+1,len(names$)-1) texts$ = info$(y,2) td = instr(texts$,"&") text1$ = substr$(texts$,0,td-1) text2$ = substr$(texts$,td+1,len(texts$)-1) 'Определение цвета для каждого персонажа ' for color = 0 to 6 if name1$ = colors$(color,0) then r1 = colors$(color,1) g1 = colors$(color,2) b1 = colors$(color,3) endif next color  for color = 0 to 6 if name2$ = colors$(color,0) then r2 = colors$(color,1) g2 = colors$(color,2) b2 = colors$(color,3) endif next color 'Создание полей ' field 11 text name1$ at 20,575 size 300,40 ro field 11 back alpha 0 field 11 font color r1/255,g1/255,b1/255 field 11 font name a$ field 11 font size 28  field 12 text name2$ at 535,575 size 300,40 ro field 12 back alpha 0 field 12 font color r2/255,g2/255,b2/255 field 12 font name a$ field 12 font size 28  field 21 text render1$ at 20,615 size screen_width()/2-20,130 ro ml field 21 back alpha 0 field 21 font color 1,1,1 field 21 font size 28 field 21 font name a$  field 22 text render2$ at 535,615 size screen_width()/2-20,130 ro ml field 22 back alpha 0 field 22 font color 1,1,1 field 22 font size 28 field 22 font name a$  'Определение максимальной и минимальной длинны ' lengthmax = max(len(text1$),len(text2$)) lengthmin = min(len(text1$),len(text2$))  'Анимация текста '  if len(text1$) = len(text2$) then for k = 0 to lengthmax-1 gosub 3 render1$ = substr$(text1$,0,k) field 21 text render1$ render2$ = substr$(text2$,0,k) field 22 text render2$ pause 0.025 next k endif  if len(text1$) > len(text2$) then for k = 0 to lengthmax-1 gosub 3 render1$ = substr$(text1$,0,k) field 21 text render1$ if k < lengthmin then render2$ = substr$(text2$,0,k) field 22 text render2$ endif pause 0.025 next k endif  if len(text2$) > len(text1$) then for k = 0 to lengthmax-1 gosub 3 render2$ = substr$(text2$,0,k) field 22 text render2$ if k < lengthmin then render1$ = substr$(text1$,0,k) field 21 text render1$ endif pause 0.025 next k endif  'Ожидание касания ' sprite "ctc_strip" show ! sprite "ctc_strip" loop gosub 1 sprite "ctc_strip" hide 

Результат:

image

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

Спасибо всем тем, кто дочитал до конца статьи. До встречи в следующей части!

ссылка на оригинал статьи http://habrahabr.ru/post/274399/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *