blocks10b
U-18プログラミングくらぶ(Ruby三鷹教室)」(2015.6.21)で行った、DXRubyで「0から作るブロック崩し」の個人的なまとめです。

プログラムは、DXRubyの作者 mirichiさんが松江Ruby会議05 で行ったライブコーディングのブロック崩しプログラム(パブリックドメイン)を一部改変しています。
→・松江Ruby会議05に参加してきた - mirichiの日記(2014-03-17)


空白行を除くと、わずか40行余りで「ブロック崩し」が出来上がってしまいます。
初心者向けのプログラミング教材として、よい題材ではないでしょうか?

⚫︎手順
1.DXRubyの練習 
 1-1.Windowを出す
 1-2.トウフを表示
 1-3.丸くする(カーソルキーで移動)
 1-4.Spriteを使う(マウスで移動)

2.ブロック崩し 
 2-1.バーを出す
 2-2.壁を出す
 2-3.ボールを出す
 2-4.ボールを動かす 
 2-5.ボールが跳ね返る
 2-6.ブロックを作る
 2-7.ブロックの衝突判定(完成)


⚫︎サンプルプログラム
変更していく箇所は赤字
(license; public domain)
 
1.DXRubyの練習 
1-1.Windowを出す
○block01.rb 
------------------------------------------------------------------------------------
# DXRubyの練習/ Windowを出す

require 'dxruby'
 
Window.loop do
 
end
------------------------------------------------------------------------------------

DXRubyの基本の形です。
横640、縦480のウィンドウが開きます。(左上の座標が(0,0))
 
blocks01a


1-2.トウフを表示
○block02.rb 
------------------------------------------------------------------------------------
# DXRubyの練習/ トウフを表示

require 'dxruby'

tohu = Image.new(100, 100, C_WHITE)

Window.loop do
  Window.draw(200, 200, tohu)
end
------------------------------------------------------------------------------------

画面表示の基本;白い四角(トウフ)を表示します。
 
blocks02a


1-3.丸くする(カーソルキーで移動)
○block03.rb 
------------------------------------------------------------------------------------
# DXRubyの練習/ 丸くする(カーソルキーで移動)

require 'dxruby'

tohu = Image.new(100, 100, C_WHITE).circle_fill(50, 50, 50, C_RED)
x = 200
y = 200

Window.loop do
  x += Input.x
  y += Input.y

  Window.draw(x, y, tohu)
end
------------------------------------------------------------------------------------

四角の中に円を描きます。さらに、カーソルキーで移動するようにします。

blocks03a


1-4.Spriteを使う(マウスで移動)
○block04.rb 
------------------------------------------------------------------------------------
# DXRubyの練習/ Spriteを使う(マウスで移動)

require 'dxruby'

tohu = Image.new(100, 100).circle_fill(50, 50, 50, C_RED)
maru = Sprite.new(200, 200, tohu)

Window.loop do
  maru.x = Input.mouse_pos_x
  maru.y = Input.mouse_pos_y

  maru.draw
end
------------------------------------------------------------------------------------

Image.new で色指定を省略すると、透明になります。
Spriteクラスを使う練習をします。また、マウスで移動するようにします。

Spriteクラスは、画面上の位置(X座標、Y座標)を持ち、描画メソッドを持っています。
また、衝突判定も持っているので、今回のブロック崩しは Spriteを使います。

blocks04a



2.ブロック崩し
今までの練習を踏まえて、ここからブロック崩しを作っていきます。
プログラムはまた最初から書き直していきます。

2-1.バーを出す
○block05.rb
------------------------------------------------------------------------------------
# ブロッック崩し/ バーを出す

require 'dxruby'

bar = Sprite.new(0, 460, Image.new(100, 20, C_WHITE))

Window.loop do
  bar.x = Input.mouse_pos_x

  bar.draw
end
------------------------------------------------------------------------------------

バーは、Spriteクラスで作ります。
初期位置は(0, 460)で、マウスで X方向だけ移動します。
画像イメージは、横 100、縦 20、白色の四角を Image.newで作ります。
 
