Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第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_ARROWRIGHT_ARROWUP_ARROWDOWN_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
濃紺の600×360のキャンバスに緑のプレイヤーと右隣の黄色い目標があり、左上にscore: 0と表示された実行結果
図 18-1 開始時は、右矢印キーで届く位置へ最初の目標を置く。
濃紺の600×360のキャンバスで緑のプレイヤーが右へ移動し、黄色い目標が左上へ移り、左上にscore: 1と表示された実行結果
図 18-2 目標へ触れると得点が1になり、次の目標が別の位置へ移る。

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と同じ初期値へ戻します。

濃い紫色の600×360のキャンバス中央に、白い文字で「5個みつけた!」と表示された終了画面
図 18-3 5点に達するとプレイヤーと目標の描画を止め、終了画面だけを表示する。

ただし「ゲームには勝敗が必要」とは限りません。次の終わり方も選べます。

  • 5 個集めると色が変わり、そのまま続く
  • スコアを表示せず、集めた位置だけ軌跡として残る
  • 目標から逃げるほど音が静かになる
  • 終了条件を置かず、好きなだけ触れる

ルールは多いほど面白いのではありません。プレイヤーにしてほしい操作と、返したい反応が一つずつ対応しているかを見ます。

同じ移動コードでも、作品の意味は変えられます。

  • 集める: 目標へ触れると増える。見つける喜びを作る
  • 避ける: 目標へ触れない時間を残す。距離と緊張を作る
  • 痕跡を描く: スコアを消し、通った場所へ線を残す。勝敗ではなく動きそのものを見る

どれがゲームらしいかを決める必要はありません。ルールは、遊ぶ人に何を大切だと感じさせるかを変えます。「速い操作が得意な人だけ有利になっていないか」「キーを押し続けにくい人も遊べるか」「失敗したとき、責める表示になっていないか」から一つ選び、必要なら速度、操作、言葉を変えます。

18.5 一人で遊び直す

作った直後は、操作もルールも全部知っています。一度保存して閉じ、翌日に説明を読まず遊び直します。

  • 最初の 3 秒で何を押せばよいか分かるか
  • 目標へ触れたことが画面から分かるか
  • 速すぎる、遅すぎると感じる場所はどこか
  • もう一度遊びたい変化が一つあるか

直すのは一度に一つで十分です。次章では、完成度を上げ続ける代わりに、自分で区切りを決め、作品を保存します。

18.6 ゲームをもっと育てるなら

今のゲームを作り直さず、変える場所を一つ選びます。

  • 第15章の場面管理を使い、開始画面と再挑戦を加える
  • プレイヤーの速さを3種類作り、いちばん操作しやすいものを選ぶ
  • スコアを消し、色、音、軌跡で触れた結果を返す
  • 集める代わりに避ける、追いかける代わりに観察するルールへ変える

ゲームを育てるとは、機能を増やすことだけではありません。遊ぶ人へどんな時間を渡したいかを選び、そのために不要なルールを外すことも改良です。

この章の小さな区切り

  • 矢印キーで円を動かし、画面内へ留められた
  • 目標へ触れた結果が、位置・得点・色のどれかで返った
  • 「集める・避ける・痕跡を描く」から、自分が続けたい一案を選んだ

終了画面まで作らなくても、操作と反応が一つつながれば保存して次章へ進めます。

参考資料