cntwork.com は、実際に運営者が訪れた国・都市での体験をもとに、海外旅行・グルメ・最新テクノロジーに関する情報を発信している 個人運営のブログサイト です。

【コピペで動く】JSで簡単ミニゲーム作り方!スマホ・音付きランナーゲーム

2026年5月1日金曜日

ゲーム ブログ

t f B! P L

【コピペで動く】JSで簡単ミニゲーム作り方!スマホ・音付きランナーゲーム

〜 ジャンプ・ランナー (音付き) 〜

シンプルゲームNo.2

〜 ジャンプ・ランナー 〜

操作: 画面【タップ】または【スペース】でジャンプ!

コピペでOK!ソースコード

 

        <!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>シンプルゲームNo.2 - 音付き版</title>
    <style>
        body {
            font-family: 'Helvetica Neue', Arial, sans-serif;
            text-align: center;
            background-color: #f0f2f5;
            margin: 0;
            padding: 10px;
            touch-action: manipulation;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            user-select: none;
        }
        h1 {
            font-size: 20px;
            color: #333;
            margin: 10px 0 5px 0;
        }
        p {
            font-size: 14px;
            color: #666;
            margin-top: 0;
        }
        .canvas-container {
            width: 100%;
            max-width: 600px;
            margin: 10px auto;
        }
        #gameCanvas {
            background-color: #ffffff;
            border: 3px solid #333;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            display: block;
            width: 100%;
            height: auto;
        }
        .instructions {
            font-size: 13px;
            color: #555;
            background: #fff;
            padding: 8px 15px;
            border-radius: 20px;
            display: inline-block;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
            margin-top: 5px;
        }
    </style>
</head>
<body>

    <h1>シンプルゲームNo.2</h1>
    <p>〜 ジャンプ・ランナー (音付き) 〜</p>
    
    <div class="canvas-container">
        <canvas id="gameCanvas" width="600" height="200"></canvas>
    </div>
    
    <div class="instructions">
        <strong>操作:</strong> 画面【タップ】または【スペース】でジャンプ!(音が出ます)
    </div>

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

// ゲームの状態
let gameState = "START"; 
let score = 0;
let highScore = 0;

// プレイヤー
const player = {
    x: 50,
    y: 130,
    width: 30,
    height: 30,
    color: "#007bff",
    gravity: 0.6,
    jumpForce: -12,
    velocity: 0,
    isJumping: false
};

// 障害物
const obstacle = {
    x: 600,
    y: 130,
    width: 20,
    height: 30,
    color: "#dc3545",
    speed: 6
};

// --- 🔊 効果音再生システム (Web Audio API) ---
let audioCtx = null;

function initAudio() {
    // 最初のタップ時にオーディオコンテキストを初期化(ブラウザ対策)
    if (!audioCtx) {
        audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    }
}

// ジャンプ音(シュピーンと高くなる音)
function playJumpSound() {
    if (!audioCtx) return;
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    
    osc.type = "triangle"; // レトロな三角波
    osc.frequency.setValueAtTime(150, audioCtx.currentTime); // 初期音程(低い)
    osc.frequency.exponentialRampToValueAtTime(600, audioCtx.currentTime + 0.15); // 一気に高く
    
    gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
    gain.gain.linearRampToValueAtTime(0.01, audioCtx.currentTime + 0.15); // フェードアウト
    
    osc.connect(gain);
    gain.connect(audioCtx.destination);
    osc.start();
    osc.stop(audioCtx.currentTime + 0.15);
}

// ぶつかった音(ドカンと低く震える音)
function playHitSound() {
    if (!audioCtx) return;
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    
    osc.type = "sawtooth"; // 濁ったノコギリ波
    osc.frequency.setValueAtTime(120, audioCtx.currentTime); // 初期音程
    osc.frequency.linearRampToValueAtTime(40, audioCtx.currentTime + 0.3); // 低く落とす
    
    gain.gain.setValueAtTime(0.4, audioCtx.currentTime);
    gain.gain.linearRampToValueAtTime(0.01, audioCtx.currentTime + 0.3); // フェードアウト
    
    osc.connect(gain);
    gain.connect(audioCtx.destination);
    osc.start();
    osc.stop(audioCtx.currentTime + 0.3);
}

// スコアアップ音(ピピッときれいな高音)
function playScoreSound() {
    if (!audioCtx) return;
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    
    osc.type = "sine"; // 澄んだ正弦波
    osc.frequency.setValueAtTime(800, audioCtx.currentTime);
    
    gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
    gain.gain.linearRampToValueAtTime(0.01, audioCtx.currentTime + 0.08);
    
    osc.connect(gain);
    gain.connect(audioCtx.destination);
    osc.start();
    osc.stop(audioCtx.currentTime + 0.08);
}


