반응형
SMALL
사실상 처음으로 혼자 써본 (선생님 코드를 좀 많이 참고 하긴 했지만 ) 3D프로젝트 였다.
코루틴을 많이 이용하다보니 코루틴에 굉장히 취약했다는 것도 알게되었고 RayCast 를 이용한 물체 충돌 감지 및 이벤트 트리거 그리고 캐릭터 회전 과 관련하여 많은 경험을 얻을수 있었다.
코드가 좀 길다보니 하나하나 새로 배운걸 정리해보면
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RectMain : MonoBehaviour
{
public Player player;
public Car Car;
public Transform targetTrans;
public Transform[] waypoints;
private int idx;
public GameObject uICArMenu;
public GameObject popUp;
private void Start()
{
//목표 위치를 자겨오고 플레이어에게 이동을 명령하자
var tPos = this.waypoints[idx].position;
//플레이어가 이동을 완료했을때 콜백
this.player.OnMoveComplete = () =>
{
idx++;
if (idx > 3) idx = 0;
tPos = this.waypoints[idx].position;
this.player.Move(tPos, this.waypoints[idx]);
};
this.player.OnMoveComplete();
this.Car.isLooking = (x) => //true 보고있다 false 안본다
{
if (!x)
{
this.player.coroutine = null;
this.idx--;
this.player.OnMoveComplete();
}
else if(x)
{
Debug.Log("코루틴 정지");
player.coroutine = StartCoroutine(this.player.MoveStop());
}
};
this.Car.popUp = (x) =>
{
if (x)
{
popUp.gameObject.SetActive(x);
}
else popUp.gameObject.SetActive(x);
};
}
}
- 플레이어와 차량의 충돌을 감지했던 RectMain 스크립트
- 웨이포인트 배열 인덱스 요소를 목표로 캐릭터를 움직여주고 목표 오브젝트를 볼시에는 오브젝트의 코루틴타입의 변수에 코루틴 (StartCorutine 을 하면 반환값으로 corutine 타입을 반환한다) 담아줌으로서 코루틴을 멈추게 하였다. idx를 이용하였기에 멈출때 마다 idx-- 후위 연산자를 붙여 기존에 추적하던 목표를 잃지 않게 하였다.
- 목표 오브젝트를 더 이상 처다보지 않을시 액션으로 부울 false값을 반환시켜 플레이어 스크립트의 corutine 변수는 null 값으로 만들어주고 다시 움직이게 액션을 발동하게 만들었다.
- 반대로 목표를 보지 않으면 코루틴은 계속 돌게 된다.
using System.Collections;
using UnityEngine;
public class Player : MonoBehaviour
{
public System.Action OnMoveComplete;
public Coroutine coroutine;
public bool isStopped = false;
public void Move(Vector3 tPos, Transform target)
{
if (this.coroutine == null)
{
this.StartCoroutine(this.MoveRoutine(tPos, target));
}
}
public IEnumerator MoveStop()
{
yield return null;
}
public IEnumerator MoveRoutine(Vector3 tPos, Transform target)
{
var controller = this.GetComponent<CharacterController>();
bool turned = false;
while (true)
{
if (this.coroutine != null) { break; }
FollowTarget(target);
controller.Move(controller.transform.forward * 2f * Time.deltaTime);
float distance = Vector3.Distance(this.transform.position, tPos);
if (distance <= 1.5f)
{
break;
}
yield return null;
}
if (this.coroutine == null)
{
Debug.Log("이동완료");
this.OnMoveComplete();
}
}
public void FollowTarget(Transform target)
{
Vector3 dir = target.transform.position - this.transform.position;
this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 1f);
}
}
- 플레이어는 멈추는 코루틴과 움직이는 코루틴 그리고 추가적으로 타겟을 추적하는 메서드를 추가해주었다.
- 타겟을 추적할땐 캐릭터 컨트롤러 컴포넌트의 Move 메서드를 사용하였고 이는 방향,속도,시간을 곱해 translate와 유사하게 이동시킬수 있었다.
- 만약 목표와의 거리가 1.5f이상 가까워진다면 while문을 중단시켰고 코루틴변수가 null 값이 아닐때 는 계속 이동시키는 루틴으로 코루틴을 만들었다.
- 목표가기전 계속해서 타겟 추적 함수를 이용해 목표를 부드럽게 바라보게 하였는데 원했던건 멈춘다 -> 바라본다 -> 바라보기가 끝나면 다시 이동 , 이런 패턴으로 하려고 했지만 정확하게 어떤 기준으로 목표를 바라본 것인지 아직 확실하게 알지못하여 좀더 연구해 보아야 하는 부분인듯하다.
- 타겟 추적 함수는 Quaternion 구조체의 Lerp메서드를 사용하였는데 요약하면 오리진의 로테이션, 목표의 방향(LookRotation 함수 사용) 이후 시간값*속도 를 이용하여 물체를 월드 좌표를 기준으로 돌게 만드는 함수였다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Car : MonoBehaviour, IPointerEnterHandler,IPointerExitHandler,IGvrPointerHoverHandler
{
private EventTrigger evtTrigger;
public System.Action<bool> isLooking;
public System.Action<bool> popUp;
public void Awake()
{
this.evtTrigger = this.GetComponent<EventTrigger>();
this.AddTrigger(EventTriggerType.PointerEnter,this.OnLookAt);
this.AddTrigger(EventTriggerType.PointerExit, this.OnLookAt);
}
private void AddTrigger(EventTriggerType eType,System.Action<bool> callback)
{
var entry = new EventTrigger.Entry();
entry.eventID = eType;
entry.callback.AddListener((evtData) =>{
var isLookAt = eType == EventTriggerType.PointerEnter;
callback(isLookAt);
});
this.evtTrigger.triggers.Add(entry);
}
//IPointerEnterHandler 구현
public void OnLookAt(bool isLookAt)
{
}
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("<color=red>Enter!!!!!!!!</color>");
this.isLooking(true);
this.popUp(true);
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("<color=blue>Exit!!!!!!!!!</color>");
this.isLooking(false);
this.popUp(false);
}
public void OnGvrPointerHover(PointerEventData eventData)
{
//Debug.Log("<color=green>Hover!!!!!!</color>");
}
}
- 목표였던 차량을 바라볼시에는 이벤트 트리거를 이용해 이벤트 트리거의 이넘 타입 상수를 이용하여 이벤트 아이디 (어떤 상태의 이벤트 인지) 를 매게 변수로 받아 해당 상태를 콘솔창에 띄우거나 경우에 따라 다른 액션 대리자를 호출하는 방식으로 사용하였다.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class RayContorller : MonoBehaviour
{
public Transform trans;
private Ray ray;
private RaycastHit hit;
public float dist = 1000f;
public Player player;
public Image img;
private bool isLoading;
private bool isClicked;
void Start()
{
this.trans = this.GetComponent<Transform>();
}
void Update()
{
this.ray = new Ray(this.trans.position, this.trans.forward);
Debug.DrawRay(this.ray.origin, this.ray.direction * this.dist, Color.green);
var mask = 1 << 7 | 1 << 8;
if (Physics.Raycast(this.ray, out this.hit, this.dist, mask) && !this.isClicked)
{
if (!this.isLoading)
{
this.StartCoroutine(StartFillamount());
}
}
else
{
this.isLoading = false;
this.img.fillAmount = 1;
}
}
public IEnumerator StartFillamount()
{
this.img.fillAmount = 0;
this.isLoading = true;
Debug.Log("StartFillamount 코루틴 시작");
while (isLoading)
{
if (this.img.fillAmount == 1)
{
this.isClicked = true;
this.StartCoroutine(this.waitForSecons());
break;
}
this.img.fillAmount += 1.0f / 500.0f;
if (!this.isLoading) { this.StopCoroutine(StartFillamount()); }
yield return null;
}
this.isLoading = false;
}
public IEnumerator waitForSecons()
{
while (this.isClicked)
{
yield return new WaitForSeconds(5f);
this.isClicked = false;
}
}
}
- UI를 RayCast 를 이용하여 Physics.Raycast 함수로 어떠한 물체와 어느정도 포지션에서 부딪혔는지를 걸러내는 방식을 이용하여 (부울값 반환) 코루틴을 실행시키고 렌더러 모드가 월드스페이스 UI의 선택창에서 로딩바를 보여주어 확정하는 형식의 코드를 짜보았다.
- 문제가 있었다면 update 메서드를 사용하다보니 부울 값을 많이 사용하게 되었고 결국 생각보다 정신 없고 헷갈리는 구조가 나오게 되었다는 것이다...
- 제일 시간을 많이 잡아 먹은 부분으로 좀더 코루틴을 이용하여 효과적으로 짤수 있는 방법이 있는지 고민해 보아야겠다.
반응형
LIST
'유니티 엔진 클라이언트 > 개인공부' 카테고리의 다른 글
| [유니티] 3D월드에서의 FPS 시점(1인칭) 움직임 구현 (0) | 2023.02.24 |
|---|---|
| [유니티] VrCity 오브젝트 위치로 이동 & VR City 마무리 (1) | 2023.02.24 |
| [유니티] 캐릭터 + UI 좌표 동기화 (복습) (0) | 2023.02.19 |
| [유니티] 미션 데이터 직렬화 복습 (0) | 2023.02.15 |
| [유니티] UI 스크롤 기능 구현 및 변하지 않는데이터 직렬화 역직렬화 (0) | 2023.02.13 |