본문 바로가기

기타/패스트캠퍼스 Unity

유니티 입문 패스트캠퍼스 챌린지 14일차

 이번강의는 Enemy.cs의 코드를 완성하는 단계이다, 지난 시간에 인터페이스만 정의 해 두고 메서드의 몸체를 구현하지않은채로 두었기 때문에 코드를 작성하고 적 비행기의 동작을 확인하게 된다.

 

적 비행기 이동하기(1)

- 이동 관련 메소드 내부 코드 작성

- 등장시 자연스러운 감속을 위해 Vector.SmoothDamp() 사용

- 퇴장시 자연스러운 가속을 위해 Mathf.Lerp() 사용

- 적 비행기를 화면 밖으로 배치

- 임시로 키보드 입력 감지 코드를 추가해서 등장/퇴장 테스트

 

 UpdateMove() 메서드 내부에서는 Vector3에서 재공하는 SmoothDamp라는것을 사용해서 부드러운 움직임을 구현한다. 인자로 현재위치, 타겟위치,  걸리는 시간, 현재 속도, 최대 속도를 전달해야하는데 이것들을 전달하려면 또 계산이 필요하다.

 

 현재위치와 타겟위치 사이의 거리를 구한다. Vector3.Distance를 사용하면 3차원 좌표상의 두 점의 거리를 쉽게 구할 수 있다, 예를들어 타겟의 3차원 좌표 (x,y,z)를 알면 현재 나의 위치로부터 거리를 구하려면 transform.position으로 현재 내 위치를 구해서 Vector3.Distance를 사용할 수 있게된다.

 

 

 만약 거리가 0이라면 이미 도착한 상황이므로 if문을 사용해서 UpdateMove를 종료한다.

 속도는 우선 방향벡터를 구한다 (TargetPosition - transform.position).normalized 를 하게되면 목표점을 향하고 크기가 1인 벡터를 얻게된다. 이 방향 단위벡터에다가 CurrentSpped(속력)을 곱하면 현재 속도를 알 수 있다.

(방향 * 속력 = 속도)

 

말로 설명하려니 참 복잡하다. 어쨋든 SmoothDamp에서 필요해서 이 값들을 구해서 전달했고, 이 값은 내부적으로 알아서 사용 될 예정이다. 이렇게 나온값을 transform.position에 적용한다.

 

    Vector3 CurrentVelocity;

    void UpdateMove()
    {
        float distance = Vector3.Distance(TargetPosition, transform.position);
        if(distance == 0)
        {
            Arrived();
            return;
        }

        //방향 * 속력
        CurrentVelocity = (TargetPosition - transform.position).normalized * CurrentSpeed;

        transform.position = Vector3.SmoothDamp(transform.position, TargetPosition, ref CurrentVelocity, distance / CurrentSpeed, MaxSpeed);
    }

 

UpdateSpeed 내부도 구현해야하는데 우선 Mathf.Lefp에 대해 알아야한다.

Lerp는 두 값 사이에 몇퍼센트정도 지점인지를 구할때 쓰는것으로 입력을 a, b, f 3개로 받는다.

예를들어 a=0, b=10이라면 f에 20%정도 떨어진 지점을 구하고 싶다하면 2가 나오고 50%정도 떨어진 지점을 입력하면 5가 나올것이다. 

 

이를 이용해서 시작 속도, 최대 속도 사이에서 어느 한 지점을 구하고싶은건데 최고 속도에 도달하는 최대 시간 중에 현재까지 흐른 시간은 뺀 지점 (설명이 힘들다 -_-; ㅠㅠ)을 구한다...

이해가 힘들면 그냥 부드럽게 퇴장하기위한 코드를 짠다고 생각하도록하자 ㅠ

 

    void UpdateSpeed()
    {
        CurrentSpeed = Mathf.Lerp(CurrentSpeed, MaxSpeed, (Time.time - MoveStartTime) / MaxSpeedTime);
    }

 

부드럽게 퇴장하도록하는 코드는 여러가지 방법이 있을것이므로 항상 강의와 똑같이 짜야하는것은아니다. 그냥 이런 방식을 연습했다고 생각하자.

 

Vector.SmoothDamp나 Mathf.Lerp의 경우 유니티엔진에서 제공하는것으로 유니티를 계속 하다보면 자연스럽게 익숙해질 예정이다.

 

테스트를위해 L을 누르면 등장, K를 누르면 퇴장 하는식으로 임시 코드를 넣어본다.

 

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.L))
        {
            Appear(new Vector3(7.0f, 0.0f, 0.0f));
        }

        if (Input.GetKeyDown(KeyCode.K))
        {
            Disappear(new Vector3(-15.0f, 0.0f, 0.0f));
        }

        if(CurrentState == State.Appear || CurrentState == State.Disappear)
        {
            UpdateSpeed();
            UpdateMove();
        }
    }

 

