Touhou: Fading Illusion

Touhou: Fading Illusion

Not enough ratings
模組製作指南
By GiMark
本指南包含了建立一個簡易模組所需的全部資訊。
   
Award
Favorite
Favorited
Unfavorite
前言
模組愛好者們好!本指南包含了建立模組所需的基本資訊。在開始製作模組之前,我們建議你先了解東方同人規約。

注意:目前模組上傳工具僅支援Windows。

注意:_game_resources資料夾中的圖片以及本指南的部分內容可能包含劇透。
創作限制
東方Project原作使用規定

《東方凋夢錄》是一部東方同人作品,因此你的模組必須遵循原作者ZUN的要求。具體而言,你的模組不能包含以下內容:

  • 任何有損東方Project聲譽的內容;

  • 任何侵犯其他智慧財產權的內容;

  • 任何意圖將你的作品與官方作品混淆的內容;

  • 任何從東方Project官作中提取的內容;

  • 東方Project官作的結局場景;

  • 任何意圖在虛構範圍外宣傳個人信仰的內容;

  • 未經創作者許可的其他東方同人內容;

  • 非法色情內容;

  • 任何煽動對個人或群體仇恨的內容;

  • 未經ZUN許可的本人照片。
你可以在此處[thwiki.cc]查閱完整的東方同人規約。

此外,你的模組不得包含任何惡意程式碼。你的模組必須包含原始碼(.rpy檔案)以供審查。如果你不希望分發原始碼,請聯絡我們,我們將在私下審查原始碼後允許你釋出模組。開始建立模組即表示你已閱讀並同意此條款。如你的模組違反此條款,我們保留隱藏或遮蔽你的模組的權利。

如果你有任何問題,請在QQ群882402531或Discord伺服器[discord.gg]與我們聯絡。
快速開始
如果你同意上述內容,那我們就開始吧。首先,你需要一個方便的文字編輯器,因為接下來需要編寫大量的文字和程式碼。「.rpy」指令碼檔案可以用系統自帶的記事本開啟,但我們推薦更高階的工具,比如VSCode。VSCode是免費使用的。

之後,確保你已安裝東方凋夢錄並訂閱官方模組。官方模組可幫助你理解模組開發過程並提供模組上傳工具。
建立模組資料夾
開啟《東方凋夢錄》安裝盤上的Steam或SteamLibrary資料夾,找到路徑為steamapps\workshop\content\2132480\3565758917的資料夾。這個資料夾應該在下載完官方模組後出現。
數字沒有特別之處,這些是Steam的識別碼,當你建立自己的模組時你也會得到一個識別碼。

在模組資料夾裡,開啟_upload_app資料夾並啟動SteamWorkshopUploader.exe。在左側選單中輸入你的模組名稱,然後點選右下角的Create Item按鈕。之後,一個存放你模組內容的資料夾將出現在WorkshopContent資料夾中,其中還有一個名稱為你的模組名.workshop.json的檔案。這就是我們需要的檔案。(我們建議你填寫完畢後將它儲存到某個地方)
開啟該檔案,複製publishedfileid欄位的值,並在steamapps\workshop\content\2132480資料夾下建立一個名為publishedfileid的資料夾。玩家將下載你的模組到這個相同路徑,因此推薦在這裡開發。在你的模組資料夾中建立mod_info.rpyscript.rpy檔案,然後用文字編輯器開啟你的模組資料夾中的mod_info.rpy檔案。
模組資訊
在官方模組的mod_info.rpy中,你可以看到以下程式碼:
init 99 python:
    tfi_official_mod_path = os.path.join(os.path.dirname(os.path.dirname(renpy.config.basedir)), "workshop/content/2132480/3565758917").replace("\\","/")
    global_game_mods["tfi_official_mod"] = {
      "start_scene": "tfi_official_mod_scene1",
      "description": _("This is a tutorial mod that also serves as a template for creating other mods. Open the folder steam/steamapps/workshop/content/2132480/3565758917 to look at the code and images in more detail."),
      "name": _("Tutorial mod"),
      "image": os.path.join(tfi_official_mod_path, "images/preview.png").replace("\\","/"),
    }
