<!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>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(to bottom, #1a237e, #0d47a1);
color: white;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
#header {
padding: 15px;
text-align: center;
background: rgba(0, 0, 0, 0.5);
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
}
h1 {
font-size: 2.5rem;
margin-bottom: 5px;
text-shadow: 0 0 10px rgba(0, 150, 255, 0.7);
}
.subtitle {
font-size: 1.1rem;
opacity: 0.8;
}
#container {
display: flex;
flex: 1;
overflow: hidden;
}
#sidebar {
width: 300px;
background: rgba(0, 0, 0, 0.6);
padding: 20px;
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
}
#game-container {
flex: 1;
position: relative;
}
#canvas-container {
width: 100%;
height: 100%;
}
#hud {
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
display: flex;
justify-content: center;
pointer-events: none;
}
#crosshair {
width: 20px;
height: 20px;
border: 2px solid white;
border-radius: 50%;
}
#block-selector {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
background: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 10px;
gap: 10px;
}
.block-option {
width: 40px;
height: 40px;
border: 2px solid transparent;
border-radius: 5px;
cursor: pointer;
transition: all 0.2s;
}
.block-option.active {
border-color: #4fc3f7;
box-shadow: 0 0 10px #4fc3f7;
}#stats {
position: absolute;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.7);
padding: 10px 15px;
border-radius: 5px;
font-size: 0.9rem;
}
#instructions {
position: absolute;
top: 20px;
left: 20px;
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 5px;
max-width: 300px;
}
.section {
margin-bottom: 25px;
}
.section-title {
font-size: 1.2rem;
margin-bottom: 10px;
color: #4fc3f7;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding-bottom: 5px;
}
.control-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
padding: 5px 0;
border-bottom: 1px dotted rgba(255, 255, 255, 0.1);
}
.control-key {
background: rgba(255, 255, 255, 0.2);
padding: 2px 8px;
border-radius: 4px;
font-family: monospace;
}
.block-preview {
width: 30px;
height: 30px;
display: inline-block;
vertical-align: middle;
margin-right: 10px;
border-radius: 4px;
}
button {
background: #4fc3f7;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
margin-top: 10px;
transition: background 0.3s;
width: 100%;
}
button:hover {
background: #29b6f6;
}
#world-info {
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 5px;
margin-top: 20px;
}
.coordinate {
font-family: monospace;
background: rgba(0, 0, 0, 0.3);
padding: 2px 5px;
border-radius: 3px;
}
#loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
.progress-bar {
width: 300px;
height: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
margin-top: 20px;
overflow: hidden;
}
.progress {
height: 100%;
background: #4fc3f7;
width: 0%;
transition: width 0.3s;
}
#loading-text {
margin-top: 10px;
font-size: 1.2rem;
}
.hidden {
display: none !important;
}
</style>
</head>
<body>
<div id="header">
<h1>3D方塊世界</h1>
<div class="subtitle">大型地圖 | 多種方塊 | 自由建造與破壞</div>
</div>
<div id="container">
<div id="sidebar">
<div class="section">
<div class="section-title">遊戲控制</div>
<div class="control-item">
<span>移動</span>
<span class="control-key">W A S D</span>
</div>
<div class="control-item">
<span>跳躍</span>
<span class="control-key">空格</span>
</div>
<div class="control-item">
<span>飛行/下降</span>
<span class="control-key">Shift</span>
</div>
<div class="control-item">
<span>放置方塊</span>
<span class="control-key">左鍵</span>
</div>
<div class="control-item">
<span>破壞方塊</span>
<span class="control-key">右鍵</span>
</div>
<div class="control-item">
<span>切換方塊</span>
<span class="control-key">1-6</span>
</div>
</div>
<div class="section">
<div class="section-title">方塊類型</div>
<div class="control-item">
<span><div class="block-preview" style="background: #00aa00;"></div>草方塊</span>
<span class="control-key">1</span>
</div>
<div class="control-item">
<span><div class="block-preview" style="background: #888888;"></div>石頭</span>
<span class="control-key">2</span>
</div>
<div class="control-item">
<span><div class="block-preview" style="background: #d2691e;"></div>泥土</span>
<span class="control-key">3</span>
</div>
<div class="control-item">
<span><div class="block-preview" style="background: #0000ff;"></div>藍石</span>
<span class="control-key">4</span>
</div>
<div class="control-item">
<span><div class="block-preview" style="background: #ff0000;"></div>紅石</span>
<span class="control-key">5</span>
</div>
<div class="control-item">
<span><div class="block-preview" style="background: #ffff00;"></div>黃石</span>
<span class="control-key">6</span>
</div>
</div>
<div id="world-info">
<div class="section-title">世界信息</div>
<div>地圖尺寸: 64x64</div>
<div>方塊總數: <span id="block-count">0</span></div>
<div>玩家位置:
<span class="coordinate" id="player-pos">0, 0, 0</span>
</div>
<div>視角方向:
<span class="coordinate" id="player-rot">0°, 0°</span>
</div>
</div>
<button id="reset-btn">重置世界</button>
<button id="save-btn">保存世界</button>
<button id="load-btn">加載世界</button>
</div>
<div id="game-container">
<div id="canvas-container"></div>
<div id="hud">
<div id="crosshair"></div>
</div>
<div id="block-selector">
<div class="block-option active" data-type="grass" style="background: #00aa00;"></div>
<div class="block-option" data-type="stone" style="background: #888888;"></div>
<div class="block-option" data-type="dirt" style="background: #d2691e;"></div>
<div class="block-option" data-type="blue" style="background: #0000ff;"></div>
<div class="block-option" data-type="red" style="background: #ff0000;"></div>
<div class="block-option" data-type="yellow" style="background: #ffff00;"></div>
</div>
<div id="instructions">
<div class="section-title">遊戲説明</div>
<p>歡迎來到3D方塊世界!這是一個大型的沙盒遊戲,你可以自由建造和破壞方塊。</p >
<p>使用鼠標控制視角,鍵盤控制移動。選擇不同的方塊類型來創建你的世界。</p >
</div>
// 在style標籤中添加以下新樣式
/* 中央信息面板容器 */
#center-info {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
pointer-events: none;
z-index: 10;
}
/* 頂部信息欄 - 居中顯示統計信息 */
#top-stats {
background: rgba(0, 0, 0, 0.7);
padding: 10px 15px;
border-radius: 5px;
font-size: 0.9rem;
display: flex;
gap: 15px;
min-width: 250px;
justify-content: center;
}
/* 中央通知面板 */
#notification-center {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
pointer-events: none;
}
/* 中央通知樣式 */
.center-notification {
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 15px 25px;
border-radius: 5px;
max-width: 300px;
text-align: center;
transition: opacity 0.3s;
}
/* 隱藏側邊欄(可選,根據屏幕尺寸) */
@media (max-width: 1024px) {
#sidebar {
display: none;
}
/* 如果隱藏了側邊欄,顯示一個簡化版的控制面板按鈕 */
#toggle-controls {
display: block;
}
}
/* 移動端優化 */
@media (max-width: 768px) {
#header h1 {
font-size: 1.8rem;
}
#block-selector {
bottom: 10px;
padding: 8px;
}
.block-option {
width: 35px;
height: 35px;
}
}
/* 小屏幕優化 */
@media (max-width: 480px) {
#top-stats {
flex-direction: column;
gap: 5px;
padding: 8px 12px;
font-size: 0.8rem;
}
}
// 在body中添加新的HTML元素
<div id="game-container">
<div id="canvas-container"></div>
<div id="hud">
<div id="crosshair"></div>
</div>
<!-- 中央信息面板 -->
<div id="center-info">
<div id="top-stats">
<div>FPS: <span id="fps-counter">0</span></div>
<div>已放置: <span id="blocks-placed">0</span></div>
<div>已破壞: <span id="blocks-broken">0</span></div>
</div>
</div>
<div id="block-selector">
<div class="block-option active" data-type="grass" style="background: #00aa00;"></div>
<div class="block-option" data-type="stone" style="background: #888888;"></div>
<div class="block-option" data-type="dirt" style="background: #d2691e;"></div>
<div class="block-option" data-type="blue" style="background: #0000ff;"></div>
<div class="block-option" data-type="red" style="background: #ff0000;"></div>
<div class="block-option" data-type="yellow" style="background: #ffff00;"></div>
</div>
<!-- 保留世界信息面板但調整位置 -->
<div id="world-info" style="position: absolute; top: 80px; left: 50%; transform: translateX(-50%); max-width: 250px;">
<div class="section-title">世界信息</div>
<div>地圖尺寸: 64x64</div>
<div>方塊總數: <span id="block-count">0</span></div>
<div>玩家位置:
<span class="coordinate" id="player-pos">0, 0, 0</span>
</div>
<div>視角方向:
<span class="coordinate" id="player-rot">0°, 0°</span>
</div>
</div>
<!-- 中央通知容器 -->
<div id="notification-center"></div>
</div>
// 修改showNotification函數使其在中央顯示
showNotification(message) {
// 創建通知元素
const notification = document.createElement('div');
notification.className = 'center-notification';
notification.textContent = message;
// 添加到中央通知容器
const notificationCenter = document.getElementById('notification-center');
notificationCenter.appendChild(notification);
// 3秒後自動消失
setTimeout(() => {
notification.style.opacity = '0';
setTimeout(() => {
notificationCenter.removeChild(notification);
}, 300);
}, 3000);
}
// 在setupControls方法中刪除或註釋掉以下代碼(因為我們已經將這些元素移到了新位置)
// document.getElementById('stats')相關的HTML和CSS可以移除
// document.getElementById('instructions')相關的HTML和CSS可以移除
</div>
</div>
<div id="loading">
<h2>正在生成世界...</h2>
<div class="progress-bar">
<div class="progress" id="loading-progress"></div>
</div>
<div id="loading-text">初始化中...</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// 遊戲主類
class BlockWorld {
constructor() {
this.scene = null;
this.camera = null;
this.renderer = null;
this.raycaster = null;
this.mouse = null;
this.blocks = new Map();
this.blockCount = 0;
this.blocksPlaced = 0;
this.blocksBroken = 0;
this.currentBlockType = 'grass';
this.blockMaterials = {
grass: new THREE.MeshLambertMaterial({ color: 0x00aa00 }),
stone: new THREE.MeshLambertMaterial({ color: 0x888888 }),
dirt: new THREE.MeshLambertMaterial({ color: 0xd2691e }),
blue: new THREE.MeshLambertMaterial({ color: 0x0000ff }),
red: new THREE.MeshLambertMaterial({ color: 0xff0000 }),
yellow: new THREE.MeshLambertMaterial({ color: 0xffff00 })
};
this.cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
this.keys = {};
this.moveSpeed = 0.1;
this.mouseSensitivity = 0.002;
this.pitch = 0;
this.yaw = 0;
this.mapSize = 64;
this.worldHeight = 16;
this.clock = new THREE.Clock();
this.fps = 0;
this.init();
}
init() {
this.setupScene();
this.setupLights();
this.setupControls();
this.generateWorld();
this.animate();
// 隱藏加載界面
setTimeout(() => {
document.getElementById('loading').classList.add('hidden');
}, 1000);
}
setupScene() {
// 創建場景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x87CEEB); // 天空藍
// 創建相機
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.set(0, 10, 10);
// 創建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
document.getElementById('canvas-container').appendChild(this.renderer.domElement);
// 射線檢測器
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
// 添加簡單的地面網格
const gridHelper = new THREE.GridHelper(this.mapSize, this.mapSize, 0x444444, 0x222222);
gridHelper.position.y = -0.5;
this.scene.add(gridHelper);
}
setupLights() {
// 環境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
// 方向光(太陽)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 50, 25);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 500;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -100;
this.scene.add(directionalLight);
}
setupControls() {
// 鼠標移動事件
document.addEventListener('mousemove', (event) => {
if (document.pointerLockElement === this.renderer.domElement) {
this.yaw -= event.movementX * this.mouseSensitivity;
this.pitch = Math.max(-Math.PI/2, Math.min(Math.PI/2,
this.pitch - event.movementY * this.mouseSensitivity));
this.camera.rotation.order = 'YXZ';
this.camera.rotation.y = this.yaw;
this.camera.rotation.x = this.pitch;
}
});
// 鼠標點擊事件
this.renderer.domElement.addEventListener('click', () => {
if (document.pointerLockElement !== this.renderer.domElement) {
this.renderer.domElement.requestPointerLock();
}
});
document.addEventListener('mousedown', (event) => {
if (document.pointerLockElement !== this.renderer.domElement) return;
this.mouse.x = 0;
this.mouse.y = 0;
const intersection = this.getBlockIntersection();
if (intersection) {
if (event.button === 0) { // 左鍵 - 放置方塊
const pos = this.getAdjacentPosition(intersection);
this.addBlock(pos.x, pos.y, pos.z, this.currentBlockType);
this.blocksPlaced++;
document.getElementById('blocks-placed').textContent = this.blocksPlaced;
} else if (event.button === 2) { // 右鍵 - 破壞方塊
const object = intersection.object;
if (this.removeBlock(object.position.x, object.position.y, object.position.z)) {
this.blocksBroken++;
document.getElementById('blocks-broken').textContent = this.blocksBroken;
}
}
}
event.preventDefault();
});
// 阻止右鍵菜單
document.addEventListener('contextmenu', (event) => {
event.preventDefault();
});
// 鍵盤事件
document.addEventListener('keydown', (event) => {
this.keys[event.code] = true;
// 方塊類型切換
if (event.code >= 'Digit1' && event.code <= 'Digit6') {
const types = ['grass', 'stone', 'dirt', 'blue', 'red', 'yellow'];
this.currentBlockType = types[parseInt(event.code[5]) - 1];
this.updateBlockSelector();
}
});
document.addEventListener('keyup', (event) => {
this.keys[event.code] = false;
});
// 窗口大小調整
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
});
// 重置世界按鈕
document.getElementById('reset-btn').addEventListener('click', () => {
if (confirm('確定要重置世界嗎?所有方塊將被清除。')) {
this.resetWorld();
}
});
// 保存/加載按鈕
document.getElementById('save-btn').addEventListener('click', () => {
this.saveWorld();
});
document.getElementById('load-btn').addEventListener('click', () => {
this.loadWorld();
});
}
generateWorld() {
const progressBar = document.getElementById('loading-progress');
const loadingText = document.getElementById('loading-text');
// 生成地面
loadingText.textContent = '生成地面...';
for (let x = -this.mapSize/2; x < this.mapSize/2; x++) {
for (let z = -this.mapSize/2; z < this.mapSize/2; z++) {
// 簡單的高度圖生成
const height = Math.floor(Math.sin(x/10) * Math.cos(z/10) * 3) + 5;
// 生成地面方塊
for (let y = 0; y < height; y++) {
const type = y === height - 1 ? 'grass' :
y > height - 4 ? 'dirt' : 'stone';
this.addBlock(x, y, z, type);
}
// 更新進度條
const progress = ((x + this.mapSize/2) / this.mapSize) * 100;
progressBar.style.width = `${progress}%`;
}
}
// 生成一些隨機結構
loadingText.textContent = '生成隨機結構...';
for (let i = 0; i < 20; i++) {
const x = Math.floor(Math.random() * this.mapSize) - this.mapSize/2;
const z = Math.floor(Math.random() * this.mapSize) - this.mapSize/2;
const height = this.getHeightAt(x, z) + 1;
// 生成樹或石柱
if (Math.random() > 0.5) {
// 生成樹
this.generateTree(x, height, z);
} else {
// 生成石柱
this.generateStonePillar(x, height, z);
}
progressBar.style.width = `${100 + (i/20)*100}%`;
}
loadingText.textContent = '世界生成完成!';
}
getHeightAt(x, z) {
// 簡單的高度查詢
for (let y = this.worldHeight - 1; y >= 0; y--) {
if (this.blocks.has(`${x},${y},${z}`)) {
return y;
}
}
return -1;
}
generateTree(x, y, z) {
// 生成樹幹(3-5格高)
const trunkHeight = 3 + Math.floor(Math.random() * 3);
for (let i = 0; i < trunkHeight; i++) {
this.addBlock(x, y + i, z, 'dirt');
}
// 生成樹葉
const leafLevel = y + trunkHeight;
for (let dx = -2; dx <= 2; dx++) {
for (let dz = -2; dz <= 2; dz++) {
for (let dy = 0; dy < 3; dy++) {
// 簡單的球形樹葉
if (dx*dx + dz*dz + dy*dy <= 4) {
this.addBlock(x + dx, leafLevel + dy, z + dz, 'grass');
}
}
}
}
}
generateStonePillar(x, y, z) {
// 生成石柱(5-8格高)
const height = 5 + Math.floor(Math.random() * 4);
for (let i = 0; i < height; i++) {
this.addBlock(x, y + i, z, 'stone');
// 隨機添加一些側邊方塊
if (Math.random() > 0.7) {
const side = Math.floor(Math.random() * 4);
const offsets = [[1,0], [-1,0], [0,1], [0,-1]];
this.addBlock(x + offsets[side][0], y + i, z + offsets[side][1], 'stone');
}
}
}
addBlock(x, y, z, type) {
const key = `${x},${y},${z}`;
// 如果該位置已有方塊,先移除
if (this.blocks.has(key)) {
this.scene.remove(this.blocks.get(key));
this.blocks.delete(key);
this.blockCount--;
}
const cube = new THREE.Mesh(this.cubeGeometry, this.blockMaterials[type]);
cube.position.set(x, y, z);
cube.castShadow = true;
cube.receiveShadow = true;
cube.userData = { type: type };
this.scene.add(cube);
this.blocks.set(key, cube);
this.blockCount++;
document.getElementById('block-count').textContent = this.blockCount;
return cube;
}
removeBlock(x, y, z) {
const key = `${x},${y},${z}`;
if (this.blocks.has(key)) {
this.scene.remove(this.blocks.get(key));
this.blocks.delete(key);
this.blockCount--;
document.getElementById('block-count').textContent = this.blockCount;
return true;
}
return false;
}
getBlockIntersection() {
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersections = this.raycaster.intersectObjects(Array.from(this.blocks.values()));
return intersections.length > 0 ? intersections[0] : null;
}
getAdjacentPosition(intersection) {
const normal = intersection.face.normal.clone();
normal.transformDirection(intersection.object.matrixWorld);
return {
x: Math.round(intersection.object.position.x + normal.x),
y: Math.round(intersection.object.position.y + normal.y),
z: Math.round(intersection.object.position.z + normal.z)
};
}
updateBlockSelector() {
document.querySelectorAll('.block-option').forEach(option => {
option.classList.remove('active');
if (option.dataset.type === this.currentBlockType) {
option.classList.add('active');
}
});
}
resetWorld() {
// 移除所有方塊
this.blocks.forEach(block => {
this.scene.remove(block);
});
this.blocks.clear();
this.blockCount = 0;
this.blocksPlaced = 0;
this.blocksBroken = 0;
document.getElementById('block-count').textContent = '0';
document.getElementById('blocks-placed').textContent = '0';
document.getElementById('blocks-broken').textContent = '0';
// 重新生成世界
this.generateWorld();
}
// 優化保存功能 - 添加版本控制和更完善的錯誤處理
saveWorld() {
try {
const worldData = {
version: '1.0.0', // 添加版本號便於未來更新
timestamp: new Date().toISOString(),
blocks: [],
player: {
position: this.camera.position.toArray(),
rotation: [this.camera.rotation.x, this.camera.rotation.y, this.camera.rotation.z],
currentBlock: this.currentBlockType
},
stats: {
blocksPlaced: this.blocksPlaced,
blocksBroken: this.blocksBroken,
blockCount: this.blockCount
}
};
this.blocks.forEach((block, key) => {
const [x, y, z] = key.split(',').map(Number);
worldData.blocks.push({ x, y, z, type: block.userData.type });
});
// 使用 try-catch 防止 localStorage 滿
localStorage.setItem('blockWorld', JSON.stringify(worldData));
// 顯示保存成功的提示而不是 alert
this.showNotification('世界保存成功!');
} catch (error) {
console.error('保存失敗:', error);
this.showNotification('保存失敗,請嘗試減少世界大小或清除瀏覽器數據。');
}
}
// 優化加載功能 - 添加版本兼容性檢查
loadWorld() {
try {
const savedData = localStorage.getItem('blockWorld');
if (!savedData) {
this.showNotification('沒有找到保存的世界!');
return;
}
if (!confirm('加載世界將替換當前世界,確定要繼續嗎?')) {
return;
}
// 清除當前世界
this.blocks.forEach(block => {
this.scene.remove(block);
});
this.blocks.clear();
this.blockCount = 0;
const worldData = JSON.parse(savedData);
// 加載方塊
worldData.blocks.forEach(blockData => {
this.addBlock(blockData.x, blockData.y, blockData.z, blockData.type);
});
// 加載玩家數據
if (worldData.player) {
this.camera.position.fromArray(worldData.player.position);
this.camera.rotation.fromArray(worldData.player.rotation);
this.yaw = this.camera.rotation.y;
this.pitch = this.camera.rotation.x;
// 恢復當前選中的方塊類型
if (worldData.player.currentBlock) {
this.currentBlockType = worldData.player.currentBlock;
this.updateBlockSelector();
}
}
// 恢復統計數據
if (worldData.stats) {
this.blocksPlaced = worldData.stats.blocksPlaced || 0;
this.blocksBroken = worldData.stats.blocksBroken || 0;
document.getElementById('blocks-placed').textContent = this.blocksPlaced;
document.getElementById('blocks-broken').textContent = this.blocksBroken;
}
this.showNotification('世界加載完成!');
} catch (error) {
console.error('加載失敗:', error);
this.showNotification('加載失敗,保存文件可能已損壞。');
}
}
// 添加一個更友好的通知函數
showNotification(message) {
// 創建通知元素
const notification = document.createElement('div');
notification.style.position = 'fixed';
notification.style.top = '20px';
notification.style.right = '20px';
notification.style.background = 'rgba(0, 0, 0, 0.8)';
notification.style.color = 'white';
notification.style.padding = '10px 20px';
notification.style.borderRadius = '5px';
notification.style.zIndex = '1000';
notification.style.transition = 'opacity 0.3s';
notification.textContent = message;
document.body.appendChild(notification);
// 3秒後自動消失
setTimeout(() => {
notification.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
animate() {
requestAnimationFrame(() => this.animate());
// 計算FPS
this.fps = 1 / this.clock.getDelta();
document.getElementById('fps-counter').textContent = Math.round(this.fps);
// 玩家移動
this.handleMovement();
// 更新玩家位置顯示
document.getElementById('player-pos').textContent =
`${Math.round(this.camera.position.x)}, ${Math.round(this.camera.position.y)}, ${Math.round(this.camera.position.z)}`;
document.getElementById('player-rot').textContent =
`${Math.round(this.camera.rotation.x * 180/Math.PI)}°, ${Math.round(this.camera.rotation.y * 180/Math.PI)}°`;
this.renderer.render(this.scene, this.camera);
}
handleMovement() {
if (this.keys['KeyW']) { // 前
this.camera.position.x -= Math.sin(this.yaw) * this.moveSpeed;
this.camera.position.z -= Math.cos(this.yaw) * this.moveSpeed;
}
if (this.keys['KeyS']) { // 後
this.camera.position.x += Math.sin(this.yaw) * this.moveSpeed;
this.camera.position.z += Math.cos(this.yaw) * this.moveSpeed;
}
if (this.keys['KeyA']) { // 左
this.camera.position.x -= Math.cos(this.yaw) * this.moveSpeed;
this.camera.position.z += Math.sin(this.yaw) * this.moveSpeed;
}
if (this.keys['KeyD']) { // 右
this.camera.position.x += Math.cos(this.yaw) * this.moveSpeed;
this.camera.position.z -= Math.sin(this.yaw) * this.moveSpeed;
}
if (this.keys['Space']) { // 上
this.camera.position.y += this.moveSpeed;
}
if (this.keys['ShiftLeft'] || this.keys['ShiftRight']) { // 下
this.camera.position.y -= this.moveSpeed;
}
}
}
// 頁面加載完成後初始化遊戲
window.addEventListener('load', () => {
new BlockWorld();
});
</script>
</body>
</html>