Menu
CoddyTech

영웅

Coddy Unity 여정의 Unity 게임 아키텍처 섹션에 포함된 레슨. 50개 중 41번째.

이 장에서는 여정의 마지막 게임인 Arena Survivor를 만듭니다. 적 무리가 벽으로 둘러싸인 경기장으로 쏟아져 들어오고, 검으로 적들을 물리칩니다. 적에는 플레이어를 향해 걸어오는 졸개와 잠시 기다렸다가 돌진하는 구체가 있습니다. 무리를 물리치면 파워업이 남고, HUD에는 체력과 웨이브가 표시됩니다. 체력이 다 떨어지면 플레이가 끝나고 최고 기록이 저장됩니다. 이 게임에서는 이 섹션의 도구를 적절히 활용합니다. 두 종류의 적을 위한 추상 Enemy, 피해를 입을 수 있는 모든 것을 위한 IDamageable, ScriptableObject 에셋으로 만든 웨이브, 인터페이스를 사용하는 픽업, C# 이벤트만 수신하는 HUD, 그리고 JSON 저장 기능입니다. 스크립트 12개는 모두 처음부터 프로젝트에 들어 있습니다. 아직 코드를 작성한 스크립트는 Hero뿐이며, 각 레슨에서 조금씩 더 채워 나갑니다.

영웅의 검은 앞에 있는 대상을 공격하므로, 검을 휘두를 때 "앞에 있는지" 확인해야 합니다. Vector3.Dot(a, b)는 두 벡터에서 서로 대응하는 부분을 곱한 뒤 모두 더합니다. 즉, a.x * b.x + a.y * b.y + a.z * b.z입니다. 길이가 1인 두 방향 벡터의 경우, 이 값은 두 벡터 사이 각도의 코사인입니다. 두 벡터가 같은 방향을 가리키면 1, 30도에서는 0.866, 45도에서는 0.707, 직각에서는 0, 서로 반대 방향을 가리키면 -1입니다. 따라서 "내가 바라보는 방향에서 30도 이내"라는 조건은 각도를 계산하지 않고도 "내적이 0.866 이상"이라는 조건으로 나타낼 수 있습니다. 이 보초 로봇은 플레이어가 10 유닛 이내에 있고 30도 시야각 안에 있으면 경보를 울립니다.

public Transform player;
public float range = 10f;

void Update()
{
    Vector3 toPlayer = player.position - transform.position;
    toPlayer.y = 0f;
    bool near = toPlayer.magnitude <= range;
    bool inView = Vector3.Dot(transform.forward, toPlayer.normalized) >= 0.866f;
    if (near && inView)
    {
        Debug.Log("Alarm!");
    }
}

두 방향 벡터의 길이는 모두 1이어야 합니다. transform.forward는 항상 길이가 1이지만, toPlayer의 길이는 거리에 따라 달라지므로 먼저 정규화합니다. 그렇지 않으면 두 배 멀리 있는 플레이어의 내적 값은 두 배가 됩니다. 그 전에 y를 0으로 설정하므로, 더 높은 통로에 있는 플레이어는 높이가 아니라 지도상 위치를 기준으로 계산됩니다. 부호를 보면 앞인지 뒤인지 알 수 있습니다. 내적이 0보다 크면 앞쪽 절반 어디에나 있는 것이고, 0보다 작으면 뒤쪽 어디에나 있는 것입니다.

challenge icon

챌린지

중급

Arena Survivor를 영웅의 검과 함께 시작하세요. 걷기 기능은 다음과 같이 작성합니다. Move 액션으로 SimpleMove를 사용해 영웅을 이동시키고, 이동하는 방향을 바라보도록 회전시킵니다. Enemy 태그가 지정된 훈련용 허수아비 다섯 개가 주변에 서 있습니다. Inspector에서 Hero에 reach(2)와 swingCooldown(0.5), 그리고 휘두를 때 표시할 베기를 지정합니다.

  • Space 키를 누르거나 마우스 왼쪽 버튼을 클릭하면 휘두릅니다. swingCooldown초마다 최대 한 번만 휘두를 수 있습니다. 쿨다운이 끝나기 전에 누르면 아무 일도 일어나지 않습니다. Keyboard.current와 Mouse.current로 키 입력을 읽고, Swing()을 호출하세요.
  • Swing()에서 베기를 표시한 다음, Enemy 태그가 지정되어 있고 reach 안에 있으며 영웅 앞에 있는 모든 오브젝트를 제거합니다. 사정거리 안에 있다는 것은 Physics.OverlapSphere가 영웅 주변에서 반경 reach로 찾은 콜라이더를 의미합니다. 앞에 있다는 것은 지면을 따라 오브젝트 방향으로 향하는 벡터와 영웅의 전방 벡터 사이의 내적이 0.5 이상인 경우를 말합니다. 즉, 양옆으로 최대 60도까지입니다.

나머지 스크립트는 그대로 두세요. Play를 누르고 허수아비 쪽으로 걸어가 휘두른 다음, Check를 누르세요.

직접 해보기

using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class Hero : MonoBehaviour
{
    [SerializeField] private float speed = 5f;
    [SerializeField] private float reach = 2f;
    [SerializeField] private float swingCooldown = 0.5f;
    [SerializeField] private GameObject slash;

    private CharacterController controller;
    private InputAction moveAction;

    void Start()
    {
        controller = GetComponent<CharacterController>();
        moveAction = InputSystem.actions.FindAction("Move");
    }

    void Update()
    {
        Vector2 input = moveAction.ReadValue<Vector2>();
        Vector3 move = new Vector3(input.x, 0f, input.y);
        controller.SimpleMove(move * speed);
        if (move != Vector3.zero)
        {
            transform.rotation = Quaternion.LookRotation(move);
        }

        // Space 또는 마우스 왼쪽 클릭으로 휘두르며, swingCooldown초마다 최대 한 번만 실행합니다
    }

    void Swing()
    {
        StartCoroutine(ShowSlash());
        // 영웅 앞에서 reach 이내에 있는 Enemy 태그가 붙은 모든 대상을 파괴합니다
    }

    IEnumerator ShowSlash()
    {
        slash.SetActive(true);
        yield return new WaitForSeconds(0.1f);
        slash.SetActive(false);
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Unity 게임 아키텍처의 모든 레슨