우리가 만든 자동차는 엔진이 두개의 바퀴에 힘을 주는 2륜 자동차이다
앞바퀴에 힘을 주는것 => 전륜 구동
뒷바퀴에 힘을 주는것 => 후륜 구동
이 중 구현할 자동차는 후륜 구동이며 앞바퀴는 방향을 담당한다
앞서 차체와 바퀴를 결합하는 데 사용한 useRaycastVehicle 의 api 를 사용하여 물리엔진을 줄 수 있다
value: 차체에 힘을 주는 값
idx: 어떤 바퀴로 힘을 줄지 결정 (후륜구동이므로 2,3 번)
value: 브레이크 걸어주는 값 ( 0 ~ 1 클수록 세게 브레이크 )
idx: 어떤 바퀴에 브레이크를 걸지 결정
value: 바퀴를 꺾는 정도 (radian)
idx: 어떤 바퀴인지
차량이 슬라이딩중인지 여부
위 속성들을 이용해서 자동차를 제어해보자
// 키 이벤트
useEffect(() => {
const keyDownPressHandler = (e) => {
setControls((controls) => ({
...controls,
[e.key]: true,
}));
};
const keyUpPressHandler = (e) => {
setControls((controls) => ({
...controls,
[e.key]: false,
}));
};
window.addEventListener("keydown", keyDownPressHandler);
window.addEventListener("keyup", keyUpPressHandler);
return () => {
window.removeEventListener("keydown", keyDownPressHandler);
window.removeEventListener("keyup", keyUpPressHandler);
};
}, []);
// 키에 따른 자동차 엔진 설정
useEffect(() => {
if (controls.ArrowUp) {
vehicleApi.applyEngineForce(120, 2);
vehicleApi.applyEngineForce(120, 3);
} else if (controls.ArrowDown) {
vehicleApi.applyEngineForce(-120, 2);
vehicleApi.applyEngineForce(-120, 3);
} else {
vehicleApi.applyEngineForce(0, 2);
vehicleApi.applyEngineForce(0, 3);
}
if (controls.ArrowLeft) {
vehicleApi.setSteeringValue(-0.1, 0);
vehicleApi.setSteeringValue(-0.1, 1);
vehicleApi.setSteeringValue(0.35, 2);
vehicleApi.setSteeringValue(0.35, 3);
} else if (controls.ArrowRight) {
vehicleApi.setSteeringValue(0.1, 0);
vehicleApi.setSteeringValue(0.1, 1);
vehicleApi.setSteeringValue(-0.35, 2);
vehicleApi.setSteeringValue(-0.35, 3);
} else {
for (let i = 0; i < 4; i++) {
vehicleApi.setSteeringValue(0, i);
}
}
}, [controls, vehicleApi, chassisApi]);
차체인 useCompoundBody 의 rotation 을 바꿔주면된다
여기서 바퀴는 자동으로 차체 방향으로 따라오므로 신경 쓸 필요 없다
정적 물체, 움직이지 않는 물체
주로 화면상 움직이지 않는 요소 (벽, 바닥 등) 에 사용
물체는 물리 시뮬레이션에 참여하지만, 중력이나 다른 외력에 의해 움직이지 않음
운동 물체, 사용자가 직접 제어하는 물체
주로 플레이어 캐릭터나 움직이는 플랫폼과 같은 요소에 사용
물리 시뮬레이션에 참여하면서 중력이나 다른 외력에 의해 움직이지 않고, 사용자가 직접 제어할 수 있음
동적 물체, 물리 시뮬레이션에 의해 움직이는 물체
주로 공, 상자 등 중력이나 다른 외력에 의해 영향을 받는 요소에 사용
동작원리
특정 포지션을 가리키고 있는 카메라를 생성하고, 해당 카메라의 포지션을 차체의 포지션과 동일하게 설정해준다
먼저 알아야 되는 Obejct3D
three.js 에서 모든 3D 객체의 부모 클래스이다
mesh, 라이트, 카메라 등 다양한 장면에 배치될 수 있는 모든 객체의 기초가 된다
THREE.Mesh 는 object3D 를 상속받아 좀 더 구체적인 3D 객체를 만들 수 있게 해준다
시작해보기
Object3D 로 pivot 변수를 생성해주고, pivot 에 camera 를 add 해준다 ( 이로써 pivot 은 카메라 속성을 상속받음 )
const { scene, camera } = useThree();
const pivot = useMemo(() => new Object3D(), []);
pivot.add(camera);
// 씬에 있는 모든 camera 의 Helper
// pivot 이 여기 포함되어 보여지는 이유는 상속받아서
const helper = new CameraHelper(camera);
scene.add(helper);
scene.add(pivot);
이제 pivot 카메라의 위치를 자동차 차체에 고정시켜주어야 한다
정확히 말하자면, 자동차 차체를 따라가는 pivot 카메라의 position 을 선형 보간을 통해 설정해주어야 한다
선형 보간이란?
1차원 직선상에서 두 점의 값이 주어졌을 떄 그 사이의 값을 추정하기위해
직선 거리에 따라 선형적으로 계산(비례식)하는 방법

const worldPosition = useMemo(() => new Vector3(), []);
const makeFollowCam = () => {
// 차의 위치를 알기위한 함수
// 이 함수는 선언만 해주어도 인자인 worldPosition 에 차체의 위치가 저장됨
chassisBody.current.getWorldPosition(worldPosition)
// 선형보간으로 pivot 과 worldPosition 의 중간값을 찾는데 사용
// 두번째 인자로 보간 비율 0 ~ 1 ( 1을 선언할 경우 pivot position 이 worldPosition 과 같아짐 )
// 두번째 인자는 카메라가 따라오는 속도라 봐도 된다. 차체와 카메라 사이의 거리가 멀어질 수록 비율에 대한 거리값이 증가하여서.
pivot.position.lerp(worldPosition, 1);
}
useFrame(() => {
makeFollowCam();
});
