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

如何在React中实现确定性3D骰子?整合dice-box-threejs或基于r3f重构

在React中实现确定性3D骰子投掷的两种方案

你不需要从零重构,可以直接在React项目中集成dice-box-threejs,也可以选择基于react-three-fiber(r3f)重构核心逻辑,以下是具体实现路径:

方案一:直接集成dice-box-threejs到React

dice-box-threejs是原生Three.js库,但可以通过React的生命周期钩子完成集成,步骤如下:

  1. 安装依赖
    先安装库本身及依赖的Three.js:

    npm install dice-box-threejs three
    
  2. 在React组件中初始化实例
    使用useRef存储dice-box实例,通过useEffect在组件挂载时初始化,同时处理卸载时的清理:

    import React, { useRef, useEffect } from 'react';
    import DiceBox from 'dice-box-threejs';
    
    const DiceContainer = () => {
      const containerRef = useRef(null);
      const diceBoxRef = useRef(null);
    
      useEffect(() => {
        if (!containerRef.current) return;
    
        // 初始化dice-box实例
        diceBoxRef.current = new DiceBox(containerRef.current, {
          scale: 10,
          gravity: -9.8,
        });
    
        // 执行确定性投掷:指定骰子类型、目标点数、初始状态
        const rollDice = async () => {
          const result = await diceBoxRef.current.roll({
            dice: [
              {
                type: 'd6',
                result: 5, // 预设朝上点数
                position: { x: 0, y: 10, z: 0 },
                rotation: { x: 0, y: 0, z: 0 },
                velocity: { x: 2, y: 0, z: 3 },
              },
            ],
          });
          console.log('投掷结果:', result);
        };
    
        rollDice();
    
        // 组件卸载时销毁实例,避免内存泄漏
        return () => {
          if (diceBoxRef.current) {
            diceBoxRef.current.destroy();
          }
        };
      }, []);
    
      return <div ref={containerRef} style={{ width: '800px', height: '600px' }} />;
    };
    
    export default DiceContainer;
    
  3. 注意事项

    • dice-box会自行管理Three.js的渲染循环,无需和r3f的渲染逻辑冲突,只需给它独立的DOM容器即可
    • 如果需要和r3f场景整合,可以将dice-box的scene对象合并到r3f的场景中,但需注意相机、灯光等资源的协调

方案二:基于react-three-fiber重构核心逻辑

如果需要深度整合React生态(比如用r3f的hooks、状态管理,或和其他3D组件联动),可以基于r3f重构,核心要点:

  1. 依赖准备
    使用r3f的物理引擎绑定库@react-three/cannon处理物理模拟:

    npm install @react-three/fiber @react-three/drei @react-three/cannon three
    
  2. 核心实现步骤

    • 骰子模型:复用dice-box的3D模型(可从其源码中提取),或用@react-three/drei的基础几何体构建
    • 确定性物理模拟:
      • 给CannonProvider设置固定的seed值,确保物理模拟结果一致
      • 为每个骰子刚体设置精确的初始位置、旋转角度、线性/角速度,反向推导这些参数以匹配目标点数(可参考dice-box的源码逻辑)
    • 点数检测:在物理模拟停止后,通过骰子的旋转角度计算朝上的面,确保与预设值匹配

    简化示例:

    import React, { useRef, useEffect } from 'react';
    import { Canvas } from '@react-three/fiber';
    import { Physics, useBox } from '@react-three/cannon';
    import { OrbitControls } from '@react-three/drei';
    
    const D6Dice = ({ position, rotation, velocity, targetResult }) => {
      const meshRef = useRef(null);
      const [ref] = useBox(() => ({
        mass: 1,
        position,
        rotation,
        velocity,
        args: [1, 1, 1], // 骰子碰撞盒尺寸
      }));
    
      // 模拟停止后检测点数,确保匹配目标值
      useEffect(() => {
        const checkResult = () => {
          if (!meshRef.current) return;
          // 此处参考dice-box的点数检测逻辑,根据骰子旋转计算朝上的面
          // 若结果不符,可微调初始参数重新模拟
        };
        const interval = setInterval(checkResult, 100);
        return () => clearInterval(interval);
      }, [targetResult]);
    
      return (
        <mesh ref={(node) => { ref(node); meshRef.current = node; }}>
          <boxGeometry args={[1, 1, 1]} />
          <meshStandardMaterial color="#ffffff" />
          {/* 添加点数纹理,此处省略 */}
        </mesh>
      );
    };
    
    const DiceScene = () => {
      return (
        <Canvas camera={{ position: [0, 5, 10] }}>
          <ambientLight />
          <pointLight position={[10, 10, 10]} />
          <Physics seed={123}> {/* 固定种子确保确定性 */}
            {/* 地面 */}
            <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -2, 0]}>
              <planeGeometry args={[20, 20]} />
              <meshStandardMaterial color="#888888" />
              <useBox args={[20, 0.1, 20]} mass={0} />
            </mesh>
            {/* 预设点数为5的骰子 */}
            <D6Dice
              position={[0, 5, 0]}
              rotation={[0, 0, 0]}
              velocity={[2, 0, 3]}
              targetResult={5}
            />
          </Physics>
          <OrbitControls />
        </Canvas>
      );
    };
    
    export default DiceScene;
    
  3. 关键细节

    • 反向推导初始参数需要参考dice-box中“根据目标点数计算初始状态”的逻辑,确保物理模拟结束后骰子朝上的面符合预期
    • 固定物理引擎的种子是实现确定性的核心,@react-three/cannon的Physics组件支持通过seed参数设置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 01:47:06