blocks05a


2-2.壁を出す
○block06.rb
------------------------------------------------------------------------------------
# ブロッック崩し/ 壁を出す

require 'dxruby'

bar   =  Sprite.new(0, 460, Image.new(100, 20, C_WHITE))

walls = [Sprite.new(  0, 0, Image.new( 20, 480, C_WHITE)),
         Sprite.new(  0, 0, Image.new(640,  20, C_WHITE)),
         Sprite.new(620, 0, Image.new( 20, 480, C_WHITE)),
         bar]

Window.loop do
  bar.x = Input.mouse_pos_x

  Sprite.draw(walls)
end
------------------------------------------------------------------------------------

壁は Spriteクラスの配列 walls にします。
左壁;位置(0, 0)、大きさ(横 20、縦 480) 、白色
上壁;位置(0, 0)、大きさ(横 640、縦 20)、白色
右壁;位置(620, 0)、大きさ(横 20、縦 480)、白色
なお、バーも描画、衝突判定に便利なため walls に入れておきます。
複数の Spriteクラスの集まった配列の描画は、Sprite.draw(walls) となります。
 
blocks06a


2-3.ボールを出す
○block07.rb
------------------------------------------------------------------------------------
# ブロッック崩し/ ボールを出す

require 'dxruby'

bar   =  Sprite.new(0, 460, Image.new(100,  20, C_WHITE))

walls = [Sprite.new(  0, 0, Image.new( 20, 480, C_WHITE)),
         Sprite.new(  0, 0, Image.new(640,  20, C_WHITE)),
         Sprite.new(620, 0, Image.new( 20, 480, C_WHITE)),
         bar]

ball  =  Sprite.new(300, 400, Image.new(20, 20).circle_fill(10, 10, 10, C_WHITE))

Window.loop do
  bar.x = Input.mouse_pos_x

  Sprite.draw(walls)

  ball.draw
end
------------------------------------------------------------------------------------

ボールは、大きさ(横 20、縦 20) で透明のイメージに、白色の円を塗りつぶしています。
初期位置は、(300, 400)です。

blocks07


2-4.ボールを動かす
○block08.rb 
------------------------------------------------------------------------------------
# ブロッック崩し/ ボールを動かす

require 'dxruby'

bar   =  Sprite.new(0, 460, Image.new(100,  20, C_WHITE))

walls = [Sprite.new(  0, 0, Image.new( 20, 480, C_WHITE)),
         Sprite.new(  0, 0, Image.new(640,  20, C_WHITE)),
         Sprite.new(620, 0, Image.new( 20, 480, C_WHITE)),
         bar]

ball  =  Sprite.new(300, 400, Image.new(20, 20).circle_fill(10, 10, 10, C_WHITE))
dx = 4
dy = -4

Window.loop do
  bar.x = Input.mouse_pos_x

  Sprite.draw(walls)

  ball.x += dx
  ball.y += dy

  ball.draw
end
------------------------------------------------------------------------------------

ボールの移動量は、X方向 4、Y方向 -4 に設定します。
 
blocks07a


2-5.ボールが跳ね返る
○block09.rb
------------------------------------------------------------------------------------
# ブロッック崩し/ ボールが跳ね返る

require 'dxruby'

bar   =  Sprite.new(0, 460, Image.new(100,  20, C_WHITE))

walls = [Sprite.new(  0, 0, Image.new( 20, 480, C_WHITE)),
         Sprite.new(  0, 0, Image.new(640,  20, C_WHITE)),
         Sprite.new(620, 0, Image.new( 20, 480, C_WHITE)),
         bar]

ball  =  Sprite.new(300, 400, Image.new(20, 20).circle_fill(10, 10, 10, C_WHITE))
dx = 4
dy = -4

Window.loop do
  bar.x = Input.mouse_pos_x

  Sprite.draw(walls)

  ball.x += dx
  if ball === walls
    ball.x -= dx
    dx = -dx
  end

  ball.y += dy
  if ball === walls
    ball.y -= dy
    dy = -dy
  end

  ball.draw
