[물리엔진 자동차(5)]

JAMEe_·2024년 7월 5일

R3F

목록 보기
19/24

페이지 진입 모션 만들기

TODO

자동차 외에 물체들에 framer-motion-3d 로 모션을 주고,
모든 모션이 마무리 되면 자동차가 나오도록 구현

HOW?

motion 태그로 만든 요소는 다음 이벤트들을 사용할 수 있음

onUpdate
애니메이션 진행중 지속적으로 호출됨
onAnimationStart
애니메이션이 시작될 때 호출됨
onAnimationComplete
애니메이션이 완료될 때 호출됨

// Tree.jsx
// 나무가 아래에서 위로 나타나는 애니메이션
<motion.mesh
   animate={{ scale: [0, 0.2], y: [-0.5, 0] }}
   transition={{ delay: 1, duration: 0.3 }}
   // 이 이벤트가 끝나면 전역상태 true
   onAnimationComplete={() => setStart(true)}
   geometry={nodes.tree.geometry}
   material={materials["Material.003"]}
   position={[0, 0, 0]}
   rotation={[-1.555, 0, 0]}
/>

// Scene.jsx
{isStart && <Car/>}

물체 충돌 시 UI 팝업 띄우기

TODO

공이나 박스들과 같은 오브젝트가 아닌 오직 자동차가 특정 물체에 충돌했을때에만 팝업을 띄워야함.

HOW?

먼저 알아야 되는 물리엔진 충돌 그룹 개념.
다양한 객체 간의 상호 작용을 관리하고 제어
예를 들어, 유저(흰색), 적(검정), 그리고 안전장치(노란색)이 있다고 가정해보자
이미지의 흰색은 노랑 벽을 통과할 수 있지만, 검정은 노랑벽을 통과하지 못함
이처럼 물리엔진 상호작용 그룹을 나누어서 특정 그룹의 Mesh 끼리만 충돌 가능하게 설정 가능

1️⃣collisionFilterGroup 속성 사용하기
이 속성은 앞선 자동차 차체와 바퀴를 결합했을 때에도 사용했다
1 이 기본값 0 으로 줄 시 모든 오브젝트 충돌에 대해 무시함

예를 들어 차체의 useCompoundBody 에 collisionFilterGroup 을 5 로 주면
이 차체와 부딪힌 모든 오브젝트들의 onClide 이벤트에서 collisionFilterGroup 5 와 부딪혔다는걸 알 수 있다

2️⃣ref 를 넣어준 컴포넌트에 name 값을 주어서 판별
마찬가지로 부딪힌 모든 오브젝트들의 onClide 이벤트에서 알 수 있음

<group ref={chassisBody} name="hi">

클릭시 페이지 이동 배너 만들기

TODO

일반 material 에 texturemap 을 부여해서 mesh 에 이미지를 입혀주고, 클릭 시 새탭에 페이지 띄워주도록

HOW?

drei 의 useTexture 훅을 사용하여 material 의 map 속성에 부여

const texture = useTexture(url)
<meshStandardMaterial map={texture} />

// 여러개의 url 을 넣을 수도 있음
const props = useTexture({metalnessMap:url1,  map:url2})

특정 지역 진입 시 이벤트 발생시키기

TODO

아래의 이미지처럼 원 안에 접근했을 시 이벤트를 발생시키고 싶다

HOW?

먼저, 기준인 차량의 Vector3 position 이 필요하다.
이전 시간에 카메라를 차량에 고정시키기 위해 사용했던 worldPosition 을 사용해도 되고, 새로 설정해주어도 된다.

chassisBody.current.getWorldPosition(worldPosition)

// worldPosition 과 chasissPosition 은 동일하다

const chassisPosition = new Vector3().setFromMatrixPosition(
      chassisBody.current.matrixWorld
)

이제 이 값으로 원의 위치와 비교하여 차량이 원에 접근했는지 알 수 있다.
절댓값을 이용해 값의 범위를 설정해줄 것이다.
첫 진입점 + 원의 반지름 더한 값이 원의 중앙점.
중앙점에서 절댓값으로 원의 반지름값과 비교.
원의 반지름: a
첫 진입점: b
끝지점: c
Math.abs(b + a - chassisBody.position) < a

if (
  Math.abs(3 - worldPosition.x) < 0.7 &&
  Math.abs(4.9 - worldPosition.z) < 0.7
) {
      setStage1(true);
} else {
      setStage1(false);
}

특정 지역 Box Drop 시키기

TODO

사각 박스 안에 진입 시 랜덤 포지션에 랜덤 컬러 박스들을 Drop

HOW?

특정 지역 진입 이벤트는 앞서 정의했던대로 하면 되고,
여기서 주요 개념은 useFrame 내에서 시간을 기준으로 반복문을 처리한다.

useFrame(({ clock }) => {
   // 경과된 시간
   // 시간을 기준으로 잡기 때문에 컴퓨터의 사양에 따라 결과가 다르게 나오지 않는다
   // 1초에 60번 실행
   const elapsedTime = Math.floor(clock.getElapsedTime());
   if (flood && elapsedTime % 5 === 0) {
     addRandomBox();
   }
});
profile
안녕하세요

0개의 댓글