JavaScript canvasで円が動いたように・・・
3日目 続 canvas ブロック崩しみたいなの作りたい
・何をしたのか?
- 円(ボールに見立てる)を書いて、それを動かす
- 指定したcanvasタグのwidth,heightを指定してその上下左右の端を壁に見立てて、1のボールがぶつかったら弾ませる(ボールが半分壁 に吸い込まれる)
- 以上を少しいじって、研究(ほぼ遊び)
- ブロックを作成 絵心の無さを再認識
-
ボールがブロックにぶつかったら消える
それでは、復習していきます
注)初学なので、間違っている箇所・認識等あると考えられますので、ご容赦ください
CSSの指定は省いています。
・ボールを描いて、壁にぶつかったら、跳ね返るようにするまで
今回からは<script>が長くなるので、別ファイルに記述していきます
HTMLファイルの関連している部分
<body>
<div class="game-area">
<canvas id="xxx" width="800" height="800"></canvas>
</div>
<div class="opreation-btn">
<button id="start" class="btn">START</button>
<button id="stop" class="btn">STOP</button>
</div>
<script type="text/javascript" src="xxxx.js"></script>
</body>
canvasタグのwidthをX軸 heightをY軸に四角形をイメージした方がわかりやすいかも。
では、ボール(円)を描きます
にDOM等について書いているので、それらの説明は省略します
window.onload = function() {} の {} 中にコードを書いていく
let canvas = document.getElementById('xxx'), // xxxはcanvasタグ内のid
context = canvas.getContext('2d'), // 2Dコンテキスト
ball_radius = 10, // ボールの半径
x = canvas.width * Math.random(), // ballのX座標を指定
y = canvas.height -30, // ballのY座標を指定
dx = 1,
dy = -1;
r = Math.random() * 256,
g = Math.random() * 256,
b = Math.random() * 256;
color = "rgb(" + r + "," + g + "," + b + ")";
// ここまでは変数を定義して、次のdraw_ball() で円を描きます
function draw_ball() {
context.beginPath();
context.arc(x, y, ball_radius, 0, Math.PI*2);
context.fillStyle = color;
context.fill();
context.closePath();
}
// ボールが動いているように見せる draw()
function draw() {
//以下の一文をコメントアウトするとどういう働きをしているかわかります
context.clearRect(0, 0, canvas.width, canvas.height); // 指定された範囲を透明な黒に
draw_ball(); // ボールを描いていく
// 以下の条件はボールが壁にぶつかったら、反転して跳ね返るための記述
if(x + dx > canvas.width || x + dx < 0) {
dx = -dx;
}
if(y + dy > canvas.height || y + dy <0) {
dy = -dy;
end
}
x += dx;
y += dy;
requestAnimationFrame(draw);
}
// スタートボタンをクリックするとボールが描き出す
document.getElementById('start').addEventListener('click', draw, false);
以上でボールが動いて、壁にぶつかって、跳ね返るように見えるようにはできました
悩んだところは、緑に色を変えた壁にぶつかった場合の条件分岐
他の部分が調べて、値を考えれば何とか時間かけずにできましたが、条件分岐が毎度頭を悩ませています。繰り返して力をつけていくしかないですね!ここら変になると、イメージしづらくなってくるので、悩んだらノートに図を書いて考えるようにすると良いかも知れないですね(絵心がない自分でも強くそう思います。模写がある程度できますが自分のイメージを絵にするの本当に難しい)
どうせ書くなら、楽しく、自分らしくと思うので、余計な話がちょいちょい入りますので、ごめんなさい
実は、上記の条件だとまだ不十分でして、動かして貰えばわかります。
ボールが壁にぶつかったら、半分消える(めり込んでいる)様に見えます。
なぜでしょうか。自分も動いて、跳ね返って、やった!!と思ったんですけど、おかしいですよね。目を逸らしたらいけません。ここで妥協したら何も変わりません。と言うことで、考えて見ました。答えは、
' ボールの半径を計算に入れていなかった '
arc(x, y, ball_radius, 0, Math.PI*2) のx yは円の中心の座標を指定していて、見えているボールはそれから ball_radius 分大きく見えているので、中心の座標が壁にぶつかったら、跳ね返る条件となっていたと考えられ(たぶん)、ball_radius分を足し引きしたら、うまくいくのでは?と推測して、以下に値を変更
if(x + dx > canvas.width - ball_radius || x + dx < 0 + ball_radius) {
dx = -dx;
}
if(y + dy > canvas.height - ball_radius || y + dy < 0 + ball_radius) {
dy = -dy;
}
今見たら、0 + の部分はいらないのでは?と思いましたが、復習の記録でもあるので、直さずに残しておきます。この様にすれば、ボールがめり込まず、壁にぶつかって跳ね返っている様に見えると思います。
今回ここまでできるのに、だいぶ時間がかかりました。一つ一つ調べながらで、次また何も見ないで書いてと言われたら、かけません。でも、また調べながらなら、少し理解してきたから、時間かけずにかけるかな?自分の中ではそんな認識です。
・ブロックの作成
どういったものを作るか?絵で例えたりするとイメージしやすいと思います。
目標としては、x軸に5個、Y軸に4個 計20個のブロック(配列)を描きたい
では、以下に記述していきます
const block_padding = 10,
block_offset_top = 30,
block_offset_left = 30,
block_column_count = 5, // column(列)の数
block_row_count = 4, // row(行)の数
margin = ( (block_offset_left * 2) + (block_padding * (block_column_count -1) ) ),
block_width = (canvas.width - margin) / block_column_count,
block_height = 50;
以上がブロックの設定値
20個分できていると思うので、図に書き換えて表すとイメージしやすいと思います(ここでは省略させてもらいます。すいません)
ブロックを描くコード
let blocks = ;
for(let c = 0; c < block_column_count; c++) {
blocks[c] = ;
for(let r = 0; r < block_row_count; r++) {
blocks[c][r] = { x: 0, y: 0, status: 1 }
}
}
// x y はブロックの座標データ statusはブロックにボールがぶつかった時に力を発揮します。配列の中に配列を作って、x yの値を保存していくようなイメージです。
ブロックを描画する処理
function draw_blocks() {
for(let c = 0; c < block_column_count; c++) {
for(let r = 0; r < block_row_count; r++) {
if (blocks[c][r].status == 1) {
let block_x = (c * (block_width + block_padding)) + block_offset_left;
let block_y = (r * (block_height + block_padding)) + block_offset_top;
blocks[c][r].x = block_x;
blocks[c][r].y = block_y;
context.beginPath();
context.rect(block_x, block_y, block_width, block_height);
//block_width: 140 height:50
context.fillStyle = '#0095dd';
context.fill();
context.closePath();
}
}
}
}
for分でX座標とY座標をsetteisite,beginPath以下でブロックの大きさを指定して、描く。その繰り返しで、20個のブロックを描画する。x座標30でY座標が違うブロックを4個、X座標が170でY座標の違うブロックを という感じでfor文を利用しています。
いろいろと値を変えてみると、どうのように描画しているか理解できるかなと思いますし、ブロックを崩す時にもっとあった方が良い!と思ったら、増やすこともできるので是非お試しください。
このdraw_blocks()関数はdraw()関数の中でボールを描く前に呼び出します。
今回はここまでにしておきます。
まだ、文を書くことに慣れていないせいもあって、結構時間がかかってしまっています。勉強しないとダメなので、この続きはまた別の記事に書こうと思います。
次はボールがブロックに当たったら、消える処理
ここも難しく感じた部分なので、これから復習して、違う機能を追加することなどを、本日のテーマにして、勉強してきます。
昨日の出来事
・2年連続でホークスが日本一(おめでとうございます)
野球でキャッチャーしていたことあるので、甲斐選手のプレイに心動きました。ちなみに、特にどの球団が好きとかはなく、すごいなと思ってみる勢です。
・ラグビー日本代表対オールブラックスを見逃す
ただただ勿体無かった。それだけです。。。
最後まで、見ていただいでありがとうございました