Post

유니티3D - 6

유니티3D - 6

이번 목표

액션 구조 정리, AI 기본 이동

구조 정리

일단 문제였던 애니메이션 델리게이트 부분에서 무기를 장착할 때 보다는 무기를 WeaponComponent에 넣었을 때 즉 무기를 획득했을 때 구독을 하기로 하자
그리고 게임 내에서 UI로 무기를 교체할 것인지 , 주어서 교체할 것인지 생각해봐야한다.
내가 원하는 최종 결과물은 AI에게도 총이 있어 그 소굴을 클리어하는것이 목표니 주어서 사용이 일단 일반적이다.
UI와도 상호작용 가능하게 첫 무기는 선택이 가능하도록 하겠다. 결론은 둘다 구현을 하는 방향으로 생각해보자..

image

애니메이터에 파이어트리거는 하나고 피스톨과 주무기를 구분해야한다. 이럴때 AnimatorOverrideController 이용하는 방법이 있는 거 같다.

여기서 구독/해제 타이밍을 “무기 장착/획득 시점”으로 일관되게 맞추면 이벤트 흐름이 안정된다. 또한 Player와 AI의 발사 원점/방향 제공은 인터페이스(예: AimInfo/GetFireInfo)로 통일하면 이동 주체가 달라도(Agent vs CharacterController) 무기 시스템 결합도가 낮아진다.

AI 기본 이동 (BT)

간단한 BT를 만들어 플레이어 인식부터 확인한다.
패키지 관리자(유니티 레지스트리)에서 Behavior를 설치하면 BT/BB를 만들 수 있다.
Action에 patrol을 추가하고 BB에 게임오브젝트 리스트 변수를 둔다.
NavMesh Surface를 베이크한 뒤 이동시켜보니 0→1→2→0 순환만 된다. 왕복을 위해서는 patrol 대신 NavMeshAgent로 목표를 지정하는 편이 깔끔했다.

  • 코드 구현 Ai 컨트롤러와 Action 노드 코드

일단 컨트롤러에서 Agent를 이용한 이동코드를 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using UnityEngine;
using UnityEngine.AI;
using Unity.Behavior;

public class AIController : MonoBehaviour
{
    private BehaviorGraphAgent behaviorGraphAgent;
    public Transform[] WayPoint;
    private NavMeshAgent agent;

    private int targetIndex = 0;
    private int direction = 1; // 1: 정방향, -1: 역방향

    [SerializeField] private bool isLooping = true; // true: 순환, false: 왕복

    void Start()
    {
        behaviorGraphAgent = GetComponent<BehaviorGraphAgent>();
        agent = GetComponent<NavMeshAgent>();
        agent.SetDestination(WayPoint[0].position);
    }

    void Update()
    {
    }

    public void SetPatrol()
    {
        if (agent.pathStatus == NavMeshPathStatus.PathComplete &&
            agent.remainingDistance - agent.stoppingDistance < 0.1f)
        {
            if(isLooping)
            {
                targetIndex = (targetIndex + 1) % WayPoint.Length;
            }
            else
            {
                if (targetIndex == 0)
                {
                    direction = 1;
                }
                else if (targetIndex == WayPoint.Length - 1)
                {
                    direction = -1;
                }

                targetIndex += direction;
                targetIndex = Mathf.Clamp(targetIndex, 0, WayPoint.Length - 1);
            }

            agent.SetDestination(WayPoint[targetIndex].position);
        }
    }
}

그리고 Action노드에서 일단 실행시키는 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System;
using Unity.Behavior;
using UnityEngine;
using Action = Unity.Behavior.Action;
using Unity.Properties;
using UnityEngine.AI;
using UnityEngine.InputSystem.XR;

[Serializable, GeneratePropertyBag]
[NodeDescription(name: "AIPatrol", story: "[Self] 이동할캐릭터", category: "Action", id: "cc1ab74039e6a0db09484e91cfe64b34")]
public partial class AiPatrolAction : Action
{
    [SerializeReference] 
    public BlackboardVariable<GameObject> Self;

    private NavMeshAgent Agent;
    private AIController Controller;

    protected override Status OnStart()
    {
        GameObject AI = Self.Value;

        if (AI == null) return Status.Failure;

        Controller = AI.GetComponent<AIController>();
        if (Controller == null) return Status.Failure;

        return Status.Running;
    }

    protected override Status OnUpdate()
    {
        Controller.SetPatrol();

        return Status.Running;
    }

    protected override void OnEnd()
    {
    }
}

루핑 true

루핑 false

사실 이 방법이 맞는지는 모르겠다. 그리고 AI애니매이션이 나오지않는다. 그런데 캐릭터무브먼트부분에서 AI와 Player를 구분해야하나? 지금 여러 구조로 인한 머리가 아프다. 천천히 시간을 쓰면서 생각해봐야겠다. 언리얼을 할 때는 이런부분들이 이미 구현되어 있거나 명확히 나뉘어져 있어 편했는데 처음부터 내가 짜려고 하니 정말 어려운 부분들이 많은 거 같다.

추가로 BT(Action) 갱신 주기에서는 회전/발사/상태 전이를 분리해 레이스 컨디션을 줄이는 편이 디버깅과 확장에 유리했다.

This post is licensed under CC BY 4.0 by the author.