0. 들어가기 전에
점토를 클릭할 때 레벨에 맞는 애정을 획득할 수 있도록 했다. 점토를 클릭하다 보면 레벨업을 하는데 최고 레벨에 도달하면 점토의 모습이 동물로 변하도록 했다.
레벨업을 할 때마다 점토의 크기가 커지도록 하기 위해서 각 레벨에 맞게 애니메이터를 할당해주었다.
점토가 다 자라서 동물이 되면 스프라이트 이미지 상 움직이는 방향에 맞게 이미지를 좌우반전시켰다.
1. 스크립트
이번에 만든 스크립트는 ClayController 이고, ClayMove 스크립트에 변동사항이 생겼다.
1.1 ClayController 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClayController : MonoBehaviour
{
[Header("Clay Data")]
public float[] loves; // 점토를 클릭했을 때 얻는 애정 수치(1~5 레벨)
public int[] touchCnts; // 해당 요소만큼 클릭되면 레벨업
public int clayLevel = 1; // 1레벨에서 시작(5레벨까지 있음)
public int curTouchCnt = 0;
[Header("Animation")]
Animator anim; // 점토가 터치될 때 애니메이션 실행하기 위함
public RuntimeAnimatorController[] animators;
[Header("Sprites")]
public Sprite clay; // 점토 모습
public Sprite animal; // 다 자란 동물 모습
private void Awake()
{
anim = GetComponent<Animator>();
}
// 점토가 터치되면 저절로 호출됨
private void OnMouseDown()
{
Debug.Log("터치됨!");
anim.SetTrigger("doTouch");
GameManager.instance.GetLove(loves[clayLevel - 1]); // 레벨에 맞는 수치를 함수로 넘겨주기
curTouchCnt++;
// 이미 점토의 레벨이 최고 레벨에 도달했으면 밑으로 진입 안 하도록..
if (clayLevel != 5)
{
if (curTouchCnt == touchCnts[clayLevel - 1])
{
clayLevel++; // 레벨 1 증가
curTouchCnt = 0; // 초기화
anim.runtimeAnimatorController = animators[clayLevel - 1]; // 레벨에 맞는 애니메이터로 바꿔주기
if (clayLevel == 5)
gameObject.GetComponent<SpriteRenderer>().sprite = animal;
}
}
}
}
1.2 ClayController 스크립트 설명
ClayController 스크립트에 대한 설명은 다음과 같다.
1. 변수
- loves: 각 레벨마다 획득할 수 있는 애정의 수치가 다르도록 하기 위해서 선언했다.
- touchCnts: 해당 요소만큼 클릭되면 레벨업 되도록 하기 위해 선언했다.
- clayLevel: 현재 점토의 레벨을 나타내기 위한 변수이다.
- curTouchCnt: 현재 레벨에서 점토를 클릭한 횟수를 나타내기 위한 변수이다.
- anim: 점토가 터치될 때 애니메이션을 실행하도록 하기 위한 변수이다
- animators: 각 레벨에 맞는 애니메이터를 할당해주기 위한 변수이다.
- clay: 점토 모습 스프라이트
- animal: 동물 모습 스프라이트
[Header("Clay Data")]
public float[] loves; // 점토를 클릭했을 때 얻는 애정 수치(1~5 레벨)
public int[] touchCnts; // 해당 요소만큼 클릭되면 레벨업
public int clayLevel = 1; // 1레벨에서 시작(5레벨까지 있음)
public int curTouchCnt = 0;
[Header("Animation")]
Animator anim; // 점토가 터치될 때 애니메이션 실행하기 위함
public RuntimeAnimatorController[] animators;
[Header("Sprites")]
public Sprite clay; // 점토 모습
public Sprite animal; // 다 자란 동물 모습
2. Awake()
anim 변수에 현재 게임 오브젝트의 Animator 컴포넌트를 할당했다.
private void Awake()
{
anim = GetComponent<Animator>();
}
3. OnMouseDown()
이는 마우스로 게임 오브젝트를 터치했을 때 자동으로 호출되는 함수이다. 직접 호출할 필요가 없다.
터치 상태의 애니메이션을 실행하도록 하기 위해 anim.SetTrigger 메서드를 실행했다. 그리고 현재 점토 레벨에 맞는 애정을 얻게 하기 위해서 GetLove 메서드에 매개변수로 loves[clayLevel - 1] 값을 넣어주었다. 그 후, curTouchCnt 값을 +1 해주었다.
만약 점토의 레벨이 이미 최고 상태라면 더 이상 레벨업 할 수 없으므로 if (clayLevel != 5) 를 통해 걸러냈다. 만약 5레벨이 아니라면 진입할 수 있도록 했다.
진입하면 점토의 레벨이 1 증가하고, curTouchCnt 값은 다시 0이 되고, 게임 오브젝트의 애니메이터가 레벨에 맞는 애니메이터로 변경될 수 있도록 했다.
최고레벨이 되면 점토의 스프라이트를 동물 모습으로 변경해주어서 다 자랐음을 표현하도록 했다.
// 점토가 터치되면 저절로 호출됨
private void OnMouseDown()
{
Debug.Log("터치됨!");
anim.SetTrigger("doTouch");
GameManager.instance.GetLove(loves[clayLevel - 1]); // 레벨에 맞는 수치를 함수로 넘겨주기
curTouchCnt++;
// 이미 점토의 레벨이 최고 레벨에 도달했으면 밑으로 진입 안 하도록..
if (clayLevel != 5)
{
if (curTouchCnt == touchCnts[clayLevel - 1])
{
clayLevel++; // 레벨 1 증가
curTouchCnt = 0; // 초기화
anim.runtimeAnimatorController = animators[clayLevel - 1]; // 레벨에 맞는 애니메이터로 바꿔주기
if (clayLevel == 5)
gameObject.GetComponent<SpriteRenderer>().sprite = animal;
}
}
}
1.3 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));
// x 축 이동 방향이 오른쪽이면 sprite 를 뒤집도록..
if (moveDirX == MoveDirection.Plus)
gameObject.GetComponent<SpriteRenderer>().flipX = true;
else if (moveDirX == MoveDirection.Minus)
gameObject.GetComponent<SpriteRenderer>().flipX = false;
}
// 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)
{
// 현재 점토의 위치가 목표 지점보다 왼쪽에 있으면 오른쪽을 보도록..
if (targetPos.x > transform.position.x)
gameObject.GetComponent<SpriteRenderer>().flipX = true;
else if (targetPos.x < transform.position.x)
gameObject.GetComponent<SpriteRenderer>().flipX = false;
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 스크립트 변경 사항 설명
1. Update()
x 축 이동 방향이 오른쪽이면 sprite 를 뒤집도록 하는 로직을 추가했다. 점토가 다 자라서 동물이 되면 방향이 구분된다. 이때 동물이 쳐다보는 방향과 이동 방향이 다르면 이상하기 때문에 이를 위해 추가했다.
// x 축 이동 방향이 오른쪽이면 sprite 를 뒤집도록..
if (moveDirX == MoveDirection.Plus)
gameObject.GetComponent<SpriteRenderer>().flipX = true;
else if (moveDirX == MoveDirection.Minus)
gameObject.GetComponent<SpriteRenderer>().flipX = false;
2. ReturnToCenter()
점토가 벽에 닿아서 중앙으로 돌아갈 때 스프라이트의 방향을 조정하기 위해 추가한 구문이다. 만약 점토가 중앙보다 왼쪽에 위치한다면 오른쪽을 쳐다보도록, 오른쪽에 위치한다면 왼쪽을 쳐다보도록 했다.
// 현재 점토의 위치가 목표 지점보다 왼쪽에 있으면 오른쪽을 보도록..
if (targetPos.x > transform.position.x)
gameObject.GetComponent<SpriteRenderer>().flipX = true;
else if (targetPos.x < transform.position.x)
gameObject.GetComponent<SpriteRenderer>().flipX = false;
2. 결과물
정상적으로 작동하는 모습을 확인할 수 있다.
3. 참고자료
RuntimeAnimatorContoller 에 대해 설명해놓은 글이 있길래 가져왔다.
3.1 RuntimeAnimatorController
유니티 기본기 : 애니메이터(Animator) 2편. 애니메이터 기초 | by Supercent.official | Supercent Blog 슈퍼센트 블로그 | Medium