🎮ArcadeLab

3D我的世界触屏版

by CosmicBear11
242 lines7.7 KB🛠️ Three.js (3D graphics)
▶ Play
<!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.