1. ์ ๋๋ฉ์ด์ ๊ฐ์ ธ์ค๊ธฐ
์บ๋ฆญํฐ ๋ชจ๋ธ์ด ํด๋จธ๋ ธ์ด๋๊ฐ ์๋๋ผ์ ๋ฆฌํ๊ฒํ ์ ๋ชป ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
์ ๋ ์์ ์คํ ์ด์์ ์บ๋ฆญํฐ ๋ชจ๋ธ์ ์๊ณ , Humonoid Animation Retargeting์ ์ด์ฉํ์ฌ Mixamo ์ฌ์ดํธ์ ์๋ ์ ๋๋ฉ์ด์ ๋ค์ ์ฌ์ฌ์ฉํ๋ ค๊ณ ํ์ต๋๋ค. ๊ทธ๋ฐ๋ฐ, ๊ตฌ๋งคํ ์์ ์ด ํด๋จธ๋ ธ์ด๋ ๋ชจ๋ธ์ด ์๋๋๋ผ๊ตฌ์. ๊ทธ๋์ Mixamo ์ฌ์ดํธ ์ ๋๋ฉ์ด์ ๋ค์ ์ ์ฉํ์ง ๋ชป ํ์ต๋๋ค. ๊ธฐ์กด ํด๋จธ๋ ธ์ด๋ ๋ผ๋ ๊ตฌ์กฐ์ ๊ตฌ๋งคํ ์์ ์ ๋ผ๋ ๊ตฌ์กฐ๊ฐ ๋ฌ๋๊ธฐ์, ๋งค์นญ์ด ์ ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋๋ผ๊ตฌ์.
๊ทธ๋์ ๊ทธ๋ฅ 3D ํด(Blender, Maya ๊ฐ์)์ ๊ฐ์ ธ์์ ๋ผ๋๋ฅผ ๋ชจ๋ ์ ๊ฑฐ ํ, Mixamo ์ฌ์ดํธ์ ์คํ ๋ฆฌ๊น (Auto Rigging)์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ด์ฉํ์ฌ ์๋์ผ๋ก ํด๋จธ๋ ธ์ด๋ ๋ผ๋ ๊ตฌ์กฐ๋ฅผ ๋ฑ๋กํด ์ฃผ์์ต๋๋ค. ์ด์ ์ ๋น๊ณต๊ฐ ์ฒ๋ฆฌํ ๊ฒ์๊ธ์์ ์์ธํ ๋ค๋ฃจ์๋ ๋ด์ฉ๋ค์ด์ง๋ง, ์ด ๊ธ์ ์ฃผ์ ๋ด์ฉ์ ์๋๋ ์๋ตํ ๊ฒ์.
Idle, Move ์ ๋๋ฉ์ด์ ์ต์ ์ค์
Idle ์ ๋๋ฉ์ด์ ๊ณผ Move ์ ๋๋ฉ์ด์ ์ค์ ์ ๋ค์๊ณผ ๊ฐ์ด ํด์ฃผ์์ต๋๋ค. Root Transform Rotation, Root Transform Position(Y), Root Transform Position(XZ)์ ์ค์ ํ์ฌ ์ ๋๋ฉ์ด์ ์ ์ํด ์บ๋ฆญํฐ์ ํธ๋์คํผ์ด ์ํฅ์ ๋ฐ์ ๋น์ ์์ ์ธ ์์ง์์ ์ ๋ฐํ๋ ๊ฒ์ ๋ฐฉ์งํ์์ต๋๋ค.
2. ๋ธ๋ ๋ ํธ๋ฆฌ ๋ง๋ค๊ธฐ
์ ๋๋ฉ์ด์ ๋ค์ ๊ด๋ฆฌํ๊ณ ์ ์ดํ๊ธฐ ์ํด ์ ๋๋ฉ์ดํฐ(Animator)๋ฅผ ์์ฑํ ์ฐจ๋ก์ ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์บ๋ฆญํฐ๋ ์ด๋์๋์ ๋ฐ๋ผ ์ด๋ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๊ฐ ์ ๋์ ์ผ๋ก ๋ฌ๋ผ์ ธ์ผ ํฉ๋๋ค. ๋กค์ ๋น์ ํด ๋ณด์๋ฉด ์ฌ๋ก์ฐ CC๊ธฐ์ ๊ฑธ๋ฆฌ๋ฉด ์ฑํผ์ธ์ด ๋๋ฆฌ๊ฒ ๊ฑท๊ณ , ์ด๋์๋๊ฐ ๋นจ๋ผ์ง๊ฒ ํ๋ ์คํฌ์ด๋ ์์ดํ ์ ์ฌ์ฉํ๋ฉด ์ฑํผ์ธ์ด ๊ทธ๋งํผ ๋น ๋ฅด๊ฒ ๊ฑท์ฃ .
๊ทธ๋ ๊ธฐ์ ์ด๋์๋์ ๋ฐ๋ผ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๋ฅผ ์ ์ดํด์ฃผ๊ธฐ ์ํด ๋ธ๋ ๋ ํธ๋ฆฌ๋ก ์์ฝ๊ฒ ๊ตฌํํ๊ณ ์ ํฉ๋๋ค. ์ด๋ค ๊ธฐ๋ฅ์ ํ๋์ง ๊ตฌ๋ณํ๊ธฐ ์ฝ๊ฒ ์ ๋ "Move"์ด๋ฆ์ ์ง์ด์ฃผ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋์๋์ ๋ฐ๋ผ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋์ ๊ณฑํด์ค Float ๋งค๊ฐ๋ณ์๋ ์์ฑํด ์ฃผ์์ต๋๋ค.
์ด์ ์์ฑํ Move ๋ธ๋ ๋ ํธ๋ฆฌ์์ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด์ฃผ๋ฉด, Velocity ๋งค๊ฐ๋ณ์ ๊ฐ์ ๋ฐ๋ผ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๊ฐ ๋ฌ๋ผ์ง๊ฒ ๋ ๊ฒ๋๋ค. ๋ฌผ๋ก ๋ธ๋ ๋ ํธ๋ฆฌ ๋ด์์ ์ ๋๋ฉ์ด์ ๊ฐ๊ฐ ๋ฐฐ์์ ๋ค๋ฅด๊ฒ ์ค์ ํ ์๋ ์๋๋ฐ, ์คํฌ๋ฆฝํธ ์์์ ์ด๊ฑธ ์ ์ด๊ฐ ๊ฐ๋ฅํ ์ง ๋ชจ๋ฅด๊ณ , ์ด๋ ๊ฒ ์งํํด๋ ๋ณ ๋ฌธ์ ๊ฐ ์๊ธฐ์ ์งํํ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ Move ๋ธ๋ ๋ ํธ๋ฆฌ ๋ด๋ถ๋ก ๋ค์ด์์, ๋ค์๊ณผ ๊ฐ์ด ์ธํ ํด์ฃผ์์ต๋๋ค.
Velocity ๋งค๊ฐ๋ณ์๊ฐ 0์ด๋ฉด Idle ์ ๋๋ฉ์ด์ ์ ์ฌ์ํ๊ณ , 1 ์ด์์ด๋ฉด Move ์ ๋๋ฉ์ด์ ์ ์ฌ์ํ๋๋ก ํ ๊ฒ์ด์ฃ .
์ฆ, Velocity ๋ณ์๋ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋์ ์ ๋๋ฉ์ด์ ๊ฐ ์ ํ, ์ด๋ ๊ฒ 2๊ฐ์ง ์ฉ๋๋ก ์ฌ์ฉ๋๊ณ ์๋ ๊ฒ์ ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด, ๋ธ๋ ๋ ํธ๋ฆฌ ๊ตฌ์ฑ์ ์๋ฃ๋ ๊ฒ์ ๋๋ค. ์บ๋ฆญํฐ ์ค๋ธ์ ํธ์ ์ ๋๋ฉ์ดํฐ(Animator) ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํ๊ณ , ์์์ ๋ง๋ค์๋ ์ ๋๋ฉ์ดํฐ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ฃ์ด์ค๋๋ค.
3. ๊ธฐ๋ฅ ๊ตฌํํ๊ธฐ
๋ฌธ์ ์ ์ด ์์ง๋ง ์ผ๋จ ๊ตฌํํ ๋ฒ์
์ด์ ์คํฌ๋ฆฝํธ์์ ์ ๋๋ฉ์ดํฐ ์ปดํฌ๋ํธ ์ฐธ์กฐ๋ฅผ ์ป์ด, Velocity ์ ๋๋ฉ์ดํฐ ๋งค๊ฐ๋ณ์๋ฅผ ๋ณ๊ฒฝํ๋๋ก ํ์ฌ ์ฌ์ํ๋๋ก ํด๋ด ์๋ค. ์ผ๋จ์ ๋ฐ๊ณผ ์ ๋๋ฉ์ด์ ์ฑํฌ๋ฅผ ์๊ฐํ์ง ๋ง๊ณ , ๋ค์๊ณผ ๊ฐ์ด ํ์ํ ๋ถ๋ถ๋ง ์์ฑํ์ต๋๋ค.
public class PlayerController : MonoBehaviour
{
protected Animator animator;
protected const float DEFAULT_CONVERT_MOVESPEED = 3f;
protected const float DEFAULT_ANIMATION_PLAYSPEED = 0.9f;
...
protected void Move()
{
float currentMoveSpeed = player.MoveSpeed * CONVERT_UNIT_VALUE;
float animationPlaySpeed = (direction == Vector3.zero) ? 0f : 1f:
LookAt();
rigidBody.velocity = direction * currentMoveSpeed + Vector3.up * rigidBody.velocity.y;
animator.SetFloat("Velocity", animationPlaySpeed);
}
๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ ๊ฒ๋๋ค.
ํ์ง๋ง ์์ ๊ฐ์ด ํ๋ค๋ฉด ์ฌ๋ฌ ๋ฌธ์ ๊ฐ ์๋ค๋ ๊ฒ์ ๋์ผ๋ก ๋จ๋ฒ์ ์์์ฐจ๋ฆฌ์ค ์ ์์ ๊ฒ๋๋ค. ํ๋์ฉ ํด๊ฒฐํด๋ณด์ฃ .
๋ฐ๋ฅ์ ๊ทธ๋ฆฌ๋(Grid) ํ ์ค์ณ ์ ํ๊ธฐ
๋์น์ฑ์ ๋ถ๋ค๋ ๊ณ์๊ฒ ์ง๋ง, ๋ฐ๋ฅ ํ ์ค์ณ๊ฐ ๊ทธ๋ฆฌ๋(Grid)๋ก ๋์ด ์์ต๋๋ค. ๋จ์ ๋ฌด์ง์ ํ ์ค์ณ๋ก๋ ์บ๋ฆญํฐ๊ฐ ๋ฐ์ด ๋ฏธ๋๋ฌ์ง๋ฏ ์ด๋ํ๋์ง ์์๋ณด๊ธฐ๊ฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์ด์ฃ . ์ด ์์ด๋์ด๋ ๐์ด ์์์ ๋ณด๊ณ ์ป์ ์ ์์์ต๋๋ค. ๋จ์ํ ์ ๋๋ฉ์ด์ ์ฑํฌ๋ฅผ ๋ง์ถ ๋๋ง ์ ๊น ์ฌ์ฉํ ์ฉ๋์ด๊ธฐ์ ๊ตฌ๊ธ์์ ์ฐพ์ ๐๊ทธ๋ฆฌ๋ ํ ์ค์ณ๋ฅผ Unity๋ก ๊ฐ์ ธ์์ต๋๋ค.
Unity๋ก ๊ฐ์ ธ์จ ํ, ์ Material์ ์์ฑํ์ฌ Albedo ํ๋กํผํฐ์ ๊ฐ์ ธ์จ ํ ์ค์ณ๋ฅผ ๋๋๊ทธ ์ค ๋๋กญํด์ฃผ์์ต๋๋ค. ๊ทธ๋ ๊ฒ ํ๋ฉด ํ ์ค์ณ๊ฐ ์ ํ์ง Material์ด ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฐ๋ฅ ์ค๋ธ์ ํธ Mesh Renderer์ Materials ํ๋กํผํฐ์ ์์์ ๋ง๋ Material์ ๋ฃ์ด์ฃผ๋ฉด ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์บ๋ฆญํฐ๊ฐ ๊ทธ๋ฆฌ๋ ๋ฌด๋ฌ ํ ์นธ์ ๋ฑ ๋ค์ด๊ฐ๋๋ก Material์ Tililing ํ๋กํผํฐ X์ Y๊ฐ์ ์์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ ๊ฐ์ ๊ฒฝ์ฐ์๋ X = 2.5, Y = 3์ผ๋ก ํ๋ ๋ฑ ์บ๋ฆญํฐ๊ฐ ํ ์นธ ์์ ๋ค์ด๊ฐ๋๋ผ๊ตฌ์.
์ด์ ์บ๋ฆญํฐ๋ฅผ ์์ง์ฌ๋ณด๋ฉฐ, ๊ฐ ์ด๋์๋ ๋๋ง๋ค ์ ์ ํ ์ฌ์ ๋ฐฐ์จ์ ๊ตฌํ๋ฉด ๋ฉ๋๋ค. ์บ๋ฆญํฐ๊ฐ ๋ฏธ๋๋ฌ์ง๋ฏ ์์ง์ด๋ฉด ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๋ฅผ ์ข ๋ ๋์ฌ์ฃผ๋ฉด ๋๊ฒ ์ฃ . ์ ๊ฐ์ ๊ฒฝ์ฐ์๋ ํ ์คํธ ํด๋ณด๋, ๋ค์๊ณผ ๊ฐ์ ์์น๋ค์ ์ป์ ์ ์์๋ค์.
์ด๋์๋์ ๋ฐ๋ฅธ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋ ์ฑํฌ ๋ง์ถ๊ธฐ
์ฌ์ค ์ด ๋ถ๋ถ์ด ๊ฐ์ฅ ๊ณ ๋์ด์์ต๋๋ค. ๋จ์ํ ์ผ์ ๊ตฌ๊ฐ๋ง๋ค ๊ฐ์ ๋๋์ด ๋ถ๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ์๋ ๋๋ฌด ํ๋ ์ฝ๋ฉ์ธ ๊ฒ ๊ฐ์ ๊ทธ๋ฌ๊ธฐ๋ ์ซ์์ฃ . ์ด๋ป๊ฒ ํ๋ฉด ํจ์จ์ ์ผ๋ก, ์ ๋์ ์ผ๋ก ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ ์ง ๊ณ ๋ฏผ์ ํ๋ ๊ฒ ๊ฐ๋ค์.
์ด๊ฒ ์ ๊ฒ ํ ์คํธ๋ฅผ ํด๋ณด๋ฉฐ ๋ฐ์ดํฐ๋ฅผ ์ป์ด๋ณด๋, ๋ค์๊ณผ ๊ฐ์ ์์ ๋ง๋ค ์ ์์์ต๋๋ค. ๋ค์ ๊ณต์์ฒ๋ผ ์ ์ฉํ์ฌ ํ์ธํด๋ณด๋ ์์ฐ์ค๋ฝ๊ฒ ์ ์ฑํฌ๊ฐ ๋ง๋๋ผ๊ตฌ์.
์ด๋์๋์ ๋ฐ๋ผ ์๋ง์ ์ ๋๋ฉ์ด์ ์ฌ์ ๋ฐฐ์จ ๊ฐ์ ๋ฆฌํดํด์ฃผ๋ ํจ์๋ฅผ ์์ฑํ์ต๋๋ค.
protected const float DEFAULT_CONVERT_MOVESPEED = 3f;
protected const float DEFAULT_ANIMATION_PLAYSPEED = 0.9f;
protected float GetAnimationSyncWithMovement(float changedMoveSpeed)
{
if (direction == Vector3.zero)
{
return -DEFAULT_ANIMATION_PLAYSPEED;
}
// (๋ฐ๋ ์ด๋ ์๋ - ๊ธฐ๋ณธ ์ด๋์๋) * 0.1f
return (changedMoveSpeed - DEFAULT_CONVERT_MOVESPEED) * 0.1f;
}
๊ทธ๋ฆฌ๊ณ Move()์์ ์ ํจ์์ ๋ฆฌํด๊ฐ์ ๋ฐ์์ฃผ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
protected void Move()
{
float currentMoveSpeed = player.MoveSpeed * CONVERT_UNIT_VALUE;
float animationPlaySpeed = DEFAULT_ANIMATION_PLAYSPEED +
GetAnimationSyncWithMovement(currentMoveSpeed);
LookAt();
rigidBody.velocity = direction * currentMoveSpeed + Vector3.up * rigidBody.velocity.y;
animator.SetFloat("Velocity", animationPlaySpeed);
}
์ด๋ ๊ฒ ํด์ฃผ๋ฉด ๋์ ๋๋ค. ์ฝ๋๊ฐ ๊ฐ๊ฒฐํ๊ณ ์ด์๊ฒ ๋ฑ ๋จ์ด์ง๋, ์ ๋ง ๊ธฐ๋ถ์ด ์ข์์ต๋๋ค.
4. ๋ง๋ฌด๋ฆฌ ํ ์คํธ
ํ ์คํธ ํด๋ณด๋ ์ ๋ง ๊ธฐ๋ถ์ด ์ข๋ค์. ๋ณ ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค ๊ฑด๋๋ฆด ํ์์์ด, ์ด๋์๋๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ์์์ ์๋์ผ๋ก ์ ๋๋ฉ์ด์ ์ฌ์ ๋ฐฐ์จ ์ฑํฌ ์กฐ์ ๊น์ง ๋๋๊น์.
๋ค์ ๊ธ์์๋ ์ค๋ ํ๋ฃจ์ข ์ผ ํด๊ฒฐํ ๋ฒ๊ทธ๋ค์ ๋ค๋ค๋ณด๋ ค๊ณ ํฉ๋๋ค. ๊ฒฝ์ฌ๋ก(Slope)์์ ์บ๋ฆญํฐ๊ฐ ์ด๋ํ ๋ ๋ฌธ์ ๊ฐ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋์๊ณ , ํ๋ฃจ์ข ์ผ ์์ ํ๊ฑฐ๋ ์... ์ด์ ๊ธ์์ ์ ๊ฐ ๋ฌธ์ ๊ฐ ์๋ค๊ณ ํ ์ฝ๋๊ฐ ์๋๋ฐ, ์ด ๋ถ๋ถ๋ ๋ค์ ๊ธ์์ ์์ ํ์ฌ ํด๊ฒฐํ ๊ณํ์ ๋๋ค.
rigidBody.velocity = direction * currentMoveSpeed + Vector3.up * rigidBody.velocity.y;
์ค๋๋ ๊ธ์ด ๊ธธ์ด์ก๋ค์. ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!