【JavaScriptを好きになろう】JavaScriptでピンポンゲームを作ろう



 【JavaScriptを好きになろう】JavaScriptでピンポンゲームを作ろう

プログラミング学習の初期段階で登場してくる JavaScript。

広範囲にわたって利用され、習得必須スキルとはわかっているものの、「なんだか苦手で学習が思うように進まない…」という方も多いのではないでしょうか?

今回の記事では、そんな方の苦手意識を改善するべく、ゲームで JavaScript に馴染んでいただこうと思います!

目次
  1. 【JavaScriptを好きになろう】JavaScriptでピンポンゲームを作ろう
  2. 今回の目標:パソコン用ピンポンゲーム
  3. JavaScriptで絵を書く
  4. JavaScriptで丸・四角を書く
  5. JavaScriptで書いた図を動かす
  6. ボールを枠内で跳ね返らせる
  7. ピンポンゲーム用の動くバーを用意
  8. バーでボールを跳ね返す
  9. JavaScriptで作ったピンポンゲームをWebにセットする
  10. まとめ

【JavaScriptを好きになろう】JavaScriptでピンポンゲームを作ろう

今回の目標:パソコン用ピンポンゲーム

https://pythonchannel.com/myapp/game

ゲームの定番:ピンポンゲームは、 JavaScript をはじめ、 Swift や PHP、 Python などでも紹介されているゲーム・プログラムの王道です。

JavaScript の場合は、 HTML の Canvas 機能を使うことで、ブラウザ上で手軽にピンポンゲームを楽しめる特徴があります。

ゲームを楽しむことはもちろん、ゲーム制作を通じて、低負荷で JavaScript を学習できるメリットが。

今回は基本的なピンポンゲームの操作までをご紹介します。尚、今回のゲームはスマホ非対応です。スマホ対応版については、別途記事を制作させていただきますね。

JavaScriptで絵を書く

今回作成するピンポンゲームには、ピンポンの "○" とピンポンを跳ね返す "バー"、 それから "ブロック" などいくつかの "絵" が必用になってきます。この基本となる "絵" を JavaScript でまずは描いてみたいと思います。

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>図形を書いてみる</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");

        ctx.beginPath();
        ctx.rect(100, 100, 200, 200);
        ctx.fillStyle = "blue";
        ctx.fill();
        ctx.closePath();
    </script>

</body>
</html>

JavaScript で 絵 を書く手順としては、以下のステップ。

  • キャンバス(canvas)をセット
  • 始点と長さ、色と形を指定
  • 描写

キャンバスのセットについては、 HTML で <canvas> とすれば OK(上図 22行目)。 それから始点については X軸 と Y軸 で指示。上図 30行目の rect(100,100 部分が始点を意味します。

そして rect(100,100 のあとの 200,200) で 長さを指定。形については rect() で四角形、 arc で丸を意味します。そして fill() を実行することで塗りつぶされ、 stroke() とすると塗りつぶしなしに。

またこれらの描画プログラムは、 <canvas> の後に記述しないとブラウザは読み込んでくれませんので注意しましょう。

JavaScriptで丸・四角を書く

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>図形を書いてみる</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");

        ctx.beginPath();
        ctx.rect(0, 0, 60, 20);
        ctx.fillStyle = "blue";
        ctx.fill();
        ctx.closePath();

        ctx.beginPath();
        ctx.arc(300, 150, 20, 0, Math.PI*2, false);
        ctx.fillStyle = "green";
        ctx.fill();
        ctx.closePath();

        ctx.beginPath();
        ctx.rect(100, 300, 100, 10);
        ctx.strokeStyle = "rgba(0, 0, 255, 0.5)";
        ctx.stroke();
        ctx.closePath();
    </script>


</body>
</html>

先程は四角だけでしたが、今度は丸や塗りつぶしなしの四角を描写。数字や色をいろいろ変えて、 JavaScript の Canvas に慣れていきましょう。

丸については少し複雑な数値指定で、内容は以下のとおりに。

arc(X方向の始点, Y方向の始点, 半径, 範囲, 計算式, 向き)

上図の場合では、 ポイント 300, 150 を始点に、半径 20px の 全周 を 時計回り(false)に描画。 全周を意味する 0 の部分を 1.5 とか 1 に変えてみると円の違いを確認できると思います。

それでは次は描いた円を動かしてみましょう。

JavaScriptで書いた図を動かす

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>図形を動かしてみる</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var x = canvas.width/2;  // 画面中央スタート
        var y = canvas.height-30;  // 画面下 枠外からのスタート
        var dx = 2;  //横の移動量
        var dy = -2;  //縦の移動量

        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, 10, 0, Math.PI*2);  //ボールの大きさ 半径 10px
            ctx.fillStyle = "red";  //ボールスタイル
            ctx.fill();
            ctx.closePath();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);  //ボールの移動軌跡を残さないために
            drawBall();
            x += dx;  // dxずつ移動
            y += dy;  // dyずつ移動
        }

        setInterval(draw, 50);  //setInterval() 繰り返し処理、 1 を100にするとゆっくり
    </script>
