【コピペで動く】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>







0 件のコメント:
コメントを投稿