You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js中通过按钮实现3D房间对象位置轮换的技术求助

实现方案

核心思路

  1. 先保存所有房间的初始位置快照,避免后续位置修改影响原始坐标的获取
  2. 针对两个按钮的偏移需求,通过索引取模运算实现循环位置映射:
    • +1按钮:每个房间移动到下一个房间的初始位置,最后一个房间回到第一个的初始位置
    • +2按钮:每个房间移动到隔一个房间的初始位置,最后两个房间依次填补前两个的位置

具体代码实现(基于你的PartOne组件修改)

import React, { useRef, useEffect } from "react";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";

export default function PartOne() {
  const { scene } = useThree();
  const roomsRef = useRef([]); // 存储所有房间的mesh实例引用
  const roomInitialPositions = useRef([]); // 存储所有房间的初始位置快照

  // 创建单个房间的工具函数
  const createRoom = (posX, posZ, index) => {
    const geometry = new THREE.BoxGeometry(5, 5, 5);
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
    const roomMesh = new THREE.Mesh(geometry, material);
    roomMesh.position.set(posX, 0, posZ);
    roomsRef.current[index] = roomMesh;
    scene.add(roomMesh);
  };

  // 初始化房间与位置快照
  useEffect(() => {
    // 按顺序创建ROOM1到ROOM8,对应索引0-7
    createRoom(-10, 10, 0);
    createRoom(0, 10, 1);
    createRoom(10, 10, 2);
    createRoom(10, 0, 3);
    createRoom(10, -10, 4);
    createRoom(0, -10, 5);
    createRoom(-10, -10, 6);
    createRoom(-10, 0, 7);

    // 保存初始位置(必须克隆,避免引用同步修改)
    roomInitialPositions.current = roomsRef.current.map(room => room.position.clone());

    // 组件卸载时清理场景
    return () => {
      roomsRef.current.forEach(room => scene.remove(room));
    };
  }, [scene]);

  // +1按钮点击逻辑
  const handleShiftPlus1 = () => {
    const offset = 1;
    roomsRef.current.forEach((room, idx) => {
      // 计算目标位置索引:(当前索引+偏移量) 对房间总数取模,实现循环
      const targetPosIndex = (idx + offset) % 8;
      const targetPosition = roomInitialPositions.current[targetPosIndex];
      // 直接赋值位置(如需平滑动画,可替换为GSAP/Tween.js)
      room.position.copy(targetPosition);
    });
  };

  // +2按钮点击逻辑
  const handleShiftPlus2 = () => {
    const offset = 2;
    roomsRef.current.forEach((room, idx) => {
      const targetPosIndex = (idx + offset) % 8;
      const targetPosition = roomInitialPositions.current[targetPosIndex];
      room.position.copy(targetPosition);
    });
  };

  return (
    <div style={{ position: "absolute", top: 20, left: 20, zIndex: 100 }}>
      <button 
        onClick={handleShiftPlus1} 
        style={{ marginRight: 12, padding: "8px 16px", fontSize: 16 }}
      >
        increase + 1
      </button>
      <button 
        onClick={handleShiftPlus2} 
        style={{ padding: "8px 16px", fontSize: 16 }}
      >
        increase + 2
      </button>
    </div>
  );
}

关键细节说明

  • 位置快照的必要性:用room.position.clone()而不是直接存room.position,因为Three.js的Vector3是引用类型,直接存储会导致后续修改房间位置时,快照数组里的坐标也跟着变化,无法获取原始位置。
  • 循环映射的实现:(idx + offset) % 8是核心逻辑,通过取模运算让索引超出范围后自动回到开头,实现8个房间的循环位置替换。
  • 平滑动画扩展:如果需要更流畅的移动效果,可以引入GSAP库,将room.position.copy(targetPosition)替换为:
    import gsap from "gsap";
    // ...
    gsap.to(room.position, {
      x: targetPosition.x,
      y: targetPosition.y,
      z: targetPosition.z,
      duration: 0.6,
      ease: "power2.inOut"
    });
    

内容的提问来源于stack exchange,提问作者Sayedul Karim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:05:19