전체 페이지뷰

2017년 9월 26일 화요일

Unity Tutorial: 2D Roguelike, part 4


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

스크립트를 저장하고 다음 순서로 넘어갑시다.

댓글 없음:

댓글 쓰기