這篇文章的目的是說明如何撰寫HUD
HUD是什麼?
HUD全名叫heads-up display,原本是在飛機駕駛前方的窗口上投射資訊,讓駕駛員不用低頭看儀器的一種方式,遊戲中則用來在地圖或戰鬥中顯示如頭像、生命、魔力等資訊。
那我們如何在地圖上增加這些資訊?
範例中我們用到以下圖像素材:
頭像
Face.png
血條
HpBar.png
血條(背景)
HpBar_Back.png
魔力條
MpBar.png
將這些素材放在Graphics/Pictures資料夾內
地圖場景的類別是Scene_Map,我們要在它的start方法內建立圖像
class Scene_Map
alias start_custom_hud start
def start
start_custom_hud
create_hud
end
end
首先說明alias的用法,這可以為一個方法(method)取別名,使你在定義過的方法裡添加新功能時不需完全重寫,而是可以呼叫舊方法再加入新功能。
alias start_custom_hud start
代表你為 start 這個方法取了別名 start_custom_hud
def start
start_custom_hud
create_hud
end
代表你在新寫的 start 裡呼叫了舊的 start ,使你沿用現有start的功能並新加東西(create_hud)
接著定義create_hud方法:
def create_hud
@actor = $game_party.members.first
h = 104
@hud_viewport = Viewport.new(0, Graphics.height-h, Graphics.width, h)
end
這裡指定要顯示的資訊是隊伍中第一個角色,如果你要顯示成資料庫中指定編號的角色可以用$game_actors[編號]。
然後我們建立一個Viewport物件,Viewport像是一個顯示用的畫板,你可以把Sprite畫在這個畫板上,當你使Viewport移動、顯示、隱藏、改變色調時,畫在上面的所有Sprite也會相應改變,建立時使用四個參數分別是螢幕上的x座標、y座標、寬度、高度。
我們希望HUD顯示在畫面下方、寬度與畫面相同,高度為104。
接著在Viewport上面畫東西:
def create_hud
...前略...
@face = Sprite.new(@hud_viewport)
@face.bitmap = Cache.picture('Face')
@face.x, @face.y = 4, 4
end
建立Sprite時可以指定Viewport給它,要注意當設定Sprite座標時,指的是它在Viewport上的座標而不是畫面的座標。
接著把血條以及血量數字畫上去:
def create_hud
...前略...
@hp_bar_back = Sprite.new(@hud_viewport)
@hp_bar_back.bitmap = Cache.picture('HpBar_Back')
@hp_bar_back.x = (Graphics.width-@hp_bar_back.bitmap.width)/2
@hp_bar_back.y = 65
@hp_bar = Sprite.new(@hud_viewport)
@hp_bar.bitmap = Cache.picture('HpBar')
@hp_bar.x, @hp_bar.y = @hp_bar_back.x, @hp_bar_back.y
@hp_bar.z = @hp_bar_back.z + 1
@hp_bar.src_rect = Rect.new(0, 0, (@hp_bar.bitmap.width*@actor.hp_rate).to_i, @hp_bar.bitmap.height)
@hp_text = Sprite.new(@hud_viewport)
@hp_text.bitmap = Bitmap.new(@hp_bar.bitmap.width, @hp_bar.bitmap.height)
@hp_text.x, @hp_text.y, @hp_text.z = @hp_bar.x, @hp_bar.y, @hp_bar.z+1
draw_hp_value
end
當血量低於最大值時,血條不是完整的而只會顯示一部份,它的長度應該是 (完整長度*角色血量比例),注意乘出來是浮點數但我們需要的是整數所以要轉換一下。
得到血條應有的長度後我們就能設定Sprite的src_rect,src_rect屬於Rect類別,功用是指定一個Sprite要顯示它的bitmap上哪一塊方形部分,四個參數是bitmap的x座標、y座標、寬度、高度。
然後我們需要更新血量的功能,當血量變動時量表的長短也會變更,處理這部分是在update這個方法裡。
alias update_custom_hud update
def update
update_custom_hud
update_hud
end
def update_hud
update_hp
end
def update_hp
current_length = @hp_bar.src_rect.width
target_length = (@hp_bar.bitmap.width * @actor.hp_rate).to_i
return if current_length == target_length
update_gauge(@hp_bar, current_length, target_length, 10)
draw_hp_value
end
在update_hp方法裡,我們先計算目前血條應有的長度(完整長度*角色血量比例),把它跟目前顯示的長度做比較,如果一樣就不用更新了,如果不一樣就需要更新量表以及數字:
def update_gauge(spr, current_length, target_length, speed)
dw = target_length - current_length
dw = dw.abs > speed ? (dw>0? speed : -speed) : dw
spr.src_rect = Rect.new(0, 0, spr.src_rect.width+dw, spr.src_rect.height)
end
更新血條長度只需要調整src_rect即可。
這裡的參數speed指的是更新血量時的消長速度,以像素/幀為單位。
接著畫上血量數字,使用Bitmap的draw_text方法,加上一些字形設定,可參考F1。
def draw_hp_value
bitmap = @hp_text.bitmap
bitmap.clear
rect = Rect.new(0, 0, bitmap.width-5, bitmap.height)
bitmap.font.outline = true
bitmap.font.bold = true
bitmap.font.size = 14
mhp_size = bitmap.text_size(@actor.mhp.to_s).width
bitmap.draw_text(rect, @actor.mhp.to_i.to_s ,2)
rect = Rect.new(0, 0, bitmap.width - 5 - mhp_size, bitmap.height)
bitmap.font.size = 20
bitmap.draw_text(rect, @actor.hp.to_i.to_s + "/", 2)
end
同樣的道理,把魔力條畫上去。
最後的重點,在離開地圖場景時需要把圖像所佔的記憶體釋放掉,包含Sprite與Viewport都要
alias terminate_custom_hud terminate
def terminate
terminate_custom_hud
dispose_hud
end
def dispose_hud
[@face, @hp_bar, @hp_bar_back, @mp_bar, @hp_text].each{|item|
item.bitmap.dispose if item.bitmap
item.dispose
}
@hud_viewport.dispose
end
但要記得在釋放Viewport前要先釋放畫在上面的Sprite,某些討論提到如果順序反過來,有可能造成一些memory leak的問題。
最終程式碼:
http://pastebin.com/Qwzt0x6W