Moving Object Script
이번에는 플레이어와 적에게 움직임을 부여해 보겠습니다. 적과 플레이어의 움직임이 기본적으로 같으므로 MovingObject라는 하나의 스크립트를 작성하고 그를 상속하는 식이 될 것입니다.
Scripts 폴더에 MovingObject라는 새 스크립트를 만들고 에디터로 열어봅시다.
이 클래스의 처음에 abstract 수식어를 추가해서 추상클래스로 선언합니다.
public abstract class MovingObject : MonoBehaviour
| cs |
그리고 변수를 선언합니다.
public float moveTime = 0.1f; // 객체가 움직이는 초단위 시간
public LayerMask blockingLayer; // collision이 체크되는 레이어
private BoxCollider2D boxCollider; // 박스콜라이더 레퍼런스
private Rigidbody2D rb2D; //rigidbody 레퍼런스
private float inverseMoveTime; // 이동 계산을 효과적으로 하기 위한 변수
| cs |
다음으로 기본 형성된 Start() 메소드에 protected virtual 선언을 해서 상속받은 클래스가 오버라이드할 수 있게 해주고 내부에서 레퍼런스를 연결합니다.
protected virtual void Start ()
{
boxCollider = GetComponent<BoxCollider2D>();
rb2D = GetComponent<Rigidbody2D>();
// 미리 역수로 계산을 해두어서 계산시 나누기가 아닌 곱하기가 가능하게 함
inverseMoveTime = 1f / moveTime;
}
| cs |
다음으로 이동시 사용될 코루틴을 작성합니다.
protected IEnumerator SmoothMovement(Vector3 end)
{
// 남은 거리의 제곱 계산
// 제곱으로 계산하는 편이 계산이 용이하여 제곱으로 사용
float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
// 0에 아주 근접한 값(엡실론)보다 큰 경우 루틴이 돌아감
while (sqrRemainingDistance > float.Epsilon)
{
// 시간에 비례하여 목적지로 향하는 새 위치 계산
Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
// 계산된 새 위치로 이동
rb2D.MovePosition(newPosition);
// 남은 거리 재계산
sqrRemainingDistance = (transform.position - end).sqrMagnitude;
// 남은 거리가 0에 근접할 때까지 루프
yield return null;
}
}
| cs |
Update() 메소드는 지우고 추상 메소드 하나를 선언합니다.
protected abstract void OnCantMove<T>(T component)
where T : Component;
| cs |
다음으로 bool 타입의 Move라는 메소드를 작성합니다.
// 움직일 수 있는지 판정
protected bool Move(int xDir, int yDir,out RaycastHit2D hit)
{
// 움직임이 시작되는 점을 저장
Vector2 start = transform.position;
// 주어진 인수에 의해 이동지점 계산
Vector2 end = start + new Vector2(xDir, yDir);
// Raycast 계산시 본인의 콜라이더가 맞게 되는 것을 피함
boxCollider.enabled = false;
// start에서 end로 라인을 캐스트 함
hit = Physics2D.Linecast(start, end, blockingLayer);
// boxCollider 다시 켬
boxCollider.enabled = true;
// 캐스팅 된 라인에 걸리는 것이 없어 움직일 수 있다면 코루틴 시작
if (hit.transform == null)
{
StartCoroutine(SmoothMovement(end));
return true;
}
// 걸리는 것이 있으면 움직일 수 없음
return false;
}
| cs |
AttmeptMove()에서는 막는 물체가 있는지를 판단하고 OnCantMove를 호출합니다. 제너릭을 사용한 것은 플레이어나 적이 모두 사용해야 하기 때문입니다. 그 둘이 상호작용할 수 있는 객체가 다르므로 T를 사용하였습니다.
protected virtual void AttemptMove<T>(int xDir, int yDir)
where T:Component
{
// Move가 호출되었을때 Linecast가 때리게 되는 것을 저장할 변수
RaycastHit2D hit;
// 움직일 수 있는지를 저장
bool canMove = Move(xDir, yDir, out hit);
// 걸리는 것이 없으면 코드를 마침
if (hit.transform == null)
return;
// hit된 것의 컴포넌트 레퍼런스를 얻어 옴
T hitComponent = hit.transform.GetComponent<T>();
// 움직일 수 없고 hitComponent가 있으면
if (!canMove && hitComponent != null)
// OnCantMove 호출
OnCantMove(hitComponent);
}
| cs |
스크립트를 저장하고 다음 순서로 넘어갑시다.
댓글 없음:
댓글 쓰기