end
------------------------------------------------------------------------------------

衝突判定 === を使うと、衝突/非衝突 が true/false で返ります。
まず、ボールを X方向に dxだけ動かして walls を衝突していたら、dxだけ戻して、ボールの移動方向を逆にします。 
次に、ボールを Y方向に動かして、同じ操作を行います。
 
blocks08a


2-6.ブロックを作る
○block10.rb
------------------------------------------------------------------------------------
# ブロッック崩し/ ブロックを作る

require 'dxruby'

bar   =  Sprite.new(0, 460, Image.new(100,  20, C_WHITE))

walls = [Sprite.new(  0, 0, Image.new( 20, 480, C_WHITE)),
         Sprite.new(  0, 0, Image.new(640,  20, C_WHITE)),
         Sprite.new(620, 0, Image.new( 20, 480, C_WHITE)),
         bar]

ball  =  Sprite.new(300, 400, Image.new(20, 20).circle_fill(10, 10, 10, C_WHITE))
dx = 4
dy = -4

image = Image.new(58, 18, C_WHITE)
blocks = []
5.times do |y|
  10.times do |x|
    blocks << Sprite.new(21 + 60 * x , 21 + 20 * y, image)
  end
end

Window.loop do
  bar.x = Input.mouse_pos_x

  Sprite.draw(walls)

  ball.x += dx
  if ball === walls
    ball.x -= dx
    dx = -dx
  end

  ball.y += dy
  if ball === walls
    ball.y -= dy
    dy = -dy
  end

  ball.draw

  Sprite.draw(blocks)
end
------------------------------------------------------------------------------------

ブロックは、横 58、縦 18、白色の四角のイメージを使います。
縦(Y方向)に5段、横に(X方向)に10列をまとめて作ります。まず、空の配列 blocks を作り、そこに Sprite.new でブロックを1つずつ作っては << で配列に追加していきます。
左上のブロックの位置は(21, 21)で、隣のブロックはそれぞれ X方向に 60、Y方向には 20 ずつずらしていきます。(上下に 2ずつ隙間ができます。)
 
blocks09a


2-7.ブロックの衝突判定(完成)
○block11.rb
------------------------------------------------------------------------------------
# ブロッック崩し/ ブロックの衝突判定(完成)
#
# 2014-03-17 松江Ruby会議05に参加してきた - mirichiの日記
# http://d.hatena.ne.jp/mirichi/20140317/p1
# より改変

require 'dxruby'

bar   =  Sprite.new(0, 460, Image.new(100,  20, C_WHITE))

walls = [Sprite.new(  0, 0, Image.new( 20, 480, C_WHITE)),
         Sprite.new(  0, 0, Image.new(640,  20, C_WHITE)),
         Sprite.new(620, 0, Image.new( 20, 480, C_WHITE)),
         bar]

ball  =  Sprite.new(300, 400, Image.new(20, 20).circle_fill(10, 10, 10, C_WHITE))
dx = 4
dy = -4

image = Image.new(58, 18, C_WHITE)
blocks = []
5.times do |y|
  10.times do |x|
    blocks << Sprite.new(21 + 60 * x , 21 + 20 * y, image)
  end
end

Window.loop do
  bar.x = Input.mouse_pos_x

  Sprite.draw(walls)

  ball.x += dx
  if ball === walls
    ball.x -= dx
    dx = -dx
  end

  col_x = ball.check(blocks).first
  if col_x
    col_x.vanish
    ball.x -= dx
    dx = -dx
  end

  ball.y += dy
  if ball === walls
    ball.y -= dy
    dy = -dy
  end

  col_y = ball.check(blocks).first
  if col_y
    col_y.vanish
    ball.y -= dy
    dy = -dy
  end

  ball.draw

  Sprite.draw(blocks)
end
------------------------------------------------------------------------------------

