3D맵 구현하기 - 캐릭터가 움직일 때 fbx 파일로 움직임 구현하기
2022. 2. 21. 18:07ㆍTechnology[Front]
이번 게시글에서는 캐릭터의 움직임을 구현하기 위해 fbx파일을 FBXLoader를 이용해 가져와서 캐릭터의 애니메이션을 구현하는 방법에 대해 알아보겠습니다.
(1) test.js
import {FBXLoader} from 'https://cdn.jsdelivr.net/npm/three@0.118.1/examples/jsm/loaders/FBXLoader.js';
class BasicCharacterControllerProxy {
constructor(animations) {
this._animations = animations;
}
get animations() {
return this._animations;
}
};
class FiniteStateMachine {
constructor() {
this._states = {};
this._currentState = null;
}
_AddState(name, type) {
this._states[name] = type;
}
SetState(name) {
const prevState = this._currentState;
if (prevState) {
if (prevState.Name == name) {
return;
}
prevState.Exit();
}
const state = new this._states[name](this);
this._currentState = state;
state.Enter(prevState);
}
Update(timeElapsed, input) {
if (this._currentState) {
this._currentState.Update(timeElapsed, input);
}
}
};
class CharacterFSM extends FiniteStateMachine {
constructor(proxy) {
super();
this._proxy = proxy;
this._Init();
}
_Init() {
this._AddState('idle', IdleState);
this._AddState('walk', WalkState);
this._AddState('run', RunState);
}
};
class State {
constructor(parent) {
this._parent = parent;
}
Enter() {}
Exit() {}
Update() {}
};
class WalkState extends State {
constructor(parent) {
super(parent);
}
get Name() {
return 'walk';
}
Enter(prevState) {
const curAction = this._parent._proxy._animations['walk'].action;
if (prevState) {
const prevAction = this._parent._proxy._animations[prevState.Name].action;
curAction.enabled = true;
if (prevState.Name == 'run') {
const ratio = curAction.getClip().duration / prevAction.getClip().duration;
curAction.time = prevAction.time * ratio;
} else {
curAction.time = 0.0;
curAction.setEffectiveTimeScale(1.0);
curAction.setEffectiveWeight(1.0);
}
curAction.crossFadeFrom(prevAction, 0.5, true);
curAction.play();
} else {
curAction.play();
}
}
Exit() {
}
Update(timeElapsed, input) {
if (moveForward || moveBackward || moveLeft || moveRight) {
if (moveShift) {
console.log("run");
this._parent.SetState('run');
}
else {
this._parent.SetState('walk');
}
return;
}
this._parent.SetState('idle');
}
};
_LoadModels() {
const loader = new FBXLoader();
loader.setPath('./resources/remy/');
loader.load('remy.fbx', (fbx) => {
fbx.scale.setScalar(0.01);
fbx.traverse(c => {
c.castShadow = true;
});
this._target = fbx;
this._params.scene.add(this._target);
this._mixer = new THREE.AnimationMixer(this._target);
this._manager = new THREE.LoadingManager();
this._manager.onLoad = () => {
this._stateMachine.SetState('idle');
};
const _OnLoad = (animName, anim) => {
const clip = anim.animations[0];
const action = this._mixer.clipAction(clip);
this._animations[animName] = {
clip: clip,
action: action,
};
};
const loader = new FBXLoader(this._manager);
loader.setPath('./resources/remy/');
loader.load('walk.fbx', (a) => { _OnLoad('walk', a); });
loader.load('run.fbx', (a) => { _OnLoad('run', a); });
loader.load('idle.fbx', (a) => { _OnLoad('idle', a); });
});
}
let mixers = [];
RAF();
function RAF() {
requestAnimationFrame((t) => {
if (previousRAF === null) {
previousRAF = t;
}
RAF();
renderer.render(scene, camera);
Step(t - previousRAF);
previousRAF = t;
});
}
function Step(timeElapsed) {
const timeElapsedS = timeElapsed * 0.001;
if (mixers) {
mixers.map(m => m.update(timeElapsedS));
}
if (new_controls) {
new_controls.Update(timeElapsedS);
}
}
위의 코드는 실제 프로젝트에서 일부 발췌한 것으로 사용시 일부 수정해서 사용하시면 됩니다. FBXLoader를 사용하여 _LoadModels()에서 remy.fbx파일을 로드하고 이를 scene에 추가합니다. 이 때 걷는 움직임, 뛰는 움직임 등은AnimationMixer를 이용하여 로드해서 움직임을 구현하고 Step()에서 mixers가 존재한다면(움직임을 로드했다면) update메서드에 timeElapsedS를 파라미터로 전달해서 시간의 흐름에 따라 애니메이션을 보여주게 됩니다.
각 상태(걷기, 뛰기, 가만히 있기)를 하나의 클래스로 정의하고 WASD와 SHIFT키를 눌렀다면 이를 Update()에서 인식해서 SetState메서드를 이용해 현재 상태를 업데이트 합니다. 상태가 업데이트 되면 Enter메서드를 호출하게 되고 각 상태의 클래스에서 Enter메서드를 정의하고 해당 메서드에서 애니메이션 움직임을 time을 이용해 구현합니다.
이번 게시글에서는 fbx 파일을 이용해 scene에 로드하고 이를 AnimationMixer를 이용해 움직임을 구현하는 방법에 대해 알아보았습니다.
'Technology[Front]' 카테고리의 다른 글
3D맵 구현하기 - cannon.js로 물리엔진 구현하기(2) (1) | 2022.02.23 |
---|---|
3D맵 구현하기 - cannon.js로 물리엔진 구현하기(1) (1) | 2022.02.22 |
3D맵 구현하기 - 3인칭 카메라 구현하기(카메라 조종, 캐릭터가 카메라를 따라서 이동, 카메라가 보는 방향을 캐릭터가 응시) (1) | 2022.02.20 |
3D맵 구현하기 - 키보드로 카메라 이동시키기 (1) | 2022.02.19 |
3D맵 구현하기 - 마우스로 카메라 각도 변경 (1) | 2022.02.18 |