對於你的模組,你需要將此程式碼複製到你自己的mod_info.rpy中,並做如下修改:
  • global_game_mods["tfi_official_mod"]中的tfi_official_mod替換為任何其他使用拉丁字母的獨特名稱。這是你的模組在遊戲檔案中的識別碼。為了避免與其他模組衝突,我們建議使用你的模組名稱的拉丁字母形式(對於中文可以使用拼音),並在之後用於命名變量和場景。

  • 更改start_scene欄位的值。這是玩家啟動你的模組後要進入的第一個場景的名稱。簡單起見,可以複製上面的模組名稱,並在其後新增_scene1。注意場景名稱也必須是獨特的。

  • descriptionname欄位是玩家在遊戲內模組選擇選單中看到的文字。你可以隨意填寫,但請注意空間是有限的。你可以使用{size}標籤來加大或減小字號。
    "{size=+2}更大的文字{/size}"
    "{size=-2}更小的文字{/size}"

  • image欄位是模組預覽圖,同樣會顯示在模組選擇選單中。如果沒有圖片,可以將此欄位留空或刪除,這樣將顯示問號佔位圖。

最後你會得到如下圖所示的內容:

完成mod_info.rpy之後,將官方模組資料夾內的script.rpy複製到你自己的模組資料夾裡。下面將介紹其中程式碼。
「init:」程式碼塊
此部分包含你希望在模組中定義的變量。例如,語句define tfi_official_mod_dev = Character(_('TFI Main Dev'))將建立一個名為TFI Main Dev的新角色。你可以建立任意個角色,但要確保角色變量名是獨特的,並且不與遊戲本體變量或其他模組的變量衝突。我們建議變量名使用你的模組名稱作為字首。

如果一個角色是臨時使用的,你可以跳過定義變量,直接在其對話前使用角色名。
"TFI Main Dev" "Hello!"
"TFI Player" "Hey!"
下表列出了我們遊戲目前使用的變量,你可以按需引用。隨著新角色加入,我們將新增新的變量。

官作角色

名稱
標籤
名稱
標籤
名稱
標籤
名稱
標籤
名稱
標籤
博麗靈夢
rei
霧雨魔理沙
mar
聖白蓮
byak
火焰貓燐
orin
森近霖之助
korin
八雲紫
yuk
藤原妹紅
moko
八坂神奈子
kan
魂魄妖夢
youmu
東風谷早苗
san
茨木華扇
kas
克勞恩皮絲
clown
八意永琳
eirin
鈴仙·優曇華院·因幡
reisen
娜茲玲
naz
古明地覺
sat
靈烏路空
okuu
洩矢諏訪子
suwa
小野冢小町
koma
四季映姬·夜摩仙那度
eiki
chen
八雲藍
ran
射命丸文
aya
星熊勇儀
yuugi
魅魔
mima
二巖猯藏
mami
宇佐見堇子
sum
紅美鈴
mei
秦心
kokoro
十六夜咲夜
sak
古明地戀
koi
蕾米莉亞·斯卡蕾特
remi
宮出口瑞靈
mizu
鬼人正邪
seija
芙蘭朵露·斯卡蕾特
flan
赫卡提亞·拉碧斯拉祖利
heca

次要或原創角色

名稱
標籤
名稱
標籤
名稱
標籤
名稱
標籤
名稱
T標籤ag
少女
girl
么夢
hito
妖怪狸
bake
兔子
usausa
天狗
tengu
河童
kappa
閻魔
yamaraja
一目小僧
itime

變量tfi_official_mod_path是從遊戲本體資料夾到你的模組資料夾的快捷方式。如果你使用自定義圖片,該變量會很有用。你需要將變量名中的tfi_official_mod替換為你的模組名,並修改路徑workshop/content/2132480/3565758917中最後一個資料夾的名稱。對於3565758917/_game_resourses資料夾下的資源,你不需要透過該變量引用,因為它們是遊戲本體的一部分。但對於自己引入的檔案,你需要透過該變量訪問。
define mod_name_path = os.path.join(os.path.dirname(os.path.dirname(renpy.config.basedir)), "workshop/content/2132480/your_folder").replace("\\","/")
show image("[mod_name_path]/images/your_image1.png")
show image(Transform("images/backgrounds/hakurei_shrine_inter_d1.jpg", zoom=0.5))
場景與臺詞
label語句標示了一個場景的開始。你需要為場景指定獨特名稱。之前你已將初始場景設定為你的模組名_scene1,所以現在將label的名稱替換為初始場景名。

