はじめに
canvasを用いてブラウザで実行できる簡単なゲームを紹介していきたいと思います。
ゲーム内容(ALL YOU NEED TO DO IS ESCAPE)
ゲームの内容は簡単です。Enemyに当たらないように逃げてください。Enemyは壁(ブラウザの端)に当たると加速します。使用するブラウザによってはEnemyの動きが悪くなるかもしれません。EnemyとMy_characterが当たるとGAME OVERです。window sizeは1920*1080になります。
-
window.innerHeight
が969 -
window.innerWidth
が1920になります
うまく開かない場合はmain関数内のif(!(WINDOW_WIDTH == 1920 || WINDOW_HEIGHT == 969))
の条件を消してみてください
function main() {
if(!(WINDOW_WIDTH == 1920 || WINDOW_HEIGHT == 969)) {
window.alert("You must resize window {innerWidth: 1920, innerHeight: 969}\nYou need to open window size: 1920*1080");
return;
} else {
//自分生成
create_my_character();
count_timer();
let size = Math.floor(Math.random() * 51) + 20;
//敵生成
let move_speed = 1;
create_canvas(size, size, 1500, 350, move_speed);
}
}
必要なもの
- ブラウザ(chromeがおすすめ)
- ソースコード(github)
当たり判定
**position: absolute;**をしているため左上絶対参照になることに注意してください。xは右に行けば行くほど値が大きくなり、yは下に行けば行くほど値が大きくなります
create_canvas関数
create_canvas(width, height, left, top, move_speed)
この関数内ではEnemyを作成します。
-
document.createElement("canvas");
でを作成 -
canvas.style.position = "absolute";
これで絶対配置にします
let canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvasを描画
毎回Enemyの色が変わります
rgb(${rgb.r},${rgb.g},${rgb.b})
はrgb(0~255, 0~255, 0~255)
になります
CANVAS.arc();に関してはさまざまな図形を描くで詳しく解説されています
let canvas_context = canvas.getContext("2d");
let rgb = {
r: Math.floor(Math.random() * 256),
g: Math.floor(Math.random() * 256),
b: Math.floor(Math.random() * 256),
};
canvas_context.fillStyle = `rgb(${rgb.r},${rgb.g},${rgb.b})`;
canvas_context.beginPath();
canvas_context.arc(width / 2, height / 2, width / 2, Math.PI*2, false);
canvas_context.fill();
divタグ内にcanvasを追加
main_fieldはグローバル変数でdivタグのidを取得しています
main_fieldの子要素にcanvasを追加しています。これで動的に<canvas></canvas>をdivタグ内に作成することができました。
main_field.appendChild(canvas);
Enemyを上方向に動かす
- top > 0でEnemyのtopが0より大きい時でflagがfalseの場合(Enemyが一番上まで達していないとき)
top -= move_speed;
減算している理由は上方向へEnemyを移動させるためにyを減らす必要があるから
- Enemyが一番上まで達したらelse
move_speedにadd_speedを加算することで1から1.001になります(スピードが速くなる)
up_way_flag = true;
これで一番上まで来たことを表します
down_way_flag = false;
にしてdown_way関数を呼び出します
下右左もup_way関数と要領は同じなので省略します
let add_speed = 0.001;
function up_way(){
if(top > 0 && up_way_flag == false) {
top -= move_speed;
canvas.style.top = top + "px";
} else {
move_speed += add_speed;
up_way_flag = true;
down_way_flag = false;
down_way();
}
}
setIntervalでEnemyを動かす
setInterval(() => {}, time);
0.001秒ごとにsetInterval内を繰り返します(1秒 = 1000)
let my_character = document.getElementById("my_character");
自分を取得してparseInt()でmy_characterのleft, top, width, heightを整数に変換します
- if内(Enemyとmy_characterが当たった時)
setTimeout(() => {my_character.style.visibility = "hidden";}, 100);
これはmy_characterのvisibilityをhiddenにしたりvisibleにしたりすることで、当たったことをユーザーに知らせます。(ポケモンの効果は抜群だeffect)
clearInterval(main_interval);
これで繰り返し処理を止めます
main_field.removeChild(canvas);
Enemyをdivタグ内から消去します
gameover_message();
GAME OVERしたことを知らせる関数を呼び出す
up_way();
とleft_way();
を呼び出しているためEnemyは最初に左斜め上方向へ動く
let main_interval = setInterval(() => {
let my_character = document.getElementById("my_character");
let m_left = parseInt(my_character.style.left);
let m_top = parseInt(my_character.style.top);
let m_width = parseInt(my_character.width);
let m_height = parseInt(my_character.height);
if(
left + width >= m_left &&
left <= m_left + m_width &&
top + height >= m_top &&
top <= m_top + m_height
) {
setTimeout(() => {my_character.style.visibility = "hidden";}, 100);
setTimeout(() => {my_character.style.visibility = "visible";}, 200);
setTimeout(() => {my_character.style.visibility = "hidden";}, 300);
setTimeout(() => {my_character.style.visibility = "visible";}, 400);
setTimeout(() => {my_character.style.visibility = "hidden";}, 500);
setTimeout(() => {my_character.style.visibility = "visible";}, 600);
clearInterval(main_interval);
main_field.removeChild(canvas);
gameover_message();
}
up_way();
left_way();
}, 1);
create_my_character関数
自分が操作するcanvasを作成します
矢印キーで移動
addEventListenerの第一引数に"keydown"を指定。ボタンが押されたときにキーコードを返します
console.log(e.keyCode);
で確認できます
- ←が押された: 37
- ↑が押された: 38
- →が押された: 39
- ↓が押された: 40
switch(e.keyCode)
で押されたkeyに応じて処理します
case: left_button
left_valが0より大きい時(my_characterのtop値left値が0より小さくなると左端の画面をはみ出てしまう)
left_val -= 50;
canvas.style.left = left_val + "px";
left_valを減算することでmy_characterを左に50px移動させる
上右下もこれと同じ要領なので省略
let left_button = 37;
let top_button = 38;
let right_button = 39;
let bottom_button = 40;
let left_val = parseInt(canvas.style.left);
let top_val = parseInt(canvas.style.top);
addEventListener("keydown", (e) => {
switch(e.keyCode) {
case left_button:
if(left_val > 0) {
left_val -= 50;
canvas.style.left = left_val + "px";
}
break;
case top_button:
if(top_val > 0) {
top_val -= 50;
canvas.style.top = top_val + "px";
}
break;
case right_button:
if(left_val + 50 + 20 < WINDOW_WIDTH) {
left_val += 50;
canvas.style.left = left_val + "px";
}
break;
case bottom_button:
if(top_val + 50 + 20 < WINDOW_HEIGHT) {
top_val += 50;
canvas.style.top = top_val + "px";
}
break;
}
});
count_timer関数
一応動きますが改善したい...
function count_timer() {
...
...
let sec = 0;
let min = 0;
TIMER = setInterval(() => {
sec++;
if(sec < 10) {
sec = "0" + sec;
}
if(sec > 59) {
sec = "0" + 0;
min++;
if(min < 10) {
min = "0" + min;
}
}
if(min >= 60) {
clearInterval(TIMER);
}
p_tag.textContent = min + ":" + sec;
}, 1000);
}
gameover_message関数
document.getElementById("timer").style.visibility = "visible";
hiddenからvisibleにしてタイムを表示させます
clearInterval(TIMER);
で計測を止めます TIMERはグローバル変数です
setTimeout(() => {}, TIME)
で5秒後にmain_fieldからmy_characterを削除します
function gameover_message() {
...
...
document.getElementById("timer").style.visibility = "visible";
clearInterval(TIMER);
setTimeout(() => {
main_field.removeChild(document.getElementById('my_character'));
}, 5000);
}
おわりに
最後まで見ていただきありがとうございました
更新
- 定数のWINDOW_HEIGHT,WINDOW_WIDTHを足したり引いたりしていたため修正しました。
if(left_val + 50 + 20 < WINDOW_WIDTH)
if(top_val + 50 + 20 < WINDOW_HEIGHT)
if(top + height + 40 < WINDOW_HEIGHT && down_way_flag == false)
if(left + width + 40 < WINDOW_WIDTH && right_way_flag == false)
- カンマが抜けていたため修正しました
let rgb = {
r: Math.floor(Math.random() * 256),
g: Math.floor(Math.random() * 256),
b: Math.floor(Math.random() * 256),
};
-
Math.floor(Math.random() * 50) + 20
これだと20~69までだったため修正しました
//20~70を生成
let size = Math.floor(Math.random() * 51) + 20;
- left_valが0より大きい時(my_characterの
top値が0より小さくなると左端の画面をはみ出てしまう)
left_valが0より大きい時(my_characterのleft値が0より小さくなると左端の画面をはみ出てしまう)
参考文献
この書籍の「CHAPTER6ラケットゲームを作成する 6-3ボール移動とラケット移動を組み合わせる」の当たり判定を発展させました。参考になりました。
canvasの基本的な使い方が書かれています。