|
|
你好!做一个带迷雾效果、精美恐怖元素,功能丰富且主界面漂亮的贪吃蛇游戏,确实很有挑战。我这里给你一个示范项目的思路和简化实现代码,帮你入门。
你可以按照下面的例子,逐步完善。另外,声音、动画等资源可能需要自己找合适素材替换。
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <title>恐怖风贪吃蛇游戏</title>
- <style>
- /* 页面整体布局 */
- body {
- background: radial-gradient(circle at center, #0b0b0b 0%, #000000 100%);
- color: #f44336;
- font-family: "Microsoft YaHei", sans-serif;
- margin: 0; padding: 0;
- user-select: none;
- overflow: hidden;
- }
- #main-menu {
- height: 100vh;
- display: flex; flex-direction: column;
- justify-content: center; align-items: center;
- background: url('https://images.unsplash.com/photo-1572072393748-508d14aaa99d?auto=format&fit=crop&w=1950&q=80') no-repeat center center / cover;
- box-shadow: inset 0 0 50px #900;
- }
- h1 {
- font-size: 4em;
- text-shadow: 0 0 15px #f44336;
- margin-bottom: 0.5em;
- letter-spacing: 0.1em;
- }
- .menu-button {
- background: rgba(255, 0, 0, 0.6);
- border: 2px solid #f44336;
- color: #fff;
- padding: 1em 3em;
- margin: 0.5em;
- font-size: 1.5em;
- border-radius: 10px;
- cursor: pointer;
- transition: all 0.3s ease;
- box-shadow: 0 0 10px #f44336;
- user-select: none;
- }
- .menu-button:hover {
- background: rgba(255, 0, 0, 0.9);
- box-shadow: 0 0 20px #ff0000;
- transform: scale(1.05);
- }
- #game-container {
- position: relative;
- margin: auto;
- width: 600px;
- height: 600px;
- background: #111;
- border: 3px solid #f44336;
- box-shadow: 0 0 30px #f44336 inset;
- display: none;
- }
- canvas {
- display: block;
- background: linear-gradient(180deg, #220000, #110000);
- box-shadow: 0 0 30px #900 inset;
- border-radius: 8px;
- }
- /* 迷雾层 */
- #fog-canvas {
- position: absolute;
- top: 0; left: 0;
- pointer-events: none;
- filter: blur(8px);
- }
- /* 状态栏 */
- #status-bar {
- margin-top: 10px;
- color: #f44336;
- font-size: 1.2em;
- text-align: center;
- letter-spacing: 0.05em;
- user-select: none;
- }
- /* 恐怖文字弹窗 */
- #scary-message {
- position: fixed;
- top: 20%;
- left: 50%;
- transform: translateX(-50%);
- background: rgba(0, 0, 0, 0.85);
- border: 3px solid #f44336;
- border-radius: 10px;
- padding: 1em 2em;
- color: #ff4444;
- font-size: 1.8em;
- font-weight: bold;
- text-shadow: 0 0 15px #f44336;
- opacity: 0;
- transition: opacity 0.5s ease;
- pointer-events: none;
- user-select: none;
- max-width: 80vw;
- z-index: 9999;
- }
- /* 游戏皮肤选择 */
- #skin-selector {
- margin-top: 1em;
- display: flex;
- justify-content: center;
- }
- .skin-option {
- width: 40px; height: 40px;
- margin: 0 10px;
- border-radius: 50%;
- border: 2px solid #f44336;
- cursor: pointer;
- box-shadow: 0 0 10px #f44336;
- transition: transform 0.3s ease;
- }
- .skin-option:hover {
- transform: scale(1.3);
- box-shadow: 0 0 20px #ff0000;
- }
- .skin-selected {
- border: 4px solid #ff0000 !important;
- box-shadow: 0 0 30px #ff0000 !important;
- }
- </style>
- </head>
- <body>
- <div id="main-menu">
- <h1>恐怖贪吃蛇</h1>
- <div>
- <button class="menu-button" id="start-normal">普通模式</button>
- <button class="menu-button" id="start-hard">困难模式</button>
- <button class="menu-button" id="start-nightmare">噩梦模式</button>
- </div>
- <div id="skin-selector" title="选择蛇的皮肤">
- <div class="skin-option skin-selected" data-color="#ff2d2d" style="background:#ff2d2d"></div>
- <div class="skin-option" data-color="#00ff66" style="background:#00ff66"></div>
- <div class="skin-option" data-color="#ff00ff" style="background:#ff00ff"></div>
- <div class="skin-option" data-color="#ffaa00" style="background:#ffaa00"></div>
- </div>
- </div>
- <div id="game-container">
- <canvas id="game-canvas" width="600" height="600"></canvas>
- <canvas id="fog-canvas" width="600" height="600"></canvas>
- <div id="status-bar">得分: 0 | 速度: 正常</div>
- </div>
- <div id="scary-message"></div>
- <script>
- (() => {
- const canvas = document.getElementById('game-canvas');
- const ctx = canvas.getContext('2d');
- const fogCanvas = document.getElementById('fog-canvas');
- const fogCtx = fogCanvas.getContext('2d');
- const startNormalBtn = document.getElementById('start-normal');
- const startHardBtn = document.getElementById('start-hard');
- const startNightmareBtn = document.getElementById('start-nightmare');
- const mainMenu = document.getElementById('main-menu');
- const gameContainer = document.getElementById('game-container');
- const statusBar = document.getElementById('status-bar');
- const scaryMessageEl = document.getElementById('scary-message');
- const skinOptions = document.querySelectorAll('.skin-option');
- const GRID_SIZE = 20; // 单元格尺寸20x20
- const WIDTH = canvas.width / GRID_SIZE; // 30格
- const HEIGHT = canvas.height / GRID_SIZE; // 30格
- let snake = [];
- let direction = { x: 1, y: 0 };
- let nextDirection = direction;
- let food = null;
- let traps = [];
- let score = 0;
- let speed = 150; // 毫秒刷新一次
- let gameInterval = null;
- let fogRadius = 6; // 迷雾半径,视野范围
- let isGameOver = false;
- let snakeColor = '#ff2d2d';
- let modeSettings = {
- normal: { trapCount: 5, speed: 150, fogRadius: 6 },
- hard: { trapCount: 10, speed: 120, fogRadius: 5 },
- nightmare: { trapCount: 15, speed: 90, fogRadius: 3 },
- };
- let currentMode = 'normal';
- function showScaryMessage(text) {
- scaryMessageEl.textContent = text;
- scaryMessageEl.style.opacity = '1';
- setTimeout(() => {
- scaryMessageEl.style.opacity = '0';
- }, 2500);
- }
- function rnd(min, max) {
- return Math.floor(Math.random() * (max - min) + min);
- }
- function resetGame() {
- snake = [];
- for (let i = 5; i >= 0; i--) {
- snake.push({ x: i, y: Math.floor(HEIGHT / 2) });
- }
- direction = { x: 1, y: 0 };
- nextDirection = direction;
- score = 0;
- traps = [];
- isGameOver = false;
- // 根据模式调整参数
- const setting = modeSettings[currentMode];
- speed = setting.speed;
- fogRadius = setting.fogRadius;
- generateFood();
- generateTraps(setting.trapCount);
- updateStatus();
- }
- function generateFood() {
- while(true) {
- const pos = { x: rnd(0, WIDTH), y: rnd(0, HEIGHT) };
- if (!isOccupied(pos)) {
- food = pos;
- break;
- }
- }
- }
- function generateTraps(count) {
- traps = [];
- let tries = 0;
- while (traps.length < count && tries < 500) {
- let pos = { x: rnd(0, WIDTH), y: rnd(0, HEIGHT) };
- if (!isOccupied(pos) && !posEqual(pos, food)) {
- traps.push(pos);
- }
- tries++;
- }
- }
- function isOccupied(pos) {
- return snake.some(s => posEqual(s, pos)) || traps.some(t => posEqual(t, pos));
- }
- function posEqual(a, b) {
- return a.x === b.x && a.y === b.y;
- }
- function setDirection(newDir) {
- // 不能直接让蛇倒退
- if (newDir.x === -direction.x && newDir.y === -direction.y) return;
- nextDirection = newDir;
- }
- function draw() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- // 画食物 - 就是鲜红的心脏样子
- ctx.fillStyle = '#ff1744';
- drawHeart(food.x, food.y);
- // 画陷阱 - 用鬼火图案和紫黑色
- traps.forEach(trap => {
- drawGhostFlame(trap.x, trap.y);
- });
- // 画蛇
- ctx.fillStyle = snakeColor;
- snake.forEach((segment, idx) => {
- let alpha = 1.0;
- // 蛇尾部慢慢透明
- if (idx > 0) alpha = Math.max(0.2, 1 - idx / snake.length);
- ctx.globalAlpha = alpha;
- drawRect(segment.x, segment.y);
- ctx.globalAlpha = 1.0;
- });
- // 绘制迷雾,除了蛇头周围 fogRadius 范围内可见
- drawFog();
- }
- function drawRect(x, y) {
- ctx.fillRect(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE -1);
- }
- function drawHeart(x, y) {
- // 用基本图形拼个心脏形状
- const px = x * GRID_SIZE + GRID_SIZE/2;
- const py = y * GRID_SIZE + GRID_SIZE/2;
- ctx.beginPath();
- ctx.moveTo(px, py + 5);
- ctx.bezierCurveTo(px + 12, py - 15, px + 25, py + 10, px, py + 28);
- ctx.bezierCurveTo(px - 25, py + 10, px - 12, py - 15, px, py + 5);
- ctx.fill();
- }
- function drawGhostFlame(x, y) {
- const px = x * GRID_SIZE;
- const py = y * GRID_SIZE;
- // 外层紫黑色底
- ctx.fillStyle = '#330022';
- ctx.beginPath();
- ctx.ellipse(px + GRID_SIZE/2, py + GRID_SIZE/2, GRID_SIZE/2, GRID_SIZE*0.7, 0, 0, 2 * Math.PI);
- ctx.fill();
- // 里层幽蓝色火焰
- let gradient = ctx.createRadialGradient(px + GRID_SIZE/2, py + GRID_SIZE/2, 2, px + GRID_SIZE/2, py + GRID_SIZE/2, GRID_SIZE/2);
- gradient.addColorStop(0, '#66ccff');
- gradient.addColorStop(1, 'transparent');
- ctx.fillStyle = gradient;
- ctx.beginPath();
- ctx.ellipse(px + GRID_SIZE/2, py + GRID_SIZE/2, GRID_SIZE/3, GRID_SIZE/2, 0, 0, 2 * Math.PI);
- ctx.fill();
- }
- function update() {
- if (isGameOver) return;
- direction = nextDirection;
- const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
- // 撞墙判死
- if (head.x < 0 || head.x >= WIDTH || head.y < 0 || head.y >= HEIGHT) {
- gameOver("撞墙了,游戏结束...");
- return;
- }
- // 撞自己判死
- if (snake.some(s => posEqual(s, head))) {
- gameOver("你被自己吞噬了,死亡降临...");
- return;
- }
- // 如果踩陷阱,掉血或减速
- let steppedTrapIndex = traps.findIndex(t => posEqual(t, head));
- if (steppedTrapIndex !== -1) {
- traps.splice(steppedTrapIndex,1);
- score = Math.max(0, score - 5);
- showScaryMessage("陷阱!你的生命在黑暗中流逝...");
- speed += 30; // 速度变慢
- clearInterval(gameInterval);
- gameInterval = setInterval(update, speed);
- }
- snake.unshift(head);
- if (posEqual(head, food)) {
- score++;
- showScaryMessage("你吃掉了鲜血之心,感觉力量汹涌...");
- generateFood();
- generateTraps(modeSettings[currentMode].trapCount);
- // 随机加速或者减速
- if (Math.random() < 0.5) {
- speed = Math.max(50, speed - 20);
- showScaryMessage("你感到一阵狂热,速度提升!");
- } else {
- speed += 20;
- showScaryMessage("血液冰冷,你的动作变缓慢...");
- }
- clearInterval(gameInterval);
- gameInterval = setInterval(update, speed);
- } else {
- snake.pop(); // 不吃食物就尾巴缩短
- }
- updateStatus();
- draw();
- }
- function drawFog() {
- fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height);
- // 先画满黑色半透明
- fogCtx.fillStyle = 'rgba(0, 0, 0, 0.85)';
- fogCtx.fillRect(0,0,fogCanvas.width, fogCanvas.height);
- // 在蛇头四周画透明圆,制造迷雾视野效果
- const head = snake[0];
- let cx = head.x * GRID_SIZE + GRID_SIZE / 2;
- let cy = head.y * GRID_SIZE + GRID_SIZE / 2;
- let radius = fogRadius * GRID_SIZE;
- let gradient = fogCtx.createRadialGradient(cx, cy, radius*0.6, cx, cy, radius);
- gradient.addColorStop(0, 'rgba(0,0,0,0)');
- gradient.addColorStop(1, 'rgba(0,0,0,0.85)');
- fogCtx.globalCompositeOperation = 'destination-out';
- fogCtx.fillStyle = gradient;
- fogCtx.beginPath();
- fogCtx.arc(cx, cy, radius, 0, Math.PI * 2);
- fogCtx.fill();
- fogCtx.globalCompositeOperation = 'source-over';
- }
- function updateStatus() {
- let speedDesc = '';
- if (speed < 80) speedDesc = '极快';
- else if (speed < 120) speedDesc = '快速';
- else speedDesc = '正常';
- statusBar.textContent = `得分: ${score} | 速度: ${speedDesc} | 模式: ${currentMode}`;
- }
- function gameOver(msg) {
- isGameOver = true;
- showScaryMessage(msg);
- clearInterval(gameInterval);
- setTimeout(() => {
- alert(`游戏结束!\n你的最终得分:${score}`);
- mainMenu.style.display = 'flex';
- gameContainer.style.display = 'none';
- }, 1500);
- }
- function startGame(mode) {
- currentMode = mode;
- resetGame();
- mainMenu.style.display = 'none';
- gameContainer.style.display = 'block';
- draw();
- clearInterval(gameInterval);
- gameInterval = setInterval(update, speed);
- }
- window.addEventListener('keydown', e => {
- if (e.key === 'ArrowUp' || e.key === 'w') setDirection({ x: 0, y: -1 });
- if (e.key === 'ArrowDown' || e.key === 's') setDirection({ x: 0, y: 1 });
- if (e.key === 'ArrowLeft' || e.key === 'a') setDirection({ x: -1, y: 0 });
- if (e.key === 'ArrowRight' || e.key === 'd') setDirection({ x: 1, y: 0 });
- });
- startNormalBtn.onclick = () => startGame('normal');
- startHardBtn.onclick = () => startGame('hard');
- startNightmareBtn.onclick = () => startGame('nightmare');
- skinOptions.forEach(el => {
- el.onclick = () => {
- skinOptions.forEach(s => s.classList.remove('skin-selected'));
- el.classList.add('skin-selected');
- snakeColor = el.getAttribute('data-color');
- };
- });
- })();
- </script>
- </body>
- </html>
复制代码
**功能 |
|