一切就緒後,剩下的就是構思一個情節並寫下來。文字應分段顯示在螢幕上以適應對話視窗。如果在一行臺詞前給定了角色識別碼,角色名將與文字一同顯示,否則只顯示文字。

遊戲還支援兩或三個角色同時說話。你只需要在臺詞右側相應新增(multiple = 2)(multiple = 3)
rei "Good luck." (multiple = 2)
mar "See ya." (multiple = 2)
影像
假設你寫好了對話,但怎麼新增圖片?這個問題要複雜一些。如果你在下面的示例中找不到問題的答案,我們建議你查閱Ren'Py文檔的相應部分[doc.renpy.cn]

背景和靜態影像
你可以使用show image("圖片路徑")語句在螢幕上顯示一張圖片。由於遊戲的目標解析度是1920x1080,而許多圖片有更高的解析度,因此在顯示前需要呼叫Transform()函式進行縮放。對於背景和插圖,我們使用的縮放係數是0.5;對於立繪則是0.6。你也可以使用其他值。另外建議在場景開始時使用scene語句,它會替換掉所有其他圖片。
scene image(Transform("images/backgrounds/human_village1_m.jpg", zoom=0.5))
show image(Transform(tfi_official_mod_reimu_happy, zoom=0.6))

動態立繪
在《東方凋夢錄》中,我們使用了非標準的Ren'Py方法來最佳化遊戲和新增演出。具體來説,我們為影像顯示系統添加了一個新的圖層,用於顯示角色分層立繪。
在我們的模組中,你可以看到以下程式碼:
$ chars = 2
$ elems = 13
$ scale = 0.6

$ pict_ = [
["none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png",],
["none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png","none.png",],
]

$ pict_1 = [
[
"none.png",
"none.png",
"none.png",
"sprites/Sakuya/sak_body1.png",
"sprites/Sakuya/sak_body1_brow1.png",
"sprites/Sakuya/sak_body1_eyes1_d.png",
"sprites/Sakuya/sak_body1_mouth2.png",
"sprites/Sakuya/sak_body1_leftarm2.png",
"sprites/Sakuya/sak_body1_rightarm6.png",
"none.png",
"none.png",
"none.png",
"none.png",
],
[
"none.png",
"none.png",
"none.png",
"sprites/Nazrin/naz_body1.png",
"sprites/Nazrin/naz_body1_brow1.png",
"sprites/Nazrin/naz_body1_eyes5_f.png",
"sprites/Nazrin/naz_body1_mouth18.png",
"sprites/Nazrin/naz_body1_arms1.png",
"none.png",
"none.png",
"none.png",
"none.png",
"none.png",
],
]

$ xp = [111000, 111000, 111000]
$ yp = [-130, -130, -130]
$ copy()
show screen _Pers_ onlayer add_l
這裡你可以看到咲夜和娜茲玲的立繪是如何實現的:我們使用了分層渲染來輕鬆改變表情和肢體位置。在指定具體的立繪之前,你需要定義變量chars(角色數量)、elems(立繪元素數量)和scale(立繪縮放係數),併為變量pict_分配與立繪類似的陣列,其中每個影像均用"none.png"

立繪的位置由兩個變量決定:xpyp,分佈控制水平和垂直位置。這兩個變量可以為每個立繪單獨設定,例如xp[1] = ...,或一次性為所有立繪設定,如xp = [..., ..., ...]

此外,我們使用一個單獨的圖層來顯示立繪:show screen _Pers_ onlayer add_l。該圖層位於標準影像圖層的前方,因此立繪將始終顯示在其他影像的前面。如果你想在立繪前面顯示影像,你需要使用onlayer add_l

以上所有都是非必需的。你可以預先將一個完整的立繪從各圖層中組合好,以類似的方式賦值給變量pict_1,然後切換到其他立繪。或者使用標準Ren'Py方法。

下面的程式碼展示了更改立繪元素以及從模組檔案組合「完整」靈夢立繪的示例。
$ pict_[0][4] = "sprites/Sakuya/sak_body1_brow3.png" # 4
$ pict_[0][5] = "sprites/Sakuya/sak_body1_eyes3_f.png" # 5
$ pict_[0][6] = "sprites/Sakuya/sak_body1_mouth6.png" # 6
$ pict_[0][7] = "sprites/Sakuya/sak_body1_leftarm3.png" # 7
$ pict_[0][8] = "sprites/Sakuya/sak_body1_rightarm1.png" # 8

$ pict_[1][4] = "sprites/Nazrin/naz_body1_brow3.png" # 4
$ pict_[1][5] = "sprites/Nazrin/naz_body1_eyes3_f.png" # 5
$ pict_[1][6] = "sprites/Nazrin/naz_body1_mouth10.png" # 6
$ pict_[1][7] = "sprites/Nazrin/naz_body1_leftarm1.png" # 7
$ pict_[1][8] = "sprites/Nazrin/naz_body1_rightarm4.png" # 8

hide image(Transform(tfi_official_mod_reimu_happy, zoom=0.6))

show image(Transform(tfi_official_mod_reimu_sad, zoom=0.6)):
    xalign 0.8
    yalign -0.1
with dissolve

更改顏色
你可以使用blur()函式更改影像的色調。該函式有四個參數,後3個分別是hue(色相)、brightness(亮度)、saturation(飽和度)。例如,blur(image, 320, 120, 100)將把image的色相改為320,亮度改為120,飽和度改為100。
$ pict_[0][3] = blur("sprites/Sakuya/sak_body1.png", -10, -0.22, 0.9) # 3
$ pict_[0][4] = blur("sprites/Sakuya/sak_body1_brow3.png", -10, -0.22, 0.9) # 4
$ pict_[0][5] = blur("sprites/Sakuya/sak_body1_eyes3_f.png", -10, -0.22, 0.9) # 5
$ pict_[0][6] = blur("sprites/Sakuya/sak_body1_mouth6.png", -10, -0.22, 0.9) # 6
$ pict_[0][7] = blur("sprites/Sakuya/sak_body1_leftarm3.png", -10, -0.22, 0.9) # 7
$ pict_[0][8] = blur("sprites/Sakuya/sak_body1_rightarm1.png", -10, -0.22, 0.9) # 8
特效
Ren'Py支援多種影像出現或消失的視覺效果。你可能已經注意到,我們提供的許多示例語句都以with dissolve結尾。這會使影像出現或消失時帶1秒的過渡效果。還有一個內建版本dissolve_f,它持續0.25秒,更改立繪元素時可避免特效過長。你可以使用類似的變量建立自己的過渡效果:
define dissolve_five = Dissolve(5) # a 5-second transition
我們基本上只使用這些特效。類似地,你也可以使用其他特效,請參考Ren'Py文檔中關於特效的更多資訊[doc.renpy.cn]
音樂和音效
你可以使用renpy.music.playrenpy.music.stop語句分別播放或停止音樂。在指定檔案路徑後,你可以定義音軌在哪個通道中播放,以及用於開始或淡出音軌的特效。
$ renpy.music.play("sounds/ambience/crowd_medium1.ogg", channel="music_2", synchro_start=True, fadein=2.0)
$ renpy.music.stop(channel="music_2", fadeout=1.5)
有三個音樂通道:musicmusic_1music_2,以及一個用於一次性播放聲音的通道sound
選擇
我們修改了標準選項選單,使其不僅顯示可能的選項,還顯示向玩家提出的問題。要顯示問題,你需要修改menu_question變量:
$ menu_question = _("Do you need an extra round?")

$ quick_menu = False
window hide dissolve

menu:
    with dissolve
    "Yes, please!":
      $ quick_menu = True
      window show dissolve
      jump tfi_official_mod_scene1
    "Nah, I got it.":
      $ quick_menu = True
      window show dissolve
使用變量
你可以使用變量來跟蹤玩家的選擇。變量型別包括但不限於字串(= "one")數字(= 1)布林值(= True)。你可以使用iffor關鍵字來操作變量。你可以在Ren'Py文檔的相關部分[doc.renpy.cn]瞭解更多關於變量的資訊。
$ menu_question = _("One or two?")

menu:
    with dissolve
    "One!":
      $ mod_name_player_choice = 1
      jump mod_name_scene_player_pick_one
    "Two!":
      $ mod_name_player_choice = 2
      jump mod_name_scene_player_pick_two
if your_mod_condition == 2:
    jump destitute1_scene2_5
else:
    jump destitute1_scene1_5
翻譯
注意:我們團隊的部分成員表示願意翻譯喜歡的模組。如果你有興趣,請在QQ群882402531或Discord伺服器[discord.gg]與我們聯絡。

如果你計劃建立一個支援多語言的模組,你需要生成翻譯檔案。要生成它們,你需要下載Ren'Py 8.3.7[www.renpy.org],指定遊戲路徑並生成所需語言的翻譯。
你不僅可以翻譯對話臺詞,還可以翻譯變量和角色名。為此,在宣告變量時,將文字用下劃線和括號擴起來:_(...)
define tfi_official_mod_dev = Character(_('TFI Main Dev'))
但請注意,某些變量翻譯可能會導致錯誤,因此建議僅為臺詞生成翻譯檔案。臺詞需要翻譯識別碼,而對於變量,可翻譯文字本身就充當識別碼。
# translatable line
translate english prologue_scene_0_89ef4bf6:
    "Humanity has always been afraid of the unknown."

# translatable variable
translate english strings:
    old "Продолжение следует..."
    new "To be continued..."
生成翻譯檔案後,將tl資料夾移動到你的模組資料夾中,並填寫檔案中所有需要翻譯的內容。
我們還建議你在模組開始時建立一個類似的選單,以便玩家瞭解模組支援哪些語言。
menu:
    with dissolve
    "{font=fonts/Merriweather-Regular.ttf}Я хочу играть на Русском{/font}":
      $ renpy.change_language("russian")
    "{font=fonts/Merriweather-Regular.ttf}I want to play in English{/font}":
      $ renpy.change_language("english")
    "{font=fonts/SourceHanSansCN-Regular.otf}{size=26}以簡體中文開始{/size}{/font}":
      $ renpy.change_language("schinese")
    "{font=fonts/NotoSansJP-Regular.ttf}{size=26}日本語でプレイする{/size}{/font}":
      $ renpy.change_language("japanese")
截至本指南編寫時,遊戲支援9種語言:俄語(russian)英語(english)法語(french)西班牙語(spanish)葡萄牙語-巴西(portuguese)繁體中文(tchinese)簡體中文(schinese)韓語(korean)日語(japanese)。如果你的語言不使用這些語言的字母表,你需要在你的模組中包含新的字型。
附加功能
鏡頭操作
由於角色顯示在一個單獨的螢幕上,如果你想放大或縮小影像,你需要同時移動這個螢幕和標準的鏡頭圖層。
camera:
    perspective True
    gl_depth True
    zpos -1100 xpos -375 ypos -380
    easein 3.0 zpos 0 xpos 0 ypos 0
show layer add_l:
    perspective True
    gl_depth True
    zpos -1100 xpos -375 ypos -380
    easein 3.0 zpos 0 xpos 0 ypos 0

日期和時間介面
遊戲內建了一個功能,可以顯示一個帶有地點、日期和時間的介面。如果你的故事也涉及以天和小時為單位的時間劃分,這將很有用。
$ date_place = _("Muenzuka")
$ date_date = _("April 17, 2018")
$ date_time = _("Evening, a few hours later")
show screen date with dissolve

載入介面
如果立繪的「重組」或顏色變化很大,可能會導致遊戲出現輕微延遲,因為Ren'Py需要時間來處理並將所有影像單獨放置在螢幕上。為了減緩體感延遲,我們建立了一個帶有「少女祈禱中」的過渡介面。要使用它,你需要show screen loadinghide screen loading語句。為了平滑過渡,我們使用以下語句隱藏然後顯示對話視窗。
$ quick_menu = False
window hide dissolve

$ quick_menu = True
window show dissolve
測試模組
在釋出模組前,你需要確保模組能正常遊玩。開發者模式有助於你測試模組。要使用開發者模式,請在你的init程式碼塊中新增以下程式碼:
define config.developer = True
define config.default_developer = True
現在按下Shift + R組合鍵,每當檔案更改,遊戲都會熱重載。這意味著你可以在編輯程式碼後立即看到結果。但注意在上傳模組前刪除開發者模式程式碼。
上傳模組
完成所有步驟後,你需要:
1. 將模組檔案放入上傳程式中的模組資料夾。你需要一張正方形預覽圖,且解析度至少為500x500畫素。
2. 輸入所有必要資訊。我們建議將整個_upload_app資料夾移動到2132480資料夾之外的某個地方,以避免與其他模組的檔案衝突。
3. 新增版本更改說明並點選Submit按鈕。所有輸入的資訊將儲存在你的模組名.workshop.json檔案中。


如果你有任何問題,請在本指南的評論區中反饋,以便我們更新資訊。祝你好運!