LyraLog25 机器人3 归纳总结

机器人的生成

通过体验的 GameFeatureAction 往 GameState 加了一个 UGameStateComponent

他在体验加载完成后会加载固定数量的机器人

void UEqZeroBotCreationComponent::SpawnOneBot()
{
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    SpawnInfo.OverrideLevel = GetComponentLevel();
    SpawnInfo.ObjectFlags |= RF_Transient;
    AAIController* NewController = GetWorld()->SpawnActor<AAIController>(BotControllerClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnInfo);

    if (NewController != nullptr)
    {
        AEqZeroGameMode* GameMode = GetGameMode<AEqZeroGameMode>();
        check(GameMode);

        if (NewController->PlayerState != nullptr) // 通过 bWantsPlayerState 来设置需要PlayerState
        {
            NewController->PlayerState->SetPlayerName(CreateBotName(NewController->PlayerState->GetPlayerId()));
        }

        // 让AI也能跑 OnGameModePlayerInitialized 的流程
        GameMode->GenericPlayerInitialization(NewController);

        // 这里面会找到 FindPlayerStart 然后生成的流程
        GameMode->RestartPlayer(NewController);

        if (NewController->GetPawn() != nullptr)
        {
            if (UEqZeroPawnExtensionComponent* PawnExtComponent = NewController->GetPawn()->FindComponentByClass<UEqZeroPawnExtensionComponent>())
            {
                PawnExtComponent->CheckDefaultInitialization();
            }
        }

        SpawnedBotList.Add(NewController);
    }
}

核心加载逻辑,SpawnActor 出来一个AIController

注意他有PlayerState是因为 AAIController 的构造函数中 bWantsPlayerState = true

RestartPlayer 让 AIController 走了 GameMode的流程

GetDefaultPawnClassForController 根据体验 PawnData 中的Class 配置,然后生成Pawn

APawn* AEqZeroGameMode::SpawnDefaultPawnAtTransform_Implementation(AController* NewPlayer, const FTransform& SpawnTransform)
{
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.Instigator = GetInstigator();
    SpawnInfo.ObjectFlags |= RF_Transient;  // We never want to save default player pawns into a map
    SpawnInfo.bDeferConstruction = true;

    if (UClass* PawnClass = GetDefaultPawnClassForController(NewPlayer))
    {
        if (APawn* SpawnedPawn = GetWorld()->SpawnActor<APawn>(PawnClass, SpawnTransform, SpawnInfo))
        {
            if (UEqZeroPawnExtensionComponent* PawnExtComp = UEqZeroPawnExtensionComponent::FindPawnExtensionComponent(SpawnedPawn))
            {
                if (const UEqZeroPawnData* PawnData = GetPawnDataForController(NewPlayer))
                {
                    PawnExtComp->SetPawnData(PawnData);
                }
            }

            SpawnedPawn->FinishSpawning(SpawnTransform);

            return SpawnedPawn;
        }
    }

    return nullptr;
}

AIController的蓝图中,首先是 Run 行为树

死亡的时候清除敌人,停止行为树逻辑等。

AI感知

AIController 加上 AIPerception 组件。配置一下视觉感知和伤害感知

  • 伤害感知需要在角色HP变化的时候调用接口report一下。
  • 角色类需要添加 AIPerceptionStimuliSource 组件,才能被感知,Lyra配置了视觉和听觉

这里判断了伤害不是自己造成的情况下,AIController->Report Damage Event

其他感知

  • Sight / Touch / Team → 配好参数就行,全自动
  • Hearing → 需要在射击、爆炸、脚步等位置调用 MakeNoise()ReportNoiseEvent()
  • Damage → 需要在受伤时手动调用 ReportDamageEvent()(如你的蓝图所做)

AI感知的EQS是这样的,他会找到一个Actor复制给行为树的 TargetEnemy

所有感知类型都会过来,如果需要过滤就配置sense to use

或者自定义。Generator和 Test

EQS概述

Generator → Tests(筛选+打分)→ 最佳 Item

EQS 的核心逻辑就三步:

  1. Generator 生成一堆候选点(Items)
  2. Tests 逐个淘汰或打分
  3. 引擎选出得分最高的 Item

例如调用一个 Grid 的 Generator 配置一下,在周围生成了一堆的 Item

基于一系列Test进行打分过滤,最后有一个排序。引擎选择出一个得分最高的点。

例如这个EQS

/EqZeroCore/Bot/EQS/EQS_MoveAgainstEnnemy.EQS_MoveAgainstEnnemy

在角色四周生成 n * n 个点,生成的规则基于正方形的边长和间距。这么多的Item,

下面加了很多个test,可以配置为过滤或者打分

第一个:PathExist 如果寻路不到路径就过滤掉。

第二个:从候选点向敌人做 Camera 通道的 Line Trace。能看到敌人 = 高分,被遮挡 = 低分

我们自己写了一个 EQS_Contex_TargetEnemy 获得了 TargetEnemy 的位置

除此之外,引擎提供的Contex 有 Queries 是查询者 就是AIController,_Item 指哪来测试的对象,例如Grid的那么多点

第三个: 基于距离,远的高分,并且过远的点还会被过滤掉。

这个组成了AI的行为,如果AI看得到你,就会走到一个尽量远的位置。

有点抽象。

想说的就是:

  1. Generator 生成一堆候选点(Items)
  2. Tests 逐个淘汰或打分
  3. 引擎选出得分最高的 Item

其中 Tests计算中 Context 可以自定义的,上面的 EQS_Contex_TargetEnemy

Generator 也可以自定义,你可以用 grid 生成 n * n 的 Item

/EqZeroCore/Bot/EQS/Generator/EQS_GetAllEnemy.EQS_GetAllEnemy

也可以继承 Env Query Generator Blueprint Base 来重写 Do Item Generation

函数里面需要用 Add Generated Actor 来添加

行为树总览

先分有子弹 和 没子弹

有子弹:

-> AI 感知能否找到敌人

-> 找到了 【攻击】【Set Focus】【BOT寻找可看到玩家的最远的点,走过去】

-> 没有敌人 【Clear Focus】【换弹】

-> 【找武器,走过去】

-> 【用get all actor of class 找敌人,这铁定找得到啊,然后走过去】

-> 休息一下

没子弹:

-> 【clear focus】【更新子弹,如果捡枪了,这里会更新是否有子弹的bool】【找枪走过去】

大致的逻辑是这样。

这Shoot和CheckAmmo的 BTService里面有更新是否有子弹的bool的逻辑

Set Focus 是 AI Controller 的接口,是必须的,会影响 Controller的朝向 从而影响弹道。

总结

总结一下Lyra的AI系统

创建带player state 的 AIController,跑和玩家一样的角色逻辑和组件的 机器人。

行为树逻辑:

有子弹时,基于AI 视觉和伤害感知敌人。成功找到敌人时,射击并向 EQS查询的看得到玩家的远处跑。

如果看不到玩家的时候,会清除目标,换子弹,去找枪补子弹。同时用 GetAllActorOfClass 找敌人然后走过去。来重写看到玩家。

看到后又开始基于感知维护目标了。

没子弹简单,直接去找枪。

上一篇