和事件開關不同的是,除非是比較結果或是強制設定
如果只是單獨判斷一個變數「if 變數...end」只要有值存在,結果就是true
字串(String)用兩個「"」或「'」包起來部分就會變成字串
字串的作用是讓Bitmap能用描繪文字(draw_text)的方法將字串物件顯示在遊戲畫面
換句話說
要讓變數顯示在畫面前要先轉化為字串但請不要作出
直接把變數名稱用「"」包起來的蠢事,我們要輸出的是
變數內容、不是名稱
要把內容轉成字串可以用「to_s」(如果它不是字串物件、而且有to_s方法)
或是使用 字串插值/內嵌表達式(string interpolation,後者中ㄍ...) :
陣列/數組(Array)數組是中國用語,但我已經講習慣了當有一系列物件需要有系統的管理就能用數組
每一格當中都能放置一個物件,當然也可以再放另一個數組
數組裡面的物件被稱為
元素(element)取用時只要知道物件放在哪個位置
(index)就好,以上面為例就是:
a[0] # "abc" a[1] # "def" a[2] # "ghi" |
(注意:index起始值為 0)如果調用的物件也是個數組,然後又要取用其中一個物件,就是繼續追加取用位置而已:
a = [[1,2,3], "def", "ghi"]
a[0][1] # 調用[1,2,3]中的「數字2」 |
當然,越多層就會越亂,這時建議用下面的 Hash 會比較清楚
雜湊/哈希表(Hash)
person1 = { :name => "小明", :age => 13, :sex => "男" }
p person1[:name] # <=顯示「小明」 |
當你覺得想幫index定名稱、這樣比較好作業的時候就用Hash吧
範例中前面加冒號的東西是
符號(symbol)物件,下面會講
Sprite、Bitmap一樣見
這篇最後
視口(Viewport)中國用語既然講到Sprite就順便說一下
腳本的Sprite說明可以在類方法的地方看到:
......這翻譯是
錯的
正確說明是:
如果需要的話可指定對應的視口(原文:
必要に応じてビューポート (Viewport) を指定します。)
紅字部分直接扔google翻譯就行了
如果是必要,那為什麼省略視口參數卻不會出現 ArgumentError 呢?這樣肯定很奇怪啊
唉,人有失神馬有亂蹄...
咳好,那什麼是視口?
簡單說就是另外設一個螢幕大小
比方說做一個(0,0,320,160)的viewport物件
如果Sprite物件和它對應,那麼超過這範圍的圖像會顯示不出來
內建戰鬥,敵方和場地用的@viewport1高度就被砍半了,
所以要把戰鬥場地改成滿版會發現「咦?我明明放了640x480的圖怎麼下面還是黑的?」
因為viewport高度沒改
可惜的是 Viewport 只能設定矩形,沒辦法做斜的,想做成像UL的必殺技cut in應該沒辦法
不要跟我說一張一張弄,一招如果20張、四招就要80張,再乘上角色數...開什麼玩笑欸等等,我沒說我要用RM搞UL啊多妮讓我舔如果有誰會寫斜向viewport,我會很感謝你
視窗(Window)RGSS新增
為了做UI而特化的Class,由許多Sprite構成,並且已弄好背景圖
能直接產生視窗是很方便,但因為結構已經寫死了,
所以顯示內容的「self.contents」必定要比視窗本身小32像素,不然會出現箭頭
看需求決定要用
Window 還是
Sprite 吧
範圍(Range)通常用在循環處理或條件分歧
如範例所示,有兩個點和三個點的差別,為什麼要這麼分我也不知道
符號 (Symbol)「:」開頭的值,在VX ACE大量使用
任何物件在建立時會隨機分配一個識別id代表不同物件(可用object_id查看),即使內容看起來一致。就好比兩個同名同姓同長相的人,但還是不同的兩個個體
但符號物件只要名稱相同就會視作是相同物件
ex:
a = :"abc" b = :"abc" p a.object_id, b.object_id # 兩個物件同ID
c = "abc" d = "abc" p c.object_id, d.object_id # 兩個物件不同ID |
執行腳本後會看到彈出兩次視窗,第一次兩個數值相同,第二次則不同
已被定義過的符號名稱會被保存直到遊戲結束,可用Symbol.all_symbols查看
如果只是需要一個判斷用的標籤(比方說Hash的key),就可以使用符號物件節省資源
因為目前腳本有需求,所以特別提
若專案需要積極的處理Hash,這個會很有幫助
不過因為符號的表達式到VX ACE才有上色,VX以前的版本會有點辨識上的麻煩
數字(Numeric)內裏定義「數字對像」的基礎方法
數字對像就是1、2、3...這種整數或3.1415926...這種小數
好,重點是Numeric的子類(Integer、Bignum、Fixnum、Float)和上面講的Symbol有個共通點:
無法使用.new做成實體
也就是他們的 物件ID 是唯一的,對應到的永遠是相同的值(一個數字1...就只是個數字1)
ex:
a = "" b = "" c = "" p a.object_id, b.object_id, c.object_id |
→ 生成三個「不同的」空字串物件,由a、b、c三個變數代入
a = 1 b = 1 c = 1 p a.object_id, b.object_id, c.object_id |
→ a、b、c三個變數代入「同樣的」
數字1物件
debug時,有時候會需要由物件id來判斷自己到底有沒有做對(改錯物件之類的)
另外為了省資源,可以先把某些容易生成的實體(字串、數組)代入一個變數
像字串,常會用來進行比較 (ex:actor.name == "阿爾西斯")
若先 「@name = "阿爾西斯"」 、判斷改成 「actor.name == @name」
能有類似這樣的做法會比較好
不然每次判斷「 actor.name == "阿爾西斯"」的話,都會另外生成一個「"阿爾西斯"」的字串物件
顏色(Color)、色調(Tone)
a = Color.new(紅, 綠, 藍[, 不透明度]) a = Tone.new(紅, 綠, 藍[, 灰階程度]) # 用 "[]" 框起來的部分為可選參數,即可以省略 |
RGSS新增
兩個都是控制色彩的類,但是Color的三色範圍是0~255,Tone的範圍則是-255~255
想臨時對遊戲圖像進行色彩變化就要使用它們(會有對應變數可代入)
控制結構if 條件分歧
if 條件1 (內容) elsif 條件2 (不符合條件1,但符合條件2的內容) elsif 條件3 (不符合條件1和2,但符合條件3的內容) else (除此以外的內容) end |
和事件大致上一樣,只差事件沒有「不滿足條件A,但滿足條件B」的設定
只能一直疊在else裏面,分歧多一點的後面就看不到內容了
else 和 elsif 可省略
unless 條件分歧if 分歧的顛倒版,「如果不是xxx的情況下 」
我個人很少用這個,就算要判定 false 的情況大多是用「 if !(a==b)」
加個驚嘆號就能判定相反狀況了
if、unless修飾符
如果分歧內容只要一行,可以簡化成如下的方式
case
case a when 0..5 # a的值為0~5 (內容) when 6...10 # a的值為6~9 (注意,範圍表示多了一個「.」) (內容) else (除此之外的內容) end |
只判斷某變數,當某變數符合when的條件就執行內容
缺點是沒辦法用複合條件(oo and xx...)
for循環指定一個範圍和一個局部變數開始運算,運算次數就是指定的範圍
其中可用 next 跳過這次運算,或者用 break 結束整個迴圈
例子中因為數組有3個單元,所以算3次,局部變數a會分別代入數組的1、2、3
你也可以無視那些變數只當作計數器使用(但Ruby其實可以用times方法)
返回(return)
初期讓我很頭大的東西,從字面上很難理解是什麼簡單說它的作用就是「中斷方法」,用事件指令講就是「中斷事件處理」
名字的由來我也是心血來潮看了C語言教學才知道,不過太佔篇幅,就略過吧
必要時可以在return後面給一個值,讓它作為方法的「返回值」
(方法其實就是個計算式,最後會有個計算結果)
說明書上寫「返回OO」可以理解成「從某方法取得OO這個計算結果」
大概就是這樣,另外因為 Ruby 的特性,會自動將方法的最後一行當作返回值
所以在一些場合可以省略用 return 指定返回值
另外用return時,如果後面沒指定數值則返回值是 nil
def abc var = "abc" end
def abc2 var = "abc" var2 ="cba" end
def abc3 return 123 var = "abc" end p abc, abc2, abc3 |
p出來的結果:
abc得到 "abc"
abc2則被後來的 "cba" 取代了
abc3一開始用了return 123,所以到這裡就停了,並且得到指定的數字 123
至於在 RM 中的用法,
可以參考一下 Game_Battler 及它的子類看怎麼定義 HP、SP 的
常用方法
p、print (VX ACE為msgbox、msgbox_p)把物件內容輸出,並彈出訊息框顯示
基本上是除錯時才會用到的功能,畢竟遊戲中突然冒出訊息框很突兀
大部分用在兩種場合:
查看物件狀況(有無被生成、返回值有沒有錯)
確認方法有沒有運行到這裡 (如果沒有就看不到訊息框了)
所以常常在說,p看看ooo變數,p看看p看看
p~~~~~~~除非不得已否則盡量不要把p腳本放在定期處理(每畫格執行1次)的方法中,不然會卡到死
真的需要我會建議另外做個簡易輸出數值的界面監視...
改造腳本沒意外都會用到的方法
隨機數
rand(100) # 隨機取得 0~99 中的值 |
和事件的亂數一樣,去獲取指定範圍「0~(指定數字-1)」中的某值
搭配增減固定數字可讓起始數字改成-1以下或1以上
loop 循環(※重看了幾次電子說明書後,發現這不是控制結構,是一個內建方法。
和while、for等控制結構的差別在於:loop會建立新的「作用域」。
也就是說,loop內產生的臨時變數無法保留到迴圈外)
和事件的「循環」一樣,每畫格執行一次,用來重複處理同樣的事情,
另外要設定 break 條件,不然就卡在這了
each
循環處理的方法,Ruby的「for...in...」 實際上會用這方法處理,使用例:
# 依序把 [1,2,3] 數組內元素代入val變數,然後p出來 [1,2,3].each { |val| p val } |
這例子寫成for...in...的話,就會是這樣:
for val in [1,2,3] p val end |
要用 each 理所當然該物件必須能支援,如果你要對數字用 each 那肯定報錯
公開實體變數attr_writer :實體變數名 # 實體變數可改寫
attr_reader :實體變數名 # 實體變數可讀取
attr_accessor :實體變數名 # 上面兩種複合(即所謂的
屬性)
算是Ruby實現其他語言存取器(get、set)的方式
把實體變數變成一個「方法」,讓它可以在外部藉由
調用的方式進行讀取或修改
所謂的
外部就是
不在自己所屬Class定義的方法中當某物件要在其他類更動實體變數時,就要先做這件事
打開Game開頭的腳本就會看到一大串
不過你不會看到attr_writer
,因為沒什麼用比方說,如果你想臨時改變地圖場景(Scene_Map)
viewport1 的色調(Game_Screen一改就是改全部,所以不改那邊),
就必須:
1.跑去Spriteset_Map把 @viewport1 宣告可寫
2.再跑去Scene_Map把 @spriteset (會代入Spriteset_Map的實體) 宣告可讀
3.之後才能在事件指令用$scene.spriteset.viewport1.tone = Tone.new(xxxx)...的方式改變色調
如果要在Scene_Map內部操作就可以少掉2的步驟變成:
@spriteset.viewport1.tone = Tone.new(xxxx) ...隔得越多層,要公開的變數就越多
總之若想要修改別的Class的實體內容
首先要找目前所在的Class有無代表該實體的變數
沒有就去找最接近的全域變數,然後慢慢用調用的方式往下抓
這部分親自操作過幾次大概就曉得了,
也是改造腳本最麻煩的部分,越大型的腳本找變數會越痛苦,更不用說還會調用參數式丟來丟去 另外這三個其實是是把三個常用結構簡化了的方法,像attr_reader其實就是:
def 實體變數名 return @實體變數名 end |
的簡化(其他電子說明書有寫)
而Ruby又有個特性是符合特定條件能省略括號,就變成上面那樣,
照平常寫法其實就是:
attr_writer(:實體變數名) attr_reader(:實體變數名) attr_accessor(:實體變數名) |
順帶一提,事件指令的
腳本指令所屬的Class為
Interpreter,
如果需要用事件腳本調用
Interpreter的方法,直接輸入方法名即可(不過裏面大部分需要事件指令的參數,你要知道那些方法需要什麼參數)
super子類限定方法,子類拿父類方法再定義時,如果想要調用父類同名方法的內容就可使用super
若父類方法有參數時,可以用super(參數1,參數2...)這樣的方式調用
若只有super,就是把調用時的返回值丟給父類
若不想要帶任何參數調用父類,用super()
以下轉貼自6R看到的一個回應,因為太經典就拿來引用了:
星矢(父類) 的攻擊方法會使用 天馬流星拳,於是: class 星矢 def attack 天馬流星拳 end end
星矢的兒子(子類) 的攻擊方法想在天馬流星拳的基礎上有所發展,於是: class 星矢的兒子 < 星矢 def attack super 小強式回血 end end 於是星矢的兒子攻擊時會先放 天馬流星拳 然後發動 小強式回血 |
看,很簡單吧?
別名(alias)為方法設定為別的名稱,格式為:
當你只是想
擴充原始方法的內容,別名會很有用
比如說我想讓物件初始化的時候一開始先多宣告@a和@b變數,並在原本內容結束之後之後進行refresh
alias old_initialize initialize def initialize @a = 0 @b = 0 old_initialize # 調用被改過名稱的原方法內容 refresh end |
※old_initialize若不調用,則只會宣告兩個新變數和執行refresh,舊方法不會被執行
※alias需寫在原始方法之後,不然一樣會出現
NoMethodError (沒方法改毛)
這樣可以緩和很多腳本衝突的情形,
但因為只能在首尾補充內容,如果是改條件分歧內部或是原本的執行順序什麼的還是只能再定義了
常用變數在RM內已經先用掉的名稱,和關鍵字一樣要避開它們
測試標誌
if $DEBUG # 測試模式中 (內容) end |
如果用測試模式進行遊戲,$DEBUG這個全域變數就會自動設為true
可用來實現只有測試模式中才能做到的事(內建的
F9選單 就是這樣)
幫忙某人製作的
遊戲有很多地方用了這個判定跳過漫長的戰鬥
因為只在測試模式下才會跳過所以對一般遊戲沒影響
只是後面有人
拆了加密檔用測試模式玩然後出問題了
所以之後只好在開頭補上這張圖:
腳本內容(寫在main腳本之前):
# 用測試模式,而且沒用F12重開遊戲過 if $DEBUG and not $warning_read Graphics.freeze # 顯示警告圖片 @warning = Sprite.new # < 其實這裡用局部變數就可以 @warning.bitmap = RPG::Cache.picture("DEBUGWARNING") Audio.se_play("Audio/SE/002-System02",80,100) Graphics.transition(40) # 停在這畫面,等待按鍵 loop do Graphics.update Input.update # 如果按下確定或取消鍵就消除圖片,開始遊戲 if Input.trigger?(Input::C) or Input.trigger?(Input::B) Graphics.freeze @warning.bitmap.dispose @warning.dispose # 設置一個全域變數,防止F12又要再看一次 $warning_read = true Graphics.transition(40) break end end end |
喔我要澄清一下,如果是為了學習而拆檔我是蠻歡迎的,我覺得獨立創作還是重在交流學習(最新版本也沒加密了)
只是當時發生這種狀況好像也不是我們的問題啊
戰鬥測試標誌
if $BTEST # 戰鬥測試中 (內容) end |
嗯...一樣是測試模式限定,但只限「
戰鬥測試」
因為條件的關係所以「
$DEBUG」也會跟著為on
測試戰鬥能持有全道具、不會接到標題畫面就是用了這個變數判斷
當前場景(Scene)
$scene = Scene_Menu.new # 切換至選單畫面 $scene.update # 執行目前場景的update方法 |
$scene是代表當前場景的變數(XP限定,VA開始有SceneManager)
根據Main腳本的結構:
while $scene != nil $scene.main end |
只要 $scene 不是nil,遊戲就能繼續運行(遊戲結束就是 $scene = nil 而已)
既然他是全域變數,就表示可以在任何地方使用
不過使用時要注意過程中會不會發生場景變化,因為一旦調用目前場景不存在的方法會出錯
(比方說在選單畫面中,某對像會定期使用$scene.xxx,而這xxx是其他場景沒有的,
這時要注意切換畫面時要把這處理中斷)
資料庫對象
Data資料夾各種資料庫的存檔在遊戲中對應的變數
全部都是數組,位置與資料庫設定的ID相同,因為資料庫沒有ID為0的數據,因此數組在0的位置都是 nil
$data_actors # 角色 $data_classes # 職業 $data_skills # 技能 $data_items # 道具 $data_weapons # 武器 $data_armors # 防具 $data_enemies # 敵人 $data_troops # 敵方隊伍 $data_states # 狀態 $data_animations # 戰鬥動畫 $data_tilesets # 地圖元件 $data_common_events # 公用事件 $data_system # 系統 |
比方說「p $data_animations[1]」 就能取得資料庫中1號戰鬥動畫的資料,依此類推...
沒什麼好講的,建議不要進行修改動作,以免影響後續運行(如果需要請用dup複製)
改了內容其實也沒意義,重開遊戲就會再重載原檔一次
Game 對象針對預設腳本中的所有名稱為Game開頭的Class做成的對象
$game_temp # 暫存 $game_system # 遊戲系統 $game_switches # 事件公共開關 $game_variables # 事件公共變數 $game_self_switches # 事件的自用(獨立)開關 $game_screen # 畫面數值管理(色調、震動、閃爍) $game_actors # 我方角色 $game_party # 我方隊伍 $game_troop # 敵方隊伍 $game_map # 地圖 $game_player # 地圖場景(Scene_Map)中的玩家(就是能被你操控、到處走的角色) |
比較容易理解的應該就是事件開關和變數,其他的...要講也會佔很多篇幅
這裡只要先知道除了
$game_temp 以外全部會被
儲存就好
另外實際上要檢查遊戲中的角色狀況要調用
$game_actors[角色ID]不用
$data_actor 的原因是角色是個會變動的數據,做成角色時還得先宣告些有的沒的數值,
用
$data_actor 只會看到最原始的設定(成長度、經驗需求...etc.)
這就是為何改變資料庫的角色圖像後無法反映在舊存檔的原因,
如果要重新取得資料庫的狀態,請重新讓角色加入隊伍並勾選「初始化」,當然資料會洗白
※ 敵人數據在內建情況不會隨著遊戲進行變動,所以直接使用
$data_enemies 取得即可
如果要判斷目前血量和狀態用
$game_troop.enemies(因為通常是要看該戰鬥中的敵人數據)
因上述對象除了
$game_temp 以外全部看記錄檔,
所以如果是已發布的遊戲最好不要再對其他的Game類的
初始化新增內容,這動作幾乎代表玩家必須重玩,如果是新增物件變數可以寫個「xxx = 0 if xxx == nil」來補救,不過不建議大量使用
(因為很醜)
Game類的物件需要什麼物件變數請在發布遊戲前都確定好,
如果想省時間用舊存檔測試可以暫時用上述的防錯法,等變數宣告後存檔、移除宣告的式子。
你看不小心就會打這麼多
額,大概就這樣(爆