トマシープが学ぶ

Unity/VR/AR/デザイン好きのミーハー 記事内容は自分用のメモです

R3Fでのオブジェクトの配置がつらい→解決ラッパー!

R3Fで空間にオブジェクトを配置するとき、x,y,zを直接値で指定しないといけない。

XとZ、どっちがどの方向かもわからないし、微調整も大変;;

UnityみたいにGizmoで動かして、その値を取得して、コピペして設定みたいにしたい。

GizmoHelper

XとYの空間の方向はGizmoHelperでわかるようになった。

dreiのstorybookが死んでる

drei.docs.pmnd.rs

これを開発の時だけ表示するにはprocess.env.NODE_ENV === "development"でできた

zenn.dev

PivotControl

DreiにPivotControlとかTransformControlなどそれっぽいものがある

drei.docs.pmnd.rs

pivot。大きさとかも変えれる

Transformのほうは位置しか変えれない?

drei.docs.pmnd.rs

Pivotを使えば良さそう。

<PivotControls>で囲めばなんでもいい感じにGizmoは出た。

OrbitControlでのドラッグ動作とバッティングすると思ったが、サンプルを参考に

 <OrbitControls makeDefault>

にしたらバッティングしなくなった。

enableZoomとかにしているとだめだった

 

ただこの値を受け取るにはどうしたらいいんだろう~

AIに頼んでもこのdiscussion通りにしてもうまくいかない;;

github.com

動かした後の値が表示されない。エラーが出る

追記 解決!

Claude sonnetにお願いしたら無事できた!

"use client";
import { useState, ReactNode } from "react";
import * as THREE from "three";
import { PivotControls, Html } from "@react-three/drei";

interface PivotControlsWrapperProps {
  children?: ReactNode;
}

const PivotControlsWrapper: React.FC<PivotControlsWrapperProps> = ({
  children,
}) => {
  // PivotControlsの位置と回転を管理するstate
  const [markerPosition, setMarkerPosition] = useState<
    [number, number, number]
  >([0, 0, 0]);
  const [markerRotation, setMarkerRotation] = useState<
    [number, number, number]
  >([0, 0, 0]);
  const [markerScale, setMarkerScale] = useState<[number, number, number]>([
    1, 1, 1,
  ]);

  // コピー成功時のフィードバック用state
  const [copySuccess, setCopySuccess] = useState(false);

  // 位置、回転、スケールの情報をクリップボードにコピーする関数
  const copyTransformToClipboard = () => {
    const transformText = `position={[${markerPosition[0].toFixed(2)}, ${markerPosition[1].toFixed(2)}, ${markerPosition[2].toFixed(2)}]}\nscale={[${markerScale[0].toFixed(2)}, ${markerScale[1].toFixed(2)}, ${markerScale[2].toFixed(2)}]}\nrotation={[${markerRotation[0].toFixed(2)}, ${markerRotation[1].toFixed(2)}, ${markerRotation[2].toFixed(2)}]}`;

    navigator.clipboard
      .writeText(transformText)
      .then(() => {
        // コピー成功時のフィードバック
        setCopySuccess(true);
        // 2秒後にフィードバックを非表示
        setTimeout(() => setCopySuccess(false), 2000);
      })
      .catch((err) => {
        console.error("クリップボードへのコピーに失敗しました:", err);
      });
  };

  return (
    <PivotControls
      autoTransform
      scale={100}
      lineWidth={2}
      fixed
      onDrag={(matrix) => {
        // matrixから位置、回転、スケールを抽出
        const position = new THREE.Vector3();
        const quaternion = new THREE.Quaternion();
        const scale = new THREE.Vector3();
        matrix.decompose(position, quaternion, scale);

        // quaternionからオイラー角に変換
        const euler = new THREE.Euler().setFromQuaternion(quaternion);

        // stateを更新
        setMarkerPosition([position.x, position.y, position.z]);
        setMarkerRotation([euler.x, euler.y, euler.z]);
        setMarkerScale([scale.x, scale.y, scale.z]);
      }}
    >
      <group>
        {/* ここに設定したいオブジェクトを位置0、回転0、大きさ1で置く */}
        {children}
        <Html
          position={[1.2, 0, 0]}
          style={{
            color: "white",
            backgroundColor: "rgba(0,0,0,0.7)",
            padding: "10px",
            borderRadius: "5px",
            width: "200px",
            // pointerEvents: "none", // マウスイベントを通過させる
          }}
        >
          <div style={{ fontSize: "12px", fontFamily: "monospace" }}>
            <div>位置:</div>
            <div>X: {markerPosition[0].toFixed(3)}</div>
            <div>Y: {markerPosition[1].toFixed(3)}</div>
            <div>Z: {markerPosition[2].toFixed(3)}</div>
            <div style={{ marginTop: "5px" }}>回転 (rad):</div>
            <div>X: {markerRotation[0].toFixed(3)}</div>
            <div>Y: {markerRotation[1].toFixed(3)}</div>
            <div>Z: {markerRotation[2].toFixed(3)}</div>
            <div style={{ marginTop: "5px" }}>スケール:</div>
            <div>X: {markerScale[0].toFixed(3)}</div>
            <div>Y: {markerScale[1].toFixed(3)}</div>
            <div>Z: {markerScale[2].toFixed(3)}</div>
            <div style={{ marginTop: "10px" }}>
              <button
                onClick={copyTransformToClipboard}
                style={{
                  backgroundColor: copySuccess ? "#4CAF50" : "#007BFF",
                  color: "white",
                  border: "none",
                  padding: "5px 10px",
                  borderRadius: "4px",
                  cursor: "pointer",
                  fontSize: "12px",
                  width: "100%",
                  transition: "background-color 0.3s",
                }}
              >
                {copySuccess ? "コピー完了!" : "位置・回転・スケールをコピー"}
              </button>
            </div>
          </div>
        </Html>
      </group>
    </PivotControls>
  );
};

export default PivotControlsWrapper;

dreiのHTMLでリアルタイムに位置、回転、スケールを表示。

コピーボタンを押したら以下のような感じでコピーできる

position={[0.00, 1.97, 0.00]}
scale={[1.00, 1.02, 1.00]}
rotation={[0.00, 0.00, 0.00]}

使い方はこんな感じ。

          <PivotControlsWrapper>
        何かオブジェクトを位置0、回転0、大きさ1でここに置く
          </PivotControlsWrapper>