Rubyで創るRPG 第十一章 戦闘シーンの完成
Rubyで創るRPG
十四歳のお誕生日のお祝いにとMacBook Air を贈られた 未來(みらい)。
憧れの遙香(はるか)お姉様から、プログラミングを教えてもらえることに。
コンピュータの仕組みを学ぶところから始まり、RPG(ロールプレイングゲーム)を創り上げてます。
本日は大域脱出を学び、ひとまずの完成を迎えます。
三項演算子
「さて、ここでちょっと紹介したいのは、三項演算子って言うんだけれど、if文の代わりに書くとちょっと便利になるものなの。」
「それってどういう風に書くの?」
「前回は123と言う番号の番号で選ぶような感じじゃない。」
「うん、そうだね。」
「こんな感じで食べるようにしたらどうかしら?」
>攻撃
回復
逃走
「で、上下に移動させて、コマンドを選べるようにするの。」
「うん、とってもいいよ〜」
「ええ、で、そのために役立つのが、三項演算子よ。」
「そうなんだね。」
# コマンドを表示する
menus = ["戦う", "呪文", "逃げる"]
# コマンドが選ばれた
command = 0
# 表示する
if command == 0
print ">"
else
print " "
end
puts menus[command]
「こんな風に書くと、>攻撃
って表示されるのは、わかるかしら。」
「うん、わかるよ。」
「攻撃、回復、逃走の前に、>
を入れるか、全角空白を入れるか、どちらかにしたいの。」
「で、if文を使って書くと、上のようになるんだけれど、ちょっと長いじゃない。」
「うん、そうだね。」
「で、それを短くかけるのが三項演算子なの。」
command = 0
print command == 0 ? ">" : " "
「if文
を使って書いたのと同じ動作なのだけれど、わかるかしら?」
「ちょっと説明して欲しいな〜」
「ええ、三項演算子という名前の通り、「項」が三つあるの。」
「三つあるっていうと?」
「普通の演算は、二項演算子っていうわ。例えば、5 + 3 を考えて欲しいのだけれど、+ が演算子で、前後にある5と3がそれぞれの項なの。二つの項があるから二項演算子ね。」
「うんうん。」
「三項演算子は、? と : が演算子なの。で、command == 0
と ">"
と " "
が項ね。三つの項があるから、三項演算子よ。」
「うん、確かに三つあるね。」
「演算子の働きとしては、最初のif文に示した通りなんだけれど、command == 0
が成り立っているなら ">"
を返す、そうでないなら " "
を返すっていう動きになるわ。」
「うん、わかったよ。」
「元々のif文は五行かかっていたけれど、一行でかけるから、とっても簡潔で良いわよね。」
「うん、そうだね。」
「みっくんも、使ってみてね。」
「うん!」
「ということで、前回、紹介したeach.with_indexメソッドと組み合わせると、次のようになるわ。」
# コマンドを表示する
menus = ["攻撃", "回復", "逃走"]
command = 0
menus.each.with_index do |menu, index|
print index == command ? ">" : " "
puts menus[index]
end
「indexとcommandが一緒なら、>を表示させているの。最初は、 コマンドが0だから、配列の添字(index)が0の要素、つまり「攻撃」ね。攻撃の前に>がついて、>攻撃って表示されるわ。」
「うん、わかったよ。組み合わせてつかうととっても便利だね。」
剰余演算子
「もう一つ、紹介しておきたいのが剰余演算子なの。贈与演算子って、割り算の余りを求める演算子なの。」
「そうなんだね。」
「ええ、普通割り算だと、7÷3だったりするけれど、答えは2余り1よね。」
「うん、そうだね。」
「この余り1を求めるのが剰余演算子よ。」
「そうなんだ。どんないいことがあるの?」
「カレンダーのように、周期的に繰り返すものにとっても便利だわ。」
「そうなの。カレンダー、別名七曜表とも言うんだけれど、日曜日から土曜日までの7つの曜日がずっと繰り返していくわよね。」
「うん、そうだね。」
「日曜日を0として、月曜日を1、火曜日を2というようにすると、土曜日は6になるわ。」
「うん、そうだね。」
「土曜日の次を日曜日にしたいんだけれど、どうしたらいいかしら?」
「う〜ん、普通に書くとこんな感じかなぁ」
youbi = 6
youbi += 1
if youbi == 7
youbi = 0
end
「えー、そうね。最後まで来ているかどうか判断して、最後まで来ていたら、先頭のゼロ番目に戻すって言う処理を書かないといけないわよね。」
「そうだね。」
「剰余演算子を使うと、これが省略できるの。つまりね、
youbi = (youbi + 1) % 7
って、書けば、先頭に戻ってくれるでしょ。」
「あ、ほんとだね。」
「ええ、今回の勇者の行動でも、回復、逃走って選んでいって、もう一回選び直したいときには上の攻撃に戻ってきて欲しいじゃない。」
「うん、そうだね。」
「なので、そのための下準備っていう感じかしら。」
大域脱出のための準備
「ということで、準備もできたので、コマンドを表示するプログラムを書いてみましょうか。」
「うん、上下の矢印キーでコマンドを選んでも良いのだけれど、せっかくだからショートカットキーでできるようにしましょうか。」
「うん、そうだね。Ctrl + P と Ctrl + N だったよね。」
「ええ、そうよ。それから、メニューを表示するための関数、メソッドとも言ったけれどを準備したりすると、少しスッキリして書きやすくなると思うわ。」
「うん、わかった。」
「じゃあ、みっくん、書いてみましょうか。」
ということで、僕はプログラムを書いていく。少し規模が大きくなるので、irbで書くよりは、小さなファイルを作って、実行させたほうが良さそうだ。前回、キー入力を練習したのがあったので、getch3.rbという名前にしてみた。
# キーボードからの入力を一文字受け取る
require "io/console"
# コマンドを表示する
def puts_menus(command)
menus = ["攻撃", "回復", "逃走"]
menus.each.with_index do |menu, index|
print index == command ? ">" : " "
puts menus[index]
end
end
command = 0 # コマンドの初期値は攻撃
puts_menus(command)
while (key = STDIN.getch) != "\C-c"
case key
when "\u0010" # Ctrl + P ↑
command = (command - 1) % 3
break
when "\u000e" # Ctrl + N ↓
command = (command + 1) % 3
break
end
end
puts_menus(command)
while (key = STDIN.getch) != "\C-c"
case key
when "\u0010" # Ctrl + P ↑
command = (command - 1) % 3
break
when "\u000e" # Ctrl + N ↓
command = (command + 1) % 3
break
end
end
puts_menus(command)
while (key = STDIN.getch) != "\C-c"
case key
when "\u0010" # Ctrl + P ↑
command = (command - 1) % 3
break
when "\u000e" # Ctrl + N ↓
command = (command + 1) % 3
break
end
end
「できたよ。」
「どれどれ。あ、素敵ね、ちゃんとCtrl + P と Ctrl + N のショートカットキーで、上下に>が移動できるわね。」
「うん、そうなんだ。そしてね、何度でも繰り返せるようにしたいんだけれど、どうしたらいいのかな?」
「全体を繰り返しで囲んであげると良いわよね。」
「こんな風?」
# キーボードからの入力を一文字受け取る
require "io/console"
# コマンドを表示する
def puts_menus(command)
menus = ["攻撃", "回復", "逃走"]
menus.each.with_index do |menu, index|
print index == command ? ">" : " "
puts menus[index]
end
end
command = 0 # コマンドの初期値は攻撃
loop do
puts <<~SLIME
/ \
/・Д・\
〜〜〜〜〜
SLIME
puts_menus(command)
while (key = STDIN.getch) != "\C-c"
case key
when "\u0010" # Ctrl + P ↑
command = (command - 1) % 3
break
when "\u000e" # Ctrl + N ↓
command = (command + 1) % 3
break
end
end
system "clear"
end
大域脱出を学ぶ
「ええ、どう? なんどでも繰り返せているわよね。」
「うん、そうだね。そして、上下に選ぶことはできるんだけれど、
これでいいってリターンキーを押したいのだけれど、止まらないんだ。リターンキーを押したら、while だけじゃなくて、loop も抜けるようにしたいのだけれど、どうしたらいいのかな?」
「ええ、いいところに気づいたわね。break は一つの繰り返しを抜けることができるのだけれど、それ以上は抜けられないわ。コマンドを決定したら、攻撃処理だったり、回復処理だったり、そういう次の処理へ移りたいものね。」
「うん、そうなんだ。」
「一気に外側のループまで抜けることができる。それが大域脱出よ。」
「大域脱出って、どう書くの?」
「Ruby では、throw catch 構文って言ったりもするけれど、こんな感じかしら。」
# キーボードからの入力を一文字受け取る
require "io/console"
# コマンドを表示する
def puts_menus(command)
menus = ["攻撃", "回復", "逃走"]
menus.each.with_index do |menu, index|
print index == command ? ">" : " "
puts menus[index]
end
end
selection = catch(:exit) do
command = 0 # コマンドの初期値は攻撃
loop do
puts <<~SLIME
/ \
/・Д・\
〜〜〜〜〜
SLIME
puts_menus(command)
while (key = STDIN.getch) != "\C-c"
case key
when "\u0010" # Ctrl + P ↑
command = (command - 1) % 3
break
when "\u000e" # Ctrl + N ↓
command = (command + 1) % 3
break
when "\r" # Enter
throw :exit, command
end
end
system "clear"
end
end
puts selection
「まず、while文の中だけれど、"\r" これは、改行文字といって、エンターキーを押した時という意味になるわよね。」
「うん、そうだね。」
「で、着目して欲しいのは、 throw :exit, command
の一文。throwは投げるっていう意味だけれど、:exitっていう目標、ラベルに向かって、command を投げているの。commandには、0番攻撃、1番回復、2番逃走っていう数字をしまっておくんだったわよね。」
「うん、そうだよ。」
「で、それと受け取っているcatchしているのが、catch(:exit) のところね。受け取ったcommandの数値、0か1か2を、selectionという変数に代入しているわ。」
「そうなんだ。throwで投げて、catchで受け取るんだね。」
「ええ、そうよ。throw で投げて catch で受け取る。お互いの目印となるように、:exitっていう目標をつけておいたの。」
「この:exitっていうのは、Rubyの決まりなの?」
「あ〜、これは出口っていう意味の英単語だからあたしがつけたのよ。みっくんがわかりやすい単語にしてももちろんいいのよ。」
「そうなんだね。わかったよ。」
「で、最後は、puts selection って書いたけれど、0番攻撃、1番回復、2番逃走という数字が入っているから、それぞれに応じた処理を書いたら、完成よね。」
「うん、そうだね。」
「僕、頑張って書いてみるね。」
「ええ、がんばって♡」
僕は、お姉さまに励まされ、かたかたキーボードをタイプしていく。
外はすっかり陽が落ちて暗くなっている。夜になるまで付き合ってくれたお姉さまに大感謝。
出来上がったプログラムはこちらだ。
「みっくん、素敵ね。ロールプレイングゲーム完成ね。」
「うん、ありがとう。お姉様のおかげだよ。」
なでなで。お姉さまは僕の頭を撫でてくれる。暖かい光に包まれて幸せな時間。ずっとこうしていたい。
「次からはオブジェクト指向を本格的に使って、学んでいきましょう。」
「うん!」
<< 未來のレベルアップ!! >>
<< HPが3上がった!! >>
<< MPが2上がった!! >>
オブジェクト指向を使ってプログラミングしていく、第二部をお楽しみに。
Discussion