ChatGPTで、一人用ピンポンゲームが30分で出来た

- **[続編：GPT-4で、JSブロックくずしをステップバイステップで作ってみた - Qiita](https://qiita.com/y_catch/items/fd4268af8e4e8976547f)**

話題のChatGPTを使って、一人用ピンポンゲームを作ったら30分でできた。すごいぞ。ちゃんと遊べる。

ぷよぷよを作ろうとしている人とかいたので、もっと簡単なものを作ってみた。

- [ソースコード](https://github.com/ycatch/gpt-pong)
- [完成したゲーム](https://ycatch.github.io/gpt-pong/)

![gpt-pong3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/eed66ceb-4051-df92-b9c4-203a448fa284.png)


## Q1.Javascriptを使って、一人用のPONGのプログラムを書いて

### A. はい、PONGゲームのプログラムをJavaScriptで書いてみましょう！

以下は、一人用のシンプルなPONGゲームのサンプルコードです。このコードは、HTML5のcanvas要素を使用してゲームを描画し、requestAnimationFrame()メソッドを使用してゲームループを制御します。また、簡単な衝突判定を実装しています。

```js
// ゲームの初期化
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var ball = { x: canvas.width/2, y: canvas.height/2, dx: 5, dy: 5, radius: 10 };
var paddle = { x: canvas.width/2, y: canvas.height-20, width: 80, height: 10 };
var leftPressed = false;
var rightPressed = false;

document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

function keyDownHandler(e) {
    if(e.keyCode == 37) {
        leftPressed = true;
    }
    else if(e.keyCode == 39) {
        rightPressed = true;
    }
}

function keyUpHandler(e) {
    if(e.keyCode == 37) {
        leftPressed = false;
    }
    else if(e.keyCode == 39) {
        rightPressed = false;
    }
}

function drawBall() {
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddle.x - paddle.width/2, paddle.y, paddle.width, paddle.height);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function draw() {
    // ボールの移動
    ball.x += ball.dx;
    ball.y += ball.dy;

    // 壁に当たった場合は反射
    if(ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
        ball.dx = -ball.dx;
    }
    if(ball.y - ball.radius < 0) {
        ball.dy = -ball.dy;
    }
    else if(ball.y + ball.radius > canvas.height) {
        alert("GAME OVER");
        document.location.reload();
    }

    // パドルとの衝突判定
    if(ball.y + ball.dy > paddle.y - ball.radius &&
       ball.x > paddle.x - paddle.width/2 &&
       ball.x < paddle.x + paddle.width/2) {
        ball.dy = -ball.dy;
    }

    // パドルの移動
    if(leftPressed && paddle.x - paddle.width/2 > 0) {
        paddle.x -= 7;
    }
    else if(rightPressed && paddle.x + paddle.width/2 < canvas.width) {
        paddle.x += 7;
    }

    // ゲーム画面のクリア
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // ボールとパドルの描画
    drawBall();
    drawPaddle();

    // ゲームループの再帰呼び出し
    requestAnimationFrame(draw);
}

// ゲームル

```

**おお！すごい。なんかコードが出てきた。途中までしかないけど、いちおう動かしてみたい。**

## Q2.このコードをpong.jsというファイル名として、pong.jsを読み込むHTMLファイルを作って

### A.以下は、先程のJavaScriptコードをpong.jsというファイル名で保存し、それを読み込むHTMLファイルの例です。

このHTMLファイルには\<canvas>要素と、\<script>要素が含まれています。\<script>要素でJavaScriptファイルを読み込み、ゲームの開始を開始するためにdraw()関数を呼び出します。

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>PONG</title>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <canvas id="canvas" width="480" height="320"></canvas>
    <script src="pong.js"></script>
</body>
</html>

```

上記のHTMLファイルをブラウザで開くと、pong.jsファイルが読み込まれ、PONGゲームが表示されます。ただし、pong.jsファイルとHTMLファイルが同じフォルダにある必要があります。また、<canvas>要素の幅と高さは、任意の値に設定できます。

**HTMLファイルもできた。でも、これだけでは動かないなぁ。どういうコードになっているのかな。**


## Q3.このpong.jsの動作について、コメントを分かりやすく追加して

### A.はい、コメントを追加して、PONGゲームの動作をさらに分かりやすく説明してみましょう。

```js
// ゲームの初期化
var canvas = document.getElementById("canvas");  // canvas要素を取得
var ctx = canvas.getContext("2d");  // canvasの2Dコンテキストを取得

var ball = { x: canvas.width/2, y: canvas.height/2, dx: 5, dy: 5, radius: 10 };  // ボールの初期位置と速度、半径
var paddle = { x: canvas.width/2, y: canvas.height-20, width: 80, height: 10 };  // パドルの初期位置とサイズ
var leftPressed = false;  // 左キーが押されているかどうかのフラグ
var rightPressed = false;  // 右キーが押されているかどうかのフラグ

// キーイベントのリスナーを追加
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

// キーが押された時の処理
function keyDownHandler(e) {
    if(e.keyCode == 37) {  // 左キー
        leftPressed = true;
    }
    else if(e.keyCode == 39) {  // 右キー
        rightPressed = true;
    }
}

// キーが離された時の処理
function keyUpHandler(e) {
    if(e.keyCode == 37) {  // 左キー
        leftPressed = false;
    }
    else if(e.keyCode == 39) {  // 右キー
        rightPressed = false;
    }
}

// ボールを描画する関数
function drawBall() {
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);  // 円を描画
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

// パドルを描画する関数
function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddle.x - paddle.width/2, paddle.y, paddle.width, paddle.height);  // 長方形を描画
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

// ゲームループの関数
function draw() {
    // ボールの移動
    ball.x += ball.dx;
    ball.y += ball.dy;

    // 壁に当たった場合は反射
    if(ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
        ball.dx = -ball.dx;
    }
    if(ball.y - ball.radius < 0) {
        ball.dy = -ball.dy;
    }
    else if(ball.y + ball.radius > canvas.height) {  // 下側の壁に当たった場合はゲームオーバー
        alert("GAME OVER");
        document.location.reload();  // ページをリロードしてリセット
    }

    // パドルとの衝突判定
    if(ball.y + ball.dy >

```

**どうみても、コードが最後までない。**

## Q4.pong.jsは、draw()が途中になっています。最後まで記述してください

### A.すみません、前回の回答の最後の部分が切れていました。以下が、PONGゲームのdraw()関数の最後の部分です。

```js
    // パドルとの衝突判定
    if(ball.y + ball.dy > canvas.height - ball.radius - paddle.height && ball.x > paddle.x - paddle.width/2 && ball.x < paddle.x + paddle.width/2) {
        ball.dy = -ball.dy;
    }

    // パドルの移動
    if(leftPressed && paddle.x > paddle.width/2) {
        paddle.x -= 7;
    }
    else if(rightPressed && paddle.x < canvas.width - paddle.width/2) {
        paddle.x += 7;
    }

    // 画面のクリア
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // ボールとパドルを描画
    drawBall();
    drawPaddle();

    // 次のフレームを描画
    requestAnimationFrame(draw);
}

// ゲームを開始する
draw();

```


draw()関数の最後に、requestAnimationFrame()を呼び出して、次のフレームを描画するようにしています。これにより、ゲームループが実現されます。また、clearRect()を使用して、前のフレームで描画されたボールとパドルを消去しています。最後に、drawBall()とdrawPaddle()を呼び出して、ボールとパドルを描画しています。

requestAnimationFrame()は、ブラウザが次の再描画をする前に、アニメーションを更新するために使用されるメソッドです。これにより、フレームレートをブラウザに任せることができ、よりスムーズなアニメーションが実現されます。

## 手動で修正

と、ここまで作ってコードを結合しても、ちゃんと動かない。そこで、コードを手動で修正。

「else if」であるべきところが「if」になってる。インデントもちょっとおかしい。

これでゲームが遊べるようになったけど、すぐにゲームオーバーになるので、ボールの初期値を修正。

![gpt-pong.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/1ac3dfbe-f0fe-7811-acc0-9920976d2a0b.png)

だいたい、ここまでで30分くらい。

pong.jsの完全版はこちら。

```js
// JS PONG Game for single player by ChatGPT and Me

// ゲームの初期化
var canvas = document.getElementById("canvas");  // canvas要素を取得
var ctx = canvas.getContext("2d");  // canvasの2Dコンテキストを取得

var ball = { x: canvas.width/2, y: canvas.height/2, dx: 5, dy: -5, radius: 10 };  // ボールの初期位置と速度、半径
var paddle = { x: canvas.width/2, y: canvas.height-20, width: 80, height: 10 };  // パドルの初期位置とサイズ
var leftPressed = false;  // 左キーが押されているかどうかのフラグ
var rightPressed = false;  // 右キーが押されているかどうかのフラグ

// キーイベントのリスナーを追加
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

// キーが押された時の処理
function keyDownHandler(e) {
    if(e.keyCode == 37) {  // 左キー
        leftPressed = true;
    }
    else if(e.keyCode == 39) {  // 右キー
        rightPressed = true;
    }
}

// キーが離された時の処理
function keyUpHandler(e) {
    if(e.keyCode == 37) {  // 左キー
        leftPressed = false;
    }
    else if(e.keyCode == 39) {  // 右キー
        rightPressed = false;
    }
}

// ボールを描画する関数
function drawBall() {
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);  // 円を描画
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

// パドルを描画する関数
function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddle.x - paddle.width/2, paddle.y, paddle.width, paddle.height);  // 長方形を描画
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

// ゲームループの関数
function draw() {
    // ボールの移動
    ball.x += ball.dx;
    ball.y += ball.dy;

    // 壁に当たった場合は反射
    if(ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
        ball.dx = -ball.dx;
        } else if(ball.y - ball.radius < 0) {
            ball.dy = -ball.dy;
        } else if(ball.y + ball.radius > canvas.height) {  
            // 下側の壁に当たった場合はゲームオーバー
            alert("GAME OVER");
            document.location.reload();  // ページをリロードしてリセット
        }

    // パドルとの衝突判定
    if(ball.y + ball.dy > canvas.height - ball.radius - paddle.height && ball.x > paddle.x - paddle.width/2 && ball.x < paddle.x + paddle.width/2) {
        ball.dy = -ball.dy;
    }

    // パドルの移動
    if(leftPressed && paddle.x > paddle.width/2) {
        paddle.x -= 7;
    } else if(rightPressed && paddle.x < canvas.width - paddle.width/2) {
        paddle.x += 7;
    }

    // 画面のクリア
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // ボールとパドルを描画
    drawBall();
    drawPaddle();

    // 次のフレームを描画
    requestAnimationFrame(draw);
}

// ゲームを開始する
draw();
```

## Q5.このpong.jsとHTMLファイルでは、白い画面しか表示されません。

**このpong.jsとHTMLファイルでは、白い画面しか表示されません。ゲームの背景を明るい灰色、ボールを黄色、パドルを白、HTMLファイルの背景を濃い灰色になるよう、pong.jsとHTMLファイルを作成してください。HTMLファイルのファイル名はindex.htmlです。**


### A. pong.jsファイルに以下のように変更を加え、背景色と描画色を指定するようにします。

``` js
// キャンバスの背景色
ctx.fillStyle = '#f2f2f2';
ctx.fillRect(0, 0, canvas.width, canvas.height);

// ボールの描画色
ctx.fillStyle = '#f1c40f';
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
ctx.fill();
ctx.closePath();

// パドルの描画色
ctx.fillStyle = '#fff';
ctx.fillRect(paddle.x - paddle.width/2, canvas.height - paddle.height, paddle.width, paddle.height);
``` 

次に、index.htmlファイルに以下のように変更を加え、背景色を指定します。


``` html
<!DOCTYPE html>
<html>
    <head>
        <title>PONG Game</title>
    </head>
    <body style="background-color: #222;">
        <canvas id="myCanvas" width="480" height="320"></canvas>
        <script src="pong.js"></script>
    </body>
</html>
``` 

bodyタグにstyle属性を追加し、背景色を濃い灰色に指定しています。また、キャンバスの背景色はpong.jsファイル内で指定しています。

以上の変更を加えた後、index.htmlファイルをブラウザで開くと、背景色と描画色が変更されたPONGゲームが表示されるはずです。


**なんか怪しいような気もするが、色合いを調整して反映。**

![gpt-pong2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/3443fda4-1243-0c0f-b62d-1ea4bd058028.png)

## Q6.JavaScriptでは、keycodeの使用が非奨励となっているため、次のコードを奨励されている形式に書き直して

```js
let leftPressed = false;  // 左キーが押されているかどうかのフラグ
let rightPressed = false;  // 右キーが押されているかどうかのフラグ

// キーイベントのリスナーを追加
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

// キーが押された時の処理
function keyDownHandler(e) {
    if(e.keyCode == 37) {  // 左キー
        leftPressed = true;
    }
    else if(e.keyCode == 39) {  // 右キー
        rightPressed = true;
    }
}
```

### A.次のように、keycodeの代わりにkeyを使用して書き直すことができます。

```js
let leftPressed = false;  // 左キーが押されているかどうかのフラグ
let rightPressed = false;  // 右キーが押されているかどうかのフラグ

// キーイベントのリスナーを追加
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

// キーが押された時の処理
function keyDownHandler(e) {
    if(e.key === "ArrowLeft") {  // 左キー
        leftPressed = true;
    }
    else if(e.key === "ArrowRight") {  // 右キー
        rightPressed = true;
    }
}

// キーが離された時の処理
function keyUpHandler(e) {
    if(e.key === "ArrowLeft") {  // 左キー
        leftPressed = false;
    }
    else if(e.key === "ArrowRight") {  // 右キー
        rightPressed = false;
    }
}
```

これにより、キーコードを使用する代わりに、キーの名前を使用して、キーハンドラー関数を書くことができます。また、キーが離された時の処理を追加することも忘れないでください。

**コメント欄で指摘を受けたので修正。へたに調べるより、聞いたほうが早いゾ。キーを離したときに処理まで指摘してされた。**

## Q7.(さらに改良)次のコードのように一人用PONGゲームがあります。このゲームで、paddleに当たる位置によってballの方向が変わるようにするには、コードのどこを修正すればいいですか。


```javascript:pong.js
// JS PONG Game for single player by ChatGPT and Me

// ゲームの初期化
let canvas = document.getElementById("canvas");  // canvas要素を取得
let ctx = canvas.getContext("2d");  // canvasの2Dコンテキストを取得

let ball = { x: canvas.width/2, y: canvas.height/2, dx: 5, dy: -5, radius: 10 };  // ボールの初期位置と速度、半径
let paddle = { x: canvas.width/2, y: canvas.height-20, width: 80, height: 10 };  // パドルの初期位置とサイズ

let leftPressed = false;  // 左キーが押されているかどうかのフラグ
let rightPressed = false;  // 右キーが押されているかどうかのフラグ

// キーイベントのリスナーを追加
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

// キーが押された時の処理
function keyDownHandler(e) {
    if(e.key === "ArrowLeft") {  // 左キー
        leftPressed = true;
    }
    else if(e.key === "ArrowRight") {  // 右キー
        rightPressed = true;
    }
}

// キーが離された時の処理
function keyUpHandler(e) {
    if(e.key === "ArrowLeft") {  // 左キー
        leftPressed = false;
    }
    else if(e.key === "ArrowRight") {  // 右キー
        rightPressed = false;
    }
}


// 背面を描画する関数
function drawBackground() {
    ctx.fillStyle = '#f2f2f2';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
}

// ボールを描画する関数
function drawBall() {
    ctx.fillStyle = '#f1c40f';
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
    ctx.fill();
    ctx.closePath();
}

// パドルを描画する関数
function drawPaddle() {
    ctx.beginPath();
    ctx.fillStyle = '#ccc';
    ctx.fillRect(paddle.x - paddle.width/2, canvas.height - paddle.height, paddle.width, paddle.height);
    ctx.closePath();
}

// ゲームループの関数
function draw() {
    // ボールの移動
    ball.x += ball.dx;
    ball.y += ball.dy;

    // 壁に当たった場合は反射
    if(ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
        ball.dx = -ball.dx;
        } else if(ball.y - ball.radius < 0) {
            ball.dy = -ball.dy;
        } else if(ball.y + ball.radius > canvas.height) {  
            // 下側の壁に当たった場合はゲームオーバー
            alert("GAME OVER");
            document.location.reload();  // ページをリロードしてリセット
        }

    // パドルとの衝突判定
    if(ball.y + ball.dy > canvas.height - ball.radius - paddle.height && ball.x > paddle.x - paddle.width/2 && ball.x < paddle.x + paddle.width/2) {
        ball.dy = -ball.dy;
    }

    // パドルの移動
    if(leftPressed && paddle.x > paddle.width/2) {
        paddle.x -= 7;
    } else if(rightPressed && paddle.x < canvas.width - paddle.width/2) {
        paddle.x += 7;
    }

    // 画面のクリア
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // ゲーム要素を描画
    drawBackground();
    drawBall();
    drawPaddle();

    // 次のフレームを描画
    requestAnimationFrame(draw);
}

// ゲームを開始する
draw();
```

### A. ボールの移動処理の後、次の行にパドルとの衝突判定があります。

```js
// パドルとの衝突判定
if(ball.y + ball.dy > canvas.height - ball.radius - paddle.height && ball.x > paddle.x - paddle.width/2 && ball.x < paddle.x + paddle.width/2) {
    ball.dy = -ball.dy;
}
```

この部分で、ボールがパドルに当たったかどうかが判定されています。パドルの位置によってボールの反射方向を変えるには、この部分でball.dxを変更すれば良いです。

たとえば、パドルの左側に当たった場合はball.dxを負の値に、右側に当たった場合は正の値に変更することができます。具体的には、以下のようになります。


```js
// パドルとの衝突判定
if(ball.y + ball.dy > canvas.height - ball.radius - paddle.height && ball.x > paddle.x - paddle.width/2 && ball.x < paddle.x + paddle.width/2) {
    if (ball.x < paddle.x) {  // パドルの左側に当たった場合
        ball.dx = -Math.abs(ball.dx);
    } else {  // パドルの右側に当たった場合
        ball.dx = Math.abs(ball.dx);
    }
    ball.dy = -ball.dy;
}
```

このようにすると、パドルの左側に当たった場合はボールが左に反射し、右側に当たった場合はボールが右に反射します。


## Q.8このプログラムのHTMLファイルは次のようになっています。ゲームの開始・停止・再開ボタンを追加するにはどうすればいいですか。

```html:index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>gpt-pong</title>
    <style>
        canvas {
            border: 1px solid #505050;
        }
    </style>
</head>

<body style="background-color: #b8b8b8;">
    <h1>gpt-pong</h1>
    <canvas id="canvas" width="480" height="320"></canvas>
    <script src="pong.js"></script>

    <p><a href="https://github.com/ycatch/gpt-pong">Source code</a></p>
</body>

</html>
```

### A. ゲームの開始・停止・再開ボタンを追加するには、以下の手順に従ってください。

HTMLファイルに、ボタンを追加するための要素を作成します。例えば、以下のようにします。

```html
<button id="start-button">Start</button>
<button id="stop-button">Stop</button>
<button id="resume-button">Resume</button>
```

JavaScriptファイルに、ボタンをクリックしたときに呼び出される関数を追加します。たとえば、以下のようにします。

```javascript
document.getElementById("start-button").addEventListener("click", function(){
    startGame();
});

document.getElementById("stop-button").addEventListener("click", function(){
    stopGame();
});

document.getElementById("resume-button").addEventListener("click", function(){
    resumeGame();
});
```

JavaScriptファイルに、ボタンを押したときに呼び出される関数を実装します。以下は、startGame、stopGame、resumeGameの例です。


```javascript
function startGame() {
    // ゲームを開始するための処理を実装します。
}

function stopGame() {
    // ゲームを停止するための処理を実装します。
}

function resumeGame() {
    // ゲームを再開するための処理を実装します。
}
```

startGame、stopGame、resumeGame関数に、ゲームの開始・停止・再開に必要な処理を追加します。
以上の手順を実行することで、ゲームの開始・停止・再開ボタンを追加することができます。

## Q9.ゲームの開始・停止・再開に必要なstartGame、stopGame、resumeGame関数を書いてください
以下のように、startGame、stopGame、resumeGame関数を追加することができます。


### A.ゲームの開始・停止・再開に必要なstartGame、stopGame、resumeGame関数を書いてください

以下のように、startGame、stopGame、resumeGame関数を追加することができます。

```js
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>gpt-pong</title>
    <style>
        canvas {
            border: 1px solid #505050;
        }
    </style>
</head>

<body style="background-color: #b8b8b8;">
    <h1>gpt-pong</h1>
    <canvas id="canvas" width="480" height="320"></canvas>
    <script src="pong.js"></script>

    <p><a href="https://github.com/ycatch/gpt-pong">Source code</a></p>

    <button onclick="startGame()">Start Game</button>
    <button onclick="stopGame()">Stop Game</button>
    <button onclick="resumeGame()">Resume Game</button>

    <script>
        var intervalId;

        function startGame() {
            if (!intervalId) {
                intervalId = setInterval(update, 10);
            }
        }

        function stopGame() {
            clearInterval(intervalId);
            intervalId = null;
        }

        function resumeGame() {
            if (!intervalId) {
                intervalId = setInterval(update, 10);
            }
        }
    </script>
</body>

</html>
```


これにより、画面にStart Game、Stop Game、Resume Gameの3つのボタンが追加され、それぞれがstartGame、stopGame、resumeGame関数を呼び出します。また、intervalIdという変数を使用して、ゲームの更新を停止、再開することができます。

## Q10.setInterval(update, 10)の役割を教えて

### A.setInterval(update, 10) は、10ミリ秒ごとに update 関数を呼び出すように設定するメソッドです。このメソッドは、ゲームのループを作成するために使用されます。

具体的には、ゲーム内の状態を更新し、描画を更新するために update 関数が呼び出されます。10ミリ秒ごとに update 関数が呼び出されるため、ゲームは60FPSで実行されます（1秒あたり1000ミリ秒 / 10ミリ秒 = 100、1秒あたり100回呼び出されるため、60FPSになります）。

したがって、 setInterval(update, 10) を使用することで、定期的に update 関数を呼び出すことができ、ゲームを実行することができます。

## Q11.一人用PONGゲームにはupdate関数はありません。setIntervalでどの関数を呼び出せばいいですか

### A.一人用PONGゲームでは、update() 関数の代わりに gameLoop() 関数が用意されています。

setInterval() メソッドで gameLoop() 関数を繰り返し実行することができます。以下のようにコードを書き換えることができます。

```js
// gameLoop() 関数を定義する
function gameLoop() {
  movePaddle();
  moveBall();
  collisionDetection();
  draw();
}

// setInterval() メソッドで gameLoop() 関数を繰り返し実行する
setInterval(gameLoop, 10);
```

これにより、gameLoop() 関数が10ミリ秒ごとに繰り返し実行されるようになります。

## 手動で修正

setIntervalで呼び出す関数名は、drawなので手動で修正。

また、drawを繰り返し実行する方法も変更。

```js:pong.js
// JS PONG Game for single player by ChatGPT and Me

// ゲームの初期化
let canvas = document.getElementById("canvas");  // canvas要素を取得
let ctx = canvas.getContext("2d");  // canvasの2Dコンテキストを取得

let ball = { x: canvas.width/2, y: canvas.height/2, dx: 5, dy: -5, radius: 10 };  // ボールの初期位置と速度、半径
let paddle = { x: canvas.width/2, y: canvas.height-20, width: 80, height: 10 };  // パドルの初期位置とサイズ

let leftPressed = false;  // 左キーが押されているかどうかのフラグ
let rightPressed = false;  // 右キーが押されているかどうかのフラグ

let intervalId;     //開始・停止・再開のフラグ

// キーイベントのリスナーを追加
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);


// ボタンイベントのリスナーを追加
document.getElementById("start-button").addEventListener("click", function(){
    startGame();
});

document.getElementById("stop-button").addEventListener("click", function(){
    stopGame();
});

document.getElementById("resume-button").addEventListener("click", function(){
    resumeGame();
});


// キーが押された時の処理
function keyDownHandler(e) {
    if(e.key === "ArrowLeft") {  // 左キー
        leftPressed = true;
    }
    else if(e.key === "ArrowRight") {  // 右キー
        rightPressed = true;
    }
}

// キーが離された時の処理
function keyUpHandler(e) {
    if(e.key === "ArrowLeft") {  // 左キー
        leftPressed = false;
    }
    else if(e.key === "ArrowRight") {  // 右キー
        rightPressed = false;
    }
}

// ボタンが押された時の処理

// ゲームを開始
function startGame() {
    if (!intervalId) {
        intervalId = setInterval(draw, 10);
    }
}

// ゲームを停止
function stopGame() {
    clearInterval(intervalId);
    intervalId = null;
}

// ゲームを再開
function resumeGame() {
    if (!intervalId) {
        intervalId = setInterval(draw, 10);
    }
}

// 背面を描画する関数
function drawBackground() {
    ctx.fillStyle = '#f2f2f2';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
}

// ボールを描画する関数
function drawBall() {
    ctx.fillStyle = '#f1c40f';
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
    ctx.fill();
    ctx.closePath();
}

// パドルを描画する関数
function drawPaddle() {
    ctx.beginPath();
    ctx.fillStyle = '#ccc';
    ctx.fillRect(paddle.x - paddle.width/2, canvas.height - paddle.height, paddle.width, paddle.height);
    ctx.closePath();
}

// ゲームループの関数
function draw() {
    // ボールの移動
    ball.x += ball.dx;
    ball.y += ball.dy;

    // 壁に当たった場合は反射
    if(ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
        ball.dx = -ball.dx;
        } else if(ball.y - ball.radius < 0) {
            ball.dy = -ball.dy;
        } else if(ball.y + ball.radius > canvas.height) {  
            // 下側の壁に当たった場合はゲームオーバー
            alert("GAME OVER");
            document.location.reload();  // ページをリロードしてリセット
        }

    // パドルとの衝突判定
    if(ball.y + ball.dy > canvas.height - ball.radius - paddle.height && ball.x > paddle.x - paddle.width/2 && ball.x < paddle.x + paddle.width/2) {
        if (ball.x < paddle.x) {  // パドルの左側に当たった場合
            ball.dx = -Math.abs(ball.dx);
        } else {  // パドルの右側に当たった場合
            ball.dx = Math.abs(ball.dx);
        }
        ball.dy = -ball.dy;
    }

    // パドルの移動
    if(leftPressed && paddle.x > paddle.width/2) {
        paddle.x -= 7;
    } else if(rightPressed && paddle.x < canvas.width - paddle.width/2) {
        paddle.x += 7;
    }

    // 画面のクリア
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // ゲーム要素を描画
    drawBackground();
    drawBall();
    drawPaddle();
}
```

## CSSフレームワークを適用

さらに、手動でCSSフレームワークのmilligramを適用。こういうのは自分でやったほうが早いかな。

```html:index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.4.1/milligram.css">
    <title>gpt-pong</title>
    <style>
        canvas {
            border: 1px solid #505050;
        }
    </style>
</head>

<body style="background-color:white;">
    <div class="container">
        <div class="row">
            <div class="column column-100">
                <h1>gpt-pong</h1>
                <canvas id="canvas" width="480" height="320"></canvas>
            </div>
        </div>
        <div class="row">
            <div class="column column-100">
                <button id="start-button">Start</button>
                <button id="stop-button">Stop</button>
                <button id="resume-button">Resume</button>
                <hr>
                <p><a href="https://github.com/ycatch/gpt-pong">Source code</a></p>
                <script src="pong.js"></script>
            </div>
        </div>
    </div>
</body>

</html>
```

![gpt-pong3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86880/eed66ceb-4051-df92-b9c4-203a448fa284.png)

## 感想

- ここまで出来るのはすごい。
- コードの出力量に制限があるみたいで、完全版が出力されない場合が多々ある。
- ある程度コードを把握して自分で組み立てる羽目になった。
- else ifが漏れるのはなんでやねん。
- あと、varで宣言されてた。直すのを忘れていた。

- このあとブロック崩しに進化させようとしたけど、コードの断片しか出力してくれない。

- アプリ開発ツールの中に組み込んで、コードリポジトリの一部みたいに使うのは有効そう
- ノーコードというかローコードツールだね

## 関連ページ

- chatGPTにぷよぷよを作らせてみた。 - Qiita
  https://qiita.com/gatuwo_jp/items/a2c63c960c3c9480a91e
- GPT-4を使って ぷよぷよ 作ってみたときの感想
  https://zenn.dev/corocn/articles/b4c473ed779935