(2022.6.17追記:アップグレードされた「rbCanvas」が発表されました。
→ インストール不要!ブラウザだけではじめるRubyゲームプログラミング環境「rbCanvas」の紹介(DXRubyスタイル) - Qiita )
概要
この記事は中学高校生向けプログラミング教室の教材として作ったものを一部改変したものです。
ブラウザだけでRubyのゲームプログラミングが始められるNyle-canvasを使って「ブロック崩し」ゲームを作っていきます。
0から少しずつ「ブロック崩し」を作っていきます。Rubyだと完成しても100数十行で「ブロック崩し」ができてしまいます。
技術解説
使用ライブラリ
- Nyle-canvas(DXRubyスタイル)
ブラウザ上で動くRubyエディタ・実行環境一体型の統合開発環境なので、Rubyのインストールは不要です。
Nyle-canvas(DXRubyスタイル)
本テキストでは、Nyle-canvasの記述スタイルの内、DXRuby API互換の「DXRubyスタイル」を使用します。
Nyle-canvasの使い方
下記の投稿を見てください。
- ブラウザではじめるRubyゲームプログラミング:Nyle-canvas(DXRubyスタイル)入門 - Qiita
Nyle-canvasに必要な動作環境
- ブラウザ(Chromeなど)
- インターネット接続
- ブラウザの動くOS(Windows、macOS、Linux、Android、iPadOS、iOSなど)
- パソコン、または(外付けキーボードを接続した)タブレット、スマートフォン
(※ タッチパネル操作にも一部対応)
Nyle-canvasのホームページ
- Nyle-canvasホームページ
https://spoolkitamura.github.io/nyle-canvas/
- Nyle-canvas(DXRubyスタイル)エディタ;実際にプログラミングするエディタ画面
「Nyle-canvasホームページ」から、「DXRubyスタイル」のリンクをクリック。
- Nyle-canvasマニュアル;エディタの操作方法
参考サイト
「DXRubyスタイル」のオリジナルであるDXRubyのページ;
-
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
DXRubyの使い方を解説した記事;
- Ruby用2Dゲームライブラリ DXRuby:使い方の初歩 - Qiita
https://qiita.com/noanoa07/items/bced6519d9b53685b651
ブラウザで動く、その他のDXRuby API互換ライブラリ;
- Rubyで始めるゲームプログラミング - DXOpal編 - Rubyist Magazine 0057 号
https://magazine.rubyist.net/articles/0057/0057-GameProgramingWithDXOpal.html
本テキストの元になった、DXRubyで作る「ブロック崩し」の記事;
- プログラミング初心者向け:DXRubyで 1ステップずつ作っていく「ブロック崩し」 - Qiita
https://qiita.com/noanoa07/items/9ebc059550c620ab223c
本テキストの実行環境
-
ブラウザ;
Google Chrome(バージョン 83.0.4103.61,64 ビット,macOS版) -
実行OS;
macOS(Catalina 10.15.5)
その他、Safari/macOS,Chrome/Windows10 でも適宜動作確認
対応バージョン
Nyle-canvas(DXRubyスタイル);dev2(2020/5/30リリース)
オリジナル
※「2014-03-17 松江Ruby会議05に参加してきた - mirichiの日記」より改変
本テキストのソースコード
本テキストのライセンス
本テキスト内の解説文、Rubyソースコード、画像データはともにパブリックドメイン
(Nyle-canvasのソースコードは MITライセンス)
プログラム解説
1. Nyle-canvasの使い方(概略)
Nyle-canvasの使い方については、下記の投稿を見てください。
- ブラウザではじめるRubyゲームプログラミング:Nyle-canvas(DXRubyスタイル)入門 - Qiita
https://qiita.com/noanoa07/items/e7ebf97edef4ae9b448a
ここでは、最低限の説明をします。
1-1. Nyle-canvasエディターを開く
Nyle-canvasは、ブラウザで以下のサイトにアクセスすることで、プログラム編集画面(エディタ)が表示されます。
「Nyle-canvasホームページ」
https://spoolkitamura.github.io/nyle-canvas/
から、
「DXRubyスタイル」のリンクをクリック。
なお、ここではブラウザとしてGoogle Chromeを使って説明していきます。(実行環境はmacOS。)
エディタ画面にはあらかじめ基本のプログラムが表示されています。
また、画面左上には各種ボタンが並んでいます。
右から、
-
▶︎ ボタン
;プログラム実行 -
↓ ボタン
;プログラム保存(ダウンロード) -
Tボタン
;フォント(文字サイズ)設定 -
?ボタン
;ヘルプ(リファレンス)画面へ
・プログラムの新規作成
ブラウザのNyle-canvas エディタ画面で 「再読み込み」をすると、最初の状態に戻ります。
「再読み込み」をする方法:
- ブラウザ上部の
「↻」ボタン
を押す -
メニュー
>ファイル
>表示
>ページを再読み込み
を選択 - ショートカット;(macOS)
Command
+R
、(Windows)Ctrl
+R
1-2. プログラムの実行・再実行
プログラムは保存操作なしで、▶︎ボタン
を押すだけで即実行されます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
Window.loop do
# 画面描画用のコード (your draw code here)
end
ブラウザの新しいタブが開き、黒い四角形の基本のウィンドウが表示されます。
座標は、左上が原点(x = 0, y = 0)、横(x)は右方向に増加、縦(y)は下方向に増加です。
Window.loop do 〜 end
の間に書いたコードは、1秒間に60回繰り返し実行されます。
→ DXRubyリファレンス:チュートリアル 1. 基本の形
http://mirichi.github.io/dxruby-doc/tutorial/basic.html
・再実行
実行画面で「再読み込み」をすることで、プログラムの再実行ができます。
「再読み込み」をする方法:
- ブラウザ上部の
「↻」ボタン
を押す -
メニュー
>ファイル
>表示
>ページを再読み込み
を選択 - ショートカット;(macOS)
Command
+R
、(Windows)Ctrl
+R
・実行画面を閉じる
実行画面のタブを閉じます。画面の上の「×」ボタン
を押します。
- ショートカット;(macOS)
Command
+W
、(Windows)Ctrl
+W
1-3. プログラムの保存(ダウンロード)
↓ボタン
を押すと、プログラムがダウンロードされます。ダウンロード先は、ブラウザの設定によりますが、「ダウンロード」フォルダになることが多いようです。
1-4. プログラムの読み込み
Nyle-canvasのプログラムファイル(HTMLファイル
)をブラウザのNyle-canvasエディタ画面
にドラッグ&ドロップします。
1-5. 画像ファイルの読み込み
Nyle-Canvasでは、画像ファイルもプログラムと合わせて一体で保存・管理します。
使いたい画像ファイルは、エディタ画面に直接ドラッグ&ドロップ
することで、Nyle-canvas内にコピーされ、エディタ画面下側に画像ファイルの一覧として表示されるようになります。
(※大文字が小文字になる場合があります。)
プログラムから画像ファイルを読み込んで使うには、Image.load(画像ファイル名)
を使います。
画像ファイル名
の前に場所を示すパス名などは不要です。
読み込んだ画像は、Imageクラス
になります。
1-6. コンソールの開き方
実行時のエラーメッセージ
やputs
などの結果は、ブラウザのコンソール
に出力されます。
Chromeでの開き方;(実行画面で)
a) macOS、Windows 共通
-
F12
(あるいはfn
+F12
)キーを押す - (画面上の任意の場所で);
右クリック
(2本指クリックなど)>検証
>開いたデベロッパーツールの中の「Console」
を選択
b) macOS
-
メニュー
>表示
>開発/管理
>Javascriptコンソール
を選択 - ショートカット;
Command
+Option
+J
c) Windows
-
メニュー
>その他のツール
>デベロッパーツール
> 開いたデベロッパーツールの中の「Console」
を選択 - ショートカット;
Ctrl
+Shift
+J
1-7. ヘルプ(リファレンス)
Nyle-canvasエディタ画面左上の「?」ボタン
を押すとヘルプ(リファレンス)画面が開きます。
→ [Nyle-canvas] APIリファレンス (DXRubyスタイル)
https://spoolkitamura.github.io/nyle-canvas/dev2/site/_man_api_dx/index.html
Nyle-canvasのAPIが簡潔にまとめられているので、困ったときにはどんどん活用してください。
2. 「ブロック崩し」を作る
いよいよ「ブロック崩し」を作っていきます。
※.htmlファイル
と.rbファイル
について
Nyle-canvasで実行するプログラムは、.htmlファイル
です。(サンプルプログラムは src/block_htmlフォルダ
内に入っています。)
ただし、以下の説明では Rubyのプログラム部分だけを載せていきます。(src/block_rubyフォルダ
内に同名の.rbファイル
が入っています。)
2-1. 壁を出す(左側)(rb_block01.html)
まず、左側の縦の壁を作ります(厚み20)。
左上隅を(0, 0)
にして、横は(厚みの)20、縦は(ウィンドウの縦幅と同じ)480の白い長方形にします。
そのため、右下隅は(20, 480)
になります
プログラムでは、Window.draw_box_fill(左上x, 左上y, 右下x, 右下y, 色)
を使います。
これをWindow.loop do 〜 end
の中に書いて、毎回描画させます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
Window.loop do
Window.draw_box_fill(0, 0, 20, 480, C_WHITE) # ◆追加
end
2-2. 壁を出す(右側も)(rb_block02.html)
次に、右側の縦の壁を作ります。
左上隅のx
はウィンドウ幅640から壁の厚み20を引いた640 - 20 = 620
、y
は0
になります。
右下隅はウィンドウの右下隅と同じ(640, 480)
です。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
Window.loop do
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE) # ◇変更(文字揃え)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE) # ◆追加
end
2-3. 壁を出す(上側も)(rb_block03.html)
上側の横の壁を作ります(厚み20)。
左上隅は(0, 0)
、右下隅のx
はウィンドウ幅と同じ640
、y
は壁の厚みと同じ20
になります。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
Window.loop do
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE) # ◆追加
end
2-4. バーを出す(rb_block04.html)
ボールを打ち返すバーとして、横100、縦20の水色の長方形の画像イメージ(src/image/bar.png
)を用意します。
まず、bar.png
ファイルをエディタ画面に直接ドラッグ&ドロップします。これにより、Nyle-canvas内にコピーされ、エディタ画面下側の画像ファイル一覧に表示されるようになります。
次に、 Image.load("bar.png")
で画像ファイルを読み込みます。(パス名は不要です。)
バーの縦位置y
は、ウィンドウの一番下480
に合わせるため、480 - バーの縦幅
にします。バーの横位置x
は、0にします。
バーを表示させるには、Window.loop do 〜 end
の中でWindow.draw(x位置, y位置, イメージ)
を使います。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png") # ◆追加
bar_x = 0 # ◆追加
bar_y = 480 - img_bar.height # ◆追加
Window.loop do
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar) # ◆追加
end
2-5. バーを矢印キーで動かす(rb_block05.html)
キーボードの左右矢印キー(←
→
)で、バーが左右(x方向
)に動くようにします。
Input.x
で左右の矢印キーの押された状態を取得し、その値(-1
, 0
, 1
)をバーのx位置
に足すことで、バーを動かします。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
Window.loop do
bar_x = bar_x + Input.x # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
end
2-6. バーを矢印キーで動かす:別の書き方(rb_block06.html)
bar_x = bar_x + Input.x
を別の書き方である、
bar_x += Input.x
に書き換えてみます。同じことですが、慣れればこちらの方が見やすいかも?
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
Window.loop do
bar_x += Input.x # ◇変更(書き方を変える)
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
end
2-7. バーの動きを素早く(rb_block07.html)
バーの動きを素早くしてみます。
Input.x
は、-1
, 0
, 1
の値しか返さないので、4倍して値を大きくして、バーのx位置
に足すことにします。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
Window.loop do
bar_x += Input.x * 4 # ◇変更(反応を速く)
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
end
2-8. バーをマウスで動かす(rb_block08.html)
マウスの動きに追従して、バーが左右(x方向
)に動くようにします。
Input.mouse_x
でマウスのx位置
を取得し、その値をバーのx位置
に代入することで、バーを動かします。
※ バー
は、左右キー
で動かしても、マウス
で動かしても構いませんが、これ以降はマウス
を使った例で示します。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合 # ◇変更(コメントアウト)
bar_x = Input.mouse_x # マウスの場合 # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
end
2-9. バーがはみ出さないように(rb_block09.html)
バーが左右の壁をはみ出さないようにします。
バーのx位置
は左端なので、最小値は、壁の厚みの20
です。
一方、右端はx位置 + バーの横幅img_bar.width
なので、最大値は、ウィンドウの横幅640 - 壁の厚み20 - バーの横幅img_bar.width
になります。
以上を、if 〜 elsif 〜 end
に書いていきます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20 # ◆追加
bar_x = 20 # ◆追加
elsif bar_x > 640 - 20 - img_bar.width # ◆追加
bar_x = 640 - 20 - img_bar.width # ◆追加
end # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
end
2-10. ボールを出す(rb_block10.html)
ボールとして、横20、縦20の赤色の円の画像イメージ(src/image/ball.png
)を用意します。
「2-4.バーを出す」と同じように、ball.png
ファイルをエディタ画面にドラッグ&ドロップして、Nyle-canvasに登録し、Image.load("ball.png")
で読み込みます。
註)画像の透明化について
2020/5/30リリースのdev2
では、Image.load
時に、画像の白色を自動的に透明にするようになっています。そのため、白地に赤色のボールが描かれているball.png
は、背景が自動的に透明になっています。
初期位置はとりあえず、x位置をball_x = 300
、y位置をball_y = 400
とします。
あとは、Window.draw
で描画します。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png") # ◆追加
ball_x = 300 # ◆追加
ball_y = 400 # ◆追加
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball) # ◆追加
end
2-11. ボールを動かす(横方向)(rb_block11.html)
ボールを横方向(x方向
)に動かします。(x
は右がプラス方向)
x方向のスピードをdx
として、ボールのx位置ball_x
に、loopで回ってくる(1/60秒)毎にdx
を足していきます(ball_x += dx
)。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2 # ◆追加
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-12. ボールを動かす(縦方向)(rb_block12.html)
今度は縦方向(y方向
)に動かします。(y
は下がプラス方向)
いったん横方向に動かすのはやめて(#ball_x += dx
でコメントアウト)、y方向のスピードをdy
として、loopで回ってくる毎にdy
を足していきます(ball_y += dy
)。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2 # ◇変更(文字揃え)
dy = -2 # ◆追加
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
#ball_x += dx # ◇変更(コメントアウト)
ball_y += dy # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-13. ボールを動かす(縦横方向)(rb_block13.html)
x方向
の動きも再開させる(ball_x += dx
とコメントアウトをはずす)と、ボールは斜めに動いていきます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx # ◇変更(コメントアウトをはずす)
ball_y += dy
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-14. ボールが跳ね返る(横方向)(rb_block14.html)
ボールを跳ね返らせます。まずは、横方向(x方向
)だけ動かします(#ball_y += dy
でコメントアウト)。
左壁にぶつかったとは、ボールのx位置
(ball_x
)が左壁の厚みの20
より小さくなった時です。
一方、右壁にぶつかったとは、ウィンドウの横幅640 - 右壁の厚み20 = 620
より大きくなった時でしょうか?
跳ね返るとは、x方向のスピードdx
が反対向きになることなので、dx = -dx
と書けます。
以上を、if 〜 end
に書いていきます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
img_ball.set_color_key(C_WHITE)
ball_x = 300
ball_y = 400
dx = 2
dy = -2
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
#ball_y += dy # ◇変更(コメントアウト)
if ball_x < 20 || ball_x > 620 # ◆追加
dx = -dx # ◆追加
end # ◆追加
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
#ball_y += dy # ◇変更(コメントアウト)
if ball_x < 20 || ball_x > 620 # ◆追加
dx = -dx # ◆追加
end # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-15. ボールが跳ね返る(横方向):修正(rb_block15.html)
ボールのx位置
(ball_x
)はボールの左端なので、右端はボールの幅
(ball_width
)を足したball_x + ball_width
になります。
"右壁にぶつかった" とは、この値がウィンドウの横幅640 - 右壁の厚み20 = 620
より大きくなった時にしなければなりません。
(ball_x + ball_width) > 620
なお、衝突の様子をゆっくり観察するために、1秒当たりの描画回数の設定(初期値:60)を半分の30にしています。
Window.fps = 30
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width # ◆追加
# 1秒当たりの描画回数の設定(初期値:60) # ◆追加
Window.fps = 30 # ◆追加
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
#ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620 # ◇変更
dx = -dx
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
これで良さそうですが、よ〜く見ると一瞬、壁にボールがめり込んでいます(左右の壁とも)。
2-16. ボールが跳ね返る(横方向):修正2(rb_block16.html)
これまでは、壁にぶつかったら、ボールのx方向の速度
(dx = -dx
)を逆向きにしただけでした。
これだと跳ね返りはしますが、ボールの位置(ball_x
)は変えていないので、ボールは壁にめり込んだままです。
そこで、ボールの位置(ball_x
)も、動いてきた分(+dx
)を逆に戻すことにします(ball_x -= dx
)。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 0
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
# 1秒当たりの描画回数の設定(初期値:60)
Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
#ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx # ◆追加
dx = -dx
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-17. ボールが跳ね返る(縦方向)(rb_block17.html)
今度はボールを縦方向(y方向
)だけ動かして、上壁で跳ね返るようにします。
上壁にぶつかったとは、ボールのy位置
(ball_y
)が上壁の厚み20
より小さくなった時です。
なお、1秒当たりの描画回数は元に戻して(コメントアウト)、バーのx位置
の初期値を中央寄りに変えています。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250 # ◇変更(数値変更)
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30 # ◇変更(コメントアウト)
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
#ball_x += dx # ◇変更(コメントアウト)
ball_y += dy # ◇変更(コメントアウトをはずす)
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20 # ◆追加
ball_y -= dy # ◆追加
dy = -dy # ◆追加
end # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-18. ボールが跳ね返る(縦横方向)(rb_block18.html)
縦横ともに動かして、跳ね返らせます。ちょっと「ブロック崩し」の雰囲気が出てきましたね。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx # ◇変更(コメントアウトをはずす)
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-19. ボールが跳ね返る(バーと)(rb_block19.html)
バーでも跳ね返るようにします。
ボールがバーにぶつかった時の条件は;
○y座標
-
ボールの下
>バーの上
(ball_y + ball_height) > (480 - bar_height)
○x座標
-
ボールの右
>バーの左
(ball_x + ball_width) > bar_x
-
ボールの左
<バーの右
ball_x < (bar_x + bar_width)
以上の条件を同時に満たす時です(&&
)。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width # ◆追加
bar_height = img_bar.height # ◆追加
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
if (ball_y + ball_height) > (480 - bar_height) && # ◆追加
(ball_x + ball_width) > bar_x && # ◆追加
ball_x < (bar_x + bar_width) # ◆追加
# ◆追加
ball_y -= dy # ◆追加
dy = -dy # ◆追加
end # ◆追加
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
これで良さそうですが、バーを動かしながら横から当たった時の跳ね返りが変です。
2-20. ボールが跳ね返る(バーと):修正(rb_block20.html)
一旦ボールがバーに入り込みすぎてしまうと、跳ね返り処理をしてもまだバーの中なので、dy
がプラスとマイナスを繰り返すばかりで、バーから出られなくなってしまいます。
そこで、跳ね返り処理をした時に、バーの外に出られる場合のみ
跳ね返るというように、条件を追加します。
y方向
で、
ボールの下
<= バーの上よりdyの絶対値分だけ下
(ball_y + ball_height) <= (480 - bar_height + dy.abs)
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
if (ball_y + ball_height) > (480 - bar_height) && # ◇変更(文字位置合わせ)
(ball_y + ball_height) <= (480 - bar_height + dy.abs) && # ◆追加
(ball_x + ball_width) > bar_x && # ◇変更(文字位置合わせ)
ball_x < (bar_x + bar_width) # ◇変更(文字位置合わせ)
ball_y -= dy
dy = -dy
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
end
2-21. ブロックを出す(1個)(rb_block21.html)
ここからは、ブロックを作っていきます。まずは1個。
ブロックとして、横58、縦18の大きさの緑の長方形の画像イメージ(src/image/block.png
)を用意します。
横幅は、横に10個置くことにして、(ウィンドウ横幅640 - 左右の壁の厚み 20 * 2) / 10 = 60
に、隣同士の隙間を左右 1ずつとって58
にしています。
このblock.png
ファイルをエディタ画面にドラッグ&ドロップして、Nyle-canvas内に登録します。
次に、 Image.load("block.png")
で画像ファイルを読み込みます。(パス名は不要です。)
最初のブロックをblock00
として、位置は左上の壁の内側(x = 20
, y = 20
)から横、縦とも1
だけ隙間を空けて、block00_x = 21
、block00_y = 21
に配置します。
そして、Window.draw(block00_x, block00_y, img_block)
で描画します。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
img_block = Image.load("block.png") # ◆追加
block_widh = img_block.width # ◆追加
block_height = img_block.height # ◆追加
block00_x = 21 # ◆追加
block00_y = 21 # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
Window.draw(block00_x, block00_y, img_block) # ◆追加
end
2-22. ブロックを出す(2個)(rb_block22.html)
2個目のブロック(block01
)は、同じイメージを使って、ブロックの横幅(block_widh)+ 隙間(2)
だけ右にずらした位置に置きます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
img_block = Image.load("block.png")
block_widh = img_block.width
block_height = img_block.height
block00_x = 21
block00_y = 21
block01_x = 21 + block_widh + 2 # ◆追加
block01_y = 21 # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
Window.draw(block00_x, block00_y, img_block)
Window.draw(block01_x, block01_y, img_block) # ◆追加
end
2-23. ブロックを出す(3個)(rb_block23.html)
同じく3個目のブロック(block02)を置きます。x位置は、ブロックと隙間を見込んでblock_widh + 2
ずつずらしていきます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
img_block = Image.load("block.png")
block_widh = img_block.width
block_height = img_block.height
block00_x = 21
block00_y = 21
block01_x = 21 + block_widh + 2
block01_y = 21
block02_x = 21 + (block_widh + 2) * 2 # ◆追加
block02_y = 21 # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
Window.draw(block00_x, block00_y, img_block)
Window.draw(block01_x, block01_y, img_block)
Window.draw(block02_x, block02_y, img_block) # ◆追加
end
2-24. ブロックを出す(10個)(rb_block24.html)
横に10個置くとちょうどぴったりの幅です。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
img_block = Image.load("block.png")
block_widh = img_block.width
block_height = img_block.height
block00_x = 21
block00_y = 21
block01_x = 21 + block_widh + 2
block01_y = 21
block02_x = 21 + (block_widh + 2) * 2
block02_y = 21
block03_x = 21 + (block_widh + 2) * 3 # ◆追加
block03_y = 21 # ◆追加
block04_x = 21 + (block_widh + 2) * 4 # ◆追加
block04_y = 21 # ◆追加
block05_x = 21 + (block_widh + 2) * 5 # ◆追加
block05_y = 21 # ◆追加
block06_x = 21 + (block_widh + 2) * 6 # ◆追加
block06_y = 21 # ◆追加
block07_x = 21 + (block_widh + 2) * 7 # ◆追加
block07_y = 21 # ◆追加
block08_x = 21 + (block_widh + 2) * 8 # ◆追加
block08_y = 21 # ◆追加
block09_x = 21 + (block_widh + 2) * 9 # ◆追加
block09_y = 21 # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
Window.loop do
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
ball_x += dx
ball_y += dy
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
if ball_y < 20
ball_y -= dy
dy = -dy
end
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
Window.draw(block00_x, block00_y, img_block)
Window.draw(block01_x, block01_y, img_block)
Window.draw(block02_x, block02_y, img_block)
Window.draw(block03_x, block03_y, img_block) # ◆追加
Window.draw(block04_x, block04_y, img_block) # ◆追加
Window.draw(block05_x, block05_y, img_block) # ◆追加
Window.draw(block06_x, block06_y, img_block) # ◆追加
Window.draw(block07_x, block07_y, img_block) # ◆追加
Window.draw(block08_x, block08_y, img_block) # ◆追加
Window.draw(block09_x, block09_y, img_block) # ◆追加
end
2-25. ブロックをまとめて作る(Itemクラス
)(rb_block25.html)
ブロックを作る/描画するプログラムが10回も繰り返したので、まとめて作ることします。
以下のような手順で作っていきます。
1) Itemクラス
というクラスを作る
ブロックを扱うのに、以下のような属性をまとめて扱えると便利です。
- x位置
- y位置
- イメージ
- イメージの幅
- イメージの高さ
それを踏まえて、Itemクラス
というクラスを作ることにします。
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
end
これで、Itemクラス
(のインスタンス)は、x位置
、y位置
、イメージ
、幅
、高さ
をもつようになります。
2) ブロックのイメージを用意する
img_block = Image.load("block.png")
3) Itemクラス
でブロックを作る
Item.new(x位置, y位置, img_block)
4) 複数のブロック(ブロック群)を作るために、配列blocks
を用意する
blocks = []
で、空の配列blocks
を作ります。
5) ブロックを1つずつ作って、配列blocks
に追加していく
配列に追加していく<<
メソッドを使って、ブロックを1つ作っては配列blocks
に追加する操作を繰り返します。
10回繰り返すので、10.times do 〜 end
を使って、1回毎にx
を増やすことで横位置をずらしたブロックを作ります。(x
は 0, 1, 2, ... , 9 と変わっていく)
10.times do |x|
blocks << Item.new(21 + (img_block.width + 2) * x, 21, img_block)
6) ブロック群の描画もまとめる
ブロック群blocks
は配列なので、繰り返し操作する 配列.each do |配列要素| 〜 end
が使えます。
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
以上で、プログラムがだいぶすっきりしました。(コメントも追加してあります。)
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20 # ◆追加(コメント)
# ブロックはItemクラスにする # ◆追加(コメント)
class Item # ◆追加
def initialize(x, y, image) # ◆追加
@x = x # ◆追加
@y = y # ◆追加
@image = image # ◆追加
@width = image.width # ◆追加
@height = image.height # ◆追加
end # ◆追加
attr_accessor :x, :y, :image, :width, :height # ◆追加
end # ◆追加
# バーの準備 # ◆追加(コメント)
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
# ボールの準備 # ◆追加(コメント)
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# ブロックの準備 # ◆追加(コメント)
img_block = Image.load("block.png")
# ブロック群の初期化 # ◆追加(コメント)
blocks = [] # ◆追加
10.times do |x| # ◆追加
blocks << Item.new(21 + (img_block.width + 2) * x, 21, img_block) # ◆追加
end # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ # ◆追加(コメント)
Window.loop do
# バーを動かす # ◆追加(コメント)
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
# ボールを動かす # ◆追加(コメント)
ball_x += dx
ball_y += dy
# 壁に当たったら、跳ね返る(x方向) # ◆追加(コメント)
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
# 壁に当たったら、跳ね返る(y方向) # ◆追加(コメント)
if ball_y < 20
ball_y -= dy
dy = -dy
end
# バーとの衝突判定 # ◆追加(コメント)
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
# 画面描画 # ◆追加(コメント)
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
blocks.each do |block| # ◆追加
Window.draw(block.x, block.y, block.image) # ◆追加
end # ◆追加
end
2-26. ブロックを出す(2段目も作る)(rb_block26.html)
2段目のブロックも作っていきます。
2段目は、yの位置
をブロックの高さ(img_block.height) + 隙間(2)
だけ大きくします。
1段目と同じく10.times do 〜 endを使って書きます。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
end
# バーの準備
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
# ボールの準備
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
blocks << Item.new(21 + (img_block.width + 2) * x, 21, img_block)
end
10.times do |x| # ◆追加
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2), img_block) # ◆追加
end # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
# ボールを動かす
ball_x += dx
ball_y += dy
# 壁に当たったら、跳ね返る(x方向)
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
# 壁に当たったら、跳ね返る(y方向)
if ball_y < 20
ball_y -= dy
dy = -dy
end
# バーとの衝突判定
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-27. ブロックを出す(5段目まで作る)(rb_block27.html)
5段目まで作りました。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
end
# バーの準備
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
# ボールの準備
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
blocks << Item.new(21 + (img_block.width + 2) * x, 21, img_block)
end
10.times do |x|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2), img_block)
end
10.times do |x| # ◆追加
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * 2, img_block) # ◆追加
end # ◆追加
10.times do |x| # ◆追加
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * 3, img_block) # ◆追加
end # ◆追加
10.times do |x| # ◆追加
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * 4, img_block) # ◆追加
end # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
# ボールを動かす
ball_x += dx
ball_y += dy
# 壁に当たったら、跳ね返る(x方向)
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
# 壁に当たったら、跳ね返る(y方向)
if ball_y < 20
ball_y -= dy
dy = -dy
end
# バーとの衝突判定
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-28. ブロックを出す(5段目までまとめて作る)(rb_block28.html)
10.times do 〜 end
が5回出てきたので、これもまとめてみます。
10.times do 〜 end
の中に、5.times do 〜 end
を入れて、2重の形にします。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
end
# バーの準備
img_bar = Image.load("bar.png")
bar_x = 250
bar_y = 480 - img_bar.height
bar_width = img_bar.width
bar_height = img_bar.height
# ボールの準備
img_ball = Image.load("ball.png")
ball_x = 300
ball_y = 400
dx = 2
dy = -2
ball_width = img_ball.width
ball_height = img_ball.height
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y| # ◆追加
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block) # ◆追加
end # ◆追加
end
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar_x += Input.x * 4 # 左右キーの場合
bar_x = Input.mouse_x # マウスの場合
if bar_x < 20
bar_x = 20
elsif bar_x > 640 - 20 - img_bar.width
bar_x = 640 - 20 - img_bar.width
end
# ボールを動かす
ball_x += dx
ball_y += dy
# 壁に当たったら、跳ね返る(x方向)
if ball_x < 20 || (ball_x + ball_width) > 620
ball_x -= dx
dx = -dx
end
# 壁に当たったら、跳ね返る(y方向)
if ball_y < 20
ball_y -= dy
dy = -dy
end
# バーとの衝突判定
if (ball_y + ball_height) > (480 - bar_height) &&
(ball_y + ball_height) <= (480 - bar_height + dy.abs) &&
(ball_x + ball_width) > bar_x &&
ball_x < (bar_x + bar_width)
ball_y -= dy
dy = -dy
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar_x, bar_y, img_bar)
Window.draw(ball_x, ball_y, img_ball)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-29. ボール、バーもItemクラス
にする(rb_block29.html)
考えてみると、ボールとバーの属性もItemクラス
とほとんど同じなので、Itemクラス
にすることにします。
これでまた、プログラムが見やすくなりました。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする # ◇変更(ボール、バーもItemクラスに作り替え)
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
end
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar) # ◇変更(Itemクラスに作り替え)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball) # ◇変更(Itemクラスに作り替え)
dx = 2 # ボールのスピード(x方向) # ◇変更(文字揃え、コメント追加)
dy = -2 # ボールのスピード(y方向) # ◇変更(文字揃え、コメント追加)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合 # ◇変更(bar.x に)
bar.x = Input.mouse_x # マウスの場合 # ◇変更(bar.x に)
if bar.x < 20 # ◇変更(bar.x に)
bar.x = 20 # ◇変更(bar.x に)
elsif bar.x > 640 - 20 - bar.width # ◇変更(bar.x、bar.width に)
bar.x = 640 - 20 - bar.width # ◇変更(bar.x、bar.width に)
end
# ボールを動かす
ball.x += dx # ◇変更(ball.x に)
ball.y += dy # ◇変更(ball.y に)
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620 # ◇変更(ball.x、ball.width に)
ball.x -= dx # ◇変更(ball.x に)
dx = -dx
end
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20 # ◇変更(ball.y に)
ball.y -= dy # ◇変更(ball.y に)
dy = -dy
end
# バーとの衝突判定
if (ball.y + ball.height) > (480 - bar.height) && # ◇変更(ball.y、ball.height、bar.height に)
(ball.y + ball.height) <= (480 - bar.height + dy.abs) && # ◇変更(ball.y、ball.height、bar.height に)
(ball.x + ball.width) > bar.x && # ◇変更(ball.x、ball.width、bar.x に)
ball.x < (bar.x + bar.width) # ◇変更(ball.x、bar.x、bar.width に)
ball.y -= dy # ◇変更(ball.y に)
dy = -dy
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image) # ◇変更(bar.x、bar.y、bar.image に)
Window.draw(ball.x, ball.y, ball.image) # ◇変更(ball.x、ball.y、ball.image に)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-30. 衝突判定collision?
を作る(rb_block30.html)
今後、バーだけでなく、ブロックとの衝突判定を何回も行うので、衝突判定collision?
を作っておきます。ここでは、四角形同士で考えることにします。
item_a
と item_b
という2つの長方形を考えると;
-
item_a
の座標;左上(a_x0
,a_y0
)、右下(a_x1
,a_y1
) -
item_b
の座標;左上(b_x0
,b_y0
)、右下(b_x1
,b_y1
)
item_a
のx座標は、a_x0〜a_x1
、y座標はa_y0〜a_y1
item_b
のx座標は、b_x0〜b_x1
、y座標はb_y0〜b_y1
それぞれの座標(x, y)を考えて、2つが衝突している(ぶつかっている)条件は、以下のようになります。
a_x0 < b_x1 かつ
a_x1 > b_x0 かつ
a_y0 < b_y1 かつ
a_y1 > b_y0
これをコーディングすると以下のようになります。(item_a
、item_b
は、Itemクラス
とする)
def collision?(item_a, item_b)
a_x0 = item_a.x
a_x1 = item_a.x + item_a.width
a_y0 = item_a.y
a_y1 = item_a.y + item_a.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
collision?
は、衝突しているとtrue
を返します。
(その他の衝突判定の考え方(円、色)については、以下を参照;
→ ・DXRuby:「当たり判定」を自分で作ってみよう - Qiita
https://qiita.com/noanoa07/items/b7d647bba20116c41a77 )
また、跳ね返る方向(x方向
、y方向
)を考える必要があるため、ボールの移動はx方向
、y方向
に分けて行い、それぞれで衝突判定をするようにします。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
end
# 衝突判定 # ◆追加
def collision?(item_a, item_b) # ◆追加
a_x0 = item_a.x # ◆追加
a_x1 = item_a.x + item_a.width # ◆追加
a_y0 = item_a.y # ◆追加
a_y1 = item_a.y + item_a.height # ◆追加
# ◆追加
b_x0 = item_b.x # ◆追加
b_x1 = item_b.x + item_b.width # ◆追加
b_y0 = item_b.y # ◆追加
b_y1 = item_b.y + item_b.height # ◆追加
# ◆追加
if a_x0 < b_x1 && # ◆追加
a_x1 > b_x0 && # ◆追加
a_y0 < b_y1 && # ◆追加
a_y1 > b_y0 # ◆追加
# ◆追加
true # ◆追加
end # ◆追加
end # ◆追加
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす # ◇変更(y方向だけに)
ball.y += dy # ◇変更(y方向だけに)
# バーとの衝突判定
if collision?(ball, bar) # ◇変更(書き直し)
if ball.y + ball.height <= 480 - bar.height + dy.abs # ◇変更(書き直し)
ball.y -= dy # ◇変更(書き直し)
dy = -dy # ◇変更(書き直し)
end # ◇変更(書き直し)
end # ◇変更(書き直し)
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす # ◇変更(x方向だけに)
ball.x += dx # ◇変更(x方向だけに)
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-31. collision?
をItemクラス
のメソッドにする(rb_block31.html)
Rubyでは、collision?(item_a, item_b)
という書き方より、item_a.collision?(item_b)
という書き方が多く使われます(オブジェクト指向)。
そのためには、collision?
をItemクラス
のメソッドにしましょう(正確にはインスタンスメソッド)。
作り方は、以下のようにします。
class Item
def 追加したいメソッド
#メソッドの内容
end
end
ここで、item_a
に相当するものは、self
と書きます。
class Item
def collision?(item_b)
a_x0 = self.x
a_x1 = self.x + self.width
a_y0 = self.y
a_y1 = self.y + self.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
end
これで、今までのcollision?(ball, bar)
という書き方から、ball.collision?(bar)
という書き方になります。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
# 衝突判定 # ◇変更(Itemクラスのメソッドに)
def collision?(item_b) # ◇変更(書き直し)
a_x0 = self.x # ◇変更(書き直し)
a_x1 = self.x + self.width # ◇変更(書き直し)
a_y0 = self.y # ◇変更(書き直し)
a_y1 = self.y + self.height # ◇変更(書き直し)
# ◇変更(書き直し)
b_x0 = item_b.x # ◇変更(書き直し)
b_x1 = item_b.x + item_b.width # ◇変更(書き直し)
b_y0 = item_b.y # ◇変更(書き直し)
b_y1 = item_b.y + item_b.height # ◇変更(書き直し)
# ◇変更(Itemクラスのメソッドに)
if a_x0 < b_x1 && # ◇変更(Itemクラスのメソッドに)
a_x1 > b_x0 && # ◇変更(Itemクラスのメソッドに)
a_y0 < b_y1 && # ◇変更(Itemクラスのメソッドに)
a_y1 > b_y0 # ◇変更(Itemクラスのメソッドに)
# ◇変更(Itemクラスのメソッドに)
true # ◇変更(Itemクラスのメソッドに)
end # ◇変更(Itemクラスのメソッドに)
end # ◇変更(Itemクラスのメソッドに)
end # ◇変更(Itemクラスのメソッドに)
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす
ball.y += dy
# バーとの衝突判定
if ball.collision?(bar) # ◇変更(書き直し)
if ball.y + ball.height <= 480 - bar.height + dy.abs
ball.y -= dy
dy = -dy
end
end
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす
ball.x += dx
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-32. ブロックとの衝突判定(当たったブロックは消える)(rb_block32.html)
ブロックとの衝突判定をします。
配列.delete_if
は、配列要素を1つずつ取り出し、条件に当てはまる場合は、配列から削除する命令です。
そのため、ブロック群の配列blocks
から、衝突判定でtrue
になったブロックを削除することができます。
blocks.delete_if do |block|
if ball.collision?(block)
true
end
end
これを使ったコードが以下のようになります。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
# 衝突判定
def collision?(item_b)
a_x0 = self.x
a_x1 = self.x + self.width
a_y0 = self.y
a_y1 = self.y + self.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
end
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす
ball.y += dy
# バーとの衝突判定
if ball.collision?(bar)
if ball.y + ball.height <= 480 - bar.height + dy.abs
ball.y -= dy
dy = -dy
end
end
# ブロックとの衝突判定(y方向) # ◆追加
blocks.delete_if do |block| # ◆追加
if ball.collision?(block) # ◆追加
true # ◆追加
end # ◆追加
end # ◆追加
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす
ball.x += dx
# ブロックとの衝突判定(x方向) # ◆追加
blocks.delete_if do |block| # ◆追加
if ball.collision?(block) # ◆追加
true # ◆追加
end # ◆追加
end # ◆追加
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
2-33. ブロックに当たったら跳ね返る;一応完成(rb_block33.html)
ブロック当たったら跳ね返るようにします。
ブロックと衝突したら、跳ね返るコードを足すだけです。
# ブロックとの衝突判定(y方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.y -= dy # ◆追加
dy = -dy # ◆追加
true
end
end
これで「ブロック崩し」は一応完成です!
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
# 衝突判定
def collision?(item_b)
a_x0 = self.x
a_x1 = self.x + self.width
a_y0 = self.y
a_y1 = self.y + self.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
end
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす
ball.y += dy
# バーとの衝突判定
if ball.collision?(bar)
if ball.y + ball.height <= 480 - bar.height + dy.abs
ball.y -= dy
dy = -dy
end
end
# ブロックとの衝突判定(y方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.y -= dy # ◆追加
dy = -dy # ◆追加
true
end
end
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす
ball.x += dx
# ブロックとの衝突判定(x方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.x -= dx # ◆追加
dx = -dx # ◆追加
true
end
end
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
end
応用問題
「ブロック崩し」を改良・発展させてみよう
「ブロック崩し」を改良・発展させてみましょう。
以下はほんの一例です。各自、自由に発展させてみてください。
A. 動作を改善する
作った「ブロック崩し」を動かしてみて、動きが気になるところを直してみましょう。
B. 機能を拡張する
「ブロック崩し」の機能を追加して、発展させてみましょう。
B-1. 画面に文字を表示する(rb_block34.html)
画面に文字を表示してみましょう。
まず、初期設定でフォントを準備します。
font = Font.new(24)
文字の表示は以下のようにします。
Window.draw_font(x位置, y位置, 文字列, font, {:color => 文字色)
残りブロック数を知るには、ブロック群の配列blocks
の要素数を調べればよいので、blocks.size
とします。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
# 衝突判定
def collision?(item_b)
a_x0 = self.x
a_x1 = self.x + self.width
a_y0 = self.y
a_y1 = self.y + self.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
end
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# フォントの準備 # ◆追加
font = Font.new(24) # ◆追加
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす
ball.y += dy
# バーとの衝突判定
if ball.collision?(bar)
if ball.y + ball.height <= 480 - bar.height + dy.abs
ball.y -= dy
dy = -dy
end
end
# ブロックとの衝突判定(y方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.y -= dy
dy = -dy
true
end
end
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす
ball.x += dx
# ブロックとの衝突判定(x方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.x -= dx
dx = -dx
true
end
end
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
# 文字の表示 # ◆追加
string = "残りブロックは #{blocks.size}個です。" # ◆追加
Window.draw_font(20, 200, string, font, {:color => C_YELLOW}) # ◆追加
end
B-2. ゲームオーバー画面を追加する(rb_block35.html)
ゲームオーバー画面を追加してみます。
ボールのy位置
(ball.y
)が、ウィンドウの縦幅480
より大きくなったら、「ゲームオーバー画面」を表示するようにします。
ゲームオーバー画面は以下のようにして作っています。
-
ウィンドウサイズ(640, 480)と同じ大きさの白色の四角形を描く(
Window.draw_box_fill(0, 0, 640, 480, C_WHITE)
) -
「ゲームオーバー」という文字を表示させる(
Window.draw_font(200, 200, "ゲームオーバー", font, {:color => C_BLACK})
)
これを追加したコードです。
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
# 衝突判定
def collision?(item_b)
a_x0 = self.x
a_x1 = self.x + self.width
a_y0 = self.y
a_y1 = self.y + self.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
end
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# フォントの準備
font = Font.new(24)
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす
ball.y += dy
# バーとの衝突判定
if ball.collision?(bar)
if ball.y + ball.height <= 480 - bar.height + dy.abs
ball.y -= dy
dy = -dy
end
end
# ブロックとの衝突判定(y方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.y -= dy
dy = -dy
true
end
end
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす
ball.x += dx
# ブロックとの衝突判定(x方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.x -= dx
dx = -dx
true
end
end
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
# 文字の表示
string = "残りブロックは #{blocks.size}個です。"
Window.draw_font(20, 200, string, font, {:color => C_YELLOW})
# ゲームオーバー画面 # ◆追加
if ball.y >= 480 # ◆追加
Window.draw_box_fill(0, 0, 640, 480, C_WHITE) # ◆追加
Window.draw_font(200, 200, "ゲームオーバー", font, {:color => C_BLACK}) # ◆追加
end # ◆追加
end
B-3. ゲームオーバー画面から再開させる(rb_block36.html)
ゲームオーバー画面から、特定のキーを押すことで、ゲームが再開するようにします。
キーが押されたかどうかは、Input.key_down?(キーボード定数)
で調べます。
あとは、再開画面をどう設定するかは、自由です。(ここでは、残ったブロックはそのままで、ボールを初期条件に戻しています。)
include DX
# 初期設定用のコード (your setup code here)
Window.width = 640
Window.height = 480
Window.bgcolor = C_BLACK
# 壁の厚み:左、右、上;20
# ボール、バー、ブロックはItemクラスにする
class Item
def initialize(x, y, image)
@x = x
@y = y
@image = image
@width = image.width
@height = image.height
end
attr_accessor :x, :y, :image, :width, :height
# 衝突判定
def collision?(item_b)
a_x0 = self.x
a_x1 = self.x + self.width
a_y0 = self.y
a_y1 = self.y + self.height
b_x0 = item_b.x
b_x1 = item_b.x + item_b.width
b_y0 = item_b.y
b_y1 = item_b.y + item_b.height
if a_x0 < b_x1 &&
a_x1 > b_x0 &&
a_y0 < b_y1 &&
a_y1 > b_y0
true
end
end
end
# バーの準備
img_bar = Image.load("bar.png")
bar = Item.new(250, 480 - img_bar.height, img_bar)
# ボールの準備
img_ball = Image.load("ball.png")
ball = Item.new(300, 400, img_ball)
dx = 2 # ボールのスピード(x方向)
dy = -2 # ボールのスピード(y方向)
# ブロックの準備
img_block = Image.load("block.png")
# ブロック群の初期化
blocks = []
10.times do |x|
5.times do |y|
blocks << Item.new(21 + (img_block.width + 2) * x, 21 + (img_block.height + 2) * y, img_block)
end
end
# フォントの準備
font = Font.new(24)
# 1秒当たりの描画回数の設定(初期値:60)
#Window.fps = 30
# メインループ
Window.loop do
# バーを動かす
#bar.x += Input.x * 4 # 左右キーの場合
bar.x = Input.mouse_x # マウスの場合
if bar.x < 20
bar.x = 20
elsif bar.x > 640 - 20 - bar.width
bar.x = 640 - 20 - bar.width
end
# ボールをy方向に動かす
ball.y += dy
# バーとの衝突判定
if ball.collision?(bar)
if ball.y + ball.height <= 480 - bar.height + dy.abs
ball.y -= dy
dy = -dy
end
end
# ブロックとの衝突判定(y方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.y -= dy
dy = -dy
true
end
end
# 壁に当たったら、跳ね返る(y方向)
if ball.y < 20
ball.y -= dy
dy = -dy
end
# ボールをx方向に動かす
ball.x += dx
# ブロックとの衝突判定(x方向)
blocks.delete_if do |block|
if ball.collision?(block)
ball.x -= dx
dx = -dx
true
end
end
# 壁に当たったら、跳ね返る(x方向)
if ball.x < 20 || (ball.x + ball.width) > 620
ball.x -= dx
dx = -dx
end
# 画面描画
Window.draw_box_fill( 0, 0, 20, 480, C_WHITE)
Window.draw_box_fill(620, 0, 640, 480, C_WHITE)
Window.draw_box_fill( 0, 0, 640, 20, C_WHITE)
Window.draw(bar.x, bar.y, bar.image)
Window.draw(ball.x, ball.y, ball.image)
blocks.each do |block|
Window.draw(block.x, block.y, block.image)
end
# 文字の表示
string = "残りブロックは #{blocks.size}個です。"
Window.draw_font(20, 200, string, font, {:color => C_YELLOW})
# ゲームオーバー画面
if ball.y >= 480
Window.draw_box_fill(0, 0, 640, 480, C_WHITE)
Window.draw_font(200, 200, "ゲームオーバー", font, {:color => C_BLACK})
Window.draw_font(200, 230, "スペースキーで続行", font, {:color => C_BLACK}) # ◆追加
if Input.key_down?(K_SPACE) # ◆追加
ball.x = 300 # ◆追加
ball.y = 400 # ◆追加
dx = 2 # ◆追加
dy = -2 # ◆追加
end # ◆追加
end
end
以上で、テキストは終わりです。
あとは皆さんでいろいろと発展させてみてください!