衝突判定 check を使うと、衝突/非衝突 が「衝突したオブジェクトの配列/空の配列」で返ります。
その結果に first を使うと、「衝突した最初のオブジェクト/nil」が返ることになります。
vanish を使うと、そのオブジェクトは描画されず、衝突判定もされなくなります。
(col_x は collision x くらいの命名です。)

blocks10a



⚫︎プログラムの簡易解説
1.DXRubyの基本
→詳しくは、DXRubyホームページの「リファレンス」を参照

○基本の形
require 'dxruby'

Window.loop do
  # ここにゲームの処理を書く 
end

 横640、縦480のウィンドウを開く(左上の座標が(0,0))


○画像のImageとSpriteの違い
・Image
 幅、高さ、色または画像データを持つ
 描画は Window.draw を使う(X座標、Y座標を指定して一つずつ)
 衝突判定機能は無い

・Sprite
 Imageのデータに加えて、画面上の位置(X座標、Y座標)をもつ
 描画は スプライトオブジェクト.draw または Sprite.draw を使う
 (X座標、Y座標のデータを持っているので、描画時には指定の必要なし)
 Sprite.draw では複数のスプライトオブジェクトを描画できる
 衝突判定機能がある

 →参考;Spriteを使うためのチュートリアル - DXRuby 1.4.1 Reference Manual


○Image.new(幅, 高さ, 色)
 イメージオブジェクトを生成


○色の指定
・省略すると透明
・[赤R, 緑G, 青B](0〜255)
・[透明度α, 赤R, 緑G, 青B](0〜255)
・色定数
 C_BLACK
 C_RED
 C_GREEN
 C_BLUE
 C_YELLOW
 C_CYAN
 C_MAGENTA
 C_WHITE
 C_DEFAULT(透明)


○イメージオブジェクト.circle_fill(x, y, r, 色)
 イメージオブジェクトの(x,y)を中心とした半径rの円を描き、塗りつぶす


○Image.load('ファイル名')
 画像ファイルを読み込んで、イメージオブジェクトを生成
 (後ろに x, y, width, height を指定するとその部分のみ切り出す)


○Sprite.new(X座標, Y座標, イメージオブジェクト)
 スプライトオブジェクトを生成


○Window.draw(X座標, Y座標, イメージオブジェクト, 描画順序(省略可) )
 イメージオブジェクトを描画


○スプライトオブジェクト.draw
 スプライトオブジェクトを描画

○Sprite.draw(配列)
 配列内の複数のスプライトオブジェクトを描画


○スプライトオブジェクト === スプライトオブジェクト(配列も可)
 衝突チェック
 衝突していた場合はtrueを、衝突していなかった場合はfalseを返す


○スプライトオブジェクト.check(スプライトオブジェクト(配列も可))
 衝突チェック
 衝突しているスプライトオブジェクトの配列を返す。衝突していなかった場合は空の配列を返す


○スプライトオブジェクト.vanish
 無効化する。描画されず、衝突判定もされず、Sprite.cleanで配列から削除される


○Sprite.clean(配列)
 配列から vanishされたスプライトオブジェクトを削除する

○スプライトオブジェクト.x
○スプライトオブジェクト.y
 X座標、Y座標を取得


○スプライトオブジェクト.x = X座標
○スプライトオブジェクト.y = Y座標
 X座標、Y座標を設定


○Input.x
 右カーソルキー押下で +1、左カーソルキー押下で -1
○Input.y
 下カーソルキー押下で +1、上カーソルキー押下で -1


○Input.mouse_pos_x
○Input.mouse_pos_y
 マウスのX座標、Y座標を取得


○スプライトオブジェクト.image
 スプライトオブジェクト内のimageオブジェクトを取得

○イメージオブジェクト.height
○イメージオブジェクト.width
 高さ、幅を取得


2.Rubyの基本
○配列 << オブジェクト
 配列の最後尾にオブジェクトを追加

○配列.first
 配列の最初のオブジェクトを返す。配列が空のときはnilを返す


 
(→次回以降、これを発展させた課題も取り上げていきます。)


小学生から楽しむ Rubyプログラミング
(株)まちづくり三鷹
日経BP社
2014-07-19