0. 들어가기 전에
이번엔 가구를 추가하고 점토를 가구에 드래그&드롭하면 애니메이션이 실행되도록 했다.
가구는 그냥 게임 오브젝트 말고 캔버스 상에 Button 으로 추가했다. 마우스가 해당 게임 오브젝트로 진입했는지 여부를 파악하기 쉽게 하기 위함이다..
1. 게임 오브젝트
가구 UI 만 따로 관리하기 위해 새로운 캔버스 두 개를 만들었다. ToyCanvas1 에 있는 가구들은 점토보다 뒤에, ToyCanvas2 에 있는 가구는 점토보다 앞에 있도록 하기 위해 두개로 나눈 것이다.
2. 스크립트
이번에 새로 만든 스크립트는 ToyController 이고, 수정한 스크립트는 GameManager, Clay, ClayMove 이다.
2.1 ToyController 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.EventSystems;
public class ToyController : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
[Header("Toy Info")]
public int toyIdx; // 장난감 인덱스(0: 캣타워, 1: 카펫, 2: 블라인더, 3: 조명)
[Header("UI")]
public Button toyButton;
private void Awake()
{
toyButton = GetComponent<Button>(); // 버튼 할당받기
SetButtonOnClick(); // onClick 컴포넌트 설정
}
private void SetButtonOnClick()
{
toyButton.onClick.RemoveAllListeners(); // 일단 다 지우기
toyButton.onClick.AddListener(() => GameManager.instance.StartInfoPanel(GameManager.instance.toyInfo[toyIdx])); // 인덱스에 알맞는 가구 정보 가져와서 메서드 연결
}
public void SetInfo()
{
GameManager.instance.curToyIdx = toyIdx; // 현재 가구의 인덱스 넣어주깅
}
public void OnPointerEnter(PointerEventData eventData)
{
GameManager.instance.curToyIdx = toyIdx; // 현재 가구 인덱스 값으로 설정..
}
public void OnPointerExit(PointerEventData eventData)
{
GameManager.instance.curToyIdx = -1; // 아무 가구도 선택되지 않았음을 알리기..
}
}
2.2 ToyController 스크립트 설명
1. 변수
장난감의 인덱스를 지정하기 위한 변수인 toyIdx 를 선언했다. 장난감은 Button 게임 오브젝트이기 때문에 Button 타입의 변수를 선언하여 관리할 수 있도록 했다.
[Header("Toy Info")]
public int toyIdx; // 장난감 인덱스(0: 캣타워, 1: 카펫, 2: 블라인더, 3: 조명)
[Header("UI")]
public Button toyButton;
2. Awake()
버튼 변수에 버튼 타입 컴포넌트를 할당해주고 버튼에 메서드를 연결하기 위해 SetButtonOnClick() 메서드를 호출했다.
private void Awake()
{
toyButton = GetComponent<Button>(); // 버튼 할당받기
SetButtonOnClick(); // onClick 컴포넌트 설정
}
3. SetButtonOnClick()
일단 중복으로 연결 될 가능성을 없애기 위해 버튼에 연결된 메서드를 모두 지우고 시작했다.
장난감을 클릭하면 해당 장난감에 대한 설명을 띄우기 위해 StartInfoPanel 메서드를 연결했다.
private void SetButtonOnClick()
{
toyButton.onClick.RemoveAllListeners(); // 일단 다 지우기
toyButton.onClick.AddListener(() => GameManager.instance.StartInfoPanel(GameManager.instance.toyInfo[toyIdx])); // 인덱스에 알맞는 가구 정보 가져와서 메서드 연결
}
4. OnPointerEnter(PointerEventData eventData)
어떤 장난감의 애니메이션을 수행할 것인지 나타내기 위해 curToyIdx 의 값을 toyIdx 값으로 설정해주는 메서드이다.
버튼에 마우스가 진입하면 자동으로 호출된다.
public void OnPointerEnter(PointerEventData eventData)
{
GameManager.instance.curToyIdx = toyIdx; // 현재 가구 인덱스 값으로 설정..
}
5. OnPointerExit(PointerEventData eventData)
마우스가 장난감을 벗어나면 다시 -1로 값을 변경해준다. 아무 장난감도 선택되지 않았음을 알리기 위함이다.
public void OnPointerExit(PointerEventData eventData)
{
GameManager.instance.curToyIdx = -1; // 아무 가구도 선택되지 않았음을 알리기..
}
2.3 GameManager 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
[Header("Game Data")]
public float love; // 애정
public float gold; // 골드
public bool[] unLockedClays; // 점토들의 해금 여부
public bool[] catchedClays; // 야생에서 잡아왔는지 확인용
public int clayHouseLevel = 1; // 점토 아파트 레벨
public int clayClickLevel = 1; // 점토 클릭 레벨
public int[] clayHouseLoveList; // 업그레이드 비용
public int[] clayClickLoveList; // 업그레이드 비용
public int curPossibleClayNum = 1; // 최대로 키울 수 있는 점토의 개수(1레벨은 1마리, 2레벨은 2마리...)
[Header("Game Manager")]
public static GameManager instance; // 싱글톤 이용하기 위함
[Header("GameDataUI")]
public GameDataUIController gameDataUI;
public delegate void SetInfoPanelHandler(string text); // 델리게이트 선언
public event SetInfoPanelHandler OnSetInfoPanel;
[Header("Pool Manager")]
public PoolManager poolManager;
[Header("Coroutine")]
public Coroutine updateTextUICoroutine;
[Header("Game Exit")]
public Button gameExitButton;
[Header("Effect")]
// 0: 점토 레벨업, 1: 점토 판매, 2: 점토 해금, 3: 업그레이드
public ParticleSystem[] effectsPrefabs; // 프리팹 넣어놓기
public ParticleSystem[] effects; // 관리용 변수
public string[] effectGameObjectNames;
[Header("Toy Control")]
public int curToyIdx = -1; // 현재 선택된 장난감
public RuntimeAnimatorController[] clayToyAnimators; // 가구랑 상호작용하는 애니메이터
public string[] toyInfo; // 가구를 클릭하면 안내 판넬에 띄울 내용
private void Awake()
{
// 싱글톤 패턴
if (instance != null && instance != this)
{
// 이미 존재하면 새로 만든거 없애
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject); // 얘는 다른 씬으로 전환되어도 안 없앨 거임
poolManager = GameObject.Find("PoolManager").GetComponent<PoolManager>(); // 풀매니저 찾아서 할당
for (int i=0; i < effectsPrefabs.Length; i++)
{
// 이펙트 생성해서 넣어놓기
effects[i] = Instantiate(effectsPrefabs[i], GameObject.Find(effectGameObjectNames[i]).transform);
effects[i].gameObject.SetActive(false); // 비활성화
}
}
void Start()
{
gameExitButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Exit Button").GetComponent<Button>();
gameExitButton.onClick.AddListener(DataManager.instance.SaveGameData); // 게임 데이터 저장 메서드 연결
gameExitButton.onClick.AddListener(GameExit); // 게임 종료 메서드 연결
GetLove(0);
GetGold(1000);
// 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
if (DataManager.instance.data.unlockClays != null)
{
// 저장된 데이터 반영해서 가져오기
for (int i = 0; i < unLockedClays.Length; i++)
{
unLockedClays[i] = DataManager.instance.data.unlockClays[i];
catchedClays[i] = DataManager.instance.data.catchClays[i];
}
}
// 저장된 게임 수치 데이터가 있는 경우 데이터 가져와서 반영
if (DataManager.instance.data.valueDatas != null)
{
// 저장된 데이터 반영해서 가져오기
gold = DataManager.instance.data.valueDatas.gold;
love = DataManager.instance.data.valueDatas.love;
clayHouseLevel = DataManager.instance.data.valueDatas.clayHouseLevel;
clayClickLevel = DataManager.instance.data.valueDatas.clayClickLevel;
curPossibleClayNum = DataManager.instance.data.valueDatas.curPossibleClayNum;
}
// 메서드 연결하기
DataManager.instance.OnSave -= SetSaveData; // 중복 방지하기 위해 먼저 빼줌
DataManager.instance.OnSave += SetSaveData;
}
// 재화 얻는 함수
public void GetGold(float capacity)
{
// 이미 코루틴이 종료되지 않은 중에 동일한게 또 들어오면 겹쳐서 반영이 돼서 이상하게 될 수 있으므로 null 인지 판단해야함.
if (updateTextUICoroutine != null)
{
StopCoroutine(updateTextUICoroutine);
}
updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("gold", gold + capacity, gold));
gold += capacity;
//PlayerPrefs.SetFloat("Gold", gold); // 데이터 저장
}
public void GetLove(float capacity)
{
// 이미 코루틴이 종료되지 않은 중에 동일한게 또 들어오면 겹쳐서 반영이 돼서 이상하게 될 수 있으므로 null 인지 판단해야함.
if (updateTextUICoroutine != null)
{
StopCoroutine(updateTextUICoroutine);
}
updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("love", love + capacity, love));
love += capacity;
//PlayerPrefs.SetFloat("Love", love); // 데이터 저장
}
public void SetSaveData()
{
int size = unLockedClays.Length;
DataManager.instance.data.unlockClays = new List<bool>();
DataManager.instance.data.catchClays = new List<bool>();
for (int i = 0; i < size; i++)
{
DataManager.instance.data.unlockClays.Add(unLockedClays[i]); // 해금 여부 저장
DataManager.instance.data.catchClays.Add(catchedClays[i]); // 포획 여부 저장
}
// 수치 데이터 저장
DataManager.instance.data.valueDatas = new ValueDatas();
DataManager.instance.data.valueDatas.gold = gold;
DataManager.instance.data.valueDatas.love = love;
DataManager.instance.data.valueDatas.clayHouseLevel = clayHouseLevel;
DataManager.instance.data.valueDatas.clayClickLevel = clayClickLevel;
DataManager.instance.data.valueDatas.curPossibleClayNum = curPossibleClayNum;
}
public void GameExit()
{
// 게임 종료
Application.Quit();
}
public void StartInfoPanel(string text)
{
// 연결된 메서드 실행시키기
OnSetInfoPanel?.Invoke(text);
}
}
2.4 GameManager 스크립트 변경 사항 설명
1. 변수
장난감과 점토의 상호작용 애니메이션을 수행하기 위해 필요한 변수들을 선언했다. 각 변수의 의미는 주석과 같다.
[Header("Toy Control")]
public int curToyIdx = -1; // 현재 선택된 장난감
public Clay curSelectedClay; // 현재 선택된 점토
public RuntimeAnimatorController[] clayToyAnimators; // 가구랑 상호작용하는 애니메이터
public string[] toyInfo; // 가구를 클릭하면 안내 판넬에 띄울 내용
2.5 Clay 스크립트
using System.Collections;
using System.Collections.Generic;
using TreeEditor;
using UnityEngine;
public class Clay : MonoBehaviour
{
[Header("Clay Data")]
public float[] loves; // 점토를 클릭했을 때 얻는 애정 수치(1~5 레벨)
public int[] touchCnts; // 해당 요소만큼 클릭되면 레벨업
public int clayLevel = 1; // 1레벨에서 시작(5레벨까지 있음)
public int curTouchCnt = 0;
public int clayIdx; // 점토 소환할 때 필요한 인덱스
public int buyPrice; // 구매 가격
public int sellPrice; // 판매 가격
public string clayName; // 점토 이름
public int effectIdx = 0;
[Header("Animation")]
Animator anim; // 점토가 터치될 때 애니메이션 실행하기 위함
public RuntimeAnimatorController[] animators;
public float[] claySizes;
[Header("Sprites")]
public Sprite clay; // 점토 모습
public Sprite animal; // 다 자란 동물 모습
public GameObject shadow; // 이거 가구 애니메이션 중일 때는 끄도록..
[Header("Drag Clay")]
public float targetTime = 1; // 드래그 시작할 수 있는 시간
public float curTime; // 현재 시간
public Vector3 prevPos; // 점토를 드래그 하기 전 위치
public bool isDragging; // 드래그 중인지 확인
[Header("Pool Manager")]
public PoolManager poolManager;
[Header("ToyAnimation")]
public bool isUsed; // 이미 사용되고 있는 중이면 클릭 버튼이 안 먹히도록 하는데 이용할 것..
private void Awake()
{
anim = GetComponent<Animator>();
shadow = transform.GetChild(0).gameObject;
}
private void Start()
{
poolManager = GameObject.Find("PoolManager").GetComponent<PoolManager>();
SetClay(); // 점토 정보 반영 세팅(애니메이션, scale)
}
// 점토가 터치되면 저절로 호출됨
private void OnMouseDown()
{
// toy 애니메이션이 수행되고 있는 중이면 빠져나가도록..
if (isUsed) return;
prevPos = transform.position; // 드래그 하기 전 위치 저장
poolManager.curClay = gameObject.GetComponent<Clay>(); // 현재 선택된 클레이 지정
Debug.Log("터치됨!");
anim.SetTrigger("doTouch");
GameManager.instance.GetLove(loves[clayLevel - 1] * GameManager.instance.clayClickLevel); // 레벨에 맞는 수치를 함수로 넘겨주기
curTouchCnt++;
LevelUpClay();
}
private void LevelUpClay()
{
// 이미 점토의 레벨이 최고 레벨에 도달했으면 밑으로 진입 안 하도록..
if (clayLevel != 5)
{
if (curTouchCnt == touchCnts[clayLevel - 1])
{
clayLevel++; // 레벨 1 증가
curTouchCnt = 0; // 초기화
SetClay(); // 점토 정보 반영 세팅(애니메이션, scale)
PlayEffect(effectIdx);
if (clayLevel == 5)
gameObject.GetComponent<SpriteRenderer>().sprite = animal;
}
}
}
private void PlayEffect(int idx)
{
if (GameManager.instance.effects[idx].gameObject.activeSelf == false)
GameManager.instance.effects[idx].gameObject.SetActive(true); // 활성화하기
GameManager.instance.effects[idx].transform.position = transform.position; // 이펙트 위치를 점토 위치로 바꿔주기
GameManager.instance.effects[idx].Play(); // 실행시키깅
}
// 점토 드래그할 때 호출되는 함수
private void OnMouseDrag()
{
// toy 애니메이션이 수행되고 있는 중이면 빠져나가도록..
if (isUsed) return;
curTime += Time.deltaTime;
isDragging = true;
// 만약 현재 시간이 targetTime 보다 크거나 같다면 점토가 마우스를 따라오도록 하기..
if (curTime >= targetTime)
{
// 마우스 위치를 가져온 후 Z 축을 카메라와의 거리로 설정한다
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = Camera.main.WorldToScreenPoint(transform.position).z;
// 스크린 좌표를 월드 좌표로 변환
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
// 오브젝트의 위치를 마우스의 월드 좌표로 이동
transform.position = worldPosition;
// 이 경우에는 UI 보다도 앞에 갈 수 있도록..
gameObject.GetComponent<SpriteRenderer>().sortingOrder = 10;
}
}
// 점토 내려놓을 때 호출되는 함수
private void OnMouseUp()
{
// toy 애니메이션이 수행되고 있는 중이면 빠져나가도록..
if (isUsed) return;
curTime = 0;
isDragging = false;
if (transform.position.x < -6.5 || transform.position.x > 6.5 || transform.position.y < -3 || transform.position.y > 0.6)
transform.position = prevPos;
// 다시 UI 보다 아래로 가도록..
gameObject.GetComponent<SpriteRenderer>().sortingOrder = -1;
if (poolManager.isSellZone == true)
{
poolManager.isPossibleSell = true; // 판매 신호 주기
poolManager.TrySellClay(); // 이벤트 호출
}
if (GameManager.instance.curToyIdx != -1)
StartToyAnim(GameManager.instance.curToyIdx); // 가구 애니메이션 수행
}
public void ResetInfo(int level, int cnt)
{
if (level == 5)
gameObject.GetComponent<SpriteRenderer>().sprite = animal; // 동물 모습으로 바꿔주기..
// 점토 정보 설정
clayLevel = level;
curTouchCnt = cnt;
SetClay();
}
public void ResetInfo()
{
// 점토 정보 초기화
clayLevel = 1;
curTouchCnt = 0;
gameObject.GetComponent<SpriteRenderer>().sprite = clay; // 점토 모습으로 바꿔주기..
SetClay();
}
private void SetClay()
{
anim.runtimeAnimatorController = animators[clayLevel - 1]; // 레벨에 맞는 애니메이터로 바꿔주기..
gameObject.transform.localScale = new Vector3(claySizes[clayLevel - 1], claySizes[clayLevel - 1], 0); // 레벨에 맞게 사이즈 설정
// 끄고 다시 켜서 애니메이터에 반영
anim.gameObject.SetActive(false);
anim.gameObject.SetActive(true);
}
public void StartToyAnim(int idx)
{
shadow.SetActive(false); // 그림자 활성화 끄기
Debug.Log("가구 애니메이션 시작했어여!!");
isUsed = true; // 메서드 빠져나갈 때 false 로 다시 설정해주기..
GetComponent<ClayMove>().isToyActioning = true;
// 애니메이션 시작
anim.runtimeAnimatorController = GameManager.instance.clayToyAnimators[idx];
// 코루틴 시작
StartCoroutine(PerformAnimation());
}
private IEnumerator PerformAnimation()
{
float duration = 3f;
yield return new WaitForSeconds(duration);
// 애니메이터 원상태로 돌려놓기
SetClay();
isUsed = false;
GetComponent<ClayMove>().isToyActioning = false;
transform.position = prevPos; // 이전 위치로 돌아가도록..
shadow.SetActive(true); // 그림자 활성화 켜기
Debug.Log("애니메이션 완료!");
}
}
2.6 Clay 스크립트 변경 사항 설명
1. 변수
isUsed 변수를 선언해서 현재 점토가 장난감과 상호작용 하고 있는 중인지를 나타내도록 했다. 이 값을 통해 특정 로직의 수행을 잠시 멈추도록 할 수 있었다.
public GameObject shadow; // 이거 가구 애니메이션 중일 때는 끄도록..
[Header("ToyAnimation")]
public bool isUsed; // 이미 사용되고 있는 중이면 클릭 버튼이 안 먹히도록 하는데 이용할 것..
2. Awake()
장난감과 상호작용 할 때 점토의 자식 오브젝트인 그림자 오브젝트를 꺼야한다. 즉, 이를 조종하기 위해 shadow 변수에 게임 오브젝트를 할당했다.
shadow = transform.GetChild(0).gameObject;
3. Start()
다음과 같은 메서드를 호출하도록 했다. 점토의 애니메이션과 scale 을 설정하는 메서드이다.
SetClay(); // 점토 정보 반영 세팅(애니메이션, scale)
4. OnMouseDown(), OnMouseDrag(), OnMouseUp()
현재 점토가 장난감과 상호작용 하고 있는 중이라면 마우스로 점토를 클릭, 드래그 했을 때 기능이 수행되지 않도록 바로 빠져나오게 했다.
// toy 애니메이션이 수행되고 있는 중이면 빠져나가도록..
if (isUsed) return;
5. ResetInfo(int level, int cnt), ResetInfo()
두 메서드에 SetClay() 메서드를 호출하도록 했다. 기존의 레벨에 맞는 애니메이터로 변경하는 로직을 SetClay() 로 추출해서 만들었다.
SetClay();
6. SetClay()
레벨에 맞는 애니메이터로 변경하는 부분과, 레벨에 맞게 점토의 scale 을 변경하는 부분이 존재한다.
그리고 변경된 점토의 scale 을 애니메이터에 반영하기 위해 애니메이터의 활성화를 껐다가 다시 켜도록 했다.
private void SetClay()
{
anim.runtimeAnimatorController = animators[clayLevel - 1]; // 레벨에 맞는 애니메이터로 바꿔주기..
gameObject.transform.localScale = new Vector3(claySizes[clayLevel - 1], claySizes[clayLevel - 1], 0); // 레벨에 맞게 사이즈 설정
// 끄고 다시 켜서 애니메이터에 반영
anim.gameObject.SetActive(false);
anim.gameObject.SetActive(true);
}
7. StartToyAnim(int idx)
장난감과 점토의 상호작용 애니메이션을 시작하도록 하는 메서드이다.
그림자의 활성화를 끄고, isUsed 과 ClayMove 의 isToyActioning 값을 true 로 설정하여 상호작용이 시작되었음을 알렸다.
후에 매개변수로 받은 장난감의 인덱스로 해당하는 애니메이터를 가져와서 할당해주었다. 그 후 코루틴을 시작했다.
public void StartToyAnim(int idx)
{
shadow.SetActive(false); // 그림자 활성화 끄기
Debug.Log("가구 애니메이션 시작했어여!!");
isUsed = true; // 메서드 빠져나갈 때 false 로 다시 설정해주기..
GetComponent<ClayMove>().isToyActioning = true;
// 애니메이션 시작
anim.runtimeAnimatorController = GameManager.instance.clayToyAnimators[idx];
// 코루틴 시작
StartCoroutine(PerformAnimation());
}
8. PerformAnimation()
코루틴이다. 애니메이션이 수행되는 3초 동안 기다릴 수 있도록 만든 코루틴이다.
애니메이션의 수행이 종료되면 애니메이터의 상태를 원상태로 돌려놓고, isUsed 와 ClayMove 의 isToyActioning 값을 false 로 설정하여 상호작용이 종료되었음을 알렸다.
그리고 드래그 해서 가져오기 이전의 점토 위치로 돌아가도록 했다.
private IEnumerator PerformAnimation()
{
float duration = 3f;
yield return new WaitForSeconds(duration);
// 애니메이터 원상태로 돌려놓기
SetClay();
isUsed = false;
GetComponent<ClayMove>().isToyActioning = false;
transform.position = prevPos; // 이전 위치로 돌아가도록..
shadow.SetActive(true); // 그림자 활성화 켜기
Debug.Log("애니메이션 완료!");
}
2.7 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 bool isToyActioning; // 현재 가구 애니메이션이 수행되고 있는 중인지 확인
public GameObject targetPosObj; // 중점에 있는 게임 오브젝트 할당해주기
private Coroutine randomMoveCoroutine;
[Header("Animation")]
public Animator anim;
void Start()
{
// 코루틴의 핸들을 저장하고 이를 통해 중단하는 것이 좋음
randomMoveCoroutine = StartCoroutine(RandomMove());
anim = GetComponent<Animator>();
targetPosObj = GameObject.Find("TargetObj"); // 게임 오브젝트 찾아서 할당하기
}
private void OnEnable()
{
// 코루틴 중복 호출 막기
if (randomMoveCoroutine != null)
{
StopCoroutine(randomMoveCoroutine);
}
randomMoveCoroutine = StartCoroutine(RandomMove());
isReturning = false; // 활성화 시 반환 상태 초기화
// 애니메이션도 초기황..
if (anim != null)
{
anim.SetBool("isWalk", false);
}
}
void Update()
{
// 점토가 움직일 수 있는 상황일 때만 아래 로직 수행하도록..
if (!isReturning && !isToyActioning)
{
// 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;
}
// 점토가 가구 애니메이션을 수행하지 않을 때 아래 로직 수행하도록..
if (!isToyActioning)
{
// Idle 상태가 아니라면 walk 애니메이션 수행하도록
if (moveDirX != 0 || moveDirY != 0)
{
anim.SetBool("isWalk", true);
}
else
{
anim.SetBool("isWalk", false);
}
}
}
// 점토가 비활성화 될 때..
private void OnDisable()
{
// 코루틴 종료
if (randomMoveCoroutine != null)
{
StopCoroutine(randomMoveCoroutine);
}
}
// 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 (isToyActioning) return; // 만약 지금 토이 액션 중이면 그냥 나가도록..
if (isReturning) return; // 만약 이미 돌아가고 있는 중에 또 부딪히면 걍 나가라..
// 부딪힌 게임 오브젝트의 태그가 Target 이면 ReturnToCenter 코루틴 호출하도록
if (collision.gameObject.CompareTag("Wall"))
{
// 랜덤 이동 코루틴은 중단시키기
StopCoroutine(randomMoveCoroutine);
// 중앙 복귀 코루틴 호출
StartCoroutine(ReturnToCenter());
}
}
}
2.8 ClayMove 스크립트 변경 사항 설명
1. Update()
기존 if 문에 !isToyActioning 조건을 추가해줬다. 즉, 점토가 움직일 수 있는 상황일 때만 로직을 수행할 수 있도록 했다. isToyActioning 의 값이 true 라는 것은 점토가 장난감과 상호작용 하는 중이므로 움직일 수 없는 상태임을 의미한다.
그리고 점토가 가구 애니메이션을 수행하는 중에는 기존 일반 애니메이터를 사용하지 않으므로 SetBool 을 이용하는 로직을 수행하지 않도록 하기 위해 if 문을 추가로 더해줬다.
void Update()
{
// 점토가 움직일 수 있는 상황일 때만 아래 로직 수행하도록..
if (!isReturning && !isToyActioning)
{
// 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;
}
// 점토가 가구 애니메이션을 수행하지 않을 때 아래 로직 수행하도록..
if (!isToyActioning)
{
// Idle 상태가 아니라면 walk 애니메이션 수행하도록
if (moveDirX != 0 || moveDirY != 0)
{
anim.SetBool("isWalk", true);
}
else
{
anim.SetBool("isWalk", false);
}
}
}
2. OnCollisionEnter2D(Collision2D collision)
점토가 벽에 닿으면 중앙으로 복귀하도록 하는 메서드이다. 근데 점토가 장난감과 상호작용 중이라면 중간으로 돌아가는 로직이 수행되면 안되기 때문에 맨 처음에 진입하자마자 isToyActioning 의 값을 확인하도록 했다. 만약 점토가 애니메이션 중이라면 바로 빠져나가도록 했다.
// 콜라이더 컴포넌트를 가진 게임 오브젝트랑 부딪히면 이 함수로 진입
private void OnCollisionEnter2D(Collision2D collision)
{
if (isToyActioning) return; // 만약 지금 토이 액션 중이면 그냥 나가도록..
if (isReturning) return; // 만약 이미 돌아가고 있는 중에 또 부딪히면 걍 나가라..
// 부딪힌 게임 오브젝트의 태그가 Target 이면 ReturnToCenter 코루틴 호출하도록
if (collision.gameObject.CompareTag("Wall"))
{
// 랜덤 이동 코루틴은 중단시키기
StopCoroutine(randomMoveCoroutine);
// 중앙 복귀 코루틴 호출
StartCoroutine(ReturnToCenter());
}
}
OnCollisionEnter2D(Collision2D collision)
3. 결과물