第三回です。前回は敵キャラを出したので、今回は自機を出しましょう。
画像を用意する
Undertaleの戦闘画面ではプレイヤーはハートの姿になるので、ハートっぽい画像を作りました。こういうのってMacの場合何で作るのが楽なんでしょうね?今回はLibreOffice Drawで作りましたが(「記号シェイプ」にハートがあったのでそれを1個置いて、赤色にして、選択してファイル→エクスポートという手順です。エクスポート時、「選択範囲」にチェックしておくとうまくハートだけpngにすることができます。)
プレイヤーを表すクラスを作る
前回、「動くものはクラスにしよう」と書きました。プレイヤーも動くものなので、クラスにしましょう。Enemyクラスを作ったときと同じようにすればOKです。
例によって変更した箇所だけコメントを入れています。
require 'dxopal'
include DXOpal
# ハートの画像を宣言
Image.register(:player, "images/heart.png")
# ...
# プレイヤーを表すクラス
class Player < Sprite
def initialize
x = 300
y = 300
super(x, y, Image[:player])
end
end
Window.load_resources do
Window.bgcolor = C_BLACK
enemy = Enemy.new
# Playerクラスのオブジェクトを作る
player = Player.new
# ...
enemy.update
enemy.draw
# プレイヤーを描画する
player.draw
# ...
これでハートが出るはず…
あれ、出ませんね。
描画順序について
しばし考えたのち、以下の箇所が悪いということに気づきました。draw_thick_boxは初回で定義した太い枠線を引くメソッドですが、外側の四角を描く→内側を背景色で塗りつぶす という動作なので、プレイヤーごと塗りつぶされてしまったようです。
player.draw
draw_thick_box(260, 240, 380, 360, C_WHITE, 4)
ということで順序を入れ替えてやると:
draw_thick_box(260, 240, 380, 360, C_WHITE, 4)
player.draw
ハートが出ました。
このようにDXOpalでは基本的に実行した順に描画が行われるので、順番には注意しましょう。(別解としてz値で描画順を指定する方法もありますが、今回のような簡単なプログラムなら単に順番に気をつけるだけで良いでしょう)
プレイヤーを動かす
次はキー操作でプレイヤーを動かせるようにしてみましょう。DXOpalではInput.key_push?(K_LEFT)
のようにしてキーの状態が取れます…が、カーソルキーに限っては、もっと簡単にInput.x
, Input.y
で状態が取れるようになっています。これを使ってみましょう。
class Player < Sprite
def initialize
x = 300
y = 300
super(x, y, Image[:player])
end
# キー操作に応じて座標を変化させる
def update
self.x += Input.x
self.y += Input.y
end
end
# ...
enemy.update
enemy.draw
draw_thick_box(260, 240, 380, 360, C_WHITE, 4)
# キー入力を処理する
player.update
player.draw
Playerを動かす処理は、Enemyのときと同様、updateというメソッドでやることにします。Input.x
はカーソルキーの左が押されたら-1、右が押されたら+1になるので、それをそのまま座標に足してやることで、自機が動くようになります。
範囲を制限する
とはいえこれだけだと白線の外にも出ていってしまうので、範囲をチェックする処理を追加しましょう。まず下準備として、プレイヤーの移動範囲を定数にしておきます。というのは「プレイヤーの移動範囲」と「白線で囲む範囲」は同じなので、定数を使って共通化しておけば、位置を微調整したくなったときに、定数の値を変えるだけで済むので楽になります。
require 'dxopal'
include DXOpal
Image.register(:player, "images/heart.png")
Image.register(:enemy, "images/computer_typing_hayai.png")
C_ORANGE = [233, 115, 51]
FONT_HP = Font.new(18)
# プレイヤーの移動範囲
PLAYAREA = [260, 240, 380, 360]
# ...
enemy.update
enemy.draw
# 移動範囲を囲むように白線を引く
draw_thick_box(PLAYAREA[0]-4, PLAYAREA[1]-4, PLAYAREA[2]+4, PLAYAREA[3]+4, C_WHITE, 4)
これで準備はOKです。では、このPLAYAREA
を使ってプレイヤーの移動範囲を制限しましょう。Playerのupdateメソッドを修正します。
class Player < Sprite
def initialize
x = 300
y = 300
super(x, y, Image[:player])
# x, yの最小値と最大値
@min_x = PLAYAREA[0]
@max_x = PLAYAREA[2] - self.image.width # xは「プレイヤー画像の左端」なので、画像の幅を引かないと右端に移動したとき枠に被ってしまう
@min_y = PLAYAREA[1]
@max_y = PLAYAREA[3] - self.image.height
end
def update
self.x += Input.x
self.x = @min_x if self.x < @min_x # 小さすぎたら@min_xにする
self.x = @max_x if self.x > @max_x # 大きすぎたら@max_xにする
self.y += Input.y
self.y = @min_y if self.y < @min_y
self.y = @max_y if self.y > @max_y
end
end
こんな感じでしょうか?これでもいいのですが、「数値をある範囲に制限したい」というときはRuby 2.4から追加されたclampメソッドを使うとシンプルに書けます。
def update
self.x = (self.x + Input.x).clamp(@min_x, @max_x)
self.y = (self.y + Input.y).clamp(@min_y, @may_y)
end
まとめ
今回は自機の表示と移動を実装しました。こんな感じでぴたっと止まってくれます。
実際に動いているところは以下で見れます。カーソルキーで自機が移動します。
次回があれば敵の攻撃パターンを実装して、完成としたいです。