대망의 아바타 옷 꾸미기 기능!
const groupRef = useRef(props);
//deco 추가하는 함수
const putDecoGltf = (gltfPath, setScale, positionX, positionY, positionZ, type) => {
const gltfLoader = new GLTFLoader();
gltfLoader.load(gltfPath, (childGltf) => {
const model = childGltf.scene;
model.scale.set(setScale, setScale, setScale); // 모델 크기 조정
model.position.set(positionX, positionY, positionZ); // 모델 위치 설정
groupRef.current.add(model);
});
}
gltfLoader.load(mouthGltfPaht, (childGltf) => {
const model = childGltf.scene;
model.scale.set(1.1, 1.1, 1.1); // 자식 모델 크기 조정
model.position.set(0,-0.04,0); // 자식 모델 위치 설정
groupRef.current.add(model);
});
나는 이렇게 생긴 gltfLoader.load
함수를 만들어서 gltf 파일을 불러와 Canvas
에 넣어주고 있다.
나는 아바타 꾸미기 기능을 만들고 있기 때문에 불러와야 하는 gltf 파일이 많았다,, 그래서 함수로 만들어서 로드하고 있었음.
그리고 아바타 사이즈 조절을 하는데 아바타+옷+악세서리 등이 함께 사이즈 조절, 위치 조절이 되어야 하기 때문에 groupRef로 그룹에 추가해서 묶어주는데,, 이건 나중에 포스팅 하겠음. 오늘 목적은 그게 아님.
슈 옷입히기 게임이라도 해보신 분들은 알겠지만 '상의'를 누르면 상의가 바뀌고 '하의'를 누르면 하의가 바뀌여야 한다.
gltfLoader.load로 불러오기만 하면 불러온 옷 위에 옷이 덧씌워지고, 덧씌워지고, 덧씌워지고...
처음에는 아무것도 입히지 않은 아바타를 매번 다시 불러오면 되지 않나? 했는데 그럼 로딩도 길어지고, 무엇보다 load도 똑같이 다시 불러와지기 때문에 소용이 없었다.
이 길로 계속 밀고 나가기엔 '상의' 파트에서는 상의만 사라지고 새로운 상의가 올라와야 하는데 저렇게 하면 다른 하의, 악세서리 등도 사라질 것 같아서.
역시 특정 gltf를 없애는 코드를 작성해야겠다고 생각했다.
그룹에 추가하는 형식으로 gltf를 load하기 때문에 그룹에서 빼면 된다고 생각했다.
내가 할일은 다음과 같다.
- GLTF remove & 그룹에서 빼기
- 눈, 입, 상의, 하의 등 특정 type을 가진 GLTF 조회하기 (이것만 삭제해야 하므로)
생각보다 적지?~??!?
groupRef.current.remove(model);
위 코드는 model 객체를 groupRef에서 제거한다.
일단 내가 삭제할 객체들은 groupRef의 자식들이므로 자식 객체를 지우면 되지 않나...
groupRef.current.remove(groupRef.current.children[0]);
ㅋㅋ
ㅋ
이러면 올라간 gltf가 랜덤으로 지워짐
이때 glft에 type을 넣어야겠다고 생각했다. 나는 삭제할 model 객체가 딱 정해진 게 아니었다.
매 순간마다 그저 '상의' 혹은 '하의' type을 가진 객체를 조회해서 해당 객체만 삭제해야 했다.
//deco 초기화하는 함수
const removeDecoGltf = (type) => {
const childToRemove = groupRef.current.children.find(
(child) => child.userData.type === type
);
if (childToRemove) {
groupRef.current.remove(childToRemove);
}
childToRemove
변수에 userData.type
가 type
과 일치하는 gltf를 저장한다.
이 gltf는 groupRef 그룹에 속해 있는 자식들 중 하나임
그리고 childToRemove
가 존재하면 지운다!
type
과 일치하는지 살펴보려면 gltf가 type을 가지고 있어야 한다. (당연함)
참고로 type
은 var itemTypeArray = ['eye', 'mouth', '상의', '하의', '한벌의상'];
이거임.
이 배열을 돌면서 일치하는지 검사한다... 내가 클릭한 의상의 type이 뭔지는 서버에서 넘어온다.
프론트에서 할일은,
//deco 추가하는 함수
const putDecoGltf = (gltfPath, setScale, positionX, positionY, positionZ, type) => {
const gltfLoader = new GLTFLoader();
gltfLoader.load(gltfPath, (childGltf) => {
const model = childGltf.scene;
model.scale.set(setScale, setScale, setScale); // 모델 크기 조정
model.position.set(positionX, positionY, positionZ); // 모델 위치 설정
model.userData.type = type; // 원하는 타입 값을 설정
itemTypeArray.map((n)=>{
if(type === n){
//기존에 있고, 현재 모델의 타입과 일치하는 타입의 모델 지우기
removeDecoGltf(type)
}
})
groupRef.current.add(model);
});
}
userData 객체에 type 속성을 추가한 후, 원하는 타입 값을 설정했다.
userData는 Three.js의 3D 객체에 사용자 정의 데이터를 저장할 수 있는 속성입니다. 이를 이용하여 특정 자식 객체의 속성을 저장하고 사용할 수 있습니다.
여기서 중요한 점은, Three.js의 userData 속성은 단순한 JavaScript 객체이므로, 어떤 속성이든 저장할 수 있습니다. 따라서 type 외에도 다양한 사용자 정의 데이터를 userData에 저장할 수 있습니다.
라고 한다.
잘 돌아가서 만족스럽군.........
ItemBox.js
에서 썸네일 box 클릭 -> AvatarDeco.js
의 isClick 함수
호출문제는 itemBox와 AvatarDeco 사이에 Item.js가 하나 더 있음. 원래는 자식 -> 부모 간의 데이터 전송으로 하려고 했지만 자식 부모간은 괜찮지만 손자와 할아버지? 이건 복잡한 문제가 생기는 거거든요.
그냥 외부함수 호출 (할아버지 외부인 취급) 로 갔더니 간편하고 좋았다. (정말?)
사실 힘들엇음...
주요한 방법은 아래와 같다.
export function isClick(index, itemtype){ //의상 index 받아오기
type = itemtype;
addGltfPath = gltfArray[index];
const event = new CustomEvent('globalFunctionCalled');
window.dispatchEvent(event);
}
const GltfGroupModels = (props) => {
useEffect(() => {
function handleGlobalFunctionCall() {
console.log(type);
// 원하는 작업 수행
//removeDecoGltf();
putDecoGltf(addGltfPath, 1.1, 0,-0.04,0, type);
console.log(addGltfPath);
}
window.addEventListener('globalFunctionCalled', handleGlobalFunctionCall);
return () => {
window.removeEventListener('globalFunctionCalled', handleGlobalFunctionCall);
};
}, []);
}
급한 사람을 위해 남겨놓고, 나는 다음에 작성해야지... 마감이 얼마 안 남앗다고.... 다시 기능개발로 돌아가야겟다,,