LoginSignup
1
2

More than 3 years have passed since last update.

JavaScriptでミニゲーム開発

Last updated at Posted at 2019-08-01

はじめに

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))の条件を消してみてください

main関数
        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は下に行けば行くほど値が大きくなります
hit.png

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は最初に左斜め上方向へ動く

index.htmlのcreate_canvas関数内
            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の基本的な使い方が書かれています。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2