1
0

ストレス発散。うちまくりの弾幕シューティングゲーム。

Posted at

スクリーンショット 2024-08-09 155351.png

スクリーンショット 2024-08-09 155555.png

スペースキーが押されている間、弾を連射します。カーソル左右で射撃角度調整です。移動はしません。

import http.server
import socketserver
import tempfile
import webbrowser

# HTMLとJavaScriptのコード
html_content = """
<!DOCTYPE html>
<html>
<head>
    <title>シューティングゲーム</title>
    <style>
        canvas {
            background: #eee;
            display: block;
            margin: auto;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas" width="480" height="320"></canvas>
    <script>
        var canvas = document.getElementById("gameCanvas");
        var ctx = canvas.getContext("2d");

        // 自機の設定
        var playerSize = 20; // 自機のサイズ
        var playerX = (canvas.width - playerSize) / 2; // 自機のX座標(初期位置は中央)
        var playerY = canvas.height - playerSize - 10; // 自機のY座標(画面下部に配置)

        // 弾の設定
        var bulletRadius = 5; // 弾の半径
        var bullets = []; // 弾のリスト
        var bulletSpeed = 5; // 弾の速度

        // 敵の設定
        var enemySize = 30; // 敵のサイズ
        var enemies = []; // 敵のリスト
        var enemySpeed = 2; // 敵の速度
        var enemySpawnInterval = 2000; // 敵の生成間隔(ミリ秒)
        var lastEnemySpawnTime = Date.now(); // 最後に敵を生成した時間

        // 射撃角度と状態
        var shootingAngle = 0; // 弾の射撃角度(初期値は0)
        var isShooting = false; // 射撃中かどうかのフラグ

        // ランダムな色を生成する関数
        function randomColor() {
            return '#' + Math.floor(Math.random() * 16777215).toString(16);
        }

        // 自機を描画する関数
        function drawPlayer() {
            ctx.beginPath();
            // 三角形の自機を描画
            ctx.moveTo(playerX + playerSize / 2, playerY); // 三角形の頂点
            ctx.lineTo(playerX + playerSize, playerY + playerSize); // 三角形の右下
            ctx.lineTo(playerX, playerY + playerSize); // 三角形の左下
            ctx.closePath(); // 三角形を閉じる
            ctx.fillStyle = randomColor(); // ランダムな色で塗りつぶし
            ctx.fill();
            ctx.strokeStyle = '#000000'; // 黒い線で縁取り
            ctx.stroke();
        }

        // 弾を描画する関数
        function drawBullet(bullet) {
            ctx.beginPath();
            ctx.arc(bullet.x, bullet.y, bulletRadius, 0, Math.PI * 2); // 弾を円形に描画
            ctx.fillStyle = randomColor(); // ランダムな色で塗りつぶし
            ctx.fill();
            ctx.strokeStyle = '#000000'; // 黒い線で縁取り
            ctx.stroke();
        }

        // 敵を描画する関数
        function drawEnemy(enemy) {
            ctx.beginPath();
            ctx.rect(enemy.x, enemy.y, enemySize, enemySize); // 敵を矩形に描画
            ctx.fillStyle = randomColor(); // ランダムな色で塗りつぶし
            ctx.fill();
            ctx.strokeStyle = '#000000'; // 黒い線で縁取り
            ctx.stroke();
        }

        // ゲームの描画と更新を行う関数
        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height); // 画面をクリア

            drawPlayer(); // 自機の描画
            
            // 弾と敵を描画
            bullets.forEach(bullet => drawBullet(bullet));
            enemies.forEach(enemy => drawEnemy(enemy));

            // 弾の移動処理
            bullets.forEach((bullet, index) => {
                bullet.x += Math.cos(bullet.angle) * bulletSpeed; // 弾のX座標を更新
                bullet.y += Math.sin(bullet.angle) * bulletSpeed; // 弾のY座標を更新

                // 弾が画面外に出た場合は削除
                if (bullet.x < 0 || bullet.x > canvas.width || bullet.y < 0 || bullet.y > canvas.height) {
                    bullets.splice(index, 1);
                }
            });

            // 敵の移動処理
            enemies.forEach((enemy, index) => {
                enemy.y += enemySpeed; // 敵のY座標を更新
                if (enemy.y > canvas.height) {
                    enemies.splice(index, 1); // 敵が画面下に出た場合は削除
                }
            });

            // 弾と敵の衝突判定
            bullets.forEach((bullet, bIndex) => {
                enemies.forEach((enemy, eIndex) => {
                    if (bullet.x > enemy.x && bullet.x < enemy.x + enemySize &&
                        bullet.y > enemy.y && bullet.y < enemy.y + enemySize) {
                        bullets.splice(bIndex, 1); // 衝突した弾を削除
                        enemies.splice(eIndex, 1); // 衝突した敵を削除
                    }
                });
            });

            // 敵の生成処理
            if (Date.now() - lastEnemySpawnTime > enemySpawnInterval) {
                var enemyX = Math.random() * (canvas.width - enemySize); // 敵のX座標をランダムに決定
                enemies.push({ x: enemyX, y: 0 }); // 新しい敵を生成
                lastEnemySpawnTime = Date.now(); // 最後の敵生成時間を更新
            }

            // 射撃中なら弾を生成
            if (isShooting) {
                bullets.push({
                    x: playerX + playerSize / 2, // 自機の中心から弾を発射
                    y: playerY,
                    angle: shootingAngle // 現在の射撃角度を設定
                });
            }

            // 次のフレームをリクエスト
            requestAnimationFrame(draw);
        }

        // キーが押されたときの処理
        document.addEventListener("keydown", function(event) {
            if (event.code === "ArrowLeft") {
                shootingAngle -= 0.05; // 左カーソルキーで射撃角度を減少
            } else if (event.code === "ArrowRight") {
                shootingAngle += 0.05; // 右カーソルキーで射撃角度を増加
            } else if (event.code === "Space") {
                isShooting = true; // スペースキーが押されたときに射撃開始
            }
        });

        // キーが離されたときの処理
        document.addEventListener("keyup", function(event) {
            if (event.code === "Space") {
                isShooting = false; // スペースキーが離されたときに射撃停止
            }
        });

        // ゲームの描画を開始
        draw();
    </script>
</body>
</html>
"""

# 一時的なファイルにHTMLコンテンツを書き込む
with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as temp_html:
    temp_html.write(html_content.encode("utf-8"))
    temp_html.flush()
    # ブラウザでファイルを開く
    webbrowser.open(f"file://{temp_html.name}")

# HTTPサーバーを作成してHTMLを提供する
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print(f"Serving at port {PORT}")
    httpd.serve_forever()

1
0
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
0