일반적으로 유니티에서 게임오브젝트를 이동시키는 방법은 Rigidbody를 이용한 물리 법칙을 통한 계산이나 Transform에 관련된 함수를 사용하는 것이다. 이는 Platformer 장르나 물리법칙이 적용된 게임에는 적합하나 RTS나 Defense장르에서 사용하기는 부적합하다. 그래서 목표지까지 이동이나 다른 GameObject를 따라가는 로직을 구현하기 위해 컴포넌트를 제작했다.
ToGoal함수를 이용해 목표지(Vector)까지 이동가능하며 ToFollow함수를 이용해 적을 따라가게 할 수 있다.
[DisallowMultipleComponent]
public class Mover : MonoBehaviour {
public enum MoveType {
None, Goal, Follow
}
public enum EndEvent {
None, DestoryGameObject, DestroyComponent, DeactiveGameObject
}
public float speed;
public float stopDistance = 0.1f;
public MoveType moveType;
public EndEvent endEvent;
public Vector3 destination;
public GameObject target;
private List<Vector3> nextGoals = new List<Vector3>();
private event Action OnCompleted;
private event Action OnFailed;
public bool IsMoving() {
return moveType != MoveType.None;
}
public Mover SetSpeed(float _speed) {
speed = _speed;
return this;
}
public Mover SetEndEvent(EndEvent _endEvent) {
endEvent = _endEvent;
return this;
}
public void ToGoal(Vector3 _destination, Action completeAct = null) {
destination = _destination;
OnCompleted += completeAct;
moveType = MoveType.Goal;
}
public void ToGoal(List<Vector3> desList, Action completeAct = null) {
if(desList.Count == 0)
return;
ToGoal(desList[0], completeAct);
desList.RemoveAt(0);
nextGoals = desList;
}
public void ToFollow(GameObject _target) {
ToFollow(_target, null, null);
}
public void ToFollow(GameObject _target, Action completeAct) {
ToFollow(_target, completeAct, null);
}
public void ToFollow(GameObject _target, Action completeAct, Action failAct) {
OnCompleted += completeAct;
OnFailed += failAct;
target = _target;
moveType = MoveType.Follow;
}
public void Stop() {
ResetState();
}
private void Update() {
if(moveType == MoveType.None)
return;
if(moveType == MoveType.Goal) {//목표지점까지 이동
transform.position = Vector3.MoveTowards(transform.position, destination, speed * Time.deltaTime);
if(Vector3.Distance(transform.position, destination) < stopDistance) {
if(nextGoals.Count == 0) {
transform.position = destination;
OnCompleted?.Invoke();
ResetState();
ExcuteEndEvent();
return;
} else {
destination = nextGoals[0];
nextGoals.RemoveAt(0);
return;
}
}
} else if(moveType == MoveType.Follow) {//목표 타겟한테 이동
if(target == null) {
OnFailed?.Invoke();
ResetState();
ExcuteEndEvent();
return;
}
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed * Time.deltaTime);
if(Vector3.Distance(transform.position, target.transform.position) < stopDistance) {
transform.position = target.transform.position;
OnCompleted?.Invoke();
ResetState();
ExcuteEndEvent();
}
}
}
private void ResetState() {
moveType = MoveType.None;
OnFailed = null;
OnCompleted = null;
}
private void ExcuteEndEvent() {
if(endEvent == EndEvent.DestroyComponent) {
Destroy(this);
} else if(endEvent == EndEvent.DestoryGameObject) {
Destroy(gameObject);
} else if(endEvent == EndEvent.DeactiveGameObject) {
gameObject.SetActive(false);
}
}
}