React/ReactThreeFiber

Edit Mode 구현 (1) - 모드 변경 GUI, 상태 관리 시스템 구축

최 수빈 2025. 3. 14. 16:57

 

Edit Mode (편집 모드) 구현

 

1. GUI 추가 : 사용자가 모드를 변경할 수 있도록 Canvas 위에 버튼 추가

2. 상태 관리 :  EditMode를 관리하는 상태 관리 시스템 구축 (React Context API 활용)

 

 

상태 관리 (EditContext) 설정

 

context 폴더를 만들고 EditContext.jsx 파일 추가

 

EditContext.jsx

import { createContext, useState } from "react";

export const EditContext = createContext(); // createContext 함수를 사용, EditContext 생성

export const EditProvider = ({ children }) => {
  const [isEditMode, setEditMode] = useState(false);

  return (
    <EditContext.Provider value={{ isEditMode, setEditMode }}>
      {children}
    </EditContext.Provider>
  );
};

isEditMode: 편집 모드 여부를 저장하는 상태

setEditMode: Edit Mode를 켜고 끄는 함수

 

 

App에 EditProvider 적용

App.jsx에서 Canvas EditProvider로 감싸 전역 상태를 관리

→ Environments 컴포넌트 안에서 useContext를 사용해 EditContext에 접근 가능

 

App.jsx

import { Canvas } from "@react-three/fiber";
import "./App.css";
import { Environments } from "./components/Environments";
import { EditProvider } from "./context/EditContext";
import { Overlay } from "./components/overlay/Overlay";

function App() {
  return (
    <EditProvider>
      <Canvas camera={{ position: [0, 0, 500] }}>
        <Environments />
      </Canvas>
      <Overlay />
    </EditProvider>
  );
}

export default App;

EditProvider를 추가하여 EditContext의 상태를 Canvas 및 UI에서 사용 가능하도록 설정

 

 

Edit Mode UI 구현

icons 폴더 생성, icons 파일 추가

components/overlay/Overlay.jsx 파일 생성 후 버튼 추가

 

 

Overlay.jsx

import { useContext } from "react";
import { EditIcon } from "../icons/EditIcon";
import { EditContext } from "../../context/EditContext";

export const Overlay = () => {
  const { setEditMode } = useContext(EditContext);

  return (
    <div className="overlay">
      <EditIcon onClick={() => setEditMode((prev) => !prev)} />
    </div>
  );
};

EditIcon을 클릭하면 Edit Mode 활성화/비활성화

 

 

Grid Helper 추가

isEditMode true일 때만 gridHelper를 보여줌

 

Environments.jsx

import { OrbitControls } from "@react-three/drei";
import { Animal } from "./Animal";
import { ZooMap } from "./ZooMap";
import { Dino } from "./Dino";
import { Suspense, useContext } from "react";
import { Physics, RigidBody } from "@react-three/rapier";
import { EditContext } from "../context/EditContext";
import { useFrame, useThree } from "@react-three/fiber";

const START_Y = 20;

export const Environments = () => {
  const { isEditMode } = useContext(EditContext);
  const { camera } = useThree();

  useFrame(() => {
    if (isEditMode) {
      camera.position.set(0, 500, 0);
    }
  });

  return (
    <>
      {isEditMode && <gridHelper args={[500, 50]} position={[0, START_Y, 0]} />}
      <ambientLight intensity={4} />
      <directionalLight intensity={4} position={[3, 3, 3]} />
      <OrbitControls />

      <Suspense>
        <Physics>
          <RigidBody type="fixed" colliders={"trimesh"}>
            <ZooMap />
          </RigidBody>
          <RigidBody lockRotations colliders={"hull"}>
            <Animal name={"Alpaca"} position={[-15, START_Y, 0]} />
          </RigidBody>
          <RigidBody enabledRotations={[false, false, false]} colliders={"hull"}>
            <Dino name={"Triceratops"} position={[10, START_Y, 0]} />
          </RigidBody>
        </Physics>
      </Suspense>
    </>
  );
};

useFrame을 사용해 Edit Mode일 때 카메라를 위에서 내려다보도록 변경

gridHelper를 추가하여 객체 배치가 용이하도록 설정

 

 

Edit Mode 스타일 적용

App.css

html,
body,
#root {
  margin: 0;
  height: 100%;
  width: 100%;
}

.overlay {
  width: max-content;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 24px;
  position: absolute;
  top: 0;
  right: 100px;
  z-index: 99;
  justify-content: center;
  gap: 16px;
  box-sizing: border-box;
}

overlay를 사용해 Edit Mode 버튼을 화면 우측 배치

 


 

✔️ Edit Mode를 활성화하면 gridHelper 표시

✔️ Edit Mode 버튼을 클릭하면 모드가 토글

✔️ Edit Mode일 때 카메라가 위에서 내려다보도록 조정

 

 

Edit Mode Button