這篇教學的目的是說明如何建立一個簡單的自訂場景(Scene)。
遊戲中已有內建Scene_Base類別提供場景的基本架構,其他場景像是標題畫面(Scene_Title)、地圖畫面(Scene_Map)、選單畫面(Scene_Menu)等等都是繼承Scene_Base,我們的自訂場景基本上也都是繼承Scene_Base。
一個場景有三個最主要的method:
1. start
這是在進入場景時做預先處理的部分,一般會在這裡建立圖像、變數等等會用到的東西。
2. update
這裡是處理場景的更新,遊戲為60幀,也就是說每秒會執行60次update,圖像的動態、輸入的處理等等都在這裡做。
3. terminate
在場景結束(要移動到別的場景)時執行,這部分最重要的工作是釋放記憶體,有的遊戲會莫名強制關閉常常是因為沒有做好記憶體釋放造成的。
所以基本上一個自訂場景的基本架構會像這樣:
class Scene_Custom < Scene_Base
def start
super # 呼叫父類別的同名方法
#建立會用到的東西
end
def update
super
#畫面、輸入等等更新
end
def terminate
super
#結束時的處理、釋放記憶體
end
end
下面接著示範做一個簡單的自訂場景
場景畫面:
這個場景中有三名角色,玩家可以用方向鍵選擇角色或離開圖示,當選擇角色並按下確定鍵時顯示該角色的對話,當選擇離開圖示並按下確定鍵時離開這個場景。
在這個場景中,我們會用到以下圖像素材,假設這些素材放在遊戲資料夾的Graphics\Pictures資料夾下:
back.jpg
char1.png
char2.png
char3.png
icon.png
接著示範如何實際撰寫場景,還記得上面說的要在start中建立圖像嗎?這邊首先示範建立背景:
def start
super
create_background
end
def create_background
@back = Sprite.new
@back.bitmap = Cache.picture('back')
end
簡單吧? 我們建立了背景@back,以@開頭的變數代表它是instance variable。
Sprite是RM基本的圖形類別,我們可以給Sprite物件指定點陣圖(bitmap)、座標、縮放、色調以及許多功能,詳細請看RPG Maker的F1說明。
@back.bitmap = Cache.picture('back') 這行指令代表它的點陣圖使用的是Pictures資料夾下名為back的檔案。
現在有了背景,接著該畫上其他東西吧?
但慢著!我們先處理場景結束的時候該做的事,也就是釋放記憶體。
記得我們說過處理場景的結束在terminate這個方法裡嗎?
def terminate
super
@back.dispose
end
dispose方法用來釋放Sprite所佔用的記憶體,一定要記住當你建立(new)一個Sprite物件就要有一個對應的dispose,否則會導致memory leak,到超過某個容許值的時候會無預警關閉遊戲。
RPG Maker有幾種類別需要你這樣手動釋放記憶體,不過我們這裡先不討論這些,總之記得這個手續是必須的,在你建立Sprite物件時最好儘快把對應的dispose寫好,以免遺漏。
接著回到start,繼續完成剩下部分,還需要畫上三個人物以及一個返回圖示,我們可以跟建立背景同樣一個個去Sprite.new,但在這裡我們為求方便使用陣列(Array)去儲存他們:
def start
#...前略...
create_icons
end
def create_icons
@icons = Array.new(4){ Sprite.new }
end
這代表建立長度為4的陣列@icons,每個內容物為Sprite物件
然後我們分別設定每個Sprite:
@icons.each_with_index{ |item, index|
case index
when 0
pic = 'char3'
x, y = 69, 100
when 1
pic = 'char1'
x, y = 214, 104
when 2
pic = 'char2'
x, y = 393, 89
when 3
pic = 'icon'
x, y = 468, 342
end
item.bitmap = Cache.picture(pic)
item.x, item.y, item.z = x, y, 1
}
這裡我們用到一些ruby特有的語法,each_with_index方法會遍歷整個陣列,並使用兩個參數,第一個item代表內容物,第二個index代表索引。
下面的case index就是依據索引去設定點陣圖名稱以及座標(x,y),相當於C語言的switch。後面的item.x, item.y就是設定該Sprite的座標,這要自己調整擺放位置。
要記得座標(0,0)在畫面最左上角,往右是X正向,往下是Y正向。
值得一提的是最後有個z值,這代表圖像的顯示優先度,z值高的Sprite會蓋住z值低的Sprite,z值相同時,y值高的會蓋住y值低的,當Sprite建立時z值預設是0,而我們已經建立背景了,其他圖形應該在背景之上,所以把他們z值設為1。
然後別忘了釋放記憶體:
def terminate
#...前略...
@icons.each{ |item| item.dispose}
end
到此我們的場景看起來已經像這樣了:
非常完美!........................................................除了不會動這點以外。
按哪個鍵都沒反應,為什麼?
因為我們還沒寫嘛!
接下來要輪到update了。
我們先處理方向鍵的輸入以及顯示目前選中的圖示,由於用了陣列儲存這些Sprite,可以簡單的用索引表示目前選到哪個。
首先在start裡先加入一個變數,紀錄目前選中的索引
def start
#...前略...
@current_index = 0
end
表示這場景一開始選擇編號0的Sprite。
然後在update裡處理方向鍵輸入,為了方便新增一個方法叫update_input,在update中呼叫他:
def update
#...前略...
update_input
end
def update_input
if Input.repeat?(:RIGHT)
@current_index = (@current_index+1)%4
Sound.play_cursor
end
end
代表當按住右方向鍵時選擇右邊(陣列的下一個元素),
Sound.play_cursor表示撥放游標音效(資料庫中的設定)。
同理,當按下左方向鍵時往左選擇:
if Input.repeat?(:LEFT)
@current_index = (@current_index-1)%4
Sound.play_cursor
end
現在我們處理了方向鍵的選擇,但還有個問題,那就是玩家看不出來選了哪個!
所以要讓被選中的Sprite出現變化:
def update
#...前略...
update_icon
end
def update_icon
@icons.each_with_index{ |item, index|
index == @current_index ? item.tone.set(255,0,0) : item.tone.set(0,0,0)
}
end
這個 a ? b : c 的三元運算學過C語言的人應該已經知道意思,就是簡化版的if..else..,在ruby中也是一樣。
這段程式碼讓目前選中的Sprite色調設為255,0,0(紅色),沒選中的設為0,0,0(原始)
看起來就是這樣:
如果你不想讓選中的目標改變色調而是其他顯示方法(改變圖像等等),則要自己設定了,詳細請參照F1說明。
接著,當按下確定鍵時要讓選中的角色說話,我們需要訊息視窗
def start
#...前略...
@message_window = Window_Message.new
end
def update_input
#...前略...
if Input.trigger?(:C) # 按下確定鍵
if @current_index <= 2 # 當選擇人物
text = [ '...' , '你好' , '嘿嘿' ]
$game_message.add(text[@current_index])
else # 當選擇離開圖示
return_scene
end
end
end
當訊息視窗顯示東西時,暫時讓玩家的方向鍵無法動
def update_input
return if $game_message.busy?
#...下略...
end
可能有人有疑問:我們沒寫訊息視窗的update,為什麼他能動,其實所有Window類別的update都已經在Scene_Base中寫好了,我們的場景繼承他就不用自己寫,而且理論上Window需要dispose釋放記憶體,這點在Scene_Base中也做好了,所以你不需要在意‧‧‧但是!Scene_Base對視窗的dispose僅限於instance variable(@開頭的變數),也就是說如果你把視窗存在陣列或非instance variable的東西內,就需要手動dispose。
return_scene這個方法也寫在Scene_Base裡,它會使場景回到SceneManager目前stack最上層的那個場景。也就是說,前一個場景使用SceneManager.call()來到目前場景,而你可以使用return_scene回去上個場景,但如果你是用SceneManager.goto()來到這個場景,也就沒有上個場景能返回,你必須用SceneManager.goto()移動到別的場景。
最後整個腳本看起來像這樣:
腳本連結想在遊戲中測試它看起來是什麼樣子,可以在事件中使用腳本指令SceneManager.call(Scene_Custom)
本文章使用的圖片素材來自Walfas