// --- 操作イベント ---
window.addEventListener("keydown", (e) => {
    if (e.code === "Space") {
        e.preventDefault();
        initAudio();
        handleAction();
    }
});

window.addEventListener("touchstart", (e) => {
    if (e.target === canvas || e.target.tagName === "BODY" || e.target.className === "canvas-container") {
        e.preventDefault();
        initAudio(); // スマホの音声を有効化
        handleAction();
    }
}, { passive: false });

window.addEventListener("mousedown", (e) => {
    if (e.target === canvas || e.target.tagName === "BODY" || e.target.className === "canvas-container") {
        initAudio();
        handleAction();
    }
});

function handleAction() {
    if (gameState === "START") {
        resetGame();
        gameState = "PLAYING";
    } else if (gameState === "PLAYING" && !player.isJumping) {
        player.velocity = player.jumpForce;
        player.isJumping = true;
        playJumpSound(); // 🔊 ジャンプ音を鳴らす
    } else if (gameState === "GAMEOVER") {
        resetGame();
        gameState = "PLAYING";
    }
}

function resetGame() {
    score = 0;
    player.y = 130;
    player.velocity = 0;
    player.isJumping = false;
    obstacle.x = 600;
    obstacle.speed = 6;
}

// ゲームループ
function gameLoop() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 地面
    ctx.strokeStyle = "#333";
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(0, 160);
    ctx.lineTo(600, 160);
    ctx.stroke();

    if (gameState === "START") {
        drawText("タップしてスタート", 300, 100, "20px Arial", "#333");
    } 
    
    else if (gameState === "PLAYING") {
        // 物理演算
        player.velocity += player.gravity;
        player.y += player.velocity;

        if (player.y >= 130) {
            player.y = 130;
            player.velocity = 0;
            player.isJumping = false;
        }

        // 障害物の移動
        obstacle.x -= obstacle.speed;
        if (obstacle.x < -obstacle.width) {
            obstacle.x = 600;
            score++;
            playScoreSound(); // 🔊 スコアアップ音を鳴らす
            if (obstacle.speed < 12) obstacle.speed += 0.3;
        }

        // 当たり判定
        if (
            player.x < obstacle.x + obstacle.width &&
            player.x + player.width > obstacle.x &&
            player.y < obstacle.y + obstacle.height &&
            player.y + player.height > obstacle.y
        ) {
            gameState = "GAMEOVER";
            playHitSound(); // 🔊 ぶつかった音を鳴らす
            if (score > highScore) highScore = score;
        }

        // 描画
        drawRect(player.x, player.y, player.width, player.height, player.color);
        drawRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height, obstacle.color);
        
        drawText(`スコア: ${score}`, 30, 30, "16px Arial", "#333", "left");
        drawText(`最高: ${highScore}`, 570, 30, "16px Arial", "#666", "right");
    } 
    
    else if (gameState === "GAMEOVER") {
        drawRect(player.x, player.y, player.width, player.height, "#6c757d");
        drawRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height, obstacle.color);
        
        drawText("ゲームオーバー", 300, 80, "24px Arial", "#dc3545");
        drawText(`スコア: ${score}`, 300, 110, "18px Arial", "#333");
        drawText("タップしてリトライ", 300, 140, "14px Arial", "#666");
    }

    requestAnimationFrame(gameLoop);
}

function drawRect(x, y, w, h, color) {
    ctx.fillStyle = color;
    ctx.fillRect(x, y, w, h);
}

function drawText(text, x, y, font, color, align = "center") {
    ctx.font = font;
    ctx.fillStyle = color;
    ctx.textAlign = align;
    ctx.fillText(text, x, y);
}

gameLoop();
</script>
</body>
</html>
       

人気記事

Welcome



はじめまして!
フリーテーマの記事をのびのび更新します!!

このブログは、実際に訪れた国や都市での体験をもとに、 海外旅行のリアルな情報・注意点・感じたことを記録している 個人運営の旅行ブログです。

人気の記事

【自作ゲーム】初心者が3分で「数当てゲーム」を作ってみたら意外と熱中した話

【自作ゲーム】何回で正解できますか? 🔢 数当てゲーム 1 から 100 までの数字を当ててみてね 予想する ...

主な掲載内容

グルメやプログラミング、明日話したくなるような会話のネタを発信しています。旅のコツなどをわかりやすく解説します。

このブログを検索

QooQ