추가적으로 Arrived 메서드에서 state를 변경하는 코드를 넣어주자. (도착했다면 등장중, 퇴장중 상태가 아니므로 적절하게 다시 바뀌어야한다.)

 

적 비행기 이동하기(2)

- 등장/전투/퇴장 흐름으로 상태를 변경하도록 코드 변경

 

 테스트용으로 등장 퇴장키를 넣어두었는데 퇴장키는 제거하고 등장키를 눌렀을때 3초 전투를 하고 자동으로 퇴장하도록 코드를 바꿔보자.

 

우선 상태가 너무많아져서 if문 대신 switch문으로 update에서 분기를하도록 했다.

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.L))
        {
            Appear(new Vector3(7.0f, 0.0f, 0.0f));
        }

        switch(CurrentState)
        {
            case State.None:
            case State.Ready:
                break;
            case State.Dead:
                break;
            case State.Appear:
            case State.Disappear:
                UpdateSpeed();
                UpdateMove();
                break;
            case State.Battle:
                break;
        }
    }

 

여기까지 작업한 Enemy.cs 코드가 좀 길지만 설명만가지고 이해하기 힘들어서 전체 코드를 다시 첨부한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public enum State : int
    {
        None = -1,  //사용전
        Ready = 0,  //준비완료
        Appear,     //등장
        Battle,     //전투중
        Dead,       //사망
        Disappear,  //퇴장
    }

    [SerializeField]
    State CurrentState = State.None;

    const float MaxSpeed = 10.0f;

    const float MaxSpeedTime = 0.5f;

    [SerializeField]
    Vector3 TargetPosition;

    [SerializeField]
    float CurrentSpeed;

    Vector3 CurrentVelocity;

    float MoveStartTime = 0.0f;

    float BattleStartTime = 0.0f;

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.L))
        {
            Appear(new Vector3(7.0f, transform.position.y, transform.position.z));
        }

        switch(CurrentState)
        {
            case State.None:
            case State.Ready:
                break;
            case State.Dead:
                break;
            case State.Appear:
            case State.Disappear:
                UpdateSpeed();
                UpdateMove();
                break;
            case State.Battle:
                UpdateBattle();
                break;
        }
    }

    void UpdateSpeed()
    {
        CurrentSpeed = Mathf.Lerp(CurrentSpeed, MaxSpeed, (Time.time - MoveStartTime) / MaxSpeedTime);
    }

    void UpdateMove()
    {
        float distance = Vector3.Distance(TargetPosition, transform.position);
        if(distance == 0)
        {
            Arrived();
            return;
        }

        //방향 * 속력
        CurrentVelocity = (TargetPosition - transform.position).normalized * CurrentSpeed;

        transform.position = Vector3.SmoothDamp(transform.position, TargetPosition, ref CurrentVelocity, distance / CurrentSpeed, MaxSpeed);
    }

    void Arrived()
    {
        CurrentSpeed = 0.0f;
        if (CurrentState == State.Appear)
        {
            CurrentState = State.Battle;
            BattleStartTime = Time.time;
        }
        else if (CurrentState == State.Disappear) 
        {
            CurrentState = State.None;
        }
    }

    public void Appear(Vector3 targetPos)
    {
        TargetPosition = targetPos;
        CurrentSpeed = MaxSpeed;

        CurrentState = State.Appear;
        MoveStartTime = Time.time;
    }

    void Disappear(Vector3 targetPos)
    {
        TargetPosition = targetPos;
        CurrentSpeed = 0;

        CurrentState = State.Disappear;
    }

    void UpdateBattle()
    {
        if (Time.time - BattleStartTime > 3.0f)
        {
            Disappear(new Vector3(-15.0f, transform.position.y, transform.position.z));
        }
    }
}

 

 이번강의에서 적과 플레이어사이의 상호작용을 모두 구현하지는 못했다. 다음강의에서 플레이어와 적의 충돌을 구현할 예정이다.

 

https://fastcampus.co.kr/dev_online_game

 

C#과 유니티로 배우는 게임 개발 올인원 패키지 Online. | 패스트캠퍼스

현직 게임업계 게임 개발자 직강 C# 프로그래밍부터 게임 물리와 수학까지 모두 정복!

fastcampus.co.kr

https://bit.ly/3FVdhDa

 

수강료 100% 환급 챌린지 | 패스트캠퍼스

딱 5일간 진행되는 환급챌린지로 수강료 100% 환급받으세요! 더 늦기전에 자기계발 막차 탑승!

fastcampus.co.kr

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.