8.ステータス画面
1.仕組みを知る
ステータス画面の処理を行っているのは、Scene_Status クラスです。
シーンクラスを見つけましたら、そのクラスの start メソッドを確認します。
大抵はこの start メソッドでウィンドウが作られています。
create_xxx みたいなメソッドの呼び出しがあれば、そのメソッドも確認しましょう。
新しいウィンドウが作られている可能性が高いです。
start の処理を確認すると、ウィンドウは1つしか作られていないことがわかります。
ということは、Window_Status クラスを改変すれば、表示される内容が変わるということです。
Window_Status クラスの定義を確認してみると大した量ではないですね。
内容も単純な作業ですし、もしかすると描画処理の入門に最適なクラスなのかもしれません。
まずは、initialize メソッドの確認を行います。
このメソッドで、オブジェクトの初期化が行われます。
ウィンドウの位置や大きさを指定して、ウィンドウを作成しています。
actor は、Game_Actor クラスのオブジェクトです。
この参照は、$game_actors や $game_party.members から取得できるものです。
refresh は、全体の描画を行っています。
この refresh 以降で呼ばれるメソッドを変更すれば、表示内容が変更されます。
メソッドの定義を探したいときは、メソッド名をダブルクリックして全検索(Shift+Ctrl+F)すると楽ですよ。
一手間加えて def も加えると、定義だけが表示されて見やすくなります。
赤文字のメソッドは、Window_Status で新しく定義された複数行の描画を行うものです。
黒い方は、Window_Base で定義されているものです。
ステータスの描画は、インスタンスの作成時に1度だけ実行されます。
L・Rボタンでアクターを切り替えることができますが、このときは描画し直すのではなく
シーン切り替えを行って、ステータス画面を作り直しています。
Scene_Status#next_actor, Scene_Status#prev_actor でですね。
シーンクラスを見つけましたら、そのクラスの start メソッドを確認します。
大抵はこの start メソッドでウィンドウが作られています。
create_xxx みたいなメソッドの呼び出しがあれば、そのメソッドも確認しましょう。
新しいウィンドウが作られている可能性が高いです。
start の処理を確認すると、ウィンドウは1つしか作られていないことがわかります。
ということは、Window_Status クラスを改変すれば、表示される内容が変わるということです。
Window_Status クラスの定義を確認してみると大した量ではないですね。
内容も単純な作業ですし、もしかすると描画処理の入門に最適なクラスなのかもしれません。
まずは、initialize メソッドの確認を行います。
このメソッドで、オブジェクトの初期化が行われます。
def initialize(actor) super(0, 0, 544, 416) # ウィンドウの作成 @actor = actor # アクターのオブジェクト refresh # 全ステータス項目の描画 endsuper は、スーパークラスの同名メソッドの呼び出しです。
ウィンドウの位置や大きさを指定して、ウィンドウを作成しています。
actor は、Game_Actor クラスのオブジェクトです。
この参照は、$game_actors や $game_party.members から取得できるものです。
refresh は、全体の描画を行っています。
この refresh 以降で呼ばれるメソッドを変更すれば、表示内容が変更されます。
メソッドの定義を探したいときは、メソッド名をダブルクリックして全検索(Shift+Ctrl+F)すると楽ですよ。
一手間加えて def も加えると、定義だけが表示されて見やすくなります。
赤文字のメソッドは、Window_Status で新しく定義された複数行の描画を行うものです。
黒い方は、Window_Base で定義されているものです。
ステータスの描画は、インスタンスの作成時に1度だけ実行されます。
L・Rボタンでアクターを切り替えることができますが、このときは描画し直すのではなく
シーン切り替えを行って、ステータス画面を作り直しています。
Scene_Status#next_actor, Scene_Status#prev_actor でですね。
2.経験値ゲージの表示
デフォルトでは、現在の経験値とレベルアップまでの経験値の2つが表示されます。
これをちょっと変えて、レベルアップまでの経験値をゲージにして、
現在の経験値の下に表示させてみます。
ゲージは既にHPやMPで実装されています。
ですので、それをコピペして使うと楽に実装できます。
Window_Base#draw_actor_hp_gauge を Window_Status にコピペして、
次のように変更すると経験値ゲージの処理となります。
def draw_actor_exp_gauge(x, y, width = 120) if @actor.level == 99 gw = width else exp_list = @actor.instance_variable_get(:@exp_list) now = @actor.exp - exp_list[@actor.level] max = exp_list[@actor.level + 1] - exp_list[@actor.level] gw = width * now / max end gc1 = text_color(28) gc2 = text_color(29) self.contents.fill_rect(x, y + WLH - 8, width, 6, gauge_back_color) self.contents.gradient_fill_rect(x, y + WLH - 8, gw, 6, gc1, gc2) endgw = width * actor.hp / actor.maxhp という計算式でゲージの長さを求めています。
これを gw = width * now / max に置き換えて考えると、
max (最大値)が、レベルアップに必要な経験値で、
now (現在値)が、レベルアップまでの経験値ということになると思います。
あとは、経験値の描画処理にゲージ処理を追加すると完成です。
def draw_exp_info(x, y) draw_actor_exp_gauge(x, y, 180) self.contents.font.color = system_color self.contents.draw_text(x, y, 180, WLH, Vocab::ExpTotal) self.contents.font.color = normal_color self.contents.draw_text(x, y, 180, WLH, @actor.exp, 2) end
3.パラメータの表示
ステータス画面で表示されるパラメータは、すべてを合計した現在の値です。
今回は、それに加えて装備品やステートの補正を含めない素の値を括弧で表示したいと思います。
パラメータを取得するために、デフォルトでは base_atk と atk などの2つのメソッドが
Game_Actor クラスに定義されています。
base_atk は、データベース+装備品。atk は、ベース(base_atk)+能力UP+ステート。
能力UPというのは、イベントやアイテムなどで底上げされたパラメータです。
例には、攻撃力(atk)を使いましたが、他のパラメータも同様に定義されています。
デフォルトのメソッドでは、いろいろ混ざっているので必要な値が取得できません。
個別に取得したい場合は、次のようにします。
- データベースでの素の値
-
$data_actors[actor.id].parameters[type+2, actor.level]
Game_Actor#draw_actor_parameter の type は、0が攻撃力になっているので、
それに2を足すことで攻撃力など取得できるとこになります。
詳細は、RPG::Actor#parameters の説明を見てください。
- 底上げされた値
-
actor.instance_variable_get(:@atk_plus)
instance_variable_get メソッドは、インスタンス変数へアクセスするためのものです。
Game_Actor オブジェクトの @atk_plus 変数の値を取得しています。
その他のパラメータの変数は以下の通りです。
@maxhp_plus:最大HP、@maxmp_plus:最大MP、
@atk_plus:攻撃力、@def_plus:防御力、@spi_plus:精神力、@agi_plus:敏捷性 - 装備品の値
-
actor.equips.compact.inject(0) {|result,item| result + item.atk }
例では、すべての装備品の攻撃力の合計値を取得しています。
その他のパラメータの値を取得するには、加算するメソッドを変更します。atk:攻撃力、def:防御力、spi:精神力、agi:敏捷性
では、実装してみます。
パラメータの描画は、Window_Status#draw_parameters で行われています。
さらに、ここから Window_Base#draw_parameters が呼び出されて、各パラメータを描画しています。
ですので、この Window_Base#draw_parameters メソッドをオーバーライドして、
素のパラメータも一緒に描画する処理に書き換えると良さそうです。
def draw_actor_parameter(actor, x, y, type) case type when 0 parameter_name = Vocab::atk parameter_value = actor.atk when 1 parameter_name = Vocab::def parameter_value = actor.def when 2 parameter_name = Vocab::spi parameter_value = actor.spi when 3 parameter_name = Vocab::agi parameter_value = actor.agi end basic = actor.actor.parameters[type + 2, actor.level] params = [:@atk_plus, :@def_plus, :@spi_plus, :@agi_plus] plus = actor.instance_variable_get(params[type]) parameter_value = "#{parameter_value} (#{sprintf("%3d", basic + plus)})" self.contents.font.color = system_color self.contents.draw_text(x, y, 100, WLH, parameter_name) self.contents.font.color = normal_color self.contents.draw_text(x + 100, y, 96, WLH, parameter_value, 2) end新しく追加した行だけ見てみます。
basic = actor.actor.parameters[type + 2, actor.level]これは、データベースでの値を取得する処理です。
actor が2つ並んでますが、ミスではありません。
1つ目は、Game_Actor のオブジェクト、2つ目が RPG::Actor のオブジェクトです。
つまり、$data_actors[actor.id].parameters[type + 2, actor.level] と同じ意味になります。
type に2を加算しているのは、HPとMPの値を飛ばすためです。
params = [:@atk_plus, :@def_plus, :@spi_plus, :@agi_plus] plus = actor.instance_variable_get(params[type])パラメータが上昇している可能性もあるので、プラスされる値も取得して、加算します。
parameter_value = "#{parameter_value} (#{sprintf("%3d", basic + plus)})"実際に表示される文字列を作成しています。sprintf については、こちらを参考にしてください。
4.属性の有効度を表示
属性の有効度は、クラスごとに設定されているので RPG::Class#element_ranks で取得できます。
データベースの値を取得するには、$data_classes を使えばいいのですが、
Game_Actor クラスの class プロパティでも可能です。
つまり、$game_actors[id].class.element_ranksのようなコードで取得できます。
また、Game_Actor#element_rate では、装備やステートの状態も反映した有効率を取得できます。
Aから順に 200, 150, 100, 50, 0, -100 というようにダメージが何%通るのかを返します。
では、実装してみます。まずは、定数の定義からです。
ELEMENT_ID_LIST 情報として表示する属性を8個選択します。
ELEMENT_ICON それらの属性のアイコンを設定します。
LETTER_LIST 有効度の文字を定義します。
COLOR_LIST それらの文字の色をウィンドウスキンの色から選択します。
ELEMENT_ID_LIST = [9, 10, 11, 12, 13, 14, 15, 16] ELEMENT_ICON = [104, 105, 106, 107, 108, 109, 110, 111] LETTER_LIST = ["A", "B", "C", "D", "E", "F"] COLOR_LIST = [10, 14, 0, 11, 9, 13]描画メソッドは次のようになります。
def draw_resistant_element(x, y) self.contents.font.color = system_color self.contents.draw_text(x, y, 140, WLH, "属性有効度") for i in 0...ELEMENT_ID_LIST.size # 有効ランクを割り出す rate = @actor.element_rate(ELEMENT_ID_LIST[i]) rank = (rate < 0) ? 5 : 4 - rate / 50 # 装備品やステートを考慮しない素の耐性を表示する場合は # 直でランクを取得するだけです。 #~ rank = @actor.class.element_ranks[ELEMENT_ID_LIST[i]] dx = x + i % 4 * 56 + 16 dy = y + i / 4 * WLH + WLH draw_icon(ELEMENT_ICON[i], dx, dy) self.contents.font.color = text_color(COLOR_LIST[rank]) self.contents.draw_text(dx + 24, dy, 32, WLH, LETTER_LIST[rank], 1) end end
5.ステートの有効率を表示
ステートの付加成功率の取得は、Game_Actor#state_probability(state_id) で、
防具の無効化されているかは、Game_Actor#state_resist?(state_id) で取得できます。
まず、表示する8つのステートを決めます。
STATE_ID_LIST = [1, 2, 3, 4, 5, 6, 7, 8]そのステートの成功率を描画する処理を定義します。
防具で無効化された部分は色違いで表示するようにしてみました。
def draw_resistant_state(x, y)
self.contents.font.color = system_color
self.contents.draw_text(x, y, 140, WLH, "ステート有効率")
last_size = self.contents.font.size
self.contents.font.size = 14
for i in 0...STATE_ID_LIST.size
id = STATE_ID_LIST[i]
# 防具で無効化されていれば、数値と色を変更
if @actor.state_resist?(id)
self.contents.font.color = text_color(14)
value = 0
else
self.contents.font.color = normal_color
value = @actor.state_probability(id)
end
dx = x + i % 4 * 56 + 16
dy = y + i / 4 * WLH + WLH
draw_icon($data_states[id].icon_index, dx, dy)
dx += 24
dy += WLH - 18
self.contents.draw_text(dx, dy, 32, 18, "#{sprintf("%3d", value)}%")
end
self.contents.font.size = last_size
end
6.特殊能力の表示
今回は、未設定の特殊能力を半透明で表示するのではなく、
設定されているものだけを表示する形にしようと思います。
そちらの方が場所もとらないですし。
まずは、能力を取得するメソッド名と能力名を配列で設定します。
メソッドの実行結果が偽のとき次の能力に移り、真のとき能力名を描画して1行下げます。
オブションの設定は、以下のメソッドで取得できます。すべて真偽値を返します。
設定されているものだけを表示する形にしようと思います。
そちらの方が場所もとらないですし。
まずは、能力を取得するメソッド名と能力名を配列で設定します。
ACTOR_OPTION = [ ["two_swords_style", "二刀流"], ["fix_equipment", "装備固定"], ["auto_battle", "自動戦闘"], ["super_guard", "強力防御"], ["pharmacology", "薬の知識"] ]描画メソッドは次のようにします。
メソッドの実行結果が偽のとき次の能力に移り、真のとき能力名を描画して1行下げます。
def draw_actor_option(x, y) for valid,name in ACTOR_OPTION # 特殊の能力が未取得なら次へ next unless @actor.instance_eval(valid) self.contents.draw_text(x, y, 180, WLH, name) y += WLH end end
オブションの設定は、以下のメソッドで取得できます。すべて真偽値を返します。
- オプション
-
[ 二刀流 ] Game_Actor#two_swords_style
[装備固定] Game_Actor#fix_equipment
[自動戦闘] Game_Actor#auto_battle
[強力防御] Game_Actor#super_guard
[薬の知識] Game_Actor#pharmacology
[クリティカル頻発] Game_Actor#actor.critical_bonus
- 武器オプション
-
[ターン内先制] Game_Actor#fast_attack
[ 連続攻撃 ] Game_Actor#dual_attack
[クリティカル頻発] Game_Actor#weapons.compact.collect{|w| w.critical_bonus }.include?(true)
- 防具オプション
-
[クリティカル防止] Game_Actor#prevent_critical
[ 消費 MP 半分 ] Game_Actor#half_mp_cost
[取得経験値 2 倍] Game_Actor#double_exp_gain
[HP 自動回復] Game_Actor#auto_hp_recover
7.立ち絵の表示
今回は、立ち絵、画像をウィンドウに直接描画する方法と
スプライトでウィンドウとは別に表示する方法の2つを紹介したいと思います。
1.ウィンドウに描画する
こちらは、単に描画するだけなので簡単に実装できます。
しかし、ウィンドウに描画するので、ウィンドウの余白部分には描画できません。
ピクチャのフォルダにある "Actor○" というファイル名の画像を描画するような処理にしたいと思います。
○のところには、アクターのIDが入ります。
もし、右下を原点としたいなら、次のように座標を変更します。
2.スプライトで表示する
こちらは多少準備が必要ですが、画面のどこにでも表示が可能です。
スプライトを使用する場合は、Scene_Status クラスを変更します。
今回は、アクターの名前と同じファイル名の画像を使用するようにしました。
注意点は、z座標をウィンドウより大きくすることです。数値は適当で結構です。
スプライトでウィンドウとは別に表示する方法の2つを紹介したいと思います。
1.ウィンドウに描画する
こちらは、単に描画するだけなので簡単に実装できます。
しかし、ウィンドウに描画するので、ウィンドウの余白部分には描画できません。
ピクチャのフォルダにある "Actor○" というファイル名の画像を描画するような処理にしたいと思います。
○のところには、アクターのIDが入ります。
def draw_actor_illust(x, y) bitmap = Cache.picture("Actor#{actor.id}") self.contents.blt(x, y, bitmap, bitmap.rect) endこれだと、左上を原点で右下へ表示する処理になります。
もし、右下を原点としたいなら、次のように座標を変更します。
x -= bitmap.width y -= bitmap.heightもし、中央下なら次のようにします。
x -= bitmap.width / 2 y -= bitmap.height
2.スプライトで表示する
こちらは多少準備が必要ですが、画面のどこにでも表示が可能です。
スプライトを使用する場合は、Scene_Status クラスを変更します。
今回は、アクターの名前と同じファイル名の画像を使用するようにしました。
注意点は、z座標をウィンドウより大きくすることです。数値は適当で結構です。
def create_actor_sprite @actor_sprite = Sprite.new @actor_sprite.x = 0 @actor_sprite.y = 0 @actor_sprite.z = @status_window.z + 10 @actor_sprite.bitmap = Cache.picture(@actor.name) endさらに、開始処理と終了処理を変更します。
# 開始処理 alias _orignal_start start def start _orignal_start create_actor_sprite end # 終了処理 alias _orignal_terminate terminate def terminate _orignal_terminate @actor_sprite.dispose endアクターの切り替えを行う際に、シーン移動を行ってすべて作り直すため追加する処理はこれだけです。
8.パーティメンバーの表示
歩行グラフィックを描画する機能はデフォルトであるのですが、
それだと、不透明度を指定して描画できません。
ですので、Window_Base#draw_character を改変して作りたいと思います。
引数は、表示する位置とパーティのアクターを描画する幅を指定できるようにしています。
指定された幅内で中央に表示するようにしているので、人数によって描画位置が変わります。
def draw_party_graphic(x, y, width = 200, height = 48) sw = width / $game_party.members.size # 一人分の幅 # パーティの人数だけ繰り返す for i in 0...$game_party.members.size # アクターを取得して、歩行グラが設定されていなければ次のアクターへ actor = $game_party.members[i] next if actor.character_name == "" # ビットマップを読み込んで、一人分のサイズを計算 bitmap = Cache.character(actor.character_name) if actor.character_name[/^(\$|\!\$)/] cw = bitmap.width / 3 ch = bitmap.height / 4 else cw = bitmap.width / 12 ch = bitmap.height / 8 end # 正面を向いた画像の位置を計算 n = actor.character_index src_rect = Rect.new((n%4*3+1)*cw, (n/4*4)*ch, cw, ch) # 表示中のアクターと処理中のアクターが違う場合は、半透明にする enabled = (@actor == actor ? 255 : 128) # 位置を計算して描画 dx = x + sw * i + (sw - cw) / 2 dy = y + height - cw self.contents.blt(dx, dy, bitmap, src_rect, enabled) end end
9.背景画像
メニュー画面の説明を参考にしてください。