Posted in: Unity, 프로젝트 When the Spring comes

8. 플레이어 기본 근접 공격

몬스터 추적 기능을 만들었으니 몬스터가 플레이어를 때리는 기본 공격을 먼저 만들려고 하는 도중 ‘플레이어 기본 공격이 우선 아닌가?’싶은 생각이 나서 플레이어 기본 근접 공격을 만들기 시작했다.

  1. 근접 공격의 범위는 원뿔형
  2. 공격 범위, 속도, 타격수(한 번 휘두를 때 2번 때리는 것같은?)는 제어 가능하게
  3. 2타 ~ 3타 공격이 이어짐
  4. 플레이어가 마지막으로 움직였던 방향이 공격 방향

위 조건들이 기획자님이 요구했던 사항들이다. 위 사항들중에 개발 기간이 오래 걸릴 것 같은 요소는 우선 제외하고 만들어도 된다고 하셨다.

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

public class Weapon_1 : MonoBehaviour
{
    public float viewAngle;
    public float distance;
    private float maxDistance;
    public float damage;
    public float maxDamage;
    public LayerMask targetMask;
    public LayerMask obstacleMask;

    public MonsterStat monsterStat;

    private void Start() {
        maxDistance = 10.0f;
        maxDamage = 100.0f;
    }

    private void Update() {
        DrawView();

        if(Input.GetKeyDown(KeyCode.K))
            FindVisibleTargets();
    }

    public Vector3 DirFromAngle(float angleInDegrees) {

        if(GameManager.GetInstance().playerDirection == new Vector3(-1.14f, 0, 1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(0, 0, 2.0f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(1.14f, 0, 1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(-2.0f, 0, 0))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(2.0f, 0, 0))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(-1.14f, 0, -1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(0, 0, -2.0f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(1.14f, 0, -1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;

        return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
    }

    public void DrawView() {
        Vector3 leftBoundary = DirFromAngle(-viewAngle / 2);
        Vector3 rightBoundary = DirFromAngle(viewAngle / 2);

        Debug.DrawLine(transform.position, transform.position + leftBoundary * distance, Color.blue);
        Debug.DrawLine(transform.position, transform.position + rightBoundary * distance, Color.blue);
    }

    public void FindVisibleTargets() {
        Collider[] targets = Physics.OverlapSphere(transform.position, distance, targetMask);

        for(int i = 0; i < targets.Length; ++i)
        {
            Transform target = targets[i].transform;

            Vector3 dirToTarget = (target.position - transform.position).normalized;

            if(Vector3.Dot(/*플레이어 앞방향 단위벡터*/, dirToTarget) > Mathf.Cos((viewAngle / 2) * Mathf.Deg2Rad))
            {
                float distToTarget = Vector3.Distance(transform.position, target.position);

                if(!Physics.Raycast(transform.position, dirToTarget, distToTarget, obstacleMask))
                {
                    Debug.DrawLine(transform.position, target.position, Color.red);
                }
            }

            //공격기능
            monsterStat = targets[i].GetComponent<MonsterStat>();
            monsterStat.health -= damage;
            Debug.Log("기본 공격! 남은 적 체력 : " + monsterStat.health);
        }
    }
}

원뿔형 공격 범위가 가장 큰 난제였다. 어떻게 제작할까 고민하다가 도통 좋은 방법이 안떠올라 검색을 해보니 벡터 내적을 활용하면 가능하다는 글을 보았다.

솔직히 벡터 내적에 관한 내용은 아직 이해가 잘 안되서 좀 더 공부해보고 유니티 카테고리에 정리해야겠다.

플레이어의 시야각 변수를 만들고 플레이어가 바라보는 방향의 단위벡터, target 위치에서 플레이어 위치 뺄샘의 단위벡터간의 내적이 cos(시야각/2)보다 커야 시야 내에 존재한다. 라는 걸 유니티 코드로 바꾸면 된다.

위 조건에 부합하는 코드를 제작하여 범위안에 존재하는 컬라이더를 받아오면 원뿔형 기본 공격을 만들 수 있다. 컬라이더를 OverlapSphere로 받아오고 범위의 길이를 담당하는 변수를 OverlapSphere 길이로 지정하면 원뿔형이 된다.

공격속도와 타격수는 개발이 오래 걸릴 것 같아서 보류하였다.

플레이어가 마지막으로 바라보는 방향을 공격 방향으로 정해야 하는데 이건 GameManager를 통해 쉽게 구현하였다. 플레이어가 움직이던 방향을 게임매니저가 매순간 받아오기만 하면 된다.

2타 ~ 3타 공격이 이어지는 건 아무래도 애니메이션으로 구현을 하는 게 좋을 것 같다. 적절한 애니메이션과 공격마다 텀을 주는 시간을 의미하는 변수들을 활용하면 만들 수 있을 것이다. 그리고 아직 공격 애니메이션에 사용될 스프라이트도 아직이다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다