💣 3D拆弹三人组 - 随机身份
by FlashPhoenix67534 lines27.0 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>💣 3D拆弹三人组 - 随机身份</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<style>
*{margin:0; padding:0; box-sizing:border-box; font-family:'Microsoft YaHei',sans-serif;}
body{background:#0f172a; color:#fff; overflow:hidden; height:100vh;}
canvas{position:fixed; top:0; left:0; z-index:1;}
.ui-layer{position:relative; z-index:2; pointer-events:none; height:100vh; display:flex; flex-direction:column;}
.ui-layer > *{pointer-events:auto;}
/* 开始界面 */
.start-screen{position:absolute; inset:0; background:rgba(15,23,42,0.95); display:flex; flex-direction:column; align-items:center; justify-content:center; padding:30px; z-index:100;}
.start-box{background:#1e293b; border-radius:20px; padding:35px; max-width:500px; width:100%; border:2px solid #334155; box-shadow:0 0 40px rgba(239,68,68,0.2);}
h1{text-align:center; color:#f87171; font-size:2rem; margin-bottom:10px;}
.subtitle{text-align:center; color:#94a3b8; margin-bottom:25px;}
.role-preview{display:grid; grid-template-columns:repeat(3,1fr); gap:12px; margin-bottom:25px;}
.role-card{background:#334155; border-radius:12px; padding:15px; text-align:center; border:2px solid transparent; transition:0.3s;}
.role-card .emoji{font-size:2.5rem; margin-bottom:8px;}
.role-card .name{font-weight:bold; font-size:1.1rem; margin-bottom:5px;}
.role-card .desc{font-size:0.8rem; color:#94a3b8; line-height:1.4;}
.btn-start{width:100%; padding:16px; font-size:1.2rem; font-weight:bold; background:linear-gradient(90deg,#ef4444,#dc2626); color:#fff; border:none; border-radius:12px; cursor:pointer; transition:0.3s;}
.btn-start:hover{transform:scale(1.03); box-shadow:0 0 20px rgba(239,68,68,0.4);}
.tip{margin-top:15px; text-align:center; color:#64748b; font-size:0.9rem;}
/* 身份分配界面 */
.assign-screen{position:absolute; inset:0; background:rgba(15,23,42,0.95); display:none; flex-direction:column; align-items:center; justify-content:center; z-index:100;}
.assign-box{background:#1e293b; border-radius:20px; padding:40px; text-align:center; border:3px solid #f59e0b; animation:glow 2s infinite;}
@keyframes glow{0%{box-shadow:0 0 20px #f59e0b40;} 50%{box-shadow:0 0 40px #f59e0b80;} 100%{box-shadow:0 0 20px #f59e0b40;}}
.assign-role-emoji{font-size:5rem; margin-bottom:15px;}
.assign-role-name{font-size:1.8rem; font-weight:bold; color:#fbbf24; margin-bottom:10px;}
.assign-role-desc{color:#cbd5e1; max-width:350px; line-height:1.8; margin-bottom:25px;}
.btn-begin{padding:14px 40px; font-size:1.1rem; font-weight:bold; background:linear-gradient(90deg,#10b981,#059669); color:#fff; border:none; border-radius:10px; cursor:pointer;}
/* 游戏UI */
.game-header{padding:15px 20px; display:flex; justify-content:space-between; align-items:center; background:rgba(0,0,0,0.5);}
.role-badge{display:flex; align-items:center; gap:10px; background:rgba(30,41,59,0.8); padding:8px 15px; border-radius:30px; border:2px solid;}
.role-badge.blind{border-color:#facc15; color:#facc15;}
.role-badge.mute{border-color:#a78bfa; color:#a78bfa;}
.role-badge.deaf{border-color:#22d3ee; color:#22d3ee;}
.level-info{font-size:1.1rem; color:#fbbf24;}
.timer-box{font-size:2rem; font-weight:bold; font-family:monospace; color:#fff; text-shadow:0 0 10px #fff;}
.timer-box.warning{color:#fbbf24; text-shadow:0 0 15px #fbbf24;}
.timer-box.danger{color:#ef4444; text-shadow:0 0 15px #ef4444; animation:blink 0.5s infinite;}
@keyframes blink{0%,100%{opacity:1;} 50%{opacity:0.4;}}
/* 提示面板 */
.hint-panel{position:absolute; bottom:0; left:0; right:0; padding:20px; background:linear-gradient(to top, rgba(0,0,0,0.9), transparent);}
.hint-content{background:rgba(30,41,59,0.9); border-radius:12px; padding:18px; margin-bottom:15px; border-left:4px solid;}
.hint-content.blind{border-left-color:#facc15;}
.hint-content.deaf{border-left-color:#22d3ee;}
.hint-title{font-weight:bold; margin-bottom:10px; font-size:1.05rem; display:flex; align-items:center; gap:8px;}
.hint-title.blind{color:#facc15;}
.hint-title.deaf{color:#22d3ee;}
.hint-text{font-family:monospace; font-size:1rem; line-height:1.8; white-space:pre-wrap; color:#e2e8f0;}
/* 电线操作区 */
.wire-control{display:flex; flex-wrap:wrap; gap:12px; margin-bottom:15px; justify-content:center;}
.wire-btn{width:80px; height:50px; border-radius:8px; border:none; font-weight:bold; font-size:1.1rem; cursor:pointer; transition:0.2s; color:#000;}
.wire-btn:hover{transform:scale(1.1);}
.wire-btn.selected{outline:3px solid #fff; box-shadow:0 0 15px #fff;}
.wire-btn:disabled{opacity:0.4; cursor:not-allowed; transform:none;}
.confirm-btn{display:block; width:200px; margin:0 auto; padding:12px; font-size:1.1rem; font-weight:bold; background:linear-gradient(90deg,#22c55e,#16a34a); color:#fff; border:none; border-radius:10px; cursor:pointer;}
.confirm-btn:disabled{opacity:0.4; cursor:not-allowed;}
/* 角色遮罩效果 */
.mask-blind{filter:grayscale(1) contrast(1.2);}
.mask-blind canvas{filter:grayscale(1);}
.mask-deaf .hint-panel .hint-content.blind{display:none !important;}
.mask-mute .wire-control{pointer-events:none; opacity:0.5;}
.mute-note{text-align:center; color:#a78bfa; font-style:italic; margin-top:10px;}
/* 结果弹窗 */
.result-popup{position:absolute; inset:0; background:rgba(0,0,0,0.85); display:none; flex-direction:column; align-items:center; justify-content:center; z-index:200;}
.result-box{background:#1e293b; border-radius:20px; padding:40px; text-align:center; max-width:400px; border:2px solid;}
.result-box.success{border-color:#22c55e;}
.result-box.fail{border-color:#ef4444;}
.result-emoji{font-size:4rem; margin-bottom:15px;}
.result-title{font-size:1.8rem; font-weight:bold; margin-bottom:10px;}
.result-desc{color:#94a3b8; margin-bottom:25px; line-height:1.6;}
.result-btn{padding:12px 30px; font-size:1rem; font-weight:bold; border:none; border-radius:8px; cursor:pointer; margin:5px;}
.btn-next{background:linear-gradient(90deg,#3b82f6,#2563eb); color:#fff;}
.btn-retry{background:linear-gradient(90deg,#f97316,#ea580c); color:#fff;}
.btn-menu{background:#475569; color:#fff;}
</style>
</head>
<body>
<!-- 3D画布 -->
<canvas id="bombCanvas"></canvas>
<!-- 开始界面 -->
<div class="start-screen" id="startScreen">
<div class="start-box">
<h1>💣 3D绝境拆弹</h1>
<p class="subtitle">三人协作 · 随机身份 · 生死与共</p>
<div class="role-preview">
<div class="role-card">
<div class="emoji">👁️🗨️</div>
<div class="name">瞎子</div>
<div class="desc">能看到数字/密码/颜色线索<br>看不到电线与操作按钮</div>
</div>
<div class="role-card">
<div class="emoji">🗣️</div>
<div class="name">哑巴</div>
<div class="desc">能看到全部界面<br>能操作但不能说话打字</div>
</div>
<div class="role-card">
<div class="emoji">👂</div>
<div class="name">耳聋</div>
<div class="desc">能看到电线颜色与顺序<br>看不到文字提示</div>
</div>
</div>
<button class="btn-start" onclick="startGame()">🎲 随机分配身份并开始</button>
<p class="tip">💡 三人各打开一份链接,每人获得不同随机身份,互相配合完成拆弹!</p>
</div>
</div>
<!-- 身份分配界面 -->
<div class="assign-screen" id="assignScreen">
<div class="assign-box">
<div class="assign-role-emoji" id="assignEmoji">🎲</div>
<div class="assign-role-name" id="assignName">分配中...</div>
<div class="assign-role-desc" id="assignDesc"></div>
<button class="btn-begin" onclick="enterGame()">✅ 确认身份,进入游戏</button>
</div>
</div>
<!-- 游戏UI层 -->
<div class="ui-layer" id="gameUI" style="display:none;">
<div class="game-header">
<div class="role-badge" id="roleBadge">
<span id="roleEmoji">👤</span>
<span id="roleName">未知</span>
</div>
<div class="level-info" id="levelInfo">第 1 关 / 10</div>
<div class="timer-box" id="timerBox">60</div>
</div>
<div class="hint-panel">
<!-- 瞎子提示区 -->
<div class="hint-content blind" id="blindHintArea">
<div class="hint-title blind">👁️🗨️ 线索提示</div>
<div class="hint-text" id="blindHint">加载中...</div>
</div>
<!-- 耳聋提示区 -->
<div class="hint-content deaf" id="deafHintArea">
<div class="hint-title deaf">👂 电线布局</div>
<div class="hint-text" id="deafHint">加载中...</div>
</div>
<!-- 哑巴操作区 -->
<div class="wire-control" id="muteControl">
<!-- 动态生成电线按钮 -->
</div>
<button class="confirm-btn" id="confirmBtn" onclick="confirmCut()" disabled>✂️ 确认剪线</button>
<p class="mute-note" id="muteNote" style="display:none;">🤐 你是哑巴 — 只能用手势/表情与队友交流</p>
</div>
</div>
<!-- 结果弹窗 -->
<div class="result-popup" id="resultPopup">
<div class="result-box" id="resultBox">
<div class="result-emoji" id="resultEmoji">🎉</div>
<div class="result-title" id="resultTitle">成功!</div>
<div class="result-desc" id="resultDesc">炸弹已拆除!</div>
<div style="display:flex; gap:10px; justify-content:center; flex-wrap:wrap;">
<button class="result-btn btn-next" id="nextBtn" onclick="nextLevel()">➡️ 下一关</button>
<button class="result-btn btn-retry" onclick="restartLevel()">🔄 重试本关</button>
<button class="result-btn btn-menu" onclick="backToMenu()">🏠 返回菜单</button>
</div>
</div>
</div>
<script>
// ========== 游戏配置 ==========
const ROLES = {
blind: {
name: '瞎子', emoji: '👁️🗨️',
desc: '你能看到数字、密码和颜色线索,但看不到电线和操作按钮。请把线索告诉队友!'
},
mute: {
name: '哑巴', emoji: '🗣️',
desc: '你能看到全部界面,可以操作按钮,但全程不能说话或打字!只能用手势和表情交流!'
},
deaf: {
name: '耳聋', emoji: '👂',
desc: '你能看到电线的颜色和顺序布局,但看不到文字提示。请指挥队友剪哪根线!'
}
};
const COLORS = {
red: '#ff4444', blue: '#0099ff', yellow: '#ffcc00',
green: '#00cc66', purple: '#9933ff', orange: '#ff8800'
};
const COLOR_NAMES = {
red: '红色', blue: '蓝色', yellow: '黄色',
green: '绿色', purple: '紫色', orange: '橙色'
};
const LEVELS = [
{ wires: 4, time: 60, difficulty: '简单' },
{ wires: 4, time: 55, difficulty: '简单' },
{ wires: 4, time: 50, difficulty: '简单' },
{ wires: 5, time: 50, difficulty: '普通' },
{ wires: 5, time: 45, difficulty: '普通' },
{ wires: 5, time: 40, difficulty: '普通' },
{ wires: 6, time: 40, difficulty: '困难' },
{ wires: 6, time: 35, difficulty: '困难' },
{ wires: 6, time: 30, difficulty: '困难' },
{ wires: 6, time: 30, difficulty: '地狱' }
];
// ========== 全局状态 ==========
let gameState = {
role: null, level: 1, timeLeft: 60, timerInterval: null,
correctWire: null, wires: [], selectedWire: null,
isRunning: false, scene: null, camera: null, renderer: null,
bomb: null, wireMeshes: []
};
// ========== 3D场景初始化 ==========
function init3D() {
const canvas = document.getElementById('bombCanvas');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0f172a);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// 灯光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xff0000, 1, 20);
pointLight.position.set(0, 2, 5);
scene.add(pointLight);
// 炸弹主体
const bombGroup = new THREE.Group();
const bodyGeo = new THREE.SphereGeometry(1.5, 32, 32);
const bodyMat = new THREE.MeshPhongMaterial({
color: 0xdc2626, shininess: 100, specular: 0x440000
});
const body = new THREE.Mesh(bodyGeo, bodyMat);
bombGroup.add(body);
// 顶部盖子
const capGeo = new THREE.CylinderGeometry(0.5, 0.8, 0.6, 32);
const capMat = new THREE.MeshPhongMaterial({ color: 0x333333 });
const cap = new THREE.Mesh(capGeo, capMat);
cap.position.y = 1.5;
bombGroup.add(cap);
// 显示屏
const screenGeo = new THREE.BoxGeometry(1, 0.6, 0.1);
const screenMat = new THREE.MeshBasicMaterial({ color: 0x111111 });
const screen = new THREE.Mesh(screenGeo, screenMat);
screen.position.set(0, 0.3, 1.45);
bombGroup.add(screen);
// 电线支架
const wireHolderGeo = new THREE.BoxGeometry(2.5, 0.2, 1);
const wireHolderMat = new THREE.MeshPhongMaterial({ color: 0x222222 });
const wireHolder = new THREE.Mesh(wireHolderGeo, wireHolderMat);
wireHolder.position.y = -1.2;
bombGroup.add(wireHolder);
bombGroup.position.z = -3;
bombGroup.position.y = 0.5;
scene.add(bombGroup);
// 存储引用
gameState.scene = scene;
gameState.camera = camera;
gameState.renderer = renderer;
gameState.bomb = bombGroup;
// 窗口适配
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// 动画循环
function animate() {
requestAnimationFrame(animate);
bombGroup.rotation.y += 0.005; // 缓慢旋转
renderer.render(scene, camera);
}
animate();
}
// 生成3D电线
function generate3DWires(wireData) {
// 清除旧电线
gameState.wireMeshes.forEach(m => gameState.scene.remove(m));
gameState.wireMeshes = [];
wireData.forEach((wire, idx) => {
const colorHex = parseInt(COLORS[wire.color].slice(1), 16);
const wireGeo = new THREE.CylinderGeometry(0.05, 0.05, 1.2, 8);
const wireMat = new THREE.MeshPhongMaterial({ color: colorHex, emissive: colorHex, emissiveIntensity: 0.3 });
const wireMesh = new THREE.Mesh(wireGeo, wireMat);
// 排列位置
const total = wireData.length;
const spacing = 2 / (total + 1);
wireMesh.position.x = -1 + spacing * (idx + 1);
wireMesh.position.y = -1.8;
wireMesh.position.z = -2.8;
wireMesh.rotation.x = Math.PI / 2;
gameState.bomb.add(wireMesh);
gameState.wireMeshes.push(wireMesh);
});
}
// ========== 身份分配 ==========
function pickRandomRole() {
const roles = Object.keys(ROLES);
// 尝试从URL参数获取,确保三人不同 - 简单伪随机但可复现
const params = new URLSearchParams(window.location.search);
let roleIndex = params.get('role');
if (roleIndex !== null && roleIndex >= 0 && roleIndex < 3) {
return roles[parseInt(roleIndex)];
}
// 纯随机
return roles[Math.floor(Math.random() * 3)];
}
function startGame() {
gameState.role = pickRandomRole();
const role = ROLES[gameState.role];
// 显示分配界面
document.getElementById('startScreen').style.display = 'none';
document.getElementById('assignScreen').style.display = 'flex';
document.getElementById('assignEmoji').textContent = role.emoji;
document.getElementById('assignName').textContent = role.name;
document.getElementById('assignDesc').textContent = role.desc;
}
function enterGame() {
document.getElementById('assignScreen').style.display = 'none';
document.getElementById('gameUI').style.display = 'flex';
// 应用角色UI效果
document.body.classList.remove('mask-blind', 'mask-deaf', 'mask-mute');
const role = gameState.role;
// 更新角色徽章
document.getElementById('roleEmoji').textContent = ROLES[role].emoji;
document.getElementById('roleName').textContent = ROLES[role].name;
document.getElementById('roleBadge').className = 'role-badge ' + role;
// 根据角色显示/隐藏内容
if (role === 'blind') {
document.body.classList.add('mask-blind');
document.getElementById('deafHintArea').style.display = 'none';
document.getElementById('muteControl').style.display = 'none';
document.getElementById('confirmBtn').style.display = 'none';
} else if (role === 'deaf') {
document.body.classList.add('mask-deaf');
document.getElementById('blindHintArea').style.display = 'none';
} else if (role === 'mute') {
document.getElementById('blindHintArea').style.display = 'none';
document.getElementById('deafHintArea').style.display = 'none';
document.getElementById('muteNote').style.display = 'block';
}
// 初始化3D
if (!gameState.renderer) init3D();
// 加载第一关
loadLevel();
}
// ========== 关卡逻辑 ==========
function loadLevel() {
const cfg = LEVELS[gameState.level - 1];
if (gameState.timerInterval) clearInterval(gameState.timerInterval);
gameState.timeLeft = cfg.time;
gameState.isRunning = true;
gameState.selectedWire = null;
// 更新UI
document.getElementById('levelInfo').textContent = `第 ${gameState.level} 关 / 10 (${cfg.difficulty})`;
updateTimerDisplay();
// 生成电线数据
generateWiresData(cfg.wires);
// 生成提示
generateHints();
// 重置按钮
document.getElementById('confirmBtn').disabled = true;
document.querySelectorAll('.wire-btn').forEach(b => b.classList.remove('selected'));
// 隐藏结果弹窗
document.getElementById('resultPopup').style.display = 'none';
// 开始计时
startTimer();
}
function generateWiresData(count) {
const colorKeys = Object.keys(COLORS);
const shuffled = [...colorKeys].sort(() => Math.random() - 0.5).slice(0, count);
gameState.wires = shuffled.map((color, idx) => ({
color, idx, isCorrect: idx === Math.floor(Math.random() * count)
}));
gameState.correctWire = gameState.wires.findIndex(w => w.isCorrect);
// 更新3D
generate3DWires(gameState.wires);
// 更新UI按钮(仅哑巴可见)
const container = document.getElementById('muteControl');
container.innerHTML = '';
gameState.wires.forEach((w, i) => {
const btn = document.createElement('button');
btn.className = 'wire-btn';
btn.style.backgroundColor = COLORS[w.color];
btn.textContent = i + 1;
btn.onclick = () => selectWire(i);
container.appendChild(btn);
});
}
function generateHints() {
const idx = gameState.correctWire;
const total = gameState.wires.length;
const targetColor = COLOR_NAMES[gameState.wires[idx].color];
let blindHint = '', deafHint = '';
// 根据关卡难度生成不同提示
if (gameState.level <= 3) {
blindHint = `📍 正确序号:第 ${idx + 1} 根\n🎨 颜色:${targetColor}`;
} else if (gameState.level <= 6) {
const n1 = Math.floor(Math.random() * 5) + 1;
blindHint = `🧮 计算:${n1} × ${idx + 1} ÷ ${n1} = ?\n结果就是序号!\n🎨 颜色:${targetColor}`;
} else if (gameState.level <= 9) {
const parity = (idx + 1) % 2 === 0 ? '偶数' : '奇数';
blindHint = `🔐 序号是 ${parity}\n🔢 序号 + 3 = ${idx + 3}\n🎨 颜色:${targetColor}`;
} else {
const parity = (idx + 1) % 2 === 0 ? '偶数' : '奇数';
const exclude = gameState.wires.filter((_, i) => i !== idx).map(w => COLOR_NAMES[w.color]).join('、');
blindHint = `☠️ 三重线索:\n1️⃣ 序号是 ${parity}\n2️⃣ 颜色:${targetColor}\n3️⃣ 不是:${exclude}`;
}
// 耳聋:仅电线顺序
deafHint = gameState.wires.map((w, i) => `${i+1}.${COLOR_NAMES[w.color]}`).join('\n');
document.getElementById('blindHint').textContent = blindHint;
document.getElementById('deafHint').textContent = deafHint;
}
function selectWire(idx) {
if (!gameState.isRunning || gameState.role !== 'mute') return;
gameState.selectedWire = idx;
document.querySelectorAll('.wire-btn').forEach((b, i) => {
b.classList.toggle('selected', i === idx);
});
document.getElementById('confirmBtn').disabled = false;
}
function confirmCut() {
if (!gameState.isRunning || gameState.selectedWire === null) return;
gameState.isRunning = false;
clearInterval(gameState.timerInterval);
const isSuccess = gameState.selectedWire === gameState.correctWire;
showResult(isSuccess);
}
function showResult(success) {
const popup = document.getElementById('resultPopup');
const box = document.getElementById('resultBox');
box.className = 'result-box ' + (success ? 'success' : 'fail');
document.getElementById('resultEmoji').textContent = success ? '✅' : '💥';
document.getElementById('resultTitle').textContent = success ? '拆弹成功!' : '炸弹爆炸!';
document.getElementById('resultDesc').textContent = success
? `第${gameState.level}关完成!准备迎接下一个挑战。`
: `剪错了电线!正确答案是第${gameState.correctWire + 1}根,${COLOR_NAMES[gameState.wires[gameState.correctWire].color]}。`;
document.getElementById('nextBtn').style.display = success && gameState.level < 10 ? 'inline-block' : 'none';
popup.style.display = 'flex';
}
function startTimer() {
gameState.timerInterval = setInterval(() => {
gameState.timeLeft--;
updateTimerDisplay();
if (gameState.timeLeft <= 0) {
gameState.isRunning = false;
clearInterval(gameState.timerInterval);
showResult(false);
}
}, 1000);
}
function updateTimerDisplay() {
const box = document.getElementById('timerBox');
box.textContent = gameState.timeLeft;
box.classList.remove('warning', 'danger');
if (gameState.timeLeft <= 20) box.classList.add('warning');
if (gameState.timeLeft <= 10) box.classList.add('danger');
}
function nextLevel() {
gameState.level++;
loadLevel();
}
function restartLevel() {
loadLevel();
}
function backToMenu() {
if (gameState.timerInterval) clearInterval(gameState.timerInterval);
document.getElementById('gameUI').style.display = 'none';
document.getElementById('resultPopup').style.display = 'none';
document.getElementById('startScreen').style.display = 'flex';
document.body.classList.remove('mask-blind', 'mask-deaf', 'mask-mute');
gameState.level = 1;
}
</script>
</body>
</html>Game Source: 💣 3D拆弹三人组 - 随机身份
Creator: FlashPhoenix67
Libraries: none
Complexity: complex (534 lines, 27.0 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 3d-flashphoenix67" to link back to the original. Then publish at arcadelab.ai/publish.