</body>
</html>

"JavaScript で絵を動かす"、と聞いてもピンとこないと思いますが、イメージとしては絵をチョットずつズラして動いているように見せる、というもの。

今回のピンポンでは、 X方向に 2px、 Y方向に 2px ずつ動かす内容(上図 30、 31行目)。

あとはボールの始点(スタート地点)を指示し、繰り返し 2px ずつ動くように setInterval() 関数で処理。

実際に上図プログラムを実行するとボールは右上方向に動きますが、枠にあたっても跳ね返ってこず、画面の外に抜けていきます。

ボールを枠内で跳ね返らせる

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>図形を Canvas の中で動かしてみる</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var ballRadius = 10; // ボールの大きさ
        var x = canvas.width/2;  // 画面中央スタート
        var y = canvas.height-30;  // 画面下 枠外からのスタート
        var dx = 2;  //横の移動量
        var dy = -2;  //縦の移動量

        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, ballRadius, 0, Math.PI*2);  //ボールの大きさ 半径 10px
            ctx.fillStyle = "red";  //ボールスタイル
            ctx.fill();
            ctx.closePath();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);  //ボールの移動軌跡を残さないために
            drawBall();

            if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { //右もしくは左にはみ出たら...
                console.log('反転前 dx:' + dx);
                dx = -dx;
                console.log('反転後 dx:' + dx);
            }
            if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) { //上もしくは下にはみ出たら..
                console.log('反転前 dy:' + dy);
                dy = -dy;
                console.log('反転後 dy:' + dy);
            }
            x += dx;  // dxずつ移動
            y += dy;  // dyずつ移動
        }

        setInterval(draw, 50);  //setInterval() 繰り返し処理、 1 を100にするとゆっくり
    </script>

</body>
</html>

2px ずつ動くようになったボールを枠で跳ね返らせようと思うと、枠に当たった時に、逆方向に進むよう "加算値を逆" にすれば OK。

例えば上図では、右端にあたった時に +2 px ずつ足していた横方向の値を -2 px ずつに変更(48行目)。また縦方向の移動も変数 dy をプラスからマイナスに反転させることで、あたかもボールが跳ね返ったような表現に。

上図からも分かるように JavaScript の if 文を使って、ボールの跳ね返りを制御していますね。

また実際にログで移動加算値を確認してみると、上図のように、枠に当たった時に加算値が反転してボールの進捗方向が変わっています。

次はボールを跳ね返らす用の "バー" を用意してみましょう。

ピンポンゲーム用の動くバーを用意

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>跳ね返し用のバーを用意</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var paddleHeight = 10;  //バーのサイズ
        var paddleWidth = 75;  //バーのサイズ
        var paddleX = (canvas.width-paddleWidth)/2;  //バーの位置、中央から
        var rightPressed = false;  //右 移動ボタンをリセット
        var leftPressed = false;  //左 移動ボタンをリセット

        document.addEventListener("keydown", keyDownHandler, false); //ボタン押されたら...
        document.addEventListener("keyup", keyUpHandler, false);  //ボタンから指離れたら...

        function keyDownHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = true;  //右矢印ボタンが押されたことをコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = true;  //左矢印ボタンが押されたことをコンピューターに伝達
            }
        }

        function keyUpHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = false;  //右矢印ボタンを押していない状態をコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = false;  //左矢印ボタンを押していない状態をコンピューターに伝達
            }
        }

        function drawPaddle() {
            ctx.beginPath();
            ctx.rect(paddleX, canvas.height-paddleHeight*5, paddleWidth, paddleHeight);
            ctx.fillStyle = "blue";
            ctx.fill();
            ctx.closePath();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawPaddle();
            if(rightPressed) {  //右矢印押された場合
                paddleX += 20;  // バーの動きスピード
                console.log('→ 右移動中 横の位置 paddleX :' + paddleX)
                if (paddleX + paddleWidth > canvas.width){ //右にはみ出るまでの処理
                    paddleX = canvas.width - paddleWidth;
                }
            }
            else if(leftPressed) {  //左矢印押された場合
                paddleX -= 20; // バーの動きスピード
                console.log('← 左移動中 横の位置 paddleX :' + paddleX)
                if (paddleX < 0){  //左にはみ出たら 0 に
                    paddleX = 0;
                }
            }
        }
        setInterval(draw, 50);
    </script>    
