Game AI

Game AI - Steering Behaviors (2)

tsyang 2021. 8. 7. 21:32

2021.06.19 - [Game AI] - Game AI - Steering Behaviors (1)

Align

Align은 타겟과 방향을 맞추는 동작이다. 타겟의 속도나 위치와는 관계가 없다. Align을 구현할 때 주의할 점은 회전 방향을 정하는 것인데 이것은 타겟과의 방향 차이를 (-180, +180)도로 매핑하면 된다.

 

protected override SteeringOutput GetSteer()
{
    var result = new SteeringOutput();
    var rotation = _target.Orientation - Character.Orientation;

    rotation = MapToRange(rotation); //회전각도를 [-180,180] 사이로 매핑
    var rotationSize = Mathf.Abs(rotation);

    if (rotationSize < _targetRadius)
    {
        result.stopRotation = true;
        return result;
    }

    var targetRotation = MaxRotation;

    if (rotationSize <= _slowRadius)
        targetRotation *= rotationSize / _slowRadius;

    targetRotation *= rotation / rotationSize; //현재 rotation의 부호를 곱해줌

    result.angular = (targetRotation - Character.Rotation) / _timeToTarget;

    var angularAccel = Mathf.Abs(result.angular);
    if (angularAccel > MaxAngularAccel)
        result.angular *= MaxAngularAccel / angularAccel;

    return result;
}

Arrive의 구현과 마찬가지로 두가지 Radius를 사용한다. 사실 반경이라기보다 차이지만. 

 

Align

 

 

 

Velocity Matching

Velocity Matching은 타겟과의 속도를 맞추는 알고리즘이다. 사실 이 행동 자체로는 잘 사용되지는 않지만 다른 동작과 섞어 사용하면 매우 쓸만해진다(예 : 날아가는 세 때).

 

protected override SteeringOutput GetSteer()
{
    var result = new SteeringOutput();

	//속도가 충분히 비슷해졌으면 아무것도 안 함.
    if ((_target.Velocity - Character.Velocity).sqrMagnitude < _stopDiffSqr)
        return result;

    result.linear = _target.Velocity - Character.Velocity;
    result.linear /= _timeToTarget;

    if (result.linear.magnitude > MaxAccel) ;
        result.linear = result.linear.normalized * MaxAccel;

    return result;
}

Velocity Matching

 

 

 


 

 

Delegated Behaviors


앞서 구현했던 동작들은 초대가 되는 기초 동작이라고 할 수 있다. 이런 동작들을 잘 이용하여 추적이나 장애물 피하기 같은 동작을 구현할 수 있다.

기초 동작들을 활용하는 방법으로는 상속을 이용한 방법과 기초 동작을 포함하는 방법이 있다. 책에서는 상속을 이용하였는데 내가 구현한 구조랑은 안 맞아서 나는 포함하는 방법으로 했다. 

 

 

Pursue

Pursue는 움직이는 타겟을 쫓는 동작이다. 이 동작은 움직이는 타겟이 미래에 어디에 있을지를 예측하여 추적한다. 이러한 예측에는 여러 알고리즘들이 있지만 보통은 과하다. 간단한 예측 방법으로는 타겟이 현재 속도로 계속 이동할 것이라고 예측하는 방법이 있다.(Craig Reynolds) 이러한 가정은 짧은 거리에서 합리적이며, 거리가 멀어진다고 해도 못 써먹을 정도는 아니다. 

 

이 동작을 구현할 때는 내가 상대방의 지점에 도달하는 예측시간을 이용한다. 이 시간이 너무 길어지면 오차가 커질 수 있으므로 최대 예측 시간을 둬서 이를 제한한다.

 

protected override SteeringOutput GetSteer()
{
    var distance = (_target.Position - Character.Position).magnitude;
    var speed = Character.Velocity.magnitude;

    //개선점 : 가속도를 고려하하도록 설정하기, 타겟의 현재위치가 아닌 예상 이동 위치까지의 거리를 distance로 하기
    var predictionTime = Mathf.Min(distance / speed , _maxPredictionTime);

    //_arriveFunc은 Arrive 동작이다. 
    //_virtaulTarget은 _arriveFunc의 타겟이다.
    _virtualTarget.Kinematic.position = _target.Position + _target.Velocity * predictionTime;

    return _arriveFunc.GetSteeringOutput();
}

Pursue의 구현에는 내부적으로 Arrive동작을 이용한다. 이 때 Arrive의 타겟을 예측한 가상의 타겟으로 설정한다. 더 정밀하게 예측 시간을 구할 수 있지만, 일단 책에 나온 의사 코드를 그대로 구현했음.

 

Arrive와 비교하면 다음과 같다. Arrive은 파랑, Pursue는 초록, 빨강은 타겟이다.

 

Arrive vs Pursue

 

 

Face

Face는 대상이 존재하는 방향을 바라보는 동작이다. 이 경우 Align 동작에 가상의 타겟을 설정하여 구현할 수 있다.

protected override SteeringOutput GetSteer()
{
    var direction = _target.Position - Character.Position;
    if (direction.sqrMagnitude.FloatEqual(0f)) //위치가 같으면 아무것도 안 함
        return new SteeringOutput();

    _virtualTarget.Kinematic.orientation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
    return _alignFunc.GetSteeringOutput();
}

 

 

Look Where You're Going

이 동작은 이동하는 방향을 서서히 바라보는 동작이다. 자신의 속도를 이용하여 가상의 타겟을 설정한 뒤 Align동작에 타겟을 넘겨준다.

 

protected override SteeringOutput GetSteer()
{
    if (Character.Velocity.sqrMagnitude.FloatEqual(0f))
        return new SteeringOutput();

    _virtualTarget.Kinematic.orientation = Mathf.Atan2(Character.Velocity.x, Character.Velocity.z) 
                                             * Mathf.Rad2Deg;
    return _alignFunc.GetSteeringOutput();
}

 

 

Wander

Wander는 어슬렁거리는 동작이다. 내부적으로 Face 동작을 이용하여 가상의 타겟을 바라본 뒤, 자신이 바라보는 방향으로 이동한다. 이때, 가상의 타겟은 일정 거리 떨어진 가상의 원 위에 존재한다.

protected override SteeringOutput GetSteer()
{
    _wanderOrientation += ASFunc.PseudoBinomialRandom() * _wanderRate;

    var targetOrientation = _wanderOrientation + _prevOrientation;
    _prevOrientation = Character.Orientation;
    _targetPos = Character.Position + _wanderOffset * Character.Orientation.AsVector();
    _targetPos += _wanderRadius * targetOrientation.AsVector();

    _virtualTarget.Kinematic.position = _targetPos;

    var result = _faceFunc.GetSteeringOutput();
    result.linear = MaxAccel * Character.Orientation.AsVector();

    return result;
}

 

가상의 원과 타겟을 시각화해봤다. 

 

책의 내용 그대로 구현했음에도 문제가 많은데.. 제자리에서 빙글빙글 돈다든지 하는 문제가 있다. 회전 각도를 적절히 보정해주는 알고리즘이 필요할 것 같은데 중요한 동작은 아니므로 넘긴다.

 

참고 : Ian Millington, AI for GAMES 3rd edition, CRC press, [63~76p]

'Game AI' 카테고리의 다른 글

Game AI - Combining Steering Behaviors (1)  (0) 2021.10.10
Game AI - Steering Behaviors (3)  (1) 2021.10.03
Game AI - Steering Behaviors (1)  (4) 2021.06.19
Game AI - Kinematic movement alogrithms  (2) 2021.06.13
게임 AI의 구현  (2) 2021.05.31