3D我的世界触屏版
by CosmicBear11242 lines7.7 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no">
<style>
*{margin:0;padding:0;box-sizing:border}
body{overflow:hidden;background:#87ceeb;font-family:system-ui}
#gameCanvas{display:block;width:100vw;height:100vh}
/* 左下角摇杆容器 */
#joystickBox{position:fixed;left:24px;bottom:24px;width:130px;height:130px;background:rgba(0,0,0,0.35);border-radius:50%;touch-action:none}
#joystickStick{position:absolute;width:52px;height:52px;background:#fff;border-radius:50%;left:50%;top:50%;transform:translate(-50%,-50%)}
/* 右侧功能按钮区 */
#btnGroup{position:fixed;right:20px;bottom:24px;display:flex;flex-direction:column;gap:14px}
.gameBtn{width:72px;height:72px;border:none;border-radius:50%;font-size:22px;font-weight:bold;color:#fff}
#jumpBtn{background:#32cd32}
#crouchBtn{background:#ffb90f}
#attackBtn{background:#ff4444}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<!-- 移动摇杆 -->
<div id="joystickBox">
<div id="joystickStick"></div>
</div>
<!-- 右侧按钮:跳跃、蹲下、攻击 -->
<div id="btnGroup">
<button class="gameBtn" id="attackBtn">⚔</button>
<button class="gameBtn" id="jumpBtn">跳</button>
<button class="gameBtn" id="crouchBtn">蹲</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script>
// ========== 全局游戏数据 ==========
let scene, camera, renderer, worldBlocks = [];
// 玩家参数
const player = {
pos: new THREE.Vector3(0, 2.2, 0),
vel: new THREE.Vector3(0, 0, 0),
yaw: 0,
height: 1.8,
crouchHeight: 0.9,
isCrouch: false,
onGround: false,
speed: 4.2,
gravity: -18,
jumpForce: 7.5
};
// 摇杆控制
const joystick = { active: false, originX: 0, originY: 0, dx:0, dy:0 };
// 按键状态
const keys = { jump:false, crouch:false, attack:false };
// ========== 初始化3D场景 ==========
function init3D(){
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb);
scene.fog = new THREE.Fog(0x87ceeb, 12, 60);
// 相机(第一人称MC视角)
camera = new THREE.PerspectiveCamera(72, window.innerWidth/window.innerHeight, 0.1, 200);
updateCameraPos();
// 渲染器
renderer = new THREE.WebGLRenderer({canvas:document.getElementById("gameCanvas"), antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// 光照
const sunLight = new THREE.DirectionalLight(0xffffff, 1.1);
sunLight.position.set(20, 35, 15);
sunLight.castShadow = true;
scene.add(sunLight);
scene.add(new THREE.AmbientLight(0xffffff, 0.4));
// 生成MC方块地形
generateWorld();
// 绑定触屏事件
bindTouchControls();
// 游戏循环
requestAnimationFrame(gameLoop);
}
// ========== 生成草地/泥土/石头方块世界 ==========
function generateWorld(){
const blockSize = 1;
const grassMat = new THREE.MeshLambertMaterial({color:0x62a843});
const dirtMat = new THREE.MeshLambertMaterial({color:0x966c4a});
const stoneMat = new THREE.MeshLambertMaterial({color:0x888888});
const blockGeo = new THREE.BoxGeometry(blockSize, blockSize, blockSize);
// 地面大片草地
for(let x=-16;x<=16;x++){
for(let z=-16;z<=16;z++){
const grass = new THREE.Mesh(blockGeo, grassMat);
grass.position.set(x,0,z);
grass.receiveShadow = true;
scene.add(grass);
worldBlocks.push(grass);
// 地下泥土层
const dirt = new THREE.Mesh(blockGeo, dirtMat);
dirt.position.set(x,-1,z);
dirt.receiveShadow = true;
scene.add(dirt);
worldBlocks.push(dirt);
}
}
// 随机石头障碍
for(let i=0;i<32;i++){
const stone = new THREE.Mesh(blockGeo, stoneMat);
const ox = (Math.random()-0.5)*22;
const oz = (Math.random()-0.5)*22;
stone.position.set(ox, 1, oz);
stone.castShadow = true;
stone.receiveShadow = true;
scene.add(stone);
worldBlocks.push(stone);
}
}
// ========== 更新相机跟随玩家 ==========
function updateCameraPos(){
const eyeHeight = player.isCrouch ? player.crouchHeight : player.height;
camera.position.copy(player.pos).add(0, eyeHeight, 0);
camera.rotation.order = "YXZ";
camera.rotation.y = player.yaw;
}
// ========== 触屏摇杆+按钮绑定 ==========
function bindTouchControls(){
const joyBox = document.getElementById("joystickBox");
const joyStick = document.getElementById("joystickStick");
const jumpBtn = document.getElementById("jumpBtn");
const crouchBtn = document.getElementById("crouchBtn");
const attackBtn = document.getElementById("attackBtn");
// 摇杆按下
joyBox.addEventListener("touchstart", e=>{
e.preventDefault();
const t = e.touches[0];
const rect = joyBox.getBoundingClientRect();
joystick.active = true;
joystick.originX = rect.left + rect.width/2;
joystick.originY = rect.top + rect.height/2;
})
// 摇杆拖动
joyBox.addEventListener("touchmove", e=>{
e.preventDefault();
if(!joystick.active) return;
const t = e.touches[0];
joystick.dx = t.clientX - joystick.originX;
joystick.dy = t.clientY - joystick.originY;
const maxRadius = joyBox.offsetWidth/2 - joyStick.offsetWidth/2;
const dist = Math.hypot(joystick.dx, joystick.dy);
if(dist > maxRadius){
joystick.dx = joystick.dx / dist * maxRadius;
joystick.dy = joystick.dy / dist * maxRadius;
}
joyStick.style.transform = `translate(calc(-50% + ${joystick.dx}px), calc(-50% + ${joystick.dy}px))`;
})
// 摇杆松开重置
joyBox.addEventListener("touchend", ()=>{
joystick.active = false;
joystick.dx = 0; joystick.dy = 0;
joyStick.style.transform = "translate(-50%, -50%)";
})
// 跳跃按钮
jumpBtn.addEventListener("touchstart",()=>keys.jump=true);
jumpBtn.addEventListener("touchend",()=>keys.jump=false);
// 蹲下按钮(长按持续蹲下)
crouchBtn.addEventListener("touchstart",()=>keys.crouch=true);
crouchBtn.addEventListener("touchend",()=>keys.crouch=false);
// 攻击按钮(点击挥刀)
attackBtn.addEventListener("touchstart",()=>{
keys.attack = true;
setTimeout(()=>keys.attack=false,180);
})
}
// ========== 游戏主循环 ==========
let lastTime = 0;
function gameLoop(time){
const dt = Math.min((time-lastTime)/1000, 0.08);
lastTime = time;
// 1. 摇杆控制移动+转向
if(joystick.active){
const moveMag = Math.hypot(joystick.dx, joystick.dy) / 64;
const angle = Math.atan2(joystick.dx, joystick.dy);
player.yaw += angle * dt * 1.8;
const forward = new THREE.Vector3(0,0,-1).applyEuler(new THREE.Euler(0,player.yaw,0));
const side = new THREE.Vector3(1,0,0).applyEuler(new THREE.Euler(0,player.yaw,0));
player.pos.add(forward.multiplyScalar(moveMag * player.speed * dt));
}
// 2. 蹲下切换高度
player.isCrouch = keys.crouch;
// 3. 跳跃(仅落地时生效)
if(keys.jump && player.onGround){
player.vel.y = player.jumpForce;
player.onGround = false;
}
// 4. 重力下落
player.vel.y += player.gravity * dt;
player.pos.y += player.vel.y * dt;
// 5. 地面碰撞检测
player.onGround = false;
if(player.pos.y <= 1){
player.pos.y = 1;
player.vel.y = 0;
player.onGround = true;
}
// 6. 攻击挥刀提示(屏幕闪烁白色)
if(keys.attack) renderer.setClearColor(0xffffff, 0.3);
else renderer.setClearColor(0x87ceeb, 1);
// 更新相机+渲染画面
updateCameraPos();
renderer.render(scene, camera);
requestAnimationFrame(gameLoop);
}
// 窗口自适应
window.addEventListener("resize",()=>{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
})
// 启动游戏
init3D();
</script>
</body>
</html>
Game Source: 3D我的世界触屏版
Creator: CosmicBear11
Libraries: three
Complexity: complex (242 lines, 7.7 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-cosmicbear11" to link back to the original. Then publish at arcadelab.ai/publish.