</body>
</html>

ピンポンゲームといえば、ボールを落ちないようにバーで操作するもの。

バー自体は四角形の描画で用意でき、ポイントはそのバーを自分のパソコンから操作できるように制御するプログラム。

パソコンのボタンを JavaScript が認識するためには、 addEventListener()keydownkeyup を使えば OK。 ボタンを押した事を認識するのが keydown で、 ボタンを離したことを認識するのが keyup になります。

そのあたりのキーボード操作を認識するプログラムが 37行目から 52行目付近で、動きの制御部分については 66行目以降でプログラム。 if文を使ってキーボードの操作を JavaScript で認識し、バーを動かすように制御していることが確認できます。

実際にログを出力するようにして、ブラウザ上でキーボードの 右 や 左 をクリックしてみると、バーが移動し、 X方向の数値も変わっていることが確認できます。

また画面端にバーがいくと if文によってそれ以上バーが横に行かないように制御。そのあたりもログを取ってみると分かりやすいですね。

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>ボールと跳ね返し用のバー(抜ける)</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var ballRadius = 10; // ボールの大きさ
        var x = canvas.width/2;  // 画面中央スタート
        var y = canvas.height-30;  // 画面下 枠外からのスタート
        var dx = 2;  //横の移動量
        var dy = -2;  //縦の移動量

        var paddleHeight = 10;  //バーのサイズ
        var paddleWidth = 75;  //バーのサイズ
        var paddleX = (canvas.width-paddleWidth)/2;  //バーの位置、中央から
        var rightPressed = false;  //右 移動ボタンをリセット
        var leftPressed = false;  //左 移動ボタンをリセット
        
        document.addEventListener("keydown", keyDownHandler, false); //ボタン押されたら...
        document.addEventListener("keyup", keyUpHandler, false);  //ボタンから指離れたら...

        function keyDownHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = true;  //右矢印ボタンが押されたことをコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = true;  //左矢印ボタンが押されたことをコンピューターに伝達
            }
        }

        function keyUpHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = false;  //右矢印ボタンを押していない状態をコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = false;  //左矢印ボタンを押していない状態をコンピューターに伝達
            }
        }

        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, ballRadius, 0, Math.PI*2);  //ボールの大きさ 半径 10px
            ctx.fillStyle = "red";  //ボールスタイル
            ctx.fill();
            ctx.closePath();
        }

        function drawPaddle() {
            ctx.beginPath();
            ctx.rect(paddleX, canvas.height-paddleHeight*5, paddleWidth, paddleHeight);
            ctx.fillStyle = "blue";
            ctx.fill();
            ctx.closePath();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawBall();
            if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { //右もしくは左にはみ出たら...
                console.log('反転前 dx:' + dx);
                dx = -dx;
                console.log('反転後 dx:' + dx);
            }
            if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) { //上もしくは下にはみ出たら..
                console.log('反転前 dy:' + dy);
                dy = -dy;
                console.log('反転後 dy:' + dy);
            }
            x += dx;  // dxずつ移動
            y += dy;  // dyずつ移動


            drawPaddle();
            if(rightPressed) {  //右矢印押された場合
                paddleX += 20;  // バーの動きスピード
                console.log('→ 右移動中 横の位置 paddleX :' + paddleX)
                if (paddleX + paddleWidth > canvas.width){ //右にはみ出るまでの処理
                    paddleX = canvas.width - paddleWidth;
                }
            }
            else if(leftPressed) {  //左矢印押された場合
                paddleX -= 20; // バーの動きスピード
                console.log('← 左移動中 横の位置 paddleX :' + paddleX)
                if (paddleX < 0){  //左にはみ出たら 0 に
                    paddleX = 0;
                }
            }
        }
        setInterval(draw, 50);
    </script>    
</body>
</html>

それでは一旦ここまでのプログラムをまとめて、バーとボールを表示させてみます。ボールは跳ね返るのですが、バーで跳ね返ることなく、キャンバスの枠にあたって跳ね返っています。

