器用貧乏の独り言

器用貧乏なおっさんが気の向くままに。

Rubyで作る!簡単ブロック崩しゲーム

◇前置き

今日は、DXRubyというフレームワークを使って簡単な「ブロック崩し」ゲームの作り方を紹介します。

◇ゲームの概要


このゲームは、プレイヤーがパドルを操作してボールを打ち返し、ブロックを壊していくゲームです。

プレイヤーが全てのブロックを壊すと、ゲームクリアとなります。

ボールが画面下部に落ちると、ゲームオーバーとなります。

◇必要な準備

このゲームを作成するためには、DXRubyのインストールが必要です。
Ruby自体のインストールは済んでいるものと仮定します。

以下のコマンドを使用して、DXRubyをインストールできます。

gem install dxruby

ソースコード

以下は、ブロック崩しゲームのソースコードです。

require 'dxruby'

# ブロックを表すクラス
class Block
  attr_reader :x, :y, :width, :height

  def initialize(x, y, width, height, color)
    @x = x
    @y = y
    @width = width
    @height = height
    @color = color
    @visible = true
  end

  def draw
    if @visible
      Window.draw(@x, @y, Image.new(@width, @height, @color))
    end
  end

  # ブロックが衝突したかどうかを判定する
  def collides_with?(ball)
    return false unless @visible

    ball_x = ball.x + ball.radius
    ball_y = ball.y + ball.radius

    if ball_x < @x
      dx = @x - ball_x
    elsif ball_x > @x + @width
      dx = ball_x - (@x + @width)
    else
      dx = 0
    end

    if ball_y < @y
      dy = @y - ball_y
    elsif ball_y > @y + @height
      dy = ball_y - (@y + @height)
    else
      dy = 0
    end

    return (dx * dx + dy * dy) < (ball.radius * ball.radius)
  end

  # ブロックを消す
  def hide
    @visible = false
  end
end

# ボールを表すクラス
class Ball
  attr_reader :x, :y, :radius

  def initialize(x, y, radius, speed)
    @x = x
    @y = y
    @radius = radius
    @speed = speed
    @angle = rand(60..120)
  end

  def draw
    Window.draw_circle(@x, @y, @radius, C_WHITE)
  end

  # ボールを動かす
  def move
    @x += @speed * Math.cos(Math::PI / 180 * @angle)
    @y -= @speed * Math.sin(Math::PI / 180 * @angle)
  end

  # 壁やパドルに衝突した場合の処理
  def reflect_horizontal
    @angle = 180 - @angle
  end

  def reflect_vertical
    @angle = -@angle
  end

  # パドルに衝突した場合の処理
  def reflect_from_paddle(paddle)
    @angle = 180 - (@x - paddle.x - paddle.width / 2) * 160 / paddle.width
    @angle = @angle.clamp(60, 120)
    reflect_vertical
  end
end

# パドルを表すクラス
class Paddle
  attr_reader :x, :y, :width, :height

  def initialize(x, y, width, height, color)
    @x = x
    @y = y
    @width = width
    @height = height
    @color = color
  end

  def draw
    Window.draw(@x, @y, Image.new(@width, @height, @color))
  end

  # パドルを動かす
  def move(dx)
    @x += dx
    @x = @x.clamp(0, Window.width - @width)
  end
end

# ゲームのメインクラス
class Game
  def initialize
    @blocks = []
    5.times do |y|
      10.times do |x|
        block_width = 50
        block_height = 20
        block_color = [rand(256), rand(256), rand(256)]
        block = Block.new(x * block_width, y * block_height + 50, block_width, block_height, block_color)
        @blocks << block
      end
    end

    @paddle = Paddle.new(200, 500, 80, 20, C_WHITE)
    @ball = Ball.new(240, 480, 8, 6)
    @score = 0
  end

  def update
    if Input.key_down?(K_LEFT)
      @paddle.move(-8)
    elsif Input.key_down?(K_RIGHT)
      @paddle.move(8)
    end

    @ball.move

    # ボールが壁に衝突した場合の処理
    if @ball.x < @ball.radius || @ball.x > Window.width - @ball.radius
      @ball.reflect_horizontal
    end
    if @ball.y < @ball.radius
      @ball.reflect_vertical
    end

    # ボールがパドルに衝突した場合の処理
    if @ball.collides_with?(@paddle)
      @ball.reflect_from_paddle(@paddle)
    end

    # ボールがブロックに衝突した場合の処理
    @blocks.each do |block|
      if @ball.collides_with?(block)
        block.hide
        @score += 10
        if @score == @blocks.size * 10
          Window.alert("You win!")
          exit
        end
        if @ball.x < block.x || @ball.x > block.x + block.width
          @ball.reflect_horizontal
        else
          @ball.reflect_vertical
        end
        break
      end
    end

    # ボールが画面下部に落ちた場合の処理
    if @ball.y > Window.height
      Window.alert("Game Over! ")
      exit
    end
  end

  def draw
    Window.draw_font(10, 10, "Score: #{@score}", Font.default)

    @paddle.draw
    @ball.draw
    @blocks.each(&:draw)
  end
end

Game.new.run

◇コード解説

このゲームでは、Window.loopを使ってゲームのメインループを処理しています。

Window.loop内では、以下のような処理を行っています。

①パドルの移動をキーボードの入力に応じて処理する
②ボールの移動を処理する
③ボールが壁に衝突した場合、反射させる
④ボールがパドルに衝突した場合、反射させる
⑤ボールがブロックに衝突した場合、ブロックを消去し、スコアを加算する
⑥ボールが画面下部に落ちた場合、ゲームオーバーとする

また、Window.drawを使ってパドル/ボール/ブロックを画面に描画しています。

最後に

いかがでしょうか?
Railsを使ったWeb開発が有名なRubyですが、ゲーム開発用のフレームワークも実は充実しています。

今日はDXRubyを使った簡単なゲームを紹介しましたが、他にもゲーム開発用のフレームワークは存在しています。

作りたいものに合わせて様々なフレームワークを使い分けることが出来るのもRubyの良いところだと思います。

この記事も誰かの役に立つと嬉しいです。