第18章 ゲームは反応のまとまり
ゲームを作るには、特別なゲーム命令が必要だと思うかもしれません。実際には、これまで使った位置、速度、入力、条件分岐、状態を組み合わせています。ゲームらしさは、操作に対して結果が返り、その結果が次の操作を変える循環から生まれます。
18.1 まず目標のない操作を作る
矢印キーで円を動かします。第9章と同じ材料です。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
end
def draw
@player_x -= 4 if keyIsDown(LEFT_ARROW)
@player_x += 4 if keyIsDown(RIGHT_ARROW)
@player_y -= 4 if keyIsDown(UP_ARROW)
@player_y += 4 if keyIsDown(DOWN_ARROW)
@player_x = constrain(@player_x, 20, width - 20)
@player_y = constrain(@player_y, 20, height - 20)
background(25, 30, 48)
fill(90, 210, 180)
ellipse(@player_x, @player_y, 40, 40)
end
constrain は値を最小と最大の範囲へ収めます。円の半径 20 を境界に使い、画面外へ出ないようにしています。
keyIsDown は、指定したキーが現在押されているかを調べます。LEFT_ARROW、RIGHT_ARROW、UP_ARROW、DOWN_ARROW は、それぞれ左・右・上・下の矢印キーを表す名前です。条件の後ろへ処理を一行だけ書く形の if を使い、押されている間は位置を4ずつ変えます。-= は左辺の値を減らし、+= は増やす記号です。
まだ勝敗はありませんが、操作への反応はあります。最初からスコアや敵を足さず、動かす感触を決めると調整点が見えます。
18.2 距離で触れたことを判定する
目標の位置を加えます。次の setup は変更部分です。18.1の長いコードにある def setup から対応する end までと置き換えます。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
@score = 0
end
続いて、18.1の長いコードの draw で位置を更新したあと、background より前へ次の変更部分を足し、中心同士の距離を調べます。
distance = dist(@player_x, @player_y, @target_x, @target_y)
if distance < 35
@score += 1
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
end
プレイヤーの半径が 20、目標の半径が 15 なら、中心間の距離が 35 より小さいときに円同士が重なります。四角い範囲で調べる方法もありますが、円には中心間距離が形と対応します。
dist(x1, y1, x2, y2) は、1つ目の点 (x1, y1) から2つ目の点 (x2, y2) までの距離を返します。ここではプレイヤーと目標の中心座標を渡し、結果を distance へ入れています。@score += 1 は、今の得点へ1を足す書き方です。
18.3 結果をすぐ画面へ返す
目標とスコアを描きます。次の変更部分は、18.1の draw の最後、プレイヤーを描く ellipse のあとへ足します。
fill(250, 190, 70)
ellipse(@target_x, @target_y, 30, 30)
fill(255)
textAlign(LEFT, TOP)
textSize(20)
text("score: #{@score}", 16, 16)
触れた瞬間に目標が移動し、スコアが増えます。操作、判定、反応が 1 フレームの中でつながりました。これがゲームの最小の循環です。
得点まで動く完成コード
ここまでの変更部分を一つへまとめます。最初の目標だけはプレイヤーの右80ピクセルへ置き、右矢印キーを押せば必ず最初の得点を試せるようにしています。2個目からは乱数で場所を決めます。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
@target_x = width / 2 + 80
@target_y = height / 2
@score = 0
end
def draw
@player_x -= 4 if keyIsDown(LEFT_ARROW)
@player_x += 4 if keyIsDown(RIGHT_ARROW)
@player_y -= 4 if keyIsDown(UP_ARROW)
@player_y += 4 if keyIsDown(DOWN_ARROW)
@player_x = constrain(@player_x, 20, width - 20)
@player_y = constrain(@player_y, 20, height - 20)
distance = dist(@player_x, @player_y, @target_x, @target_y)
if distance < 35
@score += 1
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
end
background(25, 30, 48)
fill(90, 210, 180)
ellipse(@player_x, @player_y, 40, 40)
fill(250, 190, 70)
ellipse(@target_x, @target_y, 30, 30)
fill(255)
textAlign(LEFT, TOP)
textSize(20)
text("score: #{@score}", 16, 16)
end
18.4 制限時間より先に終わり方を選ぶ
スコアが5になったら終了する作品を作ります。終了を表す値を1行だけ足しても、drawがその値を読まなければ画面は変わりません。次の完成コードでは、@finishedを用意し、得点が5になったらtrueへ変えます。drawの最初で@finishedを調べ、終了中は終了画面を描いてreturnでそのフレームの残りを行いません。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
@target_x = width / 2 + 80
@target_y = height / 2
@score = 0
@finished = false
end
def draw
if @finished
background(45, 35, 70)
fill(255)
textAlign(CENTER, CENTER)
textSize(32)
text("5個みつけた!", width / 2, height / 2)
return
end
@player_x -= 4 if keyIsDown(LEFT_ARROW)
@player_x += 4 if keyIsDown(RIGHT_ARROW)
@player_y -= 4 if keyIsDown(UP_ARROW)
@player_y += 4 if keyIsDown(DOWN_ARROW)
@player_x = constrain(@player_x, 20, width - 20)
@player_y = constrain(@player_y, 20, height - 20)
distance = dist(@player_x, @player_y, @target_x, @target_y)
if distance < 35
@score += 1
if @score >= 5
@finished = true
else
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
end
end
background(25, 30, 48)
fill(90, 210, 180)
ellipse(@player_x, @player_y, 40, 40)
fill(250, 190, 70)
ellipse(@target_x, @target_y, 30, 30)
fill(255)
textAlign(LEFT, TOP)
textSize(20)
text("score: #{@score}", 16, 16)
end
returnは、ここでは「このdrawの処理をここで終える」という命令です。終了画面を描いたあとにプレイヤーや目標を重ねず、キーによる位置更新も止められます。すぐもう一度遊ぶには、エディタの実行ボタンを押します。発展として再挑戦キーを作りたくなったら、第15章の場面管理を使い、キー入力でsetupと同じ初期値へ戻します。
ただし「ゲームには勝敗が必要」とは限りません。次の終わり方も選べます。
- 5 個集めると色が変わり、そのまま続く
- スコアを表示せず、集めた位置だけ軌跡として残る
- 目標から逃げるほど音が静かになる
- 終了条件を置かず、好きなだけ触れる
ルールは多いほど面白いのではありません。プレイヤーにしてほしい操作と、返したい反応が一つずつ対応しているかを見ます。
同じ移動コードでも、作品の意味は変えられます。
- 集める: 目標へ触れると増える。見つける喜びを作る
- 避ける: 目標へ触れない時間を残す。距離と緊張を作る
- 痕跡を描く: スコアを消し、通った場所へ線を残す。勝敗ではなく動きそのものを見る
どれがゲームらしいかを決める必要はありません。ルールは、遊ぶ人に何を大切だと感じさせるかを変えます。「速い操作が得意な人だけ有利になっていないか」「キーを押し続けにくい人も遊べるか」「失敗したとき、責める表示になっていないか」から一つ選び、必要なら速度、操作、言葉を変えます。
18.5 一人で遊び直す
作った直後は、操作もルールも全部知っています。一度保存して閉じ、翌日に説明を読まず遊び直します。
- 最初の 3 秒で何を押せばよいか分かるか
- 目標へ触れたことが画面から分かるか
- 速すぎる、遅すぎると感じる場所はどこか
- もう一度遊びたい変化が一つあるか
直すのは一度に一つで十分です。次章では、完成度を上げ続ける代わりに、自分で区切りを決め、作品を保存します。
18.6 ゲームをもっと育てるなら
今のゲームを作り直さず、変える場所を一つ選びます。
- 第15章の場面管理を使い、開始画面と再挑戦を加える
- プレイヤーの速さを3種類作り、いちばん操作しやすいものを選ぶ
- スコアを消し、色、音、軌跡で触れた結果を返す
- 集める代わりに避ける、追いかける代わりに観察するルールへ変える
ゲームを育てるとは、機能を増やすことだけではありません。遊ぶ人へどんな時間を渡したいかを選び、そのために不要なルールを外すことも改良です。
この章の小さな区切り
- 矢印キーで円を動かし、画面内へ留められた
- 目標へ触れた結果が、位置・得点・色のどれかで返った
- 「集める・避ける・痕跡を描く」から、自分が続けたい一案を選んだ
終了画面まで作らなくても、操作と反応が一つつながれば保存して次章へ進めます。