プログラミングの基礎力をつける：なるべく基本命令だけで作る「ブロック崩し」 on DXRuby

## 概要
この記事は中学高校生向けプログラミング教室の教材として作ったものを一部改変したものです。

以前、
・[DXRubyで 1ステップずつ作っていく「ブロック崩し」 - Qiita](https://qiita.com/noanoa07/items/9ebc059550c620ab223c)
という投稿をしました。

RubyやDXRubyは、衝突判定だけでなく、プログラミングに便利な数々の機能を持っています。そのため、わずか100行足らずで「ブロック崩し」ができてしまいました。

しかし、プログラミングの学習としては、便利機能に頼りすぎるのもどうなんだろうか？と思っていたところ、[『プログラムはこうして作られる』](https://www.shuwasystem.co.jp/book/9784798039251.html)という本と、その著者の平山尚さんの言葉を見つけてドキッとしました。


> [「なんか動くもん作れていい気になってんだろうが、ライブラリを剥ぎ取ったおまえ自身の力はその程度だ。よく鏡を見ておけ」平山 尚 @hirasho](
https://twitter.com/hirasho/status/618775620533616640?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E618775620533616640&ref_url=http%3A%2F%2Fblog.livedoor.jp%2Fnoanoa07%2Farchives%2F2048469.html)


もし、それらの便利な機能をなるべく使わず、基本的な機能だけを使って「ブロック崩し」を作ってみるとどうなるでしょうか？

プログラミングの本当の力が試されることになります。

そこで、今回はRubyとそのゲームライブラリDXRubyを使いますが、以下のような基本機能・命令だけを使うことにします。

- Rubyは、プログラミング言語として最低限の機能

- DXRubyは、ウィンドウの生成、座標(x,y)に点を描く、座標(x,y)の色を取得する、文字の表示、マウス／キー／カーソルキーの状態の取得のみ

今回はこの課題に挑戦してみます。

→ [『プログラムはこうして作られる プログラマの頭の中をのぞいてみよう』](https://www.shuwasystem.co.jp/book/9784798039251.html)（平山尚 著）

<img width="50%" alt="pri16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/1f0b56a9-ae32-8cee-bb22-f2a4a202faaf.png">

前の記事
・[DXRuby：「当たり判定」を自分で作ってみよう　C. 色で判定する - Qiita](https://qiita.com/noanoa07/items/52eda04c7150dd3c1ea8)

関連記事
・[Ruby用2Dゲームライブラリ DXRuby：使い方の初歩 - Qiita](https://qiita.com/noanoa07/items/bced6519d9b53685b651)
・[プログラミング初心者向け：DXRubyで 1ステップずつ作っていく「ブロック崩し」 - Qiita](https://qiita.com/noanoa07/items/9ebc059550c620ab223c)
・[DXRuby：「当たり判定」を自分で作ってみよう - Qiita](https://qiita.com/noanoa07/items/b7d647bba20116c41a77)

# 技術解説
## 使用する基本機能・命令
前述の著書で使われた「プログラミング言語 Sunaba」を参考にして以下の通りとしました。

→ プログラミング言語`Sunaba`
https://hirasho.github.io/Sunaba/

---
### ● Ruby

- 定数　変数　true　false　nil

- 数値　文字　文字列　'　''

- 演算子/式

　=（代入）
　+　-　*　/　%　<　>　<=　=>　==　!=　(　)
　&&　||

- 制御構造

　while～end

　if～（else）～end

　return

　break

- 関数

　def～end

- その他

　require　puts　p　#（コメント）

#### ※クラス、配列、ハッシュ、do～end、each などは使わない

---
### ● DXRuby
#### ○ Image（Imageクラスはスクリーン以外は使わない）
- $screen = Image.new(640, 480) （ウィンドサイズのスクリーンの設定）
- $screen[x, y]　　　　（色の取得）
- $screen[x, y] = 色　　（色の設定；これで`ドット（点）`を描く）
- 色定数

#### ○ Window
- Window.loop do～end　　　　（ウィンドウの生成、更新）
- Window.draw(x, y, $screen)　（これで`ドット（点）`を描く）
- Window.draw_font()　　　　　（文字の表示）

#### ○ Input
- Input.x　　　　　　　　（カーソルキー←→の押下の取得）
- Input.y　　　　　　　　（カーソルキー↑↓の押下の取得）
- Input.mouse_pos_x　　（マウス位置xの取得）
- Input.mouse_pos_y　　（マウス位置yの取得）
- Input.key_down?　　　（キーボードの押下の取得）

#### ○ Font
　Font.new()　　　（文字フォントの設定）

#### ※Spriteクラス、衝突判定などは使わない 
-----


## ソースコード
https://github.com/noanoa07/dxruby_blocks


## 使用ライブラリ
Windows向けRuby用2Dゲームライブラリ`DXRuby`を使用します。
バージョン`1.4.2`以上を想定しています。

### `1.4.2`より前のバージョンとの主な相違点

- `Window.loop`が複数置ける

- マウス位置を取得する`Input.mouse_pos_x`、`Input.mouse_pos_y`の新しい書き方として、`Input.mouse_x`、`Input.mouse_y`が追加

→ DXRuby 1.4.6：更新履歴

http://mirichi.github.io/dxruby-doc/CHANGELOG.html

### DXRubyインストールの注意点
→ DXRuby 1.4.6 をWindows10で使う時の注意点とインストール方法 - noanoa07 - Qiita
https://qiita.com/noanoa07/items/0ce14c2404df38de94b7

## 参考サイト
- DXRuby のホームページ
http://dxruby.osdn.jp

- DXRuby 1.4.6 リファレンスマニュアル
http://mirichi.github.io/dxruby-doc/index.html

困ったときは、このページの「チュートリアル」と「マニュアル」にだいたい書いてあります。

- DXRuby 1.4.1 リファレンスマニュアル
http://dxruby.osdn.jp/DXRubyReference/20095313382446.htm

古いバージョンのリファレンスも役に立つときがあります。

→ 「ブロック崩し」追加課題2；なるべく基本命令だけで「ブロック崩し」を作ってみよう - noanoa 日々の日記
http://blog.livedoor.jp/noanoa07/archives/2050752.html

→ 「ブロック崩し」追加課題に向けて；コンピュータの世界の下側を見てみよう - noanoa 日々の日記
http://blog.livedoor.jp/noanoa07/archives/2048469.html

→ プログラミング言語 Sunaba で「ブロック崩し」を作ってみた - noanoa 日々の日記
http://blog.livedoor.jp/noanoa07/archives/1979433.html

このテキスト関連のブログ記事です。


## ライセンス
ソースコード、本解説ともにパブリックドメイン



# プログラム解説
## 0. 練習：ウィンドウを表示
ウィンドウを表示するために、ウィンドウサイズ 640×480（色は透明）の`$screen`イメージを作成し、`Window.draw`で描画します。（`$`でグローバル変数にしています。）

→ DXRuby 1.4.1 リファレンス：Imageクラス Image.new
http://dxruby.osdn.jp/DXRubyReference/200953184038328.htm

→ DXRubyリファレンス：チュートリアル 2. 画像の読み込みと描画
http://mirichi.github.io/dxruby-doc/tutorial/basic.html

```pri_00.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/e50f418d-8e35-88c1-c59d-10db39dfe06f.png">


## 1. 練習：点（ドット）を表示する
座標(300, 200)に白色の点（ドット）を表示させてみます。

`$screen`上の座標(x,y)に色を指定することで、`点（ドット）`を表示させられます。
これには、`イメージ[x, y] = 色`を使います。

色の指定は、「DXRubyリファレンス：色配列と色定数について」
http://mirichi.github.io/dxruby-doc/api/constant_color.html

を見てください。

→ DXRuby 1.4.1 リファレンス：Imageクラス Image#[x, y]=color
http://dxruby.osdn.jp/DXRubyReference/200953184038328.htm

```pri_01.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  $screen[300, 200] = C_WHITE

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/4571d451-7958-8bdb-54a1-232a7852a582.png">
（よく見ると、画面中央やや上に白色の点が描かれています。）


## 2. 練習：点で線を描く
次は、長さ100の線を描いてみます。

線は点（ドット）の集まりとして描画します。

今回、繰り返しは`while～end`のみを使います。

```pri_02.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  width = 0
  while width < 100
    $screen[300 + width, 200] = C_WHITE
    width = width + 1
  end

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/4c101e28-177e-1cde-f304-0540d0e07b56.png">


## 3. 練習：点で四角（トウフ）を描く
線が描けたので、四角（トウフ）を描きます。
二重ループを使います。

- pri_03.rb（失敗！四角にならない）

```pri_03.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  width  = 0
  height = 0
  while width < 100
    while height < 80
      $screen[300 + width, 200 + height] = C_WHITE
      height = height + 1
    end
    width = width + 1
  end

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/498fc739-f254-6f60-e420-6b57aa641735.png">


二重ループの初期値の設定の間違いです。（ありがち？）

書き直します。

```pri_04.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  width = 0
  while width < 100
    height = 0
    while height < 80
      $screen[300 + width, 200 + height] = C_WHITE
      height = height + 1
    end
    width = width + 1
  end

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/518ae6ef-7886-5bcd-681a-3e0475271279.png">


## 4. ブロック崩し：壁を描く
それでは、ブロック崩しの壁を描いていきます。

まず、左壁（位置（0, 0）、大きさ（横 20、縦 480） 、白色）
を作ります。

```pri_05.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  x0 = 0
  y0 = 0
  width = 0
  while width < 20
    height = 0
    while height < 480
      $screen[x0 + width, y0 + height] = C_WHITE
      height = height + 1
    end
    width = width + 1
  end

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/48c12b74-695a-9016-a010-e431b457066c.png">


## 5. ブロック崩し：壁を複数描く
もう一つ、上壁（位置（0, 0）、大きさ（横 640、縦 20）、白色）を追加します。

```pri_06.rb
require 'dxruby'

$screen = Image.new(640, 480)

Window.loop do
  x0 = 0
  y0 = 0
  width0 = 0
  while width0 < 20
    height0 = 0
    while height0 < 480
      $screen[x0 + width0, y0 + height0] = C_WHITE
      height0 = height0 + 1
    end
    width0 = width0 + 1
  end

  x1 = 0
  y1 = 0
  width1 = 0
  while width1 < 640
    height1 = 0
    while height1 < 20
      $screen[x1 + width1, y1 + height1] = C_WHITE
      height1 = height1 + 1
    end
    width1 = width1 + 1
  end

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/3c2ef2e6-cf72-6e2d-37e9-4ec58bfb64c2.png">


## 6. ブロック崩し：「四角を描く」をまとめる（関数化）
続いて、左壁（位置（620, 0）、大きさ（横 20、縦 480） 、白色）
を作ります。

コードが複雑になってきたので、「四角を描く」という処理を`rect`という名前にまとめます（関数化）。

```ruby
def rect(引数)
  処理内容 
end
```

この`rect`を使ってコードを書いてみます。

```pri_07.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

Window.loop do
  rect(  0,   0,  20, 480, C_WHITE)
  rect(  0,   0, 640,  20, C_WHITE)
  rect(620,   0,  20, 480, C_WHITE)

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/21e7e62b-0bb7-8fbe-fd45-251b7989328b.png">


## 7. 練習：トウフを動かす
トウフをカーソルキーで動かす試作をします。

→ DXRubyリファレンス：チュートリアル 5. カーソルキーの入力
http://mirichi.github.io/dxruby-doc/tutorial/basic.html

- pri_08.rb（失敗！軌跡が残る）

```pri_08.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

x = 300
y = 200

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

  rect(x, y, 100, 80, C_WHITE)

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/a112d522-a7dc-ce01-3733-9be22e0d79a2.png">
図形を動かすと、前回の位置の色がそのまま残ってしまいました。

動いて見えるには、`Window.loop`が一周する毎に前回の図形を消す処理が必要でした。
書き直します。

```pri_09.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

x = 300
y = 200

Window.loop do
  rect(x, y, 100, 80, C_DEFAULT)  # 前回の位置のトウフの軌跡を消す

  x = x + Input.x
  y = y + Input.y

  rect(x, y, 100, 80, C_WHITE)

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/d2ad657c-3e36-7a90-4ab6-b68bb026ed55.png">


## 8. ブロック崩し：壁とバーを描く（pri_10.rb）
図形の動かし方がわかったので、バーを描いて、マウスと共に動くようにします。

→ DXRubyリファレンス：チュートリアル 7. マウスの入力
http://mirichi.github.io/dxruby-doc/tutorial/basic.html

```pri_10.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

# バーの初期位置
x = 300

Window.loop do
  rect(x, 460, 100, 20, C_DEFAULT)    # バーの軌跡を消す

  rect(  0,   0,  20, 480, C_WHITE)   # 左壁
  rect(  0,   0, 640,  20, C_WHITE)   # 上壁
  rect(620,   0,  20, 480, C_WHITE)   # 右壁

  x = Input.mouse_pos_x
  rect(x, 460, 100, 20, C_WHITE)      # バー

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/3212c30f-9c56-a366-b54a-49f166ff3c1f.png">


## 9. ブロック崩し：ボールを描く（動かす）
ボールを描き、動かします。移動量は、x方向 4、y方向 -4 に設定しました。

```pri_11.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

# バーの初期位置
x = 300

# ボールの初期値
ball_x = 300
ball_y = 400
dx =  4
dy = -4

Window.loop do
  rect(x, 460, 100, 20, C_DEFAULT)         # バーの軌跡を消す
  rect(ball_x, ball_y, 20, 20, C_DEFAULT)  # ボールの軌跡を消す

  rect(  0,   0,  20, 480, C_WHITE)   # 左壁
  rect(  0,   0, 640,  20, C_WHITE)   # 上壁
  rect(620,   0,  20, 480, C_WHITE)   # 右壁

  x = Input.mouse_pos_x
  rect(x, 460, 100, 20, C_WHITE)      # バー

  ball_x = ball_x + dx
  ball_y = ball_y + dy
  rect(ball_x, ball_y, 20, 20, C_RED) # ボール

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/15887487-a139-4f01-9e4c-53979408f8bb.png">


## 10. 練習：衝突判定の試作
衝突判定を作ります。判定方法は、色で判定する方法にします。
・[DXRuby：「当たり判定」を自分で作ってみよう　C. 色で判定する - Qiita](https://qiita.com/noanoa07/items/52eda04c7150dd3c1ea8)
で使った方法です。


→ 「ブロック崩し」追加課題 1c)；衝突判定の自作（色） - noanoa 日々の日記
http://blog.livedoor.jp/noanoa07/archives/2052899.html

### 10-1. 大きさ1ドット（点）での衝突判定
衝突判定`atari?(x, y)`を作ります。座標(x, y) の色が透明（C_DEFAULT）以外なら衝突と判定します。

マウスの座標(x, y)と、壁との`衝突`/`非衝突`をターミナル（コマンドプロンプト）に`true`/`false`で出力します。

```pri_12.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

def atari?(x, y)                       # 衝突判定（1ドット）
  if $screen[x,y] == C_DEFAULT
    return false
  end
  true
end

Window.loop do
  rect(  0,   0,  20, 480, C_WHITE)   # 左壁
  rect(  0,   0, 640,  20, C_WHITE)   # 上壁
  rect(620,   0,  20, 480, C_WHITE)   # 右壁

  x = Input.mouse_pos_x
  y = Input.mouse_pos_y

  p result =  atari?(x, y)            # マウスの位置と壁の衝突判定

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri12a.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/9e75e3da-63a7-babb-7ae0-5ac264122b31.png">

<img width="50%" alt="pri12b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/d38e3e46-82d8-e1d3-5525-048656137a2e.png">


### 10-2. 四角形での衝突判定
四角形が、壁と衝突しているかを`atari_4?`で判定します。

`衝突`/`非衝突`の結果をターミナル（コマンドプロンプト）に`true`/`false`で出力しつつ、四角形の色も`赤`/`白`にします。

```pri_13.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

def atari?(x, y)                       # 衝突判定（1ドット）
  if $screen[x,y] == C_DEFAULT
    return false
  end
  true
end

def atari_4?(x, y, width, height)      # 衝突判定（四角）
  atari?(x, y)         || atari?(x + width, y) ||
  atari?(x, y + width) || atari?(x + width, y + width)
end

# 四角の初期値
x = 200
y = 300
width  = 40
height = 30

Window.loop do
  rect(x, y, width, height, C_DEFAULT) # 四角の軌跡を消す

  rect(  0,   0,  20, 480, C_WHITE)    # 左壁
  rect(  0,   0, 640,  20, C_WHITE)    # 上壁
  rect(620,   0,  20, 480, C_WHITE)    # 右壁

  x = Input.mouse_pos_x
  y = Input.mouse_pos_y

  p result =  atari_4?(x, y, width, height)
  if result
    rect(x, y, width, height, C_RED)
  else
    rect(x, y, width, height, C_WHITE)
  end

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri13a.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/4c80c948-ed40-71fa-1038-32cd2294ec79.png">

<img width="50%" alt="pri13b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/34f43771-2ac6-4206-d974-9020f3f898c3.png">


## 11. ブロック崩し：壁とバーで跳ね返る
衝突判定を使って、ボールが壁とバーで跳ね返るようにします。

```pri_14.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

def atari?(x, y)                       # 衝突判定（1ドット）
  if $screen[x,y] == C_DEFAULT
    return false
  end
  true
end

def atari_4?(x, y, width, height)      # 衝突判定（四角）
  atari?(x, y)         || atari?(x + width, y) ||
  atari?(x, y + width) || atari?(x + width, y + width)
end

# バーの初期位置
x = 300

# ボールの初期値
ball_x = 300
ball_y = 400
dx =  4
dy = -4

Window.loop do
  rect(x, 460, 100, 20, C_DEFAULT)         # バーの軌跡を消す
  rect(ball_x, ball_y, 20, 20, C_DEFAULT)  # ボールの軌跡を消す

  rect(  0,   0,  20, 480, C_WHITE)   # 左壁
  rect(  0,   0, 640,  20, C_WHITE)   # 上壁
  rect(620,   0,  20, 480, C_WHITE)   # 右壁

  x = Input.mouse_pos_x
  rect(x, 460, 100, 20, C_WHITE)      # バー

  ball_x = ball_x + dx
  if atari_4?(ball_x, ball_y, 20, 20)
    ball_x = ball_x - dx
    dx = -dx
  end

  ball_y = ball_y + dy
  if atari_4?(ball_x, ball_y, 20, 20)
    ball_y = ball_y - dy
    dy = -dy
  end

  rect(ball_x, ball_y, 20, 20, C_RED) # ボール

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/c6f6a7e4-e099-9ae5-c18f-8a1d14fe5fb5.png">


## 12. ブロック崩し：ブロックを描く
ブロック群を描画します。

```pri_15.rb
require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

def atari?(x, y)                       # 衝突判定（1ドット）
  if $screen[x,y] == C_DEFAULT
    return false
  end
  true
end

def atari_4?(x, y, width, height)      # 衝突判定（四角）
  atari?(x, y)         || atari?(x + width, y) ||
  atari?(x, y + width) || atari?(x + width, y + width)
end

def block(x, y, color)                 # ブロック1つ
  rect(x, y, 58, 18, color)
end

def blocks                             # ブロック群を描画する
  count_x = 0
  while count_x < 10
    count_y = 0
    while count_y < 5
      x = 21 + 60 * count_x
      y = 21 + 20 * count_y
      block(x, y, C_WHITE)
      count_y = count_y + 1
    end
  count_x = count_x + 1
  end
end

# バーの初期位置
x = 300

# ボールの初期値
ball_x = 300
ball_y = 400
dx =  4
dy = -4

Window.loop do
  rect(x, 460, 100, 20, C_DEFAULT)         # バーの軌跡を消す
  rect(ball_x, ball_y, 20, 20, C_DEFAULT)  # ボールの軌跡を消す

  rect(  0,   0,  20, 480, C_WHITE)   # 左壁
  rect(  0,   0, 640,  20, C_WHITE)   # 上壁
  rect(620,   0,  20, 480, C_WHITE)   # 右壁

  x = Input.mouse_pos_x
  rect(x, 460, 100, 20, C_WHITE)      # バー

  ball_x = ball_x + dx
  if atari_4?(ball_x, ball_y, 20, 20)
    ball_x = ball_x - dx
    dx = -dx
  end

  ball_y = ball_y + dy
  if atari_4?(ball_x, ball_y, 20, 20)
    ball_y = ball_y - dy
    dy = -dy
  end

  rect(ball_x, ball_y, 20, 20, C_RED) # ボール

  blocks                              # ブロック群

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/a1625bc6-0842-e14d-24e5-7124ecbe4ee9.png">


## 13. ブロック崩し：ブロックとの衝突判定（衝突したら消す）をする（完成）
衝突したブロックの (x, y) を計算して、ブロックを消す処理を追加します。
ブロック群の初期状態（白色）を設定する処理が必要になります。

```pri_16.rb
# 基本機能だけでブロック崩し：衝突ブロックを消す（完成）

require 'dxruby'

$screen = Image.new(640, 480)

def rect(x, y, width, height, color)   # 四角を描画する
  w = 0
  while w < width
    h = 0
    while h < height
      $screen[x + w, y + h] = color
      h = h + 1
    end
    w = w + 1
  end
end

def atari?(x, y)                       # 衝突判定（1ドット）
  if $screen[x,y] == C_DEFAULT
    return false
  end
  true
end

def atari_4?(x, y, width, height)      # 衝突判定（四角）
  atari?(x, y)         || atari?(x + width, y) ||
  atari?(x, y + width) || atari?(x + width, y + width)
end

def block(x, y, color)                 # ブロック1つ
  rect(x, y, 58, 18, color)
end

def blocks                             # ブロック群を描画する
  count_x = 0
  while count_x < 10
    count_y = 0
    while count_y < 5
      x = 21 + 60 * count_x
      y = 21 + 20 * count_y
      block(x, y, $screen[x, y])       # 色は前回の色と同じにする
      count_y = count_y + 1
    end
  count_x = count_x + 1
  end
end

def vanish_block(x, y)                 # ブロックを消去する
  if (20 < x && x < 600) && (20 < y && y < 120)
    rect_x = x - (x - 21) % 60         # 衝突位置から衝突ブロックの
    rect_y = y - (y - 21) % 20         # (x, y)を計算する
    block(rect_x, rect_y, C_DEFAULT)
  end
end

def initialize_blocks                  # ブロック群の初期色を白にする
  count_x = 0
  while count_x < 10
    count_y = 0
    while count_y < 5
      x = 21 + 60 * count_x
      y = 21 + 20 * count_y
      $screen[x, y] = C_WHITE
      count_y = count_y + 1
    end
  count_x = count_x + 1
  end
end

# バーの初期位置
x = 300

# ボールの初期値
ball_x = 300
ball_y = 400
dx =  4
dy = -4

# ブロック群の初期色の設定
initialize_blocks

Window.loop do
  rect(x, 460, 100, 20, C_DEFAULT)         # バーの軌跡を消す
  rect(ball_x, ball_y, 20, 20, C_DEFAULT)  # ボールの軌跡を消す

  rect(  0,   0,  20, 480, C_WHITE)   # 左壁
  rect(  0,   0, 640,  20, C_WHITE)   # 上壁
  rect(620,   0,  20, 480, C_WHITE)   # 右壁

  x = Input.mouse_pos_x
  rect(x, 460, 100, 20, C_WHITE)      # バー

  ball_x = ball_x + dx
  if atari_4?(ball_x, ball_y, 20, 20)
    vanish_block(ball_x, ball_y)      # 衝突ブロックを消す
    ball_x = ball_x - dx
    dx = -dx
  end

  ball_y = ball_y + dy
  if atari_4?(ball_x, ball_y, 20, 20)
    vanish_block(ball_x, ball_y)      # 衝突ブロックを消す
    ball_y = ball_y - dy
    dy = -dy
  end

  rect(ball_x, ball_y, 20, 20, C_RED) # ボール

  blocks                              # ブロック群

  Window.draw(0, 0, $screen)
end
```

<img width="50%" alt="pri16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/1f0b56a9-ae32-8cee-bb22-f2a4a202faaf.png">
描画処理が追いつかず、ボールの移動スピードが低下してしまいました。
改良の余地があるようです。



# 参考
[プログラミング初心者向け：DXRubyで 1ステップずつ作っていく「ブロック崩し」 - Qiita](https://qiita.com/noanoa07/items/9ebc059550c620ab223c)
で作った「ブロック崩し」のプログラムです。

```block28.rb
require 'dxruby'

img_bar   = Image.new(100,  20, C_WHITE)
img_hwall = Image.new( 20, 480, C_BLUE)
img_vwall = Image.new(640,  20, C_BLUE)
img_ball  = Image.new( 20,  20).circle_fill(10, 10, 10, C_RED)
img_block = Image.new( 58,  18, C_GREEN)
img_block_y = Image.new( 58,  18, C_YELLOW)

bar   = Sprite.new(  0, 460, img_bar)
lwall = Sprite.new(  0,   0, img_hwall)
rwall = Sprite.new(620,   0, img_hwall)
twall = Sprite.new(  0,   0, img_vwall)

walls = [bar, lwall, rwall, twall]

ball  =  Sprite.new(300, 400, img_ball)
dx =  2
dy = -2

def move(sprite, speed_x, speed_y)
  sprite.x += speed_x
  sprite.y += speed_y
end

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


Window.loop do
  bar.x = Input.mouse_pos_x
  Sprite.draw(walls)

  move(ball, dx, 0)
  if ball === walls
    ball.x -= dx
    dx = -dx
  end
  coll_x = ball.check(blocks)
  if coll_x[0]
    coll_x[0].image = img_block_y
    coll_x[0].draw     #一瞬色が変わって表示
    coll_x[0].vanish   #消える
    ball.x -= dx
    dx = -dx
  end

  move(ball, 0, dy)
  if ball === walls
    ball. y -= dy
    dy = -dy
  end
  coll_y = ball.check(blocks)
  if coll_y[0]
    coll_y[0].image = img_block_y
    coll_y[0].draw     #一瞬色が変わって表示
    coll_y[0].vanish   #消える
    ball. y -= dy
    dy = -dy
  end

  ball.draw

  Sprite.draw(blocks)
end
```

<img width="50%" alt="block28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26536/1904af62-d5b0-c36c-514a-47124336d05a.png">