ゲームプログラミング入門～その４～【シューティングゲーム】完成

# 「シューティングゲーム」がやっと完成しました！！
2度目のプログラミングチャレンジで、1か月ほどかけて、やっと「シューティングゲーム」が完成しました。
![test.jpg](https://qiita-image-store.s3.amazonaws.com/0/313518/2c86f882-053c-ee52-58c3-7008813d435a.jpeg)
今回の学習では、エンジニアの友人にいろいろと教わりながら勉強＆作成できたので、途中で挫折しかけたのですが、何とか完成までたどり着くことができました。

→[URL](https://youtu.be/XYQW-wmf5sc)

今回作成したシューティングゲームのコードを下に書いておきたいと思います。

```
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="robots" content="noindex,nofollow">
        <title>WEB SCREEN GAME</title>
        <style>
            #screen {
                border: solid 1px #000000;
                background-color: #000000;
            }
            
        </style>
    </head>
    <body>
        <canvas id="screen" width="860" height="485"></canvas>
        <h2>操作方法</h2>
	<p>自機の操作 → 矢印キー</p>
        <p>ミサイルの発射 → Sキー</p>
        <p>※ゲームの時間制限はありません。自機と敵が衝突したらゲームオーバーになります。</p>
        <p>再度プレイする場合は、ブラウザの表示を更新してください。
        <script>
            //canvas要素のDOM(Document Object Model)を取得
            var canvas = document.getElementById('screen');
            var context;
            
            var bgImg1; // 背景画像１
            var bgImg2; // 背景画像２ 
            var bgImgs; // 背景画像配列

            var score = 0; // スコア
            var scoreImg;  // スコア画像
            var selfBody;  // 自機データ
            var selfPosX = 20;      // 自機の画面横方向の位置
            var selfPosY = 190;     // 自機の画面縦方向の位置
            var selfCenterPos = []; // 自機中心座標配列
            var selfSpeed = 10;     // 自機のスピード            
            var enemyBody; //敵データ
            
            var gameOverImg; // ゲームオーバー画像
            
            var playFlag = true; // ゲームプレイフラグ
            
            var frameWidth = 860;   // CANVAS横サイズ
            var frameHeight = 485;  // CANVAS縦サイズ
            
            //  キーボードの状態
            //              [ 上  ,   下 ,   右 ,   左 ] 
            var keyStatus = [false, false, false, false]

            // ミサイル
            var missiles = [];        // ミサイルデータ配列
            var missileSpeed = 30;    // ミサイルスピード
            var maxMissileCount = 5;  // ミサイル最大同時発射数

            // ミサイルデータの初期化
            for ( var i = 0; i < maxMissileCount; i++ ){
                //             状態 , x, y
                missiles[i] = [false, 0, 0];
            }

            var enemies = [];        // 敵データ配列
            var enemyCenterPos = []; // 敵データ中心座標配列
            var maxEnemyCount = 1;   // 最大表示敵数

            var enemyType = [];      // 敵タイプデータ配列
            //            X有効範囲, Y有効範囲, 画像file名, 獲得スコア
            enemyType[0] = [ 20, 20, "enemy_20x20.png", 1000];
            enemyType[1] = [ 50, 50, "enemy_50x50.png", 500];
            enemyType[2] = [ 80, 80, "enemy_80x80.png", 200];
            
            // 敵データの初期化
            for ( var i = 0; i < maxEnemyCount ; i++ ){
                enemies[i] = createEnemy();
            }            

            document.onkeydown = keyDown;   //キーダウンの検知設定
            document.onkeyup = keyUp;       //キーアップの検知設定
            
            //コンテキストの取得可否チェック
            if(canvas.getContext){

                context = canvas.getContext('2d');

                window.onload = function() {
                    // 画像オブジェクト作成
                    selfBody = new Image();
                    enemyBody = new Image();
                    gameOverImg = new Image();
                    scoreImg = new Image();
                    bgImg1 = new Image();
                    bgImg2 = new Image();

                    // 画像ファイル名設定
            	    selfBody.src = "rocket.png";
            	    gameOverImg.src = "gameover.png";
            	    scoreImg.src = "score.png";
                    bgImg1.src = "space1.jpg";
                    bgImg2.src = "space2.jpg";
                    
                    // 背景画像データ設定
                    bgImgs = [ [bgImg1, 0], [bgImg2, 860], [bgImg1, 1720] ] ;

                    selfBody.onload = function(){
                        draw(context);    //描画処理
                        startAnimation(); // 描画更新開始処理
                    }
                }
            }

            // 描画更新処理
            function startAnimation(){
                setInterval(draw, 33);
            }

            // 描画処理
            function draw(){
                if ( playFlag ){
                    // CANVASをクリア 
                    context.fillStyle = "rgb(255, 255, 255)";
                    context.fillRect(0, 0, frameWidth, frameHeight);
                    context.clearRect(0, 0, frameWidth, frameHeight);

                    drawSpace();   // 背景（宇宙）を描画

                    drawMissile(); // ミサイルを描画

                    move();        // ロケットの移動と描画
                    context.drawImage(selfBody, selfPosX, selfPosY);

                    drawEnemy();   // 敵の描画

                    judgementHitMissile(); // 当たり判定

                    drawScore(score); // スコアを描画

                    // ゲームオーバー判定
                    if( playFlag === false ){
                        // ゲームオーバー画像表示
                        context.drawImage(gameOverImg, (frameWidth/2)-(gameOverImg.width/2), (frameHeight/2)-(gameOverImg.height/2));
                    }
                }
            }
            
            // ロケットの移動
            function move(){
                if ( keyStatus[0] ){  // UP
                    if ( selfPosY > 0 ){ selfPosY -= selfSpeed; }
                }
                
                if ( keyStatus[1] ){  // DOWN
                    if ( selfPosY < 400 ){ selfPosY += selfSpeed; }
                }
                
                if ( keyStatus[2] ){  // RIGHT
                    if ( selfPosX < 775 ){ selfPosX += selfSpeed; }
                }
                
                if ( keyStatus[3] ){  // LEFT
                    if ( selfPosX > 0 ){ selfPosX -= selfSpeed; }
                }
            }
            
            // キーダウン
            function keyDown(e){
                if ( playFlag ){
                    if(e.keyCode === 38 ){ keyStatus[0] = true; } //UP KEY
                    else if(e.keyCode === 40 ){ keyStatus[1] = true; } //DOWN KEY
                    else if(e.keyCode === 39 ){ keyStatus[2] = true; } //RIGHT KEY
                    else if(e.keyCode === 37 ){ keyStatus[3] = true; } //LEFT KEY
                    else if(e.keyCode === 83 ){ missileLaunch(); } // ミサイルを発射
                }
            }
            
            // キーアップ
            function keyUp(e){
                if(e.keyCode === 38 ){ keyStatus[0] = false;} //UP KEY
                else if(e.keyCode === 40 ){ keyStatus[1] = false;} //DOWN KEY
                else if(e.keyCode === 39 ){ keyStatus[2] = false;} //RIGHT KEY
                else if(e.keyCode === 37 ){ keyStatus[3] = false;} //LEFT KEY
            }

            // 敵の描画
            function drawEnemy(){
                for( var i = 0; i < enemies.length; i++ ){
                    if( enemies[i][0] < -100 || enemies[i][1] < -100){
                        enemies[i] = createEnemy();
                    }
                    enemies[i][0] -= enemies[i][2];
                    enemies[i][1] += enemies[i][3];
                    enemyBody.src = enemies[i][4][2];
                    context.drawImage(enemyBody, enemies[i][0], enemies[i][1]);
                }
            }

            // 敵データを初期化
            function createEnemy(){
                return [
                    getRandVal(910, 1000),       // posX
                    getRandVal(10, 600),         // posY
                    getRandVal(3, 15),           // moveXspeed
                    -1 * getRandVal(1, 5),       // moveYspeed
                    enemyType[getRandVal(0, 2)]  // enemyTypeNum
                ];
            }
            
            // ミサイルを描画
            function drawMissile(){
                for ( var i = 0; i < missiles.length; i++ ){
                    if ( missiles[i][0] ){ // ミサイルが有効かどうか？
                        if( missiles[i][1] < 830 ){
                            missiles[i][1] += missileSpeed;

                            context.beginPath();

                            var missile = new Path2D();

                            missile.rect( missiles[i][1], missiles[i][2], 20, 2 );
                            context.fillStyle = "rgb(255, 255, 255)";
                            context.stroke(missile);
                            context.fill(missile);
                        } else {
                            missiles[i][0] = false; // ミサイルを無効化
                        }
                    }
                }
            }

            // ミサイルを発射
            function missileLaunch(){
                for ( var i = 0; i < missiles.length; i++ ){
                    if( missiles[i][0] === false ){ // ミサイルが有効かどうか？
                        missiles[i][0] = true; // ミサイルを有効化
                        missiles[i][1] = selfPosX + 60;
                        missiles[i][2] = selfPosY + 40;

                        return;
                    }
                }
            }

            // 乱数取得
            function getRandVal(min, max){
                return (Math.floor(Math.random() * (max - min + 1))) + min;
            }
            
            // 当たり判定(ミサイルと敵）
            function judgementHitMissile(){
                for ( var i = 0; i < enemies.length; i++ ){
                    // ミサイルと敵の当たり判定
                    for( var j = 0; j < missiles.length; j++ ){
                        if ( missiles[j][0] === true ){
                            // ミサイルのposXからエネミーのposXの差の絶対値が当たり判定値以内かどうか？
                            if( (enemies[i][0] < (missiles[j][1] + 20 )) && (missiles[j][1] < (enemies[i][0] + enemies[i][4][0]))){
                                // ミサイルのposYからエネミーのposYの差の絶対値が当たり判定値以内かどうか？
                                if( (missiles[j][2] > enemies[i][1]) && (missiles[j][2] < (enemies[i][1] +  enemies[i][4][1]))){
                                    // 当たり判定
                                    missiles[j][0] = false;
                                    score += enemies[i][4][3];
                                    enemies[i] = createEnemy();
                                }
                            }
                        }
                        
                        // 自機と敵の当たり判定
                        // 自機と敵の中心座標を計算
                        selfCenterPos[0] = selfPosX + 40;
                        selfCenterPos[1] = selfPosY + 40;
                        enemyCenterPos[0] = enemies[i][0] + (enemies[i][4][0] / 2); // center X
                        enemyCenterPos[1] = enemies[i][1] + (enemies[i][4][1] / 2); // center Y
                        
                        if ( enemies[i][0] > 0 && enemies[i][1] > 0 ){
                            if(abs(abs(selfCenterPos[0]) - abs(enemyCenterPos[0])) < 100){
                                // ミサイルのposYからエネミーのposYの差の絶対値が当たり判定値以内かどうか？
                                if(abs(abs(selfCenterPos[1]) - abs(enemyCenterPos[1])) < 100){
                                    // 当たり判定
                                    // 厳密な当たり判定
                                    var hitRange = ((enemies[i][4][0] / 2) + 40) * 0.7;
                                    if(strictHitJudge(selfCenterPos, enemyCenterPos , hitRange)){
                                         playFlag = false;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // スコアを表示
            function drawScore(score){
                // スコアラベルを描画
                context.drawImage(scoreImg, 0, 0);
                context.font = "30px Arial"; //フォントにArial,40pxを指定
                context.fillStyle = "white"; 
                context.fillText((("00000000"+score).slice(-8)),137,43);      //テキストを塗り潰しで描画
            }
            
            // スコアを表示
            function drawSpace(){
                // スコアラベルを描画
                for ( var i = 0; i < bgImgs.length; i++ ){
                    if( bgImgs[i][1] === -860){
                        if(bgImgs[i][0] === bgImg1 ){
                            bgImgs[i][0] = bgImg2;
                        } else if(bgImgs[i][0] === bgImg2 ){
                            bgImgs[i][0] = bgImg1;
                        }
                        bgImgs[i][1] = 1720;
                    }
                	context.drawImage(bgImgs[i][0], bgImgs[i][1], 0);
                	bgImgs[i][1]--;
                } 
            }
            
            // 絶対値を取得
            function abs(value){
                return Math.abs(value);
            }
            
            // 自機と敵の厳密当たり判定
            function strictHitJudge(selfCenterPos, enemyCenterPos , hitRange){
                var x = abs(abs(selfCenterPos[0]) - abs(enemyCenterPos[0]));
                var y = abs(abs(selfCenterPos[1]) - abs(enemyCenterPos[1]));
                var distance = Math.sqrt(Math.pow(x,2) + Math.pow(y,2));                
                if( hitRange > distance){ return true; } else { return false; };
            }
        </script>
        <div id="link"></div>
    </body>
</html>

```

#遊んでみたい方はこちら
[→シューティングゲーム](https://www.programming-fun.net/assets/sample/article95/shooting_game.html)