Unity

[엔진 프로젝트]Enemy Walking Around with BT

HK1206 2024. 11. 1. 01:41

이번에 학교에서 엔진 플젝을 시작하게 되었는데 그쪽에서 적을 만드는 역활을 맡아 적의 구조를 짜보았다. 앞으로도 리펙토링 해볼 것임

참고로 Behavior Desginer 에셋을 사용했음을 알린다.

https://assetstore.unity.com/packages/tools/visual-scripting/behavior-designer-behavior-trees-for-everyone-15277?srsltid=AfmBOopXb1yjcJMAv-zIJkzmU6y0K4EhlQYt1UPaeKHLiPW0htoa63Fb

 

여튼 오늘은 플레이어 주변을 도는 Walking Around Script를 어떻게 만들었는지 소개하겠다.

레퍼런스를 세키로에서 따와서 대충 어떤 느낌인지 보여주겠다.

뭐 잘 안보이긴 하는데 플레이를 두고 주위를 천천히 맴도는 것을 볼 수 있다. 세키로에서는 패턴의 쿨타임이나 보통 원거리 적이 저런 모습을 보이는데 저거랑 잘만 사용하면 좋을 거 같아서 한번 구현해서 공격이랑 같이 넣어봤다.

 

일단 저거를 구현하려면 일정 거리에 도달했을때 플레이어를 기준으로 크게 원으로 돌아야한다. 그 기능을 만들기 위해서 Sin, Cos 함수를 사용하여 원 운동을 구현했다.

그림이 좀 짜치다 지금 보니까 ㅋㅋ

3D에서 구현할 예정이니 Cos은 그대로 X축으로 보면 되고 Sin은 Y축을 Z축으로 바꾸어서 생각하면 된다.

원 운동은 지속적으로 증가하거나 감소하는 각도를 넣어주면 된다.

하지만 우리는 플레이어가 적한테 접근했을 때 그 위치에서 원 운동을 시작해야 하기 때문에 단순히 각도를 정해주면 안된다.

private void Update()
{
    if(!isRun){
        SettingValues(); // 값 초기화
        isRun = true;
    }
    _enemy.MovementCompo.LookToTarget(_playerTrm.position); // 대상 바라보기
    float x = 0f, z = 0f;
    Vector3 point;

    _currentValue += Time.deltaTime * _walkSpeed;
    if(_currentValue >= _walkingLimits){
        _currentValue = 0;
        isRun = false;
        return;
    }

    switch(_currentDir){
        case WalkingDir.Left: // + 
        x = Mathf.Cos((_originAngle - _currentValue) * Mathf.Deg2Rad) * _range;
        z = Mathf.Sin((_originAngle - _currentValue) * Mathf.Deg2Rad) * _range;
        break;
        case WalkingDir.Right: // -
        x = Mathf.Cos((_originAngle + _currentValue) * Mathf.Deg2Rad) * _range;
        z = Mathf.Sin((_originAngle + _currentValue) * Mathf.Deg2Rad) * _range;
        break;
    }
    point = new Vector3(x + _playerPos.x, 0, z + _playerPos.z);
    _enemy.MovementCompo.SetDirectMovement(point, false); // 넣어준 Vector3 값으로 이동해주는 함수
    return;
}

일단 다른 코드를 빼고 원 운동하는 부분만 설명하겠다.

if(_currentValue >= _walkingLimits){
    _currentValue = 0;
    isRun = false;
    return;
}

switch(_currentDir){
    case WalkingDir.Left: // + 
    x = Mathf.Cos((_originAngle - _currentValue) * Mathf.Deg2Rad) * _range;
    z = Mathf.Sin((_originAngle - _currentValue) * Mathf.Deg2Rad) * _range;
    break;
    case WalkingDir.Right: // -
    x = Mathf.Cos((_originAngle + _currentValue) * Mathf.Deg2Rad) * _range;
    z = Mathf.Sin((_originAngle + _currentValue) * Mathf.Deg2Rad) * _range;
    break;
}

보이지는 않지만 처음에 방향을 설정해준다.

public enum WalkingDir{
    Left, Right
}

그리고 Left(시계 방향)라면  Right(반시계 방향)으로 돈다.

gif로 보자 대충 이해될 것임

사실 원 운동은 굉장히 쉽다. 하지만 우리는 특정 위치에서 원 운동을 시작해야 하므로 그 값을 구하는 방법을 알아 보자.

일단 어떻게 구하는지 방법을 설명하자면 플레이어의 Right와 적의 위치에서 플레이어의 방향 백터의 노말을 내적하고 그 내적한 값을 ACos에 넣어주면 된다.

Vector3 dirToOwner = Owner.transform.position - _playerPos;
dirToOwner.y = 0;
_playerDir.y = 0;
_originAngle = Mathf.Acos(Vector3.Dot(dirToOwner.normalized, _playerDir.normalized)) * Mathf.Rad2Deg;

두 백터 사이의 끼인각을 구하는 공식이다. 두 백터의 normal를 내적하고 Acos에 넣으면 된다. 왜 그런지 설명하려면 너무 오래 걸리니 나중에 다루도록 하겠다.

여튼 이런식으로 하면 0~180도만 잘 작동할 거다. 그래서 우리는 외적을 해서 음수라면 360도에서 빼주는 작업을 해야한다. 

if(Vector3.Cross(dirToOwner, _playerDir.normalized).y < 0)
    _originAngle = 360 - _originAngle;

이러면 둘다 구했다. 이 코드들을 깔삼하게 잘 적용시켜보면 잘 된다.

BT 구조는 다음 파트에서 다루겠다.