모름

07. enemy attack

이번 시간엔 에너미의 공격을 구현했습니다. 일정 범위 내에 들어서면 공격을 하고 다시 제자리로 돌아갑니다.

 

여기서 대칭함수, 코루틴, Lerp 등 다양한 방식으로 공격을 구현했는데요. 추가된 부분은 이렇습니다.

 

많이 변화됐습니다. 하나씩 정리해보겠습니다.

 

if (Time.time > nextTimeAttack)
{
    float sqrDistToTarget = (target.position - transform.position).sqrMagnitude;
    if (sqrDistToTarget < Mathf.Pow(attackDistanceThreshold + myCollisionRadius + targetCollisionRadius, 2))
    {
        nextTimeAttack = Time.time + timeBetweenAttacks;
        StartCoroutine(Attack());
    }
}

공격범위를 체크하고, 일정 간격을 두고 공격을 하는 코드입니다. sqrDistToTarget에 적과 타겟과의 제곱된 거리를 구합니다. 이 거리가 attackDistanceThreshold(공격이 시작되는 거리 마지노선) + myCollisionRadius(적의 몸체 반지름) + targetCollisionRadius(플레이어의 몸체 반지름)의 제곱보다 적은지 체크하여 공격을 시작합니다. 공격은 코루틴으로 호출했습니다.

 

참고로 제곱하여 거리비교하는 이유는 비용적인 측면에서 더 좋기 때문이고, Mathf.Pow함수는 ^2,^3등의 계산을 쉽게 해줍니다.

 

IEnumerator Attack()
{
    currentState = State.Attacking;
    pathfinder.enabled = false;

    Vector3 originalPosition = transform.position;
    Vector3 dirToTarget = (target.position - transform.position).normalized;
    Vector3 attackPosition = target.position - dirToTarget * (myCollisionRadius);

    float attackSpeed = 3;
    float percent = 0;

    skinMaterial.color = Color.red;
        
    while (percent <= 1)
    {
        percent += Time.deltaTime * attackSpeed;
        float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
        transform.position = Vector3.Lerp(originalPosition, attackPosition, interpolation);

        yield return null;
    }

    skinMaterial.color = originalColour;
    currentState = State.Chasing;
    pathfinder.enabled = true;
}

간단한 상태패턴을 이용하여 Attacking상태에 돌입합니다. 그럼 타겟 방향으로 dirToTarget*myCollisionRadius만큼의 방향벡터 위치로 공격을 하게됩니다. 

대략 이 정도 거리가 되겠네요. 내 콜리젼의 반지름만큼 떨어진 거리가 공격지점이 되는겁니다. 어쨋든 그쯤 만큼의 방향벡터만큼 이동하게 되겠구요.

 

그리고 percent를 살펴보겠습니다. Attack에 들어가면 percent는 0으로 초기화 됩니다. 그 후 While문에서 공격애니메이션을 수행합니다. percent += Time.deltaTime * attackSpeed공격합니다. percent가 1이 되면 while문에서 빠져나옵니다. 만약 percent += Time.deltaTime 이었다면, 공격속도는 1을 의미합니다. 하지만 attackSpeed로 3을 곱해줬기 때문에 1초에 3배빨리 도달할 수 있습니다. 즉 현재 공격애니메이션이 보여지는 시간이 0.3초정도인것입니다.

 

그리고 interpolation(보간)이 percent의 값과 같아집니다. 여기선 interpolation의 값을 추출할때 대칭함수를 이용했습니다. 대칭함수 식은 (x^2+x)*4입니다. 그리고 마지막으로 Lerp를 통해 오리지날포지션에서 어택포지션으로 이동합니다. 하지만 보간이 대칭함수이기 때문에 pecent의 0.5지점까지 이동 후 다시 원점으로 되돌아오는 에니메이션을 보여주게됩니다.

 

이 부분은 꽤 이해하기 어려웠습니다. 당장 자고 일어나서 보게되면 뭔 코든가 싶을것같네요 :(

 

IEnumerator UpdatePath()
{
    float refreshRate = .25f;

    while(target != null)
    {
        if(currentState == State.Chasing)
        {
            Vector3 dirToTarget = (target.position - transform.position).normalized;
            Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius + attackDistanceThreshold/2f);
            if (!dead)
            {
                pathfinder.SetDestination(targetPosition);
            }
        }
        yield return new WaitForSeconds(refreshRate);
    }
}

그 다음에 state.Chasing단계일때는 상대와 딱 몸이 붙는 지점이 타겟지점이 되며 플레이어와 몸을 겹치지 않게 해줍니다. 그리고 참고로 State를 사용하는 이유는, 공격하면서 적에게 달라붙거나, 적에게 이동하면서 공격하거나 등 각 상태들이 서로의 행위를 침범하지 않게하기 위함입니다.

 

이번 내용은 많이 어려웠습니다. 스스로 이런 코드를 만들어서 짤 수 있을 때까지 연습해야겠습니다.