嗨!各位觀眾大家好,我是雪凡......
「各位好,我是愛洛 (arrow)!」
「我是音符 (info) 呀。」
「嘛,我是絲蔻兒 (secure)。」
「很高興見到大家,我們是概念少女們。」
謝謝各位的自我介紹,那個......
「ㄟ豆~歡迎各位螢幕前的觀眾們,準時收看『好朋友與雪凡們的 Ren'Py 遊戲引擎初學心得提示』第三回:電子小說快速上手!」
啊啊......台詞被搶走了......而且好像有什麼地方怪怪的......!
「本回將概述如何運用 Ren'Py 遊戲引擎,編寫一本基本的電子小說。」
「相關的前置作業與必要觀念,主持人都會像個奴隸一般地進行解說。」
「除此之外......那個,這次我們還提供了一份特別範例哦。」
「踩在遊戲製作的康莊大道上......戰士們啊!讓我們果斷而又勇壯地前進吧!奏樂!」
......
「對嗎?」
喂喂......
好吧,謝謝,非常正確,就是這樣。你們滿意了嗎?
(搶回麥克風!)不過,因為該講可講的東西實在太多太囉唆,本回的實操部份,雪凡沒打算詳細說明細節與背後原因。您只要照著我說的做,就能達成效果。為了後續深入時有能力自行做實驗,這邊我們會衝得稍微快一點!
本章完成之後,您應該就能用 Ren'Py 撰寫出最基本的電子小說了。
那麼,請各位跟上。
「啊啊,上次沒戲份,這次可要好好表現囉!」
Let’s Go!
請打開您的 script.rpy 檔案。
今天的主角就是它了。
▲ 圖1:script.rpy 檔案的初始狀態,裡面記錄了關於劇情如何發展的指示。
script.rpy 檔案,記錄了玩家在遊戲主選單中,按下「開始遊戲」按鈕後,遊戲具體會做些什麼事情。精確地說,當您按下「開始遊戲」按鈕後,Ren'Py 引擎將會從 "label start:" 的位置開始往下運行。
以上圖的腳本為例,這遊戲總共會執行三行命令:兩行 e 開頭的對話,加上一行 return。
您現在看不懂這些語句沒關係,請繼續往下翻。
我們來講講在遊戲中演出故事的方法。
要演出故事情節,最重要的是旁白與對話;而要講出旁白與對話,我們得先定義出誰是「敘事者」。
請注意 script.rpy 檔案最上方那行。它是這樣寫的。
define e = Character('Eileen', color="#c8ffc8")
這就是敘事者的定義方法。
其格式如下:
define 角色代號 = Character('角色名字', color = "角色名字的顏色")
角色代號請用英文,並儘量取簡單一點,如此一來您之後打腳本會方便許多。
具體定義時,大約會寫成如下這般:
define info = Character('音符', color = "#fce94f") define secure = Character('絲蔻兒', color = "#ffc9c9") define arrow = Character('愛洛', color = "#fff3c4")
【RGB 色碼】在 Ren'Py 遊戲中,顏色的定義方式和網頁用色的色碼定義法相同,是前面加上 # 字號的十六進位 RGB 色碼。其中 00 為最小值,FF(十進位中的意思是 255)為最大值。
舉個例子:在 #BF7C51 這個顏色中,紅色 (Red) 強度值為 BF,綠色 (Green) 強度值為 7C、藍色 (Blue) 強度值為 51,RGB 三原色混色後的結果就是最終顏色。
當然啦,就算這麼說,您大概也很難感覺出那到底是什麼顏色。推荐您去網路上搜搜 RGB 色碼表,對照著參考,這樣選色就容易多了。比方說這個:
https://www.eion.com.tw/Blogger/?Pid=1008
哦還有一件事必須說明:上述定義角色用的 define 語句,強制必須寫在檔案的最頂端--也就是第一個 block 之前。
以當前的 script.rpy 來說,您必須將 define 放在 label start: 這行前面。
【初始區段 (init block)】在 Ren'Py 的術語中,每個檔案最前面的地方被稱為初始區塊 (init block)。
初始區塊收藏著在遊戲啟動瞬間(不是按下「遊戲開始」按鈕時,而是在連遊戲主選單都還沒出現前),就必須第一時間立刻執行的腳本,所以才叫作「初始」。有些特殊類型的腳本語句只能放在初始區段裡,就好比我們剛剛介紹的 define 語句。
其實還有好幾個地方可以作為初始區段,我們日後會見到更多,這邊不表。
完成角色定義後,就讓角色說話吧。
非常簡單,格式如下:
角色代號 "對話內容"
姑且做個示範。絲蔻兒小姐請......
secure "哈?這不用我示範吧?你是笨蛋嗎?"
▲ 圖2:讓角色說話看看。
絲蔻兒:「以上,示範終了!有什麼不滿嗎?」
......總之非常感謝絲蔻兒小姐的示範。
事實上,若在敘事中,我們不需要「明確指定敘事者」是誰,我們也可以不使用角色代號,而直接打字。如下:
"不指定敘事者的狀況,用這種方式來表示旁白效果。"
▲ 圖3:不指定敘事者時,畫面效果就像這樣。可使用的情境相當地多。
角色能對話是不錯,但畫面空空如也沒有圖片,這遊戲看起來依然很不像話。
遊戲必須要有能顯示圖片,操作圖片的方法......
就如同敘事者一樣,圖片在使用前需 要先行定義,而且也同樣得定義在「初始區段中(檔案的最上邊)。
定義圖片的格式如下:
image 圖名 = "圖檔路徑(相對於 game)"
其中,圖名必須要用英文字;且圖名可以(但不一定要)用空白分成好幾節。
看起來就像是這樣......
image arrow confident = "char/arrow_confident.png" image arrow smile = "char/arrow_smile.png" image info pity = "char/info_pity.png"
以上圖片分別被命名為:"arrow confident"、"arrow smile" 和 "info pity" 。
您日後可以用這些名稱,來操作他們對應到的圖片。
【圖片分類】圖片分類在 Ren'Py 中是一個很方便的小概念。
圖片的「分類」,是在定義的過程中,由圖片名稱中的「第一節」所決定的。只要圖片名稱第一節相同,就會被視為相同分類。
在上例中,三張圖總共會被分成兩類,也就是 "arrow" 與 "info"。
同分類圖片會被 Ren'Py 的顯示機制視為「相同圖片的不同變體」。在實作上,這表示同分類圖片在畫面中一次最多只會出現一張。換句話說,當您嘗試叫出第二張同類圖片時,先前的圖片將會被自動隱去,而新圖片則會繼承舊圖片的各種屬性,比方說位置與先後順序等等。其效果就等同於更換圖片一樣。
在一般的遊戲製作情境下,利用「角色名 狀態」的方式來替圖片命名,就能輕鬆替角色變更表情與動作,而不用擔心切換圖片時失誤,讓同個角色在螢幕上表現分身術之類的......您可在本文後面看到更多使用範例。
音符:「其實除了命名以外,Ren'Py 也允許用手動的方式替圖片分類。但這種技巧使用場合很少,各位平常不理會也無所謂的。知道有這件事,要用時再找就好囉。」
前面把圖片定義好了之後,有三個指 令可用來操作圖片。
分別為 scene、show、hide:
▲ 圖4:任何遊戲都是從一片空白開始。遊戲作者必須從往其中填圖。有時候,您也會希望透過 scene 指令將螢幕清回這種狀態。
▲ 圖5:用 scene 貼出背景。順便一提,這是網路上 Uncle Mugen 用 3D 技術繪製釋出的背景,我做了一點後加工......
▲ 圖6:show 圖效果,將角色放出來。
scene、show、hide 三個指令,用法上可說大同小異。以下以用途最廣的 show 為主,實際示範一下看看 :
image library = "bg/library.png" image info happy = "char/info_happy.png" image arrow talk = "char/arrow_talk.png" image arrow smile = "char/arrow_smile.png" define info = Character('音符', color = "#fce94f") define arrow = Character('愛洛', color = "#fff3c4") label start: scene library # 清除之前一切圖片,秀出背景作為底圖 show arrow happy # 秀出角色 arrow "背景與角色登場後,就讓我們這些角色說話。" show arrow smile # 更換表情 arrow "我們可以隨著劇情變更表情與動作......總而言之就是換圖啦。" show info happy at right # 秀出其他角色(用 at 關鍵字可以變更圖片位置) info "其他角色也可能登場。" hide arrow # 隱藏某些圖片。注意:這裡可以只寫圖片名字的第一節。 arrow "讓一些角色退場,就這樣。"
【上面的範例無法執行?】本回正文中有部份範例,直接抓下貼上依然無法執行。
這是因為光有程式碼而缺少圖片檔案,所以跑不起來的關係......還請各位注意。
愛洛:「(插嘴)真要說。其實本回正文中提供的,大半都是簡化後不能騙字數的範例啦。」
啊啊,快給我住口!這也太過誠實了......
愛洛:「不過這次的附件中,還有更加完整的好範例哦,抓來看看或許不錯--請見識我的華麗演出吧!」
............
稍等,妳不覺得誠實是種重要的美德嗎?
show 指令後面還可以上一些額外的關鍵字。包括決定圖片位置的 "at"、決定先後順序的 "behind"、決定轉場手法的 "with" 等。
使用方式如下:
show 圖片名 at 圖片位置 behide 其他圖片的分類名 with 轉場特效
實際使用時,會像是以下這般......
show text normal at right behind secure with dissolve
您可省略其中任何一個項目,但順序請千萬別弄錯,比方說 with 如果您要寫,那就一定要放在最後。順序弄錯的話,遊戲會掛掉的。
透過在 show 後面接上 at 指令,您可以決定圖片的顯示位置。
▲ 圖7:利用 at 重新配置角色位置。
常用的位置包括:
right、left、center 主要被用來決定角色立繪位置,而 truecenter 則可用來顯示一些其他圖片......比方說道具或Q版小事件之類的。
您可以從官網手冊中 (https://www.renpy.org/doc/html/transforms.html#default-transforms) 找到所有可用的預設位置。
透過 with 關鍵字,您可以在畫面有變化時,套用轉場特效。
【轉場特效】所謂的轉場特效,就是將舊有的螢幕畫面,漸進地過渡到新畫面的一種手段。其特徵是漸進的,而不是突兀地去做切換,如果巧妙使用可以達成一些動畫效果。
音符:「妥善使用轉場特效,可讓遊戲質感大幅提升,感覺會很豪華說!但因為轉場本身是動畫,需要花去額外時間,所以也會相應地拖慢遊戲節奏......使用時要注意這點。」
順便一提,with 語句可以放在 show 語句後面,但也可以單獨使用,一次處理多個圖片漸變效果。
比方說,可以像下面這樣......
show A at left show B at right with fade # 單獨使用 with 語句,而不是接在 show 語句後方。
......用上面的寫法,就可在同一次轉場中,同時變更兩張以上圖片的位置。
with 後面可接的預設轉場特效相當多,其中包括:
除此之外,還有一些其他的特效可用,您可以自行試試。不過我覺得預設轉場中,好用的似乎只有這些,其他的效果都太那個,呃,特別了一點。您可以從這邊 (https://www.renpy.org/doc/html/transitions.html#pre-defined-transitions) 查到所有可用項目。
最後補充一下 "behind" 指令。
就 Ren'Py 的預設行為而言,比較晚才被顯示的圖片,會蓋住先前顯示過的圖片。舉例來說:
show A show B # 如果兩張圖有重疊,則重疊的部份,先出現的 A 會被後出現的 B 蓋住。
不過,如果您在 show 圖時用了 "behind" 的指令,則新顯示的圖將會強制躲在其他圖片後面......
show A show B behind A # 如果兩張圖有重疊,B 圖會被藏在 A 圖的下面
......behind 指令用到機會較少,但偶爾需微調顯示效果時,這還真不可或缺。多少留個印象吧。
腳本寫作者可以透過 play 與 stop 兩個指令,來操作遊戲中的音樂與音效。
play music "檔案名" #播放音樂 stop music #停止音樂 play sound "檔案名" #播放音效
您可以用 play music 來播放音樂,用 play sound 來播放音效。音樂與音效間的最主要差別,在於音樂會不停重播,而音效只會播出一次。因為音效不會自動重播,所以通常情形下,也就只有 music 需要使用 stop 指令。
您甚至可以用以下語句來建立一連串的播放效果。
play music ["檔案名1", "檔案名2", "檔案名3"]
【聲道 (channel)】……技術上來說,前述腳本中的 sound 與 music,其實是「聲道 (channel)」的名稱。而 play 與 stop,則是操控指定聲道的方法。
music 與 sound 的本質是 channel 這點,會在遊戲的某些方面起影響。比方說,玩家可以在遊戲的功能選單中分別切換 music channel 與 sound channel 的音樂大小,而不會同時影響兩者。
而更進一步地,music 與 sound 不是關鍵字,這表示您還可以建立有著特殊用途的自定義 channel!甚至同時播放兩首音樂,達成動態混音效果。
自定義 channel 的特技不是人人都用得到呢,您若有需要請自行參考著資料讀讀吧。見此:https://www.renpy.org/wiki/r enpy/doc/reference/functions/renpy.music.register_channel
透過 play 與 stop 切替音樂時,您還可以透過額外的 fadeout 與 fadein 關鍵字設定音樂淡出淡入秒數(當然 stop 是不支援淡入的)。
示範見下......
stop music fadeout 1.0 # 關閉之前的音樂,淡出時間為 1 秒。 play music "music/coffee_shop.ogg" fadein 2.0 # 播放 coffee_shop.ogg 作為背景音樂,淡入時間為2秒。
【聲音格式】 絲蔻兒:「Ren'Py 能接受的聲音檔案只有 .ogg、.mp3、.wav(wav 僅限於採用未壓縮的 PCM 編碼檔案)三種而已,餵它其他檔案是播不出來的。餵食時稍微注意一下啊。」
Ren'Py 還有替對話播放配音的功能。關鍵在於使用 voice 指令,如下......
voice "WA00004.ogg" # 播放語音檔。 info "等、等等呀!就算突然要我說話,我也不知道要說什麼……" # 會在這一句話的顯示期間播放語音 arrow "如果按按鈕跳到下一句話,語音沒播完也會立刻停止喵。"
voice 指令與 play sound 的主要差異在於:voice 播到一半時,若使用者按按鈕跳到下一句話,則原本播放中的語音會立刻被打斷、終止。其效果就如同在一般同類遊戲中會碰到的那樣。
雖說 voice 有提供自動打斷的特性,但當有必要的時候,您也可以用 voice sustain 命令,來避免下一句顯示內容打斷語音播放。就像下面這樣:
voice "WA00004.ogg" info "等、等等呀!就算突然要我說話,我也不知道要說什麼……" # 會在這一句話的顯示期間播放語音 voice sustain "這句話顯示時,不會打斷原本的語音。"
請看您 script.rpy 檔案的最下面。
return
當您的遊戲演完結局、秀出了 Fin 圖片,又或是讓角色拉拉雜雜地說完後日談之後。您就可以用 return 命令讓遊戲跳回主選單。
其實 return 的實際表達意義比較抽象,不過對於遊戲製作來說,暫時這樣理解就可以了。
有了前面那些東西,您已經可以做出美觀的「單線式」電子小說了。但話又說回來,既然是遊戲,玩家當然會期待能與劇情互動,主動影響情節發展。
毫無互動算什麼遊戲呢?......是的!光看是不夠的。Good Ending 並不重要,重在參與啊!
好像有什麼奇怪的東西混進去了......不,請別在乎那些枝微末節的小事。
那麼,問題來了,要怎麼讓玩家與遊戲進行互動呢?
請先翻翻 script.rpy 檔案。在您的 script.rpy 檔案中,應該有著這樣的一行字......
label start:
若您剛剛有稍微測試過您的腳本,您應該感覺不出來它到底有什麼功能。
事實上這行指令,確實沒有具體地去做什麼事,去執行什麼行動......而僅僅就像字面那樣,只是一個「標籤」。這標籤的作用,是用來標示「下方縮進區塊 (block) 的名字為 "start"」
這邊說的「標示」,並不是標示給玩家看的,而是在遊戲引擎內部,將其標示為一個撰寫腳本時可供單獨運用的場景段。一旦用 label 標示後,Ren'Py 遊戲引擎就能透過這個名字,來使用被標示的情節段落。
用戲劇概念來說,每個 label 區塊,往往代表了戲劇中「一場戲」-- 用許多 label 將故事分成多個場,然後讓故事不斷從上一場跳往下一場--就是遊戲正常的進行方式。
【特別的 label:"start"】 愛洛:「那個啊,"start" 這個 label,在眾多 label 中,其實還是有著特殊地位的。比較技術性的說法是,它是『開始遊戲』的按鈕所連接到的腳本對象。」
愛洛:「啊~這樣說吧,當您在遊戲主畫面選擇了「開始遊戲」時,其實程式內部所做的事,就是讓故事從名為 "start" 的區塊開始,讓腳本從這裡發展下去。嗯……至少 Ren'Py 的預設是這樣設定的啦。」
label 能將故事切成片段。而要讓故事在各片段間切換跳躍,要依靠 jump 指令:
jump 標籤名稱
比方說......
jump start
這樣就可將故事跳往任何一個單獨的 label 位置。
jump 指令單獨使用的話,也就只能讓每一場戲連接到固定的下一場戲。
為了讓遊戲能真正產生分歧,遊戲設計者通常會提供選單機制,讓玩家能透過選單做出決定,並對遊戲進程產生影響。
要提供玩家選單,最基本方法是使用 menu。
以下提供一個完整的範例,您可以把它貼到您自己的腳本裡試試:
define secure = Character('絲蔻兒', color = "#ffc9c9") label start: secure "那就由我來示範一下選單 (menu) 功能。請聽好......" menu: secure "「咳嗯......玩家啊玩家,請問生命、宇宙以及任何事情的終極答案是什麼?」" "「?......那個......請把問題再說一遍好嗎?」": secure "真拿你沒辦法,那我就再說一遍。咳嗯......玩家啊玩家,請問你是狗呢,還是狗呢又或是狗呢?" "「等等!這我聽過,好像是4什麼......」": secure "哇啊,看來是有讀書的傢伙呢......不過給我注意一下著作權問題啊!" "「......藍綠藻?」": secure "(微笑)喂喂,不對吧?我問的又不是你的存在意義......" secure "總之謝謝您誠實地回答。選單示範結束囉。" return
▲ 圖8:menu 產生的選單,可以讓使用者進行選擇。
一個 menu 的結構大約如下:
menu: "問題文字" # 本行可以省略 "選項文字1": "選擇「選項文字1」後會執行的腳本內容......" "選項文字2": "選擇「選項文字2」會執行的腳本內容......"
使用 menu 請注意以下事項:
在遊戲進行過程中,總不能每次故事分歧都讓玩家進行選擇。有很多時候,我們會先把故事早期時做出的選擇記錄到變數中,然後在故事後期,才依據變數儲存的內容對情節分支。
如果您忘了變數是什麼東西,請去複習第二回的內容。
在使用變數之前,我們先瞭解一下此處變數的指定法:
$ 變數名 = 變數值
舉例如下:
$ relation = 3 $ name = "克拉克肯特"
變數名您可以自己取,指定方式基本上跟上一篇講的完全相同,唯一的差別在於,這前面要加上一個 "$" 符號。
【為什麼要加錢符號】因為 Ren'Py 的作者很窮......當然不是這樣!
Ren'Py (.rpy) 腳本,與 Python (.py) 語言腳本很像,但說到底,他們並不相同。
Ren'Py 遊戲引擎,提供使用者較為簡單的 .rpy 腳本來設計情節,以便略去一些瑣碎的語法與細節操作。但在遊戲引擎底層,為了更強而更大的彈性,它是使用強大而且複雜的 Python 語言來進行驅動。這表示,當您需要更進一步地控制遊戲時,您就需要直接以 Python 程式碼的形式操控遊戲引擎。
在 rpy 檔案中,透過一個 "$" 符號,我們可以快速地「進入 Python 模式」,來執行單行的 Python 語句。總之,用 "$" 開頭的行,會被視為單行的 Python 腳本解析,而不是 Ren'Py 腳本。
在 $ 後設定的變數,是 Python 中的變數,而不是 Ren'Py 中的變數--這兩者雖然互有關係但並不相同。而 if 語句能夠處理的是 Python 變數,所以我們必須透過 $ 來指定。
音符:「如果您還是有點困惑,先別想太多呀。反正設定變數前,加個錢符號就對了。」
當您有了變數後,您就能透過 if 對變數進行判讀,格式如下......
if 判斷式: # 當上述判斷式滿足時就會執行這邊的語句 elif 判斷式: # 當前面其他的判斷式都不滿足,且本判斷滿足時,就會執行這邊的語句 else: # 所有判斷式都不滿足時,執行這邊的語句。
舉例來說:
if relation == 10: # relation 變數剛好等於 10,注意必須要用兩個 "=" 符號才能代表「等於」的意思。 koitsu "太好了!我正在找你呢。快來幫我的忙。" elif relation > 5: # relation 變數大於 5 且不等於 10 koitsu "原來是你啊。" else: # 其他任何狀況。 koitsu "你是誰啊?"
使用 if 語句時,需要注意順序:具體說來 if 一定要放在最前面,而 else 一律只能放在最後一項。除此之外,if 語句中只有 「if 項」 是必要的,如果不需要用到「elif 項」或「else 項」,則這兩者都可以直接省略不寫。
elif 可以有很多個,但 if 與 else 都只能有一個。
--這些都算是程式設計的常識,記下來不會吃虧的啦。
if 後接的判斷式中,"==" 代表「等於」、">=" 代表「大於等於」、">" 代表 「大於」、"<=" 代表「小於等於」、"!=" 代表「不等於」......
諸如此類,以此類推!
巧妙運用 if 指令,可在玩家察覺不到的地方,偷偷做出各種分歧情節。請務必參考著附件範例來感覺看看!
▲ 圖9:華麗麗的特別附加範例 ,歡迎各位享用!
本回提供了一個可供各位下載的特別附加範例。這次的心得大都有示範出來啦。長度不算太長,但應該講解得相當清楚。這就交給各位了。
「有需要的話,就請各位拿走吧,拍這個範例還真挺費事的。」
請往這邊下載:https://www.openfoundry.org/tw/papers-and-teaching-materials/doc_download/1628-welcomedemo3
使用方法為:將壓縮檔解壓縮。然後將解出來的資料夾放入您的專案目錄中(就是您在啟動器中設定的 "Projects Directory" 目錄,忘記那是什麼的人快回去翻第一回!)。接著重開啟動器、打開 script.rpy 檔案,邊看檔案邊開始遊戲,試著跑一輪看看,應該十分鐘就能結束哦。
那麼今天的心得就到此結束了。
「說不解釋,結果還是解釋了不少東西呀......」
是啊......真奇怪,和計劃中不一樣?大概是因為終於能實戰,心情很好的關係吧?
「實戰的是觀眾又不是你......」
別吐嘈啦。嗯哼,不管怎麼說,有了本回的心得,各位觀眾想要挽起袖子,做出基本的電子小說已經不成問題了--不不不,不只是電子小說而已,就算是比較簡單的遊戲,也能過關斬將地一路解決了吧 ?
希望大家能對今日的節目感到滿意,要是能賞賜在下幾文錢那就更高興了。
「這、這個就有點......」
嘖,乞討募款是禁止事項嗎?
好吧,這先不管。那麼之後還有什麼要講的呢?
4:3的顯示比例也該看膩了吧?不把比例與解析度律定,沒有辦法開始製作素材啊!還有那個啊,每用一張圖,就要在腳本中定義一次,是不是很煩人?角色對話的文字能否不要瞬間顯示,而能啪啦啪啦地,像說話一樣慢慢流出......?
......諸如此類,需要設定的東西還有大把可說!
欲知後事如何,請看下回分解!......
齊聲:「敬請期待!」
......怎麼老搶我台詞啊你們這些傢伙!
(本回完!)