バーでボールを跳ね返す

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>跳ね返す</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var ballRadius = 10; // ボールの大きさ
        var x = canvas.width/2;  // 画面中央スタート
        var y = canvas.height-30;  // 画面下 枠外からのスタート
        var dx = 2;  //横の移動量
        var dy = -2;  //縦の移動量

        var paddleHeight = 10;  //バーのサイズ
        var paddleWidth = 120;  //バーのサイズ
        var paddleX = (canvas.width-paddleWidth)/2;  //バーの位置、中央から
        var rightPressed = false;  //右 移動ボタンをリセット
        var leftPressed = false;  //左 移動ボタンをリセット
        
        document.addEventListener("keydown", keyDownHandler, false); //ボタン押されたら...
        document.addEventListener("keyup", keyUpHandler, false);  //ボタンから指離れたら...

        function keyDownHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = true;  //右矢印ボタンが押されたことをコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = true;  //左矢印ボタンが押されたことをコンピューターに伝達
            }
        }

        function keyUpHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = false;  //右矢印ボタンを押していない状態をコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = false;  //左矢印ボタンを押していない状態をコンピューターに伝達
            }
        }

        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, ballRadius, 0, Math.PI*2);  //ボールの大きさ 半径 10px
            ctx.fillStyle = "red";  //ボールスタイル
            ctx.fill();
            ctx.closePath();
        }

        function drawPaddle() {
            ctx.beginPath();
            ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
            ctx.fillStyle = "blue";
            ctx.fill();
            ctx.closePath();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawBall();
            if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { //右もしくは左にはみ出たら...
                console.log('反転前 dx:' + dx);
                dx = -dx;
                console.log('反転後 dx:' + dx);
            }
            //if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) { //上もしくは下にはみ出たら..
            //    console.log('反転前 dy:' + dy);
            //    dy = -dy;
            //    console.log('反転後 dy:' + dy);
            //}
            console.log('y値:' + y + dy);
            if(y + dy  < ballRadius) { //上の跳ね返り処理、ボール下に移動
                dy = -dy;
            } else if(y + dy > canvas.height-paddleHeight-ballRadius) {  //ボール下に抜けたら...
                if(x > paddleX && x < paddleX + paddleWidth) { // バーの範囲内だったら...
                    dy = -dy;  // ボールを跳ね返す、上に
                }
                else {
                    //alert("GAME OVER");
                    document.location.reload();
                    clearInterval(interval);
                }
            }
            x += dx;  // dxずつ移動
            y += dy;  // dyずつ移動


            drawPaddle();
            if(rightPressed) {  //右矢印押された場合
                paddleX += 10;  // バーの動きスピード
                console.log('→ 右移動中 横の位置 paddleX :' + paddleX)
                if (paddleX + paddleWidth > canvas.width){ //右にはみ出るまでの処理
                    paddleX = canvas.width - paddleWidth;
                }
            }
            else if(leftPressed) {  //左矢印押された場合
                paddleX -= 10; // バーの動きスピード
                console.log('← 左移動中 横の位置 paddleX :' + paddleX)
                if (paddleX < 0){  //左にはみ出たら 0 に
                    paddleX = 0;
                }
            }
        }
        var interval = setInterval(draw, 30);
    </script>
</body>
</html>

バーでボールを跳ね返すプログラムはちょっと複雑で、ボールの縦方向(Y)とボールの横方向(X) 両方が関係。

縦方向については、バーの縦方向値にボールが来た時の処理が一つ、それからその時にバーがあるかどうかの分岐文が必用。そしてボールの縦方向が、バーと同じポイントにきて、バーの横幅にボールが入っていれば跳ね返し処理、入っていなければゲームオーバー処理となりますね。

ココの制御文は少し複雑なので、一行一行ゆっくり確認していきましょう。

