Files
yangbear 0a1fce450c fix: 清理AI遗留代码,修复场景配置
- 删除三套旧菜单系统 (MainMenu.cs, MainMenuUI.cs, ModeSelectionUI.cs)
- Ground 添加 BoxCollider 并设置 tag 为 Ground
- UIManager.OnReturnClicked 改为通过 PageController 返回首页
- PageController 增加 ReturnToHome 方法,管理 BtnFixationBack 可见性
- 清理废弃场景 Experiment.unity / ModeSelection.unity 及历史截图
2026-06-25 04:37:45 +08:00

366 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const express = require('express');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 8080;
const ROOT = __dirname;
// 首页 —— 全屏启动页面
// 必须在静态文件中间件之前注册,否则 express.static 会优先返回 index.html
app.get('/', (req, res) => {
res.send(`<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>运动伤害现场急救虚拟仿真实验</title>
<link rel="shortcut icon" href="TemplateData/favicon.ico">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #231F20;
color: #fff;
overflow: hidden;
width: 100vw;
height: 100vh;
height: 100dvh;
}
/* ========== 启动封面 ========== */
#landing {
position: fixed; inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, #2a2526 0%, #1a1516 70%, #0d0a0b 100%);
z-index: 100;
transition: opacity 0.5s ease;
}
#landing.hidden {
opacity: 0;
pointer-events: none;
}
#landing .logo-area {
margin-bottom: 60px;
text-align: center;
}
#landing .logo-area .icon {
width: 120px;
height: 120px;
margin: 0 auto 30px;
background: url('TemplateData/unity-logo-light.png') no-repeat center;
background-size: contain;
filter: drop-shadow(0 0 30px rgba(255,255,255,0.15));
}
#landing h1 {
font-size: clamp(24px, 4vw, 42px);
font-weight: 700;
letter-spacing: 0.08em;
color: #fff;
text-shadow: 0 2px 20px rgba(255,255,255,0.2);
}
#landing .subtitle {
margin-top: 12px;
font-size: clamp(14px, 1.8vw, 18px);
color: rgba(255,255,255,0.45);
letter-spacing: 0.15em;
}
#btn-start {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 16px 56px;
font-size: clamp(16px, 2vw, 20px);
font-weight: 600;
letter-spacing: 0.1em;
color: #fff;
background: linear-gradient(135deg, #4a90d9 0%, #357abd 100%);
border: none;
border-radius: 50px;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 8px 32px rgba(74, 144, 217, 0.35);
}
#btn-start:hover {
transform: translateY(-2px) scale(1.03);
box-shadow: 0 12px 40px rgba(74, 144, 217, 0.5);
}
#btn-start:active {
transform: translateY(0) scale(0.98);
}
#btn-start .arrow {
display: inline-block;
font-size: 1.2em;
transition: transform 0.2s ease;
}
#btn-start:hover .arrow {
transform: translateX(4px);
}
#landing .hint {
margin-top: 40px;
font-size: 13px;
color: rgba(255,255,255,0.25);
letter-spacing: 0.05em;
}
/* ========== Unity 容器 (默认隐藏) ========== */
#unity-container {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
height: 100dvh;
display: none;
background: #231F20;
}
#unity-container.active {
display: block;
}
#unity-canvas {
width: 100% !important;
height: 100% !important;
background: #231F20;
display: block;
}
/* ========== 加载进度条 ========== */
#unity-loading-bar {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: none;
text-align: center;
z-index: 10;
}
#unity-loading-bar.show { display: block; }
#unity-logo {
width: 154px; height: 130px;
margin: 0 auto;
background: url('TemplateData/unity-logo-dark.png') no-repeat center;
}
#unity-progress-bar-empty {
width: 141px; height: 18px;
margin: 10px auto 0;
background: url('TemplateData/progress-bar-empty-dark.png') no-repeat center;
}
#unity-progress-bar-full {
width: 0%; height: 18px;
background: url('TemplateData/progress-bar-full-dark.png') no-repeat center;
}
#unity-footer {
position: absolute;
bottom: 16px;
left: 16px;
right: 16px;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 10;
}
#unity-webgl-logo {
width: 204px; height: 38px;
background: url('TemplateData/webgl-logo.png') no-repeat center;
}
#unity-build-title {
color: rgba(255,255,255,0.5);
font-family: arial, sans-serif;
font-size: 14px;
}
#unity-fullscreen-button {
cursor: pointer;
width: 38px; height: 38px;
background: url('TemplateData/fullscreen-button.png') no-repeat center;
}
#unity-warning {
position: absolute; left: 50%; top: 5%;
transform: translate(-50%);
background: white; color: #333;
padding: 10px; display: none; z-index: 20;
}
</style>
</head>
<body>
<!-- ===== 启动封面 ===== -->
<div id="landing">
<div class="logo-area">
<div class="icon"></div>
<h1>运动伤害现场急救虚拟仿真实验</h1>
<p class="subtitle">VIRTUAL SIMULATION EXPERIMENT</p>
</div>
<button id="btn-start" onclick="enterFullscreen()">
进入全屏实验
<span class="arrow">→</span>
</button>
<p class="hint">点击按钮进入全屏沉浸式实验环境</p>
</div>
<!-- ===== Unity 容器 ===== -->
<div id="unity-container">
<canvas id="unity-canvas" tabindex="-1"></canvas>
<div id="unity-loading-bar">
<div id="unity-logo"></div>
<div id="unity-progress-bar-empty">
<div id="unity-progress-bar-full"></div>
</div>
</div>
<div id="unity-warning"></div>
<div id="unity-footer">
<div id="unity-webgl-logo"></div>
<div id="unity-fullscreen-button"></div>
<div id="unity-build-title">运动伤害现场急救虚拟仿真实验</div>
</div>
</div>
<script>
var container = document.querySelector("#unity-container");
var canvas = document.querySelector("#unity-canvas");
var loadingBar = document.querySelector("#unity-loading-bar");
var progressBarFull = document.querySelector("#unity-progress-bar-full");
var fullscreenButton = document.querySelector("#unity-fullscreen-button");
var warningBanner = document.querySelector("#unity-warning");
var landing = document.querySelector("#landing");
function unityShowBanner(msg, type) {
function updateBannerVisibility() {
warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
}
var div = document.createElement('div');
div.innerHTML = msg;
warningBanner.appendChild(div);
if (type == 'error') div.style = 'background: red; padding: 10px;';
else {
if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
setTimeout(function() {
warningBanner.removeChild(div);
updateBannerVisibility();
}, 5000);
}
updateBannerVisibility();
}
var buildUrl = "Build";
var loaderUrl = buildUrl + "/Build.loader.js";
var config = {
dataUrl: buildUrl + "/Build.data",
frameworkUrl: buildUrl + "/Build.framework.js",
codeUrl: buildUrl + "/Build.wasm",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "运动伤害现场急救虚拟仿真实验",
productVersion: "0.1.0",
showBanner: unityShowBanner,
};
var unityInstance = null;
// ========== 进入全屏 ==========
function enterFullscreen() {
var el = document.documentElement;
var requestFS = el.requestFullscreen || el.webkitRequestFullscreen
|| el.msRequestFullscreen || el.mozRequestFullScreen;
if (requestFS) {
requestFS.call(el).then(function() {
startUnity();
}).catch(function(err) {
// 全屏被拒绝(例如用户取消),仍然启动 Unity
console.warn('全屏请求失败,以普通模式启动:', err);
startUnity();
});
} else {
// 不支持全屏 API 的浏览器
startUnity();
}
}
// ========== 启动 Unity ==========
function startUnity() {
// 隐藏封面
landing.classList.add('hidden');
// 显示 Unity 容器
container.classList.add('active');
loadingBar.classList.add('show');
// 加载 Unity
var script = document.createElement("script");
script.src = loaderUrl;
script.onload = function() {
createUnityInstance(canvas, config, function(progress) {
progressBarFull.style.width = 100 * progress + "%";
}).then(function(instance) {
unityInstance = instance;
loadingBar.classList.remove('show');
// 全屏按钮
fullscreenButton.onclick = function() {
unityInstance.SetFullscreen(1);
};
}).catch(function(message) {
alert('加载失败: ' + message);
});
};
document.body.appendChild(script);
}
// ========== 监听全屏退出 ==========
document.addEventListener('fullscreenchange', function() {
if (!document.fullscreenElement && unityInstance) {
canvas.style.width = '100%';
canvas.style.height = '100%';
}
});
// ========== 防止意外滚动 ==========
document.addEventListener('gesturestart', function(e) { e.preventDefault(); });
</script>
</body>
</html>`);
});
// 静态文件服务
app.use(express.static(ROOT, {
setHeaders: (res, filePath) => {
// 确保 .wasm 有正确的 MIME typeChrome 需要)
if (filePath.endsWith('.wasm')) {
res.setHeader('Content-Type', 'application/wasm');
}
if (filePath.endsWith('.js')) {
res.setHeader('Content-Type', 'application/javascript');
}
}
}));
// 启动服务
app.listen(PORT, () => {
console.log('');
console.log(' ╔══════════════════════════════════════════════════╗');
console.log(' ║ 运动伤害现场急救虚拟仿真实验 - WebGL Server ║');
console.log(' ║ ║');
console.log(' ║ Server running at: http://localhost:' + PORT + ' ║');
console.log(' ║ Press Ctrl+C to stop ║');
console.log(' ╚══════════════════════════════════════════════════╝');
console.log('');
});