<!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>Swipe Snake Game</title>
<style>
body {
background-color: #222;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
color: white;
font-family: 'Courier New', Courier, monospace;
position: relative;
/* スマホでのスクロールや長押し選択を防止 */
touch-action: none;
user-select: none;
-webkit-user-select: none;
overflow: hidden;
}
#scoreBoard {
font-size: 24px;
margin-bottom: 10px;
z-index: 5;
}
canvas {
border: 2px solid #555;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.2);
background-color: #000;
/* スマホの画面幅に合わせて自動調整 */
max-width: 95vw;
max-height: 80vh;
/* タッチ時のハイライトを無効化 */
-webkit-tap-highlight-color: transparent;
}
#gameOverScreen {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
background-color: rgba(0, 0, 0, 0.9);
padding: 30px;
border: 2px solid #fff;
z-index: 10;
min-width: 200px;
border-radius: 10px;
}
#gameOverScreen h2 {
color: red;
font-size: 36px;
margin-top: 0;
}
#replayBtn {
padding: 15px 20px;
font-size: 20px;
font-family: inherit;
cursor: pointer;
background-color: #00ffcc;
border: none;
color: #000;
margin-top: 20px;
width: 100%;
border-radius: 5px;
font-weight: bold;
}
#replayBtn:active {
background-color: #00ccaa;
}
#startMessage {
margin-top: 15px;
color: #aaa;
font-size: 14px;
text-align: center;
}
</style>
</head>
<body>
<div id="scoreBoard">Score: <span id="scoreVal">0</span></div>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="startMessage">PC: 矢印キー<br>スマホ: スワイプ<br>でスタート</div>
<div id="gameOverScreen">
<h2>GAME OVER</h2>
<p>Final Score: <span id="finalScore">0</span></p>
<button id="replayBtn">REPLAY</button>
</div>
<script>
let canvas, ctx;
let gameInterval;
const gs = 20; // グリッドサイズ
const tc = 20; // タイルカウント
let px, py;
let ax, ay;
let xv, yv;
let trail;
let tail;
let score;
let isGameOver = false;
let isGameStarted = false;
// タッチ操作用の変数
let touchStartX = 0;
let touchStartY = 0;
window.onload = function() {
canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
// キーボード操作
document.addEventListener("keydown", keyPush);
// タッチ操作(スマホ用)
document.addEventListener("touchstart", handleTouchStart, {passive: false});
document.addEventListener("touchend", handleTouchEnd, {passive: false});
document.getElementById("replayBtn").addEventListener("click", resetGame);
resetGame();
}
function resetGame() {
px = py = 10;
ax = ay = 15;
xv = yv = 0;
trail = [];
tail = 5;
score = 0;
isGameOver = false;
isGameStarted = false;
updateScore(0);
document.getElementById("gameOverScreen").style.display = "none";
document.getElementById("startMessage").style.visibility = "visible";
clearInterval(gameInterval);
gameInterval = setInterval(game, 1000 / 15);
}
function game() {
px += xv;
py += yv;
if (px < 0) px = tc - 1;
if (px > tc - 1) px = 0;
if (py < 0) py = tc - 1;
if (py > tc - 1) py = 0;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "lime";
for (var i = 0; i < trail.length; i++) {
ctx.fillRect(trail[i].x * gs, trail[i].y * gs, gs - 2, gs - 2);
if (isGameStarted && trail[i].x == px && trail[i].y == py) {
gameOver();
return;
}
}
trail.push({ x: px, y: py });
while (trail.length > tail) {
trail.shift();
}
ctx.fillStyle = "cyan";
ctx.fillRect(ax * gs + 1, ay * gs + 1, gs - 4, gs - 4);
if (ax == px && ay == py) {
tail++;
score += 10;
updateScore(score);
do {
ax = Math.floor(Math.random() * tc);
ay = Math.floor(Math.random() * tc);
} while(isPositionOnSnake(ax, ay));
}
}
function isPositionOnSnake(x, y) {
for (let i = 0; i < trail.length; i++) {
if (trail[i].x === x && trail[i].y === y) {
return true;
}
}
return false;
}
function updateScore(newScore) {
document.getElementById("scoreVal").innerText = newScore;
}
function gameOver() {
isGameOver = true;
clearInterval(gameInterval);
document.getElementById("finalScore").innerText = score;
document.getElementById("gameOverScreen").style.display = "block";
document.getElementById("startMessage").style.visibility = "hidden";
}
// --- 入力処理 ---
// ゲーム開始トリガー
function startGameTrigger() {
if (!isGameStarted) {
isGameStarted = true;
document.getElementById("startMessage").style.visibility = "hidden";
}
}
// キーボード操作
function keyPush(evt) {
if (isGameOver) return;
startGameTrigger();
switch (evt.keyCode) {
case 37: if (xv !== 1) { xv = -1; yv = 0; } break; // Left
case 38: if (yv !== 1) { xv = 0; yv = -1; } break; // Up
case 39: if (xv !== -1) { xv = 1; yv = 0; } break; // Right
case 40: if (yv !== -1) { xv = 0; yv = 1; } break; // Down
}
}
// タッチ開始(指が触れた位置を記憶)
function handleTouchStart(evt) {
// スクロール防止
if(evt.target.id === "gameCanvas" || evt.target.tagName === "BODY") {
evt.preventDefault();
}
const firstTouch = evt.touches[0];
touchStartX = firstTouch.clientX;
touchStartY = firstTouch.clientY;
}
// タッチ終了(指が離れた位置と開始位置を比較して方向を決定)
function handleTouchEnd(evt) {
if (isGameOver) return;
// スクロール防止
if(evt.target.id === "gameCanvas" || evt.target.tagName === "BODY") {
evt.preventDefault();
}
const xUp = evt.changedTouches[0].clientX;
const yUp = evt.changedTouches[0].clientY;
const xDiff = touchStartX - xUp;
const yDiff = touchStartY - yUp;
// スワイプ判定の感度(これ以上動かしたら反応する)
if (Math.abs(xDiff) < 10 && Math.abs(yDiff) < 10) {
return; // タップのような微細な動きは無視
}
startGameTrigger();
// 横移動の距離(xDiff)と縦移動の距離(yDiff)を比較して、どちら向きのスワイプか判定
if (Math.abs(xDiff) > Math.abs(yDiff)) {
// 横方向のスワイプ
if (xDiff > 0) {
// 左スワイプ (開始点より終了点の方が左にある=差がプラス)
if (xv !== 1) { xv = -1; yv = 0; }
} else {
// 右スワイプ
if (xv !== -1) { xv = 1; yv = 0; }
}
} else {
// 縦方向のスワイプ
if (yDiff > 0) {
// 上スワイプ
if (yv !== 1) { xv = 0; yv = -1; }
} else {
// 下スワイプ
if (yv !== -1) { xv = 0; yv = 1; }
}
}
}
</script>
</body>
</html>
使用変数
| ax | |
| ay | |
| canvas | |
| charset | |
| content | |
| ctx | |
| display | |
| fillStyle | |
| firstTouch | |
| game -------( Function ) | |
| gameInterval | |
| gameOver -------( Function ) | |
| gs | |
| handleTouchEnd -------( Function ) | |
| handleTouchStart -------( Function ) | |
| height | |
| i | |
| id | |
| innerText | |
| isGameOver | |
| isGameStarted | |
| isPositionOnSnake -------( Function ) | |
| keyPush -------( Function ) | |
| lang | |
| name | |
| onload | |
| px | |
| py | |
| resetGame -------( Function ) | |
| scalable | |
| scale | |
| score | |
| startGameTrigger -------( Function ) | |
| tagName | |
| tail | |
| tc | |
| touchStartX | |
| touchStartY | |
| trail | |
| updateScore -------( Function ) | |
| visibility | |
| width | |
| x | |
| xDiff | |
| xUp | |
| xv | |
| y | |
| yDiff | |
| yUp | |
| yv |