実際にブラウザで動きを確認すると、ボールがバーで跳ね返ったらログで Y値が反転していく様子が確認できます。

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>跳ね返す</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var ballRadius = 10; // ボールの大きさ
        var x = canvas.width/2;  // 画面中央スタート
        var y = canvas.height-30;  // 画面下 枠外からのスタート
        var dx = 2;  //横の移動量
        var dy = -2;  //縦の移動量

        var paddleHeight = 10;  //バーのサイズ
        var paddleWidth = 120;  //バーのサイズ
        var paddleX = (canvas.width-paddleWidth)/2;  //バーの位置、中央から
        var rightPressed = false;  //右 移動ボタンをリセット
        var leftPressed = false;  //左 移動ボタンをリセット
        
        document.addEventListener("keydown", keyDownHandler, false); //ボタン押されたら...
        document.addEventListener("keyup", keyUpHandler, false);  //ボタンから指離れたら...

        function keyDownHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = true;  //右矢印ボタンが押されたことをコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = true;  //左矢印ボタンが押されたことをコンピューターに伝達
            }
        }

        function keyUpHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = false;  //右矢印ボタンを押していない状態をコンピューターに伝達
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = false;  //左矢印ボタンを押していない状態をコンピューターに伝達
            }
        }

        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, ballRadius, 0, Math.PI*2);  //ボールの大きさ 半径 10px
            ctx.fillStyle = "red";  //ボールスタイル
            ctx.fill();
            ctx.closePath();
        }

        function drawPaddle() {
            ctx.beginPath();
            ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
            ctx.fillStyle = "blue";
            ctx.fill();
            ctx.closePath();
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawBall();
            if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { //右もしくは左にはみ出たら...
                console.log('反転前 dx:' + dx);
                dx = -dx;
                console.log('反転後 dx:' + dx);
            }
            //if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) { //上もしくは下にはみ出たら..
            //    console.log('反転前 dy:' + dy);
            //    dy = -dy;
            //    console.log('反転後 dy:' + dy);
            //}
            console.log('y値:' + y + dy);
            if(y + dy  < ballRadius) { //上の跳ね返り処理、ボール下に移動
                dy = -dy;
            } else if(y + dy > canvas.height-paddleHeight-ballRadius) {  //ボール下に抜けたら...
                if(x > paddleX && x < paddleX + paddleWidth) { // バーの範囲内だったら...
                    dy = -dy;  // ボールを跳ね返す、上に
                }
                else {
                    alert("まけ");
                    document.location.reload();
                    clearInterval(interval);
                }
            }
            x += dx;  // dxずつ移動
            y += dy;  // dyずつ移動


            drawPaddle();
            if(rightPressed) {  //右矢印押された場合
                paddleX += 10;  // バーの動きスピード
                console.log('→ 右移動中 横の位置 paddleX :' + paddleX)
                if (paddleX + paddleWidth > canvas.width){ //右にはみ出るまでの処理
                    paddleX = canvas.width - paddleWidth;
                }
            }
            else if(leftPressed) {  //左矢印押された場合
                paddleX -= 10; // バーの動きスピード
                console.log('← 左移動中 横の位置 paddleX :' + paddleX)
                if (paddleX < 0){  //左にはみ出たら 0 に
                    paddleX = 0;
                }
            }
        }
        var interval = setInterval(draw, 10);
    </script>
</body>
</html>

ボールを跳ね返すことができなかったら、 alert() でポップアップ表示するとゲームぽくなりますね。

ここまでボールとバーの動きを制御できたら、あとはブロックと得点を表示できたら "ピンポンゲーム" ですよね。

上図のコードを今確認する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />
        <style>
            h1{
                text-align:center;
            }
            canvas {
                background: #eee;
                }
            #canvas-wrapper{
                width:480px;
                margin:0 auto;
            }
        </style>
    </head>
