0. 들어가기 전에
점토가 게임 화면을 돌아다니는 모습에 생동감을 주기 위해 애니메이션 기능을 추가했다.
젤리 키우기 에셋 목록에서 직접 만든 애니메이션이 있길래 그걸 토대로 각 레벨마다 애니메이션과 애니메이터를 만들었다.
일단 점토가 돌아다니는 기능만 구현되어 있기에 만들어놓은 애니메이션 중 Walk 애니메이션만 사용했다. Touch 애니메이션은 마우스로 점토를 클릭했을 때 실행되도록 할 것이다.
구현한 기능
- 점토 애니메이션 기능
1. 점토 애니메이션 기능
우선 점토에 애니메이션을 적용하기 위해서 Walk, Idle, Touch 애니메이션을 만들어주었다.
1.1 애니메이션 & 애니메이터 제작
점토의 레벨을 1~5 레벨로 설정했다. 레벨이 올라갈수록 점토의 크기가 커지는데 이를 애니메이션에도 적용을 해줘야 해서 부득이하게 Walk, Idle, Touch 애니메이션을 각 레벨마다 만들어주었다.
애니메이터도 1~5 레벨에 맞게 각각 만들어주었다.
1.2 애니메이터 상태
현재 애니메이터 상태에 대해서 간략하게 소개하려고 한다.
1~5 레벨 애니메이터 모두 구조는 위 사진과 같게 구성되어 있다.
1.3 애니메이터 상태 설명
어떤 상태에서든 Touch 애니메이션을 실행할 수 있도록 Any State 를 Touch 로 연결해주었다. 여기서 어떤 상태에서든 이라는 의미는 점토가 멈춰있거나, 걷고 있거나, 터치 되었거나 이 세 가지 경우에 상관 없이 무조건 Touch 애니메이션을 실행하도록 한다는 것이다.
Parameter 에 trigger 타입의 doTouch 를 만들어서 Any State 에서 Touch 로 넘어가는 조건으로 사용하도록 했다. 이제 코드 상에서 SetTrigger() 메서드를 이용해서 이 애니메이션이 실행되도록 할 수 있다.
Idle 과 Walk 애니메이션은 서로 오고 갈 수 있도록 했다.
Parameter 에 bool 타입의 isWalk 를 만들어서 이 값이 참인지 거짓인지에 여부에 따라 Idle 에서 Walk 로, Walk 에서 Idle 로 애니메이션이 전환될 수 있도록 했다.
이것도 이제 코드 상에서 SetBool() 메서드를 이용하여 값을 설정해주어 진정으로 애니메이션이 정상적으로 작동하도록 했다.
1.3 ClayMove 코드
이를 적용한 ClayMove 스크립트는 다음과 같다.
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum MoveDirection
{
Minus = -1, Plus = 1, Idle = 0
}
public class ClayMove : MonoBehaviour
{
[Header("ClayMovement")]
public float moveSpeed; // 움직이는 속도
public MoveDirection moveDirX; // x 축 방향
public MoveDirection moveDirY; // y 축 방향
public bool isMoving; // 돌아다니는지 여부 확인
public bool isReturning; // 중점으로 돌아가는 중인지 여부 확인
public GameObject targetPosObj; // 중점에 있는 게임 오브젝트 할당해주기
private Coroutine randomMoveCoroutine;
[Header("Animation")]
public Animator anim;
void Start()
{
// 코루틴의 핸들을 저장하고 이를 통해 중단하는 것이 좋음
randomMoveCoroutine = StartCoroutine(RandomMove());
anim = GetComponent<Animator>();
}
void Update()
{
if (!isReturning)
{
// Time.deltaTime 을 곱해주는 이유는 게임 오브젝트가 순간이동 하는 것을 막기 위함.
transform.Translate(moveSpeed * Time.deltaTime * new Vector2((int)moveDirX, (int)moveDirY));
}
// Idle 상태가 아니라면 walk 애니메이션 수행하도록
if (moveDirX != 0 || moveDirY != 0)
{
anim.SetBool("isWalk", true);
}
else
{
anim.SetBool("isWalk", false);
}
}
// 3초에 한 번씩 이동 방향 바꾸도록 하는 코루틴..
// 코루틴은 IEnumerator 를 반환함
private IEnumerator RandomMove()
{
while (true)
{
// 방향 랜덤으로 설정
moveDirX = (MoveDirection)Random.Range(-1, 2);
moveDirY = (MoveDirection)Random.Range(-1, 2);
yield return new WaitForSeconds(3f); // 3초 동안 기다령
}
}
private IEnumerator ReturnToCenter()
{
isReturning = true;
float curTime = 0f;
float duration = 5f; // 중점으로 돌아가는 시간 5초 만큼 줄 것..
Vector2 targetPos = targetPosObj.transform.position; // 중점에 있는 오브젝트의 위치 할당
while (curTime < duration)
{
transform.position = Vector2.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime * 1.5f);
curTime += Time.deltaTime;
yield return null; // 다음 프레임까지 대기
}
isReturning = false; // 랜덤 이동 재개
randomMoveCoroutine = StartCoroutine(RandomMove()); // 다시 시작시키기
}
// 콜라이더 컴포넌트를 가진 게임 오브젝트랑 부딪히면 이 함수로 진입
private void OnCollisionEnter2D(Collision2D collision)
{
if (isReturning) return; // 만약 이미 돌아가고 있는 중에 또 부딪히면 걍 나가라..
// 부딪힌 게임 오브젝트의 태그가 Target 이면 ReturnToCenter 코루틴 호출하도록
if (collision.gameObject.CompareTag("Wall"))
{
// 랜덤 이동 코루틴은 중단시키기
StopCoroutine(randomMoveCoroutine);
// 중앙 복귀 코루틴 호출
StartCoroutine(ReturnToCenter());
}
}
}
1.4 ClayMove 스크립트 설명
ClayMove 스크립트에 대한 설명은 다음과 같다.
1. Animator 타입 변수
Animator 타입의 anim 변수를 추가했다. 게임 오브젝트의 Animator 컴포넌트 기능을 이용하기 위해서는 이 변수가 필요하다.
public Animator anim;
2. anim 변수에 값 할당
Animator 타입의 변수를 선언 한다고 해서 바로 기능을 이용할 수 있는 것은 아니다. Animator 는 참조 타입으로 이에 값을 할당해줘야 한다.
값의 할당은 인스펙터 창에서 직접 하는 방법도 있고, 코드 상에서 하는 방법도 있는데 나는 코드 상에서 값을 할당해주었다.
코드상에서 값을 할당하기 위해서는 GetComponent<>() 라는 메서드가 필요하다. <> 안에는 변수 타입에 맞는 타입을 적어주어야 한다. 그래야 게임 오브젝트에서 해당 타입에 맞는 기능을 가지고 올 수 있기 때문이다.
나는 게임 오브젝트에 부착되어 있는 Animator 컴포넌트를 이용해야 하기 때문에 <> 안에 Animator 를 적어주었다.
void Start()
{
// 코루틴의 핸들을 저장하고 이를 통해 중단하는 것이 좋음
randomMoveCoroutine = StartCoroutine(RandomMove());
anim = GetComponent<Animator>();
}
3. anim 변수 이용
애니메이션을 실행시키기 위해서 본격적으로 anim 변수를 사용한다.
void Update()
{
if (!isReturning)
{
// Time.deltaTime 을 곱해주는 이유는 게임 오브젝트가 순간이동 하는 것을 막기 위함.
transform.Translate(moveSpeed * Time.deltaTime * new Vector2((int)moveDirX, (int)moveDirY));
}
// Idle 상태가 아니라면 walk 애니메이션 수행하도록
if (moveDirX != 0 || moveDirY != 0)
{
anim.SetBool("isWalk", true);
}
else
{
anim.SetBool("isWalk", false);
}
}
Update 메서드 안에 애니메이션 실행 기능을 구현해놓았다. 점토의 x, y 이동 방향이 둘 다 0 이라는 것은 Idle(가만히 있기) 상태임을 의미한다. 즉, 그 경우가 아닐 때는 SetBool 메서드를 이용해서 isWalk 값을 true 로 설정해주었다.
isWalk 값이 true 가 되면 Idle 상태에서 Walk 로 넘어가게 된다. 즉, true 로 값을 바꿔주었으므로 이제 점토는 Walk 애니메이션을 실행하게 되는 것이다.
2. 결과물
점토의 이동 방향이 둘 중에 하나라도 Idle 이 아니면 Walk 애니메이션이 실행되고, 둘 다 Idle 이면 Idle 애니메이션이 실행되는 모습을 확인할 수 있다.
3. 참고 자료
1. 애니메이션
좀 잘 정리해놓은 글이 있길래 가져와봤다. 나중에 헷갈릴 때 와서 보면 될 것 같다.
[19일차] 유니티 (Animator, Ble.. : 네이버블로그