Project1
标题: 【转载】RPG Maker VX Ace 帮助文档 脚本-实践篇 [打印本页]
作者: taroxd 时间: 2014-6-22 08:22
标题: 【转载】RPG Maker VX Ace 帮助文档 脚本-实践篇 本帖最后由 taroxd 于 2014-6-22 10:57 编辑
转载自 https://googledrive.com/host/0B2GGQwUSXTWfVVNMd05iVlB5Nkk/ 需要翻墙
如果繁体字看着不舒服,请在论坛左上角Logo上方点一次“繁体中文”,再点一次“简体中文”即可。
部分繁体中文属于与简体中文不同,请凑合着看……
目录
2L 改造计时器
3L 制作剧情提示画面
4L 制作逃脱道具
5L 制作道具消耗技能
6L 制作代币商店
7L 制作传送道具
8L 改造标题画面
作者: taroxd 时间: 2014-6-22 08:25
本帖最后由 taroxd 于 2014-6-22 08:32 编辑
在實踐篇中,筆者會一邊實際改造遊戲內容,一邊解說必要的知識。
一開始先來個簡單的範例,我們的目標是變更透過事件指令製作的計時器的顯示方式。
[box=Sienna]製作計時器事件
[/box]
計時器事件
首先將腳本編輯器關閉後,製作一個測試用的事件。
按照右邊的截圖,將計時器放入一個自動執行的事件,讓自己能夠馬上在遊戲中測試這個計時器是如何顯示的。
[box=Sienna]製作自訂腳本
[/box]
在基礎篇中,我們一開始設置在最上方區域的腳本已經沒有用,請把它移除。並在最下方標示「自訂腳本」的區域下方新建一個區域。給它一個合適的名字即可。
要直接編輯內建的腳本也沒關係,不過將自己更改的部分集合在同一個地方,在很多方面會比較方便。也因此,即使沒有打算把腳本當成素材發布給其他人,建議也請把它當成一個獨立的素材來處理。
[box=Sienna]尋找需修改處的方式
[/box]
製作自訂腳本的時候,首先要確定變更**才能讓程式運行出自己需要的結果。
因為我們這次想改造的是計時器,所以先用「Timer」這個字串去搜尋全部的區域。「僅全詞符合」的選項請不要打勾。等到數筆搜尋結果顯示之後,依序往下找就能找到可能和計時器的顯示有關的段落。
在本次的情況,Sprite_Timer這個類別就是我們要的。這個類別在Sprite的操作這個章節一開始的表也有提到呢。鎖定自己要改造的區域並不是每次都可以這麼簡單,不過如果能夠掌握訣竅的話,就能在相對較少的時間內找到。
[box=Sienna]重新宣告類別和方法
[/box]
點進在自訂腳本區新創的區域後,按照下方輸入。
在Ruby中,已經宣告過的類別可以在後頭變更內容。所以只要在新的類別中寫進方法,會把它們當作是在原來Sprite_Timer這個類別中宣告一樣進行處理。
讀讀看原本的Sprite_Timer,我們會發現create_bitmap這個方法。這個方法的意義是,將匯入的點陣圖檔案實際顯示出影像,並設定顯示的文字大小與顏色等參數。筆者這次想改造的就是這個部分,因此將這個方法複製貼上到自訂腳本的類別裡面。
class Sprite_Timer
def create_bitmap
self .bitmap = Bitmap.new ( 96 , 48 )
self .bitmap .font .size = 32
self .bitmap .font .color .set ( 255 , 255 , 255 )
end
end
class Sprite_Timer
def create_bitmap
self .bitmap = Bitmap.new ( 96 , 48 )
self .bitmap .font .size = 32
self .bitmap .font .color .set ( 255 , 255 , 255 )
end
end
如果宣告了相同名字的方法,後宣告的方法有較高的優先度,這樣一來,我們就能只將create_bitmap這個方法的內容作替換。
[box=Sienna]變更方法的宣告內容
[/box]
接著實際改改看計時器的顯示方式吧。
class Sprite_Timer
def create_bitmap
self .bitmap = Bitmap.new ( 128 , 96 )
self .bitmap .font .size = 64
self .bitmap .font .color .set ( 255 , 0 , 0 )
end
end
class Sprite_Timer
def create_bitmap
self .bitmap = Bitmap.new ( 128 , 96 )
self .bitmap .font .size = 64
self .bitmap .font .color .set ( 255 , 0 , 0 )
end
end
像上方一樣修改數值,並測試看看。如果計時器變成用大大的紅字顯示的話就是成功了。
若是有人不清楚這裡變更的數值所代表的意義,不妨自己改成別的數值,實驗看看會有什麼變化吧。而如果想要確定詳細的設定內容,請參考以下類別的文件: Bitmap、Font、Color
[box=Sienna]别名
[/box]
按照上述方式,這樣就算是完成了「用大大的紅字顯示計時器的腳本素材」,不過若是真的 要當成「素材」發布的時候,一旦將原本的方法全部改寫,有可能會發生和其他的腳本素材相衝的狀況。所以若是不透過改寫,而是想在原本的處理中的前後新增一些處理的話,會使用別名(alias)這個功能。
class Sprite_Timer
alias xxx001_create_bitmap create_bitmap
def create_bitmap
xxx001_create_bitmap
self .bitmap .font .color .set ( 255 , 0 , 0 )
end
end
class Sprite_Timer
alias xxx001_create_bitmap create_bitmap
def create_bitmap
xxx001_create_bitmap
self .bitmap .font .color .set ( 255 , 0 , 0 )
end
end
按照上面宣告後,就能在執行原本的create_bitmap方法後,單純變更字體的顏色。
alias xxx001_create_bitmap create_bitmap
我們利用這一行,給了舊的create_bitmap一個新的名字xxx001_create_bitmap,讓它避免衝突。因為先寫了這一行,所以才能夠在這個新的create_bitmap中呼叫舊的處理內容。只不過如果取的別名本身也撞名,那還是會發生衝突。因此為了避免xxx001和其他素材撞名,獨自取一個名字是有必要的。如果一個方法在不同的自訂腳本中被重新宣告過,也比照辦理。
這個部分容易搞混,重新宣告create_bitmap方法,並不因此代表舊的create_bitmap方法的內容遭到取代。比較像是把另一個方法用同樣的名字宣告,只是我們從外部直接觀察結果的時候,看起來就是這個方法的內容有被置換過。事先將舊的方法利用別名避免衝突的話,舊方法的內容並不會受到新的宣告的影響。
作者: taroxd 时间: 2014-6-22 08:38
在本章中筆者會解說在選單畫面中加入新的指令,呼叫出專屬的佈景的方法。
我們這次以全畫面顯示的視窗「劇情提示」為例,製作「劇情提示」指令。
[box=Sienna]新增自訂指令
[/box]
在選單畫面中,與選擇「道具」、「技能」之類指令的那塊視窗對應的類別是Window_MenuCommand。
麻煩到Window_MenuCommand的區域,大致確認一遍內容看看。有看見add_original_commands這個空的方法吧。在VX Ace裡頭,只要重新宣告這個方法,就可以很容易地追加指令。具體的寫法如下所述:
class Window_MenuCommand
alias xxx001_add_original_commands add_original_commands
def add_original_commands
xxx001_add_original_commands
add_command( "劇情提示" , :story )
end
end
class Window_MenuCommand
alias xxx001_add_original_commands add_original_commands
def add_original_commands
xxx001_add_original_commands
add_command( "劇情提示" , :story )
end
end
請將這段程式碼加到自訂腳本的地方,在測試遊戲的時確認是否自訂指令已經增加到選單中。
儘管這個方法原本就是空白的,使用別名是考慮到別的腳本素材可能已經重新宣告過這個方法,所以才會用這個方法避免衝突。再讓筆者重複一次,在網路上發布獨立的腳本素材時,請務必更改xxx001這一處。
add_command( "劇情提示" , :story )
add_command( "劇情提示" , :story )
add_command這個方法,是在解讀篇的視窗的操作中解說過的東西。 我們在這裡令叫作「劇情提示」的指令和叫作:story的符號(symbol)產生聯繫。
[box=Sienna]設定Command Handler
[/box]
指令被選擇時,實際在進行處理的不是Window_MenuCommand,而是Scene_Menu這個類別。麻煩進到Scene_Menu的區域,並確認內容。應該會在create_command_window這個方法中,看見以下的這一行:
@command_window .set_handler ( :item , method( :command_item ) )
@command_window .set_handler ( :item , method( :command_item ) )
就像在視窗的操作中解說過的,這是在對應:item的指令被選擇之後,呼叫出command_item這個方法的指定方式。
呼叫出的command_item以下面的方式宣告。在這裡使用了解在解讀篇的佈景的操作中解說過的佈景的呼叫方式。
def command_item
SceneManager.call ( Scene_Item)
end
def command_item
SceneManager.call ( Scene_Item)
end
接下來,在選擇了和:story這個符號產生關連的指令之後,要切換到Scene_Story(後面解釋)這個類別的話,腳本如下方所寫。基本上只要模仿內建腳本,依樣畫葫蘆就可以了。
class Scene_Menu
alias xxx001_create_command_window create_command_window
def create_command_window
xxx001_create_command_window
@command_window .set_handler ( :story , method( :command_story ) )
end
def command_story
SceneManager.call ( Scene_Story)
end
end
class Scene_Menu
alias xxx001_create_command_window create_command_window
def create_command_window
xxx001_create_command_window
@command_window .set_handler ( :story , method( :command_story ) )
end
def command_story
SceneManager.call ( Scene_Story)
end
end
[box=Sienna]製作佈景
[/box]
接下來要製作轉移到的Scene_Story這個類別。這次當作範例製作的「劇情提示」畫面屬於選單畫面類,因此將Scene_MenuBase指定為父類別。
class Scene_Story < Scene_MenuBase
end
class Scene_Story < Scene_MenuBase
end
將佈景產生時會被呼叫的start方法用置換(override)重新定義,進行產生視窗的處理。在以下所用到的Window_Story這個類別會在後面宣告。
class Scene_Story < Scene_MenuBase
def start
super
@story_window = Window_Story.new
@story_window .set_handler ( :cancel , method( :return_scene ) )
end
end
class Scene_Story < Scene_MenuBase
def start
super
@story_window = Window_Story.new
@story_window .set_handler ( :cancel , method( :return_scene ) )
end
end
只要是從Window_Selectable這個類別衍生出來的視窗,因為已經設定了:cancel這個符號的Handler,所以可以指定在「有效狀態」下按下取消鈕後所進行的處理。return_scene是在Scene_Base類別中宣告的方法。人如其名,它負責進行回到上一個佈景的處理。
此外,scene類別中,直接被帶入到實體變數的視窗,會由Scene_Base自行釋放,因此在這裡不需要寫下dispose的處理。
註:我們製造了一個視窗,會占用電腦的記憶體空間,所以需要dispose這樣的動作去釋放記憶體。
[box=Sienna]製作視窗
[/box]
再來是製作顯示出劇情提示畫面的視窗,也就是Window_Story這個類別。如前面所說,雖然我們沒有利用到多重選項的處理,因為筆者想使用:cancel的處理,所以讓這個類別繼承了Window_Selectable類別。
class Window_Story < Window_Selectable
def initialize
super ( 0 , 0 , Graphics.width , Graphics.height )
draw_text_ex( 4 , 0 , "要顯示在視窗裡的字串" )
activate
end
end
class Window_Story < Window_Selectable
def initialize
super ( 0 , 0 , Graphics.width , Graphics.height )
draw_text_ex( 4 , 0 , "要顯示在視窗裡的字串" )
activate
end
end
在基礎篇的類別的宣告中也解說過,initialize是用來初始化的方法,在一個物件產生之後會自動呼叫。super則是呼叫父類別中同名的方法的保留字。我們在這裡叫出了Window_Selectable這個類別的initialize方法。
至於Graphics.width和Graphics.height,則是分別取得畫面寬度(x軸)與高度(y軸)的方法。透過這兩者,我們令充滿整個畫面大小的視窗產生。
draw_text_ex乃是在視窗中畫出字串內容的方法,也對應\C[n]或是\V[n] 等指令文字。但是要注意,Ruby本身在處理字串時,\ 這個符號本身就具有特殊的意義,在利用""雙引號夾住字串的時候,必須要用\\這樣的雙重寫法來表示\。
所謂的activate,是讓視窗成為有效狀態的方法。這樣一來:cancel的處理才會生效(這次我們只設定了:cancel的處理,因此像是按下確定鍵也不會發生任何事情)。
輸入完以上的程式碼後,就可以測試遊戲了。趕快執行看看吧。
註:第六章會有更多關於activate的解釋。簡單來說,即使我們在畫面上畫了一個視窗,如果不用這個方法告訴系統說「我現在要操作那個視窗」,自然就無法操作該視窗,包含用取消鍵來關掉也不行。
[box=Sienna]參照遊戲中的變數
[/box]
如果要依據事件的進行狀況來變更顯示的內容,利用RM的變數功能就很簡單。比方說要參照變數07的值,可以用$game_variables[7]。如果要在遊戲中控制變數的值,請利用事件指令(變數的操作),或者是用F9叫出debug畫面後再進行操作。
class Window_Story < Window_Selectable
def initialize
super ( 0 , 0 , Graphics.width , Graphics.height )
case $game_variables [ 7 ]
when 0
story = "顯示的是劇情在第0階段時的提示"
when 1
story = "顯示的是劇情在第1階段時的提示"
when 2
story = "顯示的是劇情在第2階段時的提示"
end
draw_text_ex( 4 , 0 , story)
activate
end
end
class Window_Story < Window_Selectable
def initialize
super ( 0 , 0 , Graphics.width , Graphics.height )
case $game_variables [ 7 ]
when 0
story = "顯示的是劇情在第0階段時的提示"
when 1
story = "顯示的是劇情在第1階段時的提示"
when 2
story = "顯示的是劇情在第2階段時的提示"
end
draw_text_ex( 4 , 0 , story)
activate
end
end
這次的範例用的是case ~ end形式的條件分岐,但若使用雜湊的話,可以寫得更漂亮。覺得太簡單的人務必挑戰看看。
作者: taroxd 时间: 2014-6-22 08:46
本帖最后由 taroxd 于 2014-6-22 09:00 编辑
在這部分,筆者將會解說從選單畫面使用道具或技能時,進行該技能或道具的專屬處理的方法。
我們這次以製作從迷宮內部傳送脫逃到入口的道具為範例。
[box=Sienna]製作道具資料
[/box]
在製作腳本之前,先打開資料庫增設定逃道具。請在適當的地方增加一個新的項目。
把該道具的作用對象設為「無」,使用時機設為「僅在選單中」,然後預先在備註欄輸入<ESCAPE>這串文字(要包含<>)。
要設定道具或技能的專屬效果時,利用備註欄這個設定項目很方便,因為系統可以透過備註欄中是否寫有特定的文字進行判定,比起透過道具名稱或ID來判斷更具泛用性。本次的系統在道具含有<ESCAPE>這個關鍵字時,就會將之認定為逃脫道具。
資料庫的設定結束之後,請製作一個含有「道具增減」指令的事件,讓自己能夠馬上在測試遊戲中使用它。
註:是備註欄不是道具說明文字。
[box=Sienna]道具使用的處理
[/box]
從選單畫面使用技能或道具時,進行處理的是Scene_ItemBase 類別的use_item這個方法(雖然叫作item,但也包含技能的處理)。按照下面的方式宣告當作第一步吧。
class Scene_ItemBase
alias xxx001_use_item use_item
def use_item
xxx001_use_item
use_escape_item if item.note .include ?( "<ESCAPE>" )
end
def use_escape_item
print "逃脫!\n "
end
end
class Scene_ItemBase
alias xxx001_use_item use_item
def use_item
xxx001_use_item
use_escape_item if item.note .include ?( "<ESCAPE>" )
end
def use_escape_item
print "逃脫!\n "
end
end
在這數行中,追加了這樣的處理:如果使用的道具的「備註欄」中含有<ESCAPE> 這個字串的話,會呼叫use_escape_item方法。
use_escape_item if item.note .include ?( "<ESCAPE>" )
use_escape_item if item.note .include ?( "<ESCAPE>" )
所謂的item,指的是所使用的道具項目的物件,也就是RPG::Item或是 RPG::Skill這些回傳實體的方法(item的物件並不是在Scene_ItemBase類別宣告的,而是在繼承這個類別的Scene_Item以及Scene_Skill這兩者中宣告)。note則是對應「備註欄」的字串。至於字串類別String中的include?方法,則是判定是否包含特定的字串。
而use_escape_item中的內容則是將「逃脫!」字串輸出到控制台──先用這個替代實際處理。
def use_escape_item
print "逃脫!\n "
end
def use_escape_item
print "逃脫!\n "
end
實際進行測試,使用剛才製作的逃脫道具,確認方法有沒有正常被呼叫,有的話就進到下一步吧。
註:這就是類別(class)和實體(instance)的差別,在資料庫裡面的那些道具,並不是真正的道具,而是像設計圖一樣的東西。在遊戲中獲得的真正的道具就是依那份設計圖生產的,也就是實體的概念。可想而知,同樣的道具使用的是同一份設計圖。
[box=Sienna]佈景的移轉
[/box]
請將use_escape_item按照下面的方式改寫。使用道具之後,就能夠自動回到地圖畫面。在前一章我們使用過call這個方法,不過在不需要回到前一個畫面時,就直接用goto方法。
def use_escape_item
SceneManager.goto ( Scene_Map)
end
def use_escape_item
SceneManager.goto ( Scene_Map)
end
Scene_Map正如其名,是對應地圖畫面的佈景類別。因為像這樣利用了SceneManager模組呼叫出goto方法,在使用道具後就能夠自動回到地圖畫面。
[box=Sienna]場所移動
[/box]
在對應主角人物的Game_Player這個類別中有能夠保存傳送的目的地的reserve_transfer方法。
def use_escape_item
$game_player .reserve_transfer ( 1 , 10 , 8 )
SceneManager.goto ( Scene_Map)
end
def use_escape_item
$game_player .reserve_transfer ( 1 , 10 , 8 )
SceneManager.goto ( Scene_Map)
end
引述依序是地圖ID、X座標和Y座標。上面的例子表示往地圖ID=1,x座標=10,y座標=8的地點傳送。麻煩像這樣先指定出適合的地點,實驗看看是否能夠正常傳送。
確保了傳送的動作之後,將程式變更為由遊戲內的變數決定傳送地。
def use_escape_item
m = $game_variables [ 21 ]
x = $game_variables [ 22 ]
y = $game_variables [ 23 ]
$game_player .reserve_transfer ( m, x, y)
SceneManager.goto ( Scene_Map)
end
def use_escape_item
m = $game_variables [ 21 ]
x = $game_variables [ 22 ]
y = $game_variables [ 23 ]
$game_player .reserve_transfer ( m, x, y)
SceneManager.goto ( Scene_Map)
end
在這例子中筆者使用了變數21~23當作移動地。當然使用的變數編號不是這三個也無妨
註:第一張地圖的ID為1而不是0,以此類推。
[box=Sienna]道具的無效狀態
[/box]
逃脫道具在迷宮以外的地方不能使用的情況比較多吧。所以更改一下道具可以使用的判定條件,當存放地圖ID的變數為0的時候,就設為無法選用。
class Game_BattlerBase
alias xxx001_usable_item_conditions_met? usable_item_conditions_met?
def usable_item_conditions_met?( item)
if item.note .include ?( "<ESCAPE>" ) && $game_variables [ 21 ] == 0
false
else
xxx001_usable_item_conditions_met?( item)
end
end
end
class Game_BattlerBase
alias xxx001_usable_item_conditions_met? usable_item_conditions_met?
def usable_item_conditions_met?( item)
if item.note .include ?( "<ESCAPE>" ) && $game_variables [ 21 ] == 0
false
else
xxx001_usable_item_conditions_met?( item)
end
end
end
判定道具或技能是否可以使用,主要是由Game_BattlerBase類別 所負責。在這裡我們重新宣告了usable_item_conditions_met?這個方法。就像腳本內的註解所寫的一樣,這個方法是用來確認技能或道具可以使用的共通條件。回傳true的話代表可以使用,false的話則代表不能。
如果想要掌握可以使用與否的判定,只要從usable?方法開始追溯並嘗試解讀看看就好囉。
作者: taroxd 时间: 2014-6-22 08:50
本帖最后由 taroxd 于 2014-6-22 09:01 编辑
本章,筆者將針對技能,解說如何設定MP和TP以外的技能消耗。
這一次就以製作會消耗道具的技能作為範例。
[box=Sienna]新增道具和技能
[/box]
首先打開資料庫,新增測試用的資料。
資料的內容是什麼都沒差,只是若是用以下的概念設定,也許比較好理解:使用「毒雲箭」技能需要消耗「毒箭」道具。不過這只是範例的緣故,所以技能的種類不設成戰鬥用的也無所謂。像是使用時機設定為「隨時」、作用對象則為「無」的話,會比較容易測試運作。
至於道具消耗的設定,則麻煩在新增的技能的備註欄中寫入:<ITEM_COST n>。將n指定為道具ID──舉例來說,「毒箭」的道具ID為25,就在「毒雲箭」的備註欄寫進<ITEM_COST 25>。
新增的技能就讓想要的角色學會吧。 要讓角色學會技能,就看一下該角色的職業,追加到該職業上。還有也別忘了製作一個可以取得所需道具的事件。
[box=Sienna]正則表達式
[/box]
在前一章我們利用了include?對備註欄的字串進行判定,然而如果我們想指定道具ID等等參數的話,不適合用這個方法。
通常這種情況會使用正則表達式(Regular Expression)這個功能。所謂的正則表達式是,用單一字串表達複數的字串的一種表達方式。不光是Ruby,其他的程式語言或是工具和文字編輯器的搜索功能常常使用到。
而Ruby的情況,只要是前後用/這個記號包住的地方,就會被視作是正則表達式,成為Regexp這個類別的實體變數。對應我們這次備註欄寫法的正則表達式長得像下面這樣:
\s表示「空格」,\d表示「數字0~9」,*表示「重複0次以上」,+表示「重複1次以上」,( )代表把括號內的內容當成一組字串,作為後方參考之用。
有關正則表達式的詳細表現方式,請參考RGSS文件正則表達式的頁面。
註:所謂的「後方參考」,簡單來說就是括號內的東西在會作為變數供之後使用,舉例來說,「毒雲箭」只接受道具ID=25的「毒箭」,所以我們必須要偵測所使用的道具,它的ID是否符合25這個數字。
[box=Sienna]取得道具ID
[/box]
接著就實際使用正則表達式,製作用來取得消耗的道具ID的方法。
class RPG::Skill
def item_cost
/<ITEM_COST\s*( \d+) >/ =~ note ? $1 .to_i : 0
end
end
class RPG::Skill
def item_cost
/<ITEM_COST\s*( \d+) >/ =~ note ? $1 .to_i : 0
end
end
這次我們在 RPG::Skill 類別中額外宣告了這個方法。只要像這樣做,就算是RGSS內已經宣告好的類別,也可以用這個腳本教學以來一直使用的方法變更宣告的內容。而且我們並不是重新宣告item_cost方法,只是純粹增加新的內容,所以 不需要使用到別名。
下方將進行解說內容的部分。
/<ITEM_COST\s*( \d+) >/ =~ note ? $1 .to_i : 0
/<ITEM_COST\s*( \d+) >/ =~ note ? $1 .to_i : 0
=~這個運算符號,是用來判斷字串(的一部分)是否符合正則表達式。
note在上一章也使用過,是對應備註欄的字串。因為note是RPG::Skill自己的方法,所以不需要寫呼叫該方法的對象(receiver)。
註:通常方法的呼叫方式是物件名.方法名,呼叫該方法的物件即是receiver,不過RPG::Skill和它所繼承的RPG::BaseItem類別一樣,note是「自己的方法」,這種情況下可以寫self.note而self.實際上又可省略,因此變成上述的note。
這裡的$1,是一種內建變數,在這裡是指正則表達式中被括號括起來的部分。比方說我們指定的是 <ITEM_COST 25>這個內容,$1反映的就是25這一部分。因為$1是字串的關係,我們使用了to_i方法將之轉換為整數的形式。
簡言之,以上的方法表示的是,如果我們有指定消耗道具的ID,就會回傳該ID,反之就回傳0。
註:所以$1就是前面提到的後方參考的使用方法。一個正則表達式內可以有數個內建變數,$1完整的意涵是「第一個」括號內的東西,如果要參考的是第二個括號內的東西,就可以使用$2,以此類推。由於使用這個所得到的值為字串,如果要將它變成可運算的整數,就需要用到to_i這個方法。
註:通常我們呼叫一個方法,除非有特別聲明,在該方法結束的時候需要它回傳(return)一個值,代表它已經執行完它的任務,這也是為什麼我們通常只讓一個方法做一件事情的關係。而在Ruby中,return可以省略不寫,實際上/<ITEM_COST\s*(\d+)>/ =~ note ? $1.to_i : 0這一行就代表了回傳的動作與值本身。相較於方法的回傳動作,主程式所負責的就是取得(get)回傳的值。
[box=Sienna]是否滿足技能消耗條件
[/box]
Game_BattlerBase類別中的skill_cost_payable?方法,負責判定技能消耗是否達到條件,在這裡如果回傳了false,則代表判定了該技能無法使用。
但由於這次是道具的消耗,且和敵人沒有任何關聯,因此改在Game_BattlerBase的子類別Game_Actor中重新宣告。
class Game_Actor
alias xxx001_skill_cost_payable? skill_cost_payable?
def skill_cost_payable?( skill)
item = $data_items [ skill.item_cost ]
xxx001_skill_cost_payable?( skill) &&
( !item || $game_party .has_item ?( item) )
end
end
class Game_Actor
alias xxx001_skill_cost_payable? skill_cost_payable?
def skill_cost_payable?( skill)
item = $data_items [ skill.item_cost ]
xxx001_skill_cost_payable?( skill) &&
( !item || $game_party .has_item ?( item) )
end
end
就像在解說資料庫時提到的,$data_items是道具的陣列,第一個元素(也就是編號0)中放置的是空值nil。正因此,因為剛剛製作的item_cost在沒有指定消耗道具的ID的時候一律回傳0,導致:
item = $data_items [ skill.item_cost ]
item = $data_items [ skill.item_cost ]
這裡的item在消耗道具ID有指定的情況下回傳該物件,沒有的話就會回傳nil。
xxx001_skill_cost_payable?( skill) &&
( !item || $game_party .has_item ?( item) )
xxx001_skill_cost_payable?( skill) &&
( !item || $game_party .has_item ?( item) )
!item是用來判斷item是真是假的片語(是nil的話就會判定為假)。$game_party.has_item?方法則是判定隊伍中是否持有一個以上的該道具。
換句話說,這個判斷式除了判定是否符合原本的使用條件(MP等等),又進行了「是否有指定消耗道具的ID、隊伍中是否真的持有所指定的消耗道具」這兩個條件判定。
[box=Sienna]技能消耗
[/box]
使用技能後,實際進行使用消耗的處理的是pay_skill_cost這個方法。我們同樣在Game_Actore中重新宣告。
class Game_Actor
alias xxx001_pay_skill_cost pay_skill_cost
def pay_skill_cost( skill)
xxx001_pay_skill_cost( skill)
item = $data_items [ skill.item_cost ]
$game_party .lose_item ( item, 1 ) if item
end
end
class Game_Actor
alias xxx001_pay_skill_cost pay_skill_cost
def pay_skill_cost( skill)
xxx001_pay_skill_cost( skill)
item = $data_items [ skill.item_cost ]
$game_party .lose_item ( item, 1 ) if item
end
end
$game_party.lose_item這個方法是用來減少隊伍中持有的某道具我們指定的數量。
$game_party .lose_item ( item, 1 ) if item
$game_party .lose_item ( item, 1 ) if item
這句的意思是,只要item不為空值,就會呼叫lose_item方法,減少該道具一個數量。
這次解說的使用消耗面的改造,在許多的應用上都能發揮作用。比方說HP的消耗、金錢的消耗,或者是導致使用者的狀態產生變化等等,有興趣的話務必挑戰看看。
作者: taroxd 时间: 2014-6-22 08:53
本帖最后由 taroxd 于 2014-6-22 09:02 编辑
筆者將在本章解說商店畫面的改造方法。
我們會製作可以用某個變數的值當成持有金,以進行道具交換的代幣商店作為範例。
[box=Sienna]宣告常數
[/box]
到目前為止的範例中,比方說要參考變數7號的值時,我們都是用$game_variables[7]這樣的方式直接指定數字。這麼做是以範例必須簡單易懂為主要的考量,然而一般的情況,如果某個變數之後會常常變更值,不要用7而是用常數的形式去宣告會是較好的做法。
關於常數,就跟在 Vocab模組的章節中解說過的一樣,只要以英文大寫字母為開頭宣告變數名稱就好,只是習慣上常常會全部字母都用大寫。
這次我們要製作的商店的規格是,在開關20為ON的狀態時,呼叫一般商店時會變成代幣商店,而且將以變數20的值當作持有的代幣額度。
PRIZE_SHOP_SID = 20
PRIZE_SHOP_VID = 20
PRIZE_SHOP_SID = 20
PRIZE_SHOP_VID = 20
我們將開關ID省略成SID、變數ID省略成VID的形式命名。這個宣告直接寫在類別宣告的外部就好。如果要在不同的地方使用同樣的數值的話,像這樣先用常數來定義好,後面想要變更常數的值的時候,發生有地方漏掉變更這種失誤的風險就會消失。
在實作商店之前,考量到等等需要測試,麻煩先製作一個事件,讓變數20增加到適當的值、開關20為ON的狀態,並且會呼叫出商店。
註:以上兩段的說明可能很容易誤解。常數通常是相對變數,宣告之後就也無法變更。比方說我們會宣告圓周率PI=3.14,之後就會直接用PI作運算。上面的意思是想表達,如果需要常常對一個變數作運算,比起用$game_variables[7]這樣的方式,不如使用$game_variables[常數]的方式去宣告要操控的變數,經驗上會是比較好的做法。原因在於,比方說如果我今天想把3.14改成3.1415,那我只要修改常數宣告時的那個地方就好,不用每個有用到這個變數的地方都要修改。
[box=Sienna]變更指令名稱
[/box]
第一步是要將商店畫面的「購買」指令名稱改成「兌換」,然後移除「販賣」指令。
而對應選擇「購買」等指令的視窗的類別是Window_ShopCommand。
class Window_ShopCommand
alias xxx001_make_command_list make_command_list
def make_command_list
if $game_switches [ PRIZE_SHOP_SID]
add_command( "兌換" , :buy )
add_command( "離開" , :cancel )
else
xxx001_make_command_list
end
end
end
class Window_ShopCommand
alias xxx001_make_command_list make_command_list
def make_command_list
if $game_switches [ PRIZE_SHOP_SID]
add_command( "兌換" , :buy )
add_command( "離開" , :cancel )
else
xxx001_make_command_list
end
end
end
我們讓它參考了剛才定義的常數,如果特定的開關變成了ON,就會顯示「兌換」、「離開」兩個指令。
關於add_command方法,因為筆者已經在製作劇情提示畫面中解說過,內容應該很容易理解。由於我們要直接使用一般商店的購買處理,所以符號:buy的部分就不作更動。
[box=Sienna]變更金額的顯示
[/box]
在顯示持有金的視窗中,我們改成顯示一個變數的值。這件事情透過重新宣告Window_Gold類別的value方法就能辦到。
class Window_Gold
alias xxx001_value value
def value
if $game_switches [ PRIZE_SHOP_SID]
$game_variables [ PRIZE_SHOP_VID]
else
xxx001_value
end
end
end
class Window_Gold
alias xxx001_value value
def value
if $game_switches [ PRIZE_SHOP_SID]
$game_variables [ PRIZE_SHOP_VID]
else
xxx001_value
end
end
end
同樣地,需要變更貨幣的單位的緣故,我們重新宣告currency_unit方法。筆者假想的是賭場的情況,所以單位就用「枚」來顯示。
class Window_Gold
alias xxx001_currency_unit currency_unit
def currency_unit
if $game_switches [ PRIZE_SHOP_SID]
"枚"
else
xxx001_currency_unit
end
end
end
class Window_Gold
alias xxx001_currency_unit currency_unit
def currency_unit
if $game_switches [ PRIZE_SHOP_SID]
"枚"
else
xxx001_currency_unit
end
end
end
在這個視窗顯示的變數值和貨幣單位,在選擇商品或是輸入購買個數的時候,會自動沿用下去。
[box=Sienna]變更購買的處理
[/box]
實際上決定購買商品之後,會呼叫出Scene_Shop類別的do_buy方法。透過重新宣告這個方法,我們能讓系統不扣掉金幣,而是減少變數的值。
class Scene_Shop
alias xxx001_do_buy do_buy
def do_buy( number)
if $game_switches [ PRIZE_SHOP_SID]
$game_variables [ PRIZE_SHOP_VID] -= number * buying_price
$game_party .gain_item ( @item, number)
else
xxx001_do_buy( number)
end
end
end
class Scene_Shop
alias xxx001_do_buy do_buy
def do_buy( number)
if $game_switches [ PRIZE_SHOP_SID]
$game_variables [ PRIZE_SHOP_VID] -= number * buying_price
$game_party .gain_item ( @item, number)
else
xxx001_do_buy( number)
end
end
end
引述number表示購買數量,@item是購買的物品本身,buying_price則是單價。
在這邊呼叫的gain_item則是Game_Party這個類別的方法,進行增加所購買的道具指定個數到物品欄中的處理。
[box=Sienna]關閉商店時的處理
[/box]
如同在解讀篇的佈景的管理中解說的,佈景類別中備有用來進行「處理開始」和「處理結束」的方法。
這次我們要向下面一樣重新宣告處理結束的方法terminate。
class Scene_Shop
alias xxx001_terminate terminate
def terminate
xxx001_terminate
$game_switches [ PRIZE_SHOP_SID] = false
end
end
class Scene_Shop
alias xxx001_terminate terminate
def terminate
xxx001_terminate
$game_switches [ PRIZE_SHOP_SID] = false
end
end
如此一來,在關閉商店畫面的時候,用來識別代幣商店的開關會自動變為OFF。
作者: taroxd 时间: 2014-6-22 08:57
本帖最后由 taroxd 于 2014-6-22 09:03 编辑
在本章,筆者會解說在選單畫面中選擇道具或技能之後,又在另一個新視窗進行分岐處理的方法。
在包含城鎮等地點的選單中選擇任意地點之後就會進行傳送──這次將以這樣的道具作為製作範例。
新增道具項目
和製作逃脫道具時相同,我們先設定傳送用的道具資料。
作用對象是「無」,使用時機則設為「僅在選單中」,備註欄就記入<TELEPORT>吧。設定完畢以後,再麻煩製作一個包含「道具的增減」事件指令的事件,測試看看道具的效果。
補充一點,為了方便起見,筆者都是用「道具」這個詞進行說明,但在本章製作的腳本,是可以直接對應技能的。換句話說,若在技能的備註欄中寫下 <TELEPORT> ,並讓作用對象和使用時機都採用同樣的設定的話,那樣也能夠做出同樣效果的技能出來。
[box=Sienna]新增視窗
[/box]
接著我們新增選擇傳送地點用的視窗類別。
這次別繼承Window_Selectable,改繼承看看Window_Command類別吧。什麼時候要繼承哪個類別視情況而定,而這次用Window_Command的話,因為不需要自己寫提供分支選項的程式碼,所以處理上比較方便。
class Window_Teleport < Window_Command
def initialize
super ( 0 , 0 )
hide
deactivate
end
end
class Window_Teleport < Window_Command
def initialize
super ( 0 , 0 )
hide
deactivate
end
end
Window_Command類別的initialize方法會接收x座標以及y座標兩個引數。這次不需要指定座標,所以筆者不設定Window_Teleport類別的initialize方法的引數,而是用super(0, 0)的形式叫出父類別的方法,將x、y座標都設為0。
再來呼叫hide讓視窗成為隱藏狀態,又呼叫deactivate方法將視窗設為無效中的狀態。這是因為視窗實際上產生的時間點是在移動到道具畫面之後,如果沒有選擇道具的就顯示這個畫面的話時機就錯誤了。
接著還需要視窗大小的指定。請將下面的程式碼寫在Window_Teleport類別中。
def window_width
return 240
end
def window_height
Graphics.height
end
def window_width
return 240
end
def window_height
Graphics.height
end
在Window_Command類別中,可以透過呼叫自己內部的window_width和window_height這兩個方法取得視窗的寬與高。這個設計是為了能夠進行更具彈性的處理,比方說如果指令的數量不同,視窗的大小就可以隨之調整。
這次我們用240這個值回傳寬,用Graphics.height回傳高。順帶一提,Graphics.height是回傳整個畫面高度的方法。
註:之所以要寫hide、deactivate的原因在於,繼承Window_Command類別所創造出的視窗,一開始就是activate的狀態,如果不寫的話,一但進入任何由Window_Comannd產生的視窗,就會跟著一併顯示。hide跟deactivate差別在於,前者是讓視窗不要出現,後者是讓使用者無法操作該視窗。倘若沒有經過後者,就會發現雖然看不見視窗,但還是能夠操作該視窗。因此兩者是不同的。
[box=Sienna]顯示視窗
[/box]
前一項製作出的視窗,我們在Scene_ItemBase類別中把它當成實體變數保管。然後像下面一樣重新宣告start方法,加上產生出Window_Teleport這個類別的實體變數的處理。另外在製作劇情提示畫面時也說過的,Window_Base中已經包含了所有視窗的釋放處理,所以不需要寫出dispose的處理。
class Scene_ItemBase
alias xxx001_start start
def start
xxx001_start
@teleport_window = Window_Teleport.new
end
end
class Scene_ItemBase
alias xxx001_start start
def start
xxx001_start
@teleport_window = Window_Teleport.new
end
end
然後我們重新宣告determine_item方法。
alias xxx001_determine_item determine_item
def determine_item
if item.note .include ?( "<TELEPORT>" )
show_sub_window( @teleport_window)
else
xxx001_determine_item
end
end
alias xxx001_determine_item determine_item
def determine_item
if item.note .include ?( "<TELEPORT>" )
show_sub_window( @teleport_window)
else
xxx001_determine_item
end
end
determine_item是在將游標移動到道具上方,按下決定鍵的瞬間呼叫的方法。這部分的處理指定的是,如果在備註欄中含有<TELEPORT> 這個字串的話,就會呼叫show_sub_window方法,而如果沒有包含的話則是進行一般的處理。
show_sub_window方法是用來在道具畫面或技能畫面上方顯示別的視窗。在預設的程式碼中,只有在選擇道具或技能要作用在哪個角色上時有使用到。這個方法所進行的處理是,透過操作顯示域(viewport)的位置等參數,讓視窗在重疊的時候不會看起來怪怪的。只不過使用這個方法有一個前提,那就是視窗的高度一定要和整個畫面的高度一樣。
雖然目前只寫到一半,我們先在這裡測試一下吧。雖然會在選擇道具的瞬間變得進行無法操作,至少可以確認一下視窗現在是如何顯示的吧。
[box=Sienna]製作傳送點的清單場所
[/box]
我們把傳送點的資料用常數的形式準備一下吧。
Ruby的陣列可以不受限地同時保管數值或是文字列,所以直接寫出像下面這樣的程式碼很方便。
TELEPORT_PLACES =
[
[ 41 , "地點A" , 1 , 10 , 10 ] ,
[ 42 , "地點B" , 2 , 25 , 20 ] ,
[ 43 , "地點C" , 3 , 30 , 15 ] ,
]
TELEPORT_PLACES =
[
[ 41 , "地點A" , 1 , 10 , 10 ] ,
[ 42 , "地點B" , 2 , 25 , 20 ] ,
[ 43 , "地點C" , 3 , 30 , 15 ] ,
]
這個二維陣列構造,可以將內部保管各種常數的陣列當成一整個單位看待,然後這些陣列又包含在TELEPORT_PLACES這個陣列裡頭。筆者打算將各個常數,依序作為開關ID、傳送點的名稱、地圖ID、x座標和y座標使用。
所謂的開關ID是指,當指定的開關為ON的狀態,就可以傳送到到那個地點。舉例來說可以作為這樣的目的使用:當進入新的城鎮時開關就會變為ON,那麼以後就能傳送到那個城鎮。
為了確保能夠運作,請把地點改寫為測試用的專案裡頭有效的地點。上面的範例只列有三個地點,但當然要幾個都沒有關係。然後麻煩再製作一個可以將指定的開關ID設為ON的事件。
[box=Sienna]選擇傳送點
[/box]
接下來要讓傳送點的視窗能夠顯示剛才製作出來的場所資料。
請在Window_Teleport類別中加入以下的程式碼:
def make_command_list
TELEPORT_PLACES.each do |place|
if $game_switches [ place[ 0 ] ]
add_command( place[ 1 ] , :teleport , true , place)
end
end
end
def make_command_list
TELEPORT_PLACES.each do |place|
if $game_switches [ place[ 0 ] ]
add_command( place[ 1 ] , :teleport , true , place)
end
end
end
利用陣列的each方法執行迴圈,指定的開關如果是ON,那麼屬於該開關的地點的名稱就會以「指令」的形式追加上去。
add_command的第四個引數能夠指定擴充資料。所謂的擴充資料是指附帶在那些指令之後的資料,這裡的例子就是讓指令帶有傳送點的資料本身。關於第三個引數,筆者會在下一章說明。
[box=Sienna]設定handler
[/box]
最後要進行決定和取消處理的設定。
請將Scene_ItemBase類別按照下方修正。
alias xxx001_start start
def start
xxx001_start
@teleport_window = Window_Teleport.new
@teleport_window .set_handler ( :teleport , method( :on_teleport ) )
@teleport_window .set_handler ( :cancel , method( :on_teleport_cancel ) )
end
alias xxx001_start start
def start
xxx001_start
@teleport_window = Window_Teleport.new
@teleport_window .set_handler ( :teleport , method( :on_teleport ) )
@teleport_window .set_handler ( :cancel , method( :on_teleport_cancel ) )
end
接著實作on_teleport和on_teleport_cancel方法。
def on_teleport
place = @teleport_window .current_ext
$game_player .reserve_transfer ( place[ 2 ] , place[ 3 ] , place[ 4 ] )
SceneManager.goto ( Scene_Map)
end
def on_teleport_cancel
hide_sub_window( @teleport_window)
end
def on_teleport
place = @teleport_window .current_ext
$game_player .reserve_transfer ( place[ 2 ] , place[ 3 ] , place[ 4 ] )
SceneManager.goto ( Scene_Map)
end
def on_teleport_cancel
hide_sub_window( @teleport_window)
end
current_ext方法可以取得目前被選取的指令所對應的擴充資料。這裡表示的就是這些地點的資料,然後再從中取出地圖ID和座標,進行地點移動。
hide_sub_window方法是進行和show_sub_window相反的處理。在按下取消鍵後會關閉視窗,回到道具選擇的畫面。
透過以上的程式碼,就完成了傳送的基本處理。如果要更加實用,還需要一些例如讓傳送本身無效的處理等等,但那一點在製作逃脫道具已經解說過所以在此省略。要是有人一直留著之前的逃脫道具腳本,會造成同樣的地方被多次宣告,考量到腳本衝突,因此有必要使用別名。
作者: taroxd 时间: 2014-6-22 09:04
在最後,我們來簡單地客製化視窗的呈現方式,讓本階段的學習告個尾聲吧。
筆者這次要示範的是,將在標題畫面中顯示「開始新遊戲」等指令的視窗進行改造。如果有人用自己的圖片設計標題畫面,透過更改指令視窗的顯示方式,畫面的呈現就能更具於原創性。
[box=Sienna]視窗透明化
[/box]
從Window_TitleCommand這個類別改造起吧。第一步是重新宣告initialize。
class Window_TitleCommand
alias xxx001_initialize initialize
def initialize
xxx001_initialize
self .opacity = 0
end
end
class Window_TitleCommand
alias xxx001_initialize initialize
def initialize
xxx001_initialize
self .opacity = 0
end
end
進行遊戲測試之後,應該會馬上發現變化之處。opacity是在RGSS內建的 Window 中宣告的屬性,表示不透明度。將這個值設為零的話,應該就看不見視窗了。
此外,視窗內的東西(例如游標或是「開始新遊戲」等字串)不會受到opacity的影響。
註:opacity值的範圍為0~255。
[box=Sienna]變更指令
[/box]
因為我們要利用較具設計性的歐語字體,所以將指令名稱變更為英文。將make_command_list 以下面的方式重新宣告。
class Window_TitleCommand
def make_command_list
add_command( "New Game" , :new_game )
add_command( "Continue" , :continue , continue_enabled)
add_command( "Shutdown" , :shutdown )
end
end
class Window_TitleCommand
def make_command_list
add_command( "New Game" , :new_game )
add_command( "Continue" , :continue , continue_enabled)
add_command( "Shutdown" , :shutdown )
end
end
我們這次不呼叫原本的make_command_list,而是覆寫掉它。這種重新宣告的作法在不想要繼承原本預設的處理時是比較有效率的方法,不過因為和其他腳本素材衝突的可能性會變高,所以使用時務必多留意。
add_command的第三個引數是用來指定該項指令是否處於有效的狀態。省略不寫的話會視為有效。
註:新方法和舊方法的宣告形式完全一樣時,使用別名並不具任何意義,只不過是讓這個設定連續讀取兩次而已(讀取預設和新的設定)。但若是其他腳本有重新宣告過這個方法的形式時就需要注意。
add_command( "Continue" , :continue , continue_enabled)
add_command( "Continue" , :continue , continue_enabled)
在這行中,如果「繼續遊戲」是有效的,代表continue_enabled這個方法回傳的是true,反之則是false──實裝到遊戲上的時候,紀錄檔存在時會回傳true。
[box=Sienna]變更字型
[/box]
為了變更字型,我們重新宣告create_contents方法。create_contents是實際產生出以視窗為呈現方式的點陣圖contents這個物件,並對其設定的方法。
class Window_TitleCommand
alias xxx001_create_contents create_contents
def create_contents
xxx001_create_contents
contents.font .name = "Times New Roman"
contents.font .bold = true
contents.font .size = 28
end
end
class Window_TitleCommand
alias xxx001_create_contents create_contents
def create_contents
xxx001_create_contents
contents.font .name = "Times New Roman"
contents.font .bold = true
contents.font .size = 28
end
end
結束呼叫原本的create_contents之後的下一步,contents指的是Bitmap的物件,因此只要和在改造計時器的時候用一樣的訣竅變更屬性就OK了。在這裡將文字的使用指定為字型"Times New Roman"、粗體字、大小為28pt。其他詳情請參照 Window、Bitmap、Font。
[box=Sienna]變更對齊方式
[/box]
在呈現字串的時候,字要從左邊、中間還是右邊為基準線對齊就叫作對齊方式。對應的是Bitmap中draw_text方法的最後一個引數。Window_Command這個類別通常是置左對齊,不過透過重新宣告alignment這個方法就能夠輕鬆變更。
class Window_TitleCommand
def alignment
return 1
end
end
class Window_TitleCommand
def alignment
return 1
end
end
像這樣,回傳1的話就是用置中對齊的方式呈現。回0的話就和預設一樣是置左對齊,2的話就是置右對齊。
[box=Sienna]結語
[/box]
「RGSS腳本入門篇(VX ACE版)」在這裡告一段落,辛苦各位了。
在實踐篇中,筆者透過了相對簡單的腳本改造,解說了腳本素材的製作是用什麼樣的流程進行的。只要和基礎篇以及解讀篇學習到的知識融會貫通的話,在這裡沒有解說到的部分,也應該能夠解讀和改造了。RGSS文件也請務必有效活用。萬一找不到方法或類別的說明,就用用看幫助區的搜尋功能吧。
只要熟習RGSS的話,大致上的目標都可以實現,不過如果突然間就立下「自訂戰鬥系統」這種很高的目標的話,會很容易受到挫折。建議大家從自己比較可能學會的項目開始磨練技術。此外嘗試解讀看看網路上公開的腳本素材,應該也會有很大的幫助吧。
作者: 高须小龙 时间: 2020-2-12 14:41
传送那个案例有bug
欢迎光临 Project1 (https://rpg.blue/)
Powered by Discuz! X3.1