<body>
    <h1>ポイント付き</h1>
    <div id="canvas-wrapper">
    <canvas id="myCanvas" width="480" height="320"></canvas>
    </div>

    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d");
        var ballRadius = 10;
        var x = canvas.width/2;
        var y = canvas.height-30;
        var dx = 2;
        var dy = -2;
        var paddleHeight = 10;
        var paddleWidth = 120;
        var paddleX = (canvas.width-paddleWidth)/2;
        var rightPressed = false;
        var leftPressed = false;
        var brickRowCount = 5;
        var brickColumnCount = 3;
        var brickWidth = 75;
        var brickHeight = 20;
        var brickPadding = 10;
        var brickOffsetTop = 30;
        var brickOffsetLeft = 30;
        var score = 0;
        
        var bricks = [];
        for(var c=0; c<brickColumnCount; c++) {
          bricks[c] = [];
          for(var r=0; r<brickRowCount; r++) {
            bricks[c][r] = { x: 0, y: 0, status: 1 };
          }
        }
        
        document.addEventListener("keydown", keyDownHandler, false);
        document.addEventListener("keyup", keyUpHandler, false);
        
        function keyDownHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = true;
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = true;
            }
        }
        
        function keyUpHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") {
                rightPressed = false;
            }
            else if(e.key == "Left" || e.key == "ArrowLeft") {
                leftPressed = false;
            }
        }
        
        function collisionDetection() {
          for(var c=0; c<brickColumnCount; c++) {
            for(var r=0; r<brickRowCount; r++) {
              var b = bricks[c][r];
              if(b.status == 1) {
                if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
                  dy = -dy;
                  b.status = 0;
                  score++;
                  if(score == brickRowCount*brickColumnCount) {
                    alert("やったね!");
                    document.location.reload();
                    clearInterval(interval); // ゲーム再スタート
                  }
                }
              }
            }
          }
        }
        
        function drawBall() {
          ctx.beginPath();
          ctx.arc(x, y, ballRadius, 0, Math.PI*2);
          ctx.fillStyle = "red";
          ctx.fill();
          ctx.closePath();
        }
        function drawPaddle() {
          ctx.beginPath();
          ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
          ctx.fillStyle = "blue";
          ctx.fill();
          ctx.closePath();
        }
        function drawBricks() {
          for(var c=0; c<brickColumnCount; c++) {
            for(var r=0; r<brickRowCount; r++) {
              if(bricks[c][r].status == 1) {
                var brickX = (r*(brickWidth+brickPadding))+brickOffsetLeft;
                var brickY = (c*(brickHeight+brickPadding))+brickOffsetTop;
                bricks[c][r].x = brickX;
                bricks[c][r].y = brickY;
                ctx.beginPath();
                ctx.rect(brickX, brickY, brickWidth, brickHeight);
                ctx.fillStyle = "black";
                ctx.fill();
                ctx.closePath();
              }
            }
          }
        }
        function drawScore() {
          ctx.font = "16px Arial";
          ctx.fillStyle = "blue";
          ctx.fillText("点数: "+score, 8, 20);
        }
        
        function draw() {
          ctx.clearRect(0, 0, canvas.width, canvas.height);
          drawBricks();
          drawBall();
          drawPaddle();
          drawScore();
          collisionDetection();
        
          if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
            dx = -dx;
          }
          console.log('y値:' + y + dy);
          if(y + dy < ballRadius) {
            dy = -dy;
          }
          else if(y + dy > canvas.height-ballRadius) {
            if(x > paddleX && x < paddleX + paddleWidth) {
              dy = -dy;
            }
            else {
              alert("まけ");
              document.location.reload();
              clearInterval(interval); // Needed for Chrome to end game
            }
          }
        
          if(rightPressed && paddleX < canvas.width-paddleWidth) {
            paddleX += 7;
          }
          else if(leftPressed && paddleX > 0) {
            paddleX -= 7;
          }
        
          x += dx;
          y += dy;
        }
        var interval = setInterval(draw, 20);
    </script>
</body>
</html>

すこし飛躍しますが、ブロックと得点を表示してみました。コードを確認して、ブロック制御や加算制御などをチェックしてみて下さい。

ゲームとしてはシンプルなピンポンゲームですが、プログラムで作っていくと案外大変ですよね。

JavaScriptで作ったピンポンゲームをWebにセットする

今回作成した JavaScript製のピンポンゲームは、コードをコピペすればレンタルサーバー上で動かすことができます。

また以下のように入力欄を設け、 JavaScript の制御値に任意の値を代入することで、お好みのピンポンゲームを楽しむ事が可能に。

https://pythonchannel.com/myapp/game

予め数値を用意して レベル 1、 レベル 2 とかすると本格的なゲームになっていきますね。

Webサイト担当者としてのスキルが身に付く

CodeCampの無料体験はこちら

まとめ

今回 JavaScript に馴染む目的でピンポンゲームを作成しましたが、どうだったでしょうか?

実際にコードをコピペして試された方、いらっしゃいますでしょうか?

JavaScript に限らず、プログラミング上達の近道はやっぱり自分でコードを書いて、実行してみることです。「あれ、同じようにやっているのに動かない」「エラーが出るけど動く」など実際にコードを書いて、実行してみるといろんなことが起きると思います。

そしてその先を乗り越えられるか、乗り超えられないかは個人次第。しかし、 ;セミコロン が抜けているだけ、とかスペスミスとかボンミスでプログラミング学習を止めるのは、チョットもったいないないですよね。

一人で上手く行かない時は、マンツーマンのプログラミング・スクールどうでしょうか? 今晩チョット時間ある、日中は外出できない、という方は CodeCamp の体験レッスン、受講してみませんか?

オシママサラ
この記事を書いた人
オシママサラ
\ 無料体験開催中!/自分のペースで確実に習得!
オンライン・プログラミングレッスンNo.1のCodeCamp