LyraLog23 机器人 1

初步整理

有哪些类?

/ShooterCore/Bot/B_AI_Controller_LyraShooter_Passive

/ShooterCore/Bot/B_ShooterBotSpawner.B_ShooterBotSpawner

继承关系

B_AI_Controller_LyraShooter_Passive=>B_ShooterBotSpawner=>ULyraBotCreationComponent

/ShooterCore/Bot/B_AI_Controller_LyraShooter.B_AI_Controller_LyraShooter

/ShooterCore/Bot/B_ShooterBotSpawner_Perf.B_ShooterBotSpawner_Perf

父类是

ALyraPlayerBotController

===

通过体验,在 LyraGameState 上加上了 /ShooterCore/Bot/B_ShooterBotSpawner.B_ShooterBotSpawner

蓝图是配置,创建机器人的逻辑是ULyraBotCreationComponent

规划,我们先搞出 ULyraBotCreationComponent,他配置了一个AIController的类

然后我们关联 ALyraPlayerBotController,比较简单的两个类

UEqZeroBotCreationComponent

核心逻辑是体验加载完成后按数量加载 bot

    for (int32 Count = 0; Count < EffectiveBotCount; ++Count)
    {
        SpawnOneBot();
    }
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);
    }
}

AEqZeroPlayerBotController

补充逻辑

void AEqZeroGameMode::RequestPlayerRestartNextFrame(AController* Controller, bool bForceReset)
{
    if (bForceReset && Controller)
    {
        Controller->Reset();
    }

    if (APlayerController* PC = Cast<APlayerController>(Controller))
    {
        GetWorldTimerManager().SetTimerForNextTick(PC, &APlayerController::ServerRestartPlayer_Implementation);
    }
    else if (AEqZeroPlayerBotController* BotController = Cast<AEqZeroPlayerBotController>(Controller))
    {
        GetWorldTimerManager().SetTimerForNextTick(BotController, &AEqZeroPlayerBotController::ServerRestartController);
    }
}

尝试配置一下

创建

/EqZeroCore/Bot/B_ShooterBotSpawner.B_ShooterBotSpawner

关联

/EqZeroCore/Bot/B_AI_Controller_EqShooter.B_AI_Controller_EqShooter

在体验的 game feature add comp 上

加一个 server only 的 eq game state => B_ShooterBotSpawner

运行一下机器人居然就出来了!!!

为啥呢?

GameMode->RestartPlayer(NewController);

内部会跑到这里

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;
}

GetDefaultPawnClassForController 中我们重写了

UClass* AEqZeroGameMode::GetDefaultPawnClassForController_Implementation(AController* InController)
{
    if (const UEqZeroPawnData* PawnData = GetPawnDataForController(InController))
    {
        if (PawnData->PawnClass)
        {
            return PawnData->PawnClass;
        }
    }

    return Super::GetDefaultPawnClassForController_Implementation(InController);
}

拿的是pawn data 的 class

具体调用

AEqZeroGameMode::SpawnDefaultPawnAtTransform_Implementation AGameModeBase::SpawnDefaultPawnFor_Implementation (GameModeBase.cpp:1228) AGameModeBase::RestartPlayerAtPlayerStart (GameModeBase.cpp:1301) AGameModeBase::RestartPlayer (GameModeBase.cpp:1267) UEqZeroBotCreationComponent::SpawnOneBot (EqZeroBotCreationComponent.cpp:109) UEqZeroBotCreationComponent::ServerCreateBots_Implementation (EqZeroBotCreationComponent.cpp:64) UEqZeroBotCreationComponent::OnExperienceLoaded (EqZeroBotCreationComponent.cpp:36)

行为树相关

有弹药找敌人

黑板:

/EqZeroCore/Bot/BT/BB_Eq_Shooter_Bot.BB_Eq_Shooter_Bot

SelfActor | Actor

TargetEnemy | Actor

MoveGoal | Vector

OutOfAmmo | bool

行为树的创建:

/EqZeroCore/Bot/BT/BT_Eq_Shooter_BotBT_Lyra_Shooter_Bot.BT_Eq_Shooter_BotBT_Lyra_Shooter_Bot

如果有子弹就环境查询找敌人

创建一个找敌人的EQS

/EqZeroCore/Bot/EQS/EQS_AIPerceptionEnemy.EQS_AIPerceptionEnemy

ROOT-> perceived actors 再右键 add test distance

寻找对于类型的character

=== 注意

对于角色Character B Hero Default

要加一个 AIPerceptionStimuliSource

右边 AI Perception 要加上 AISense_Signt 和 AISense_Hearing

完善AIController

完成AIController 类

顺便添加一个AI感知

初始化流程

【常见加 Nav bound volume】按p 可以看到绿色的那个

【'】开调试,没小键盘

[/Script/GameplayDebugger.GameplayDebuggerConfig]
CategorySlot0=One
CategorySlot1=Two
CategorySlot2=Three
CategorySlot3=Four
CategorySlot4=Five
CategorySlot5=Six
CategorySlot6=Seven
CategorySlot7=Eight
CategorySlot8=Nine
CategorySlot9=Zero

【Have Ammo 的条件False,其中 Notify Observer 的 OnValue Change 和 Both 会让后面的都失败。所以一直卡root。所以还给继续写】

ps:

Flow Control:

  • On Vaslue Change : 仅当 Blackboard 值实际变化时才触发重新评估
  • On Result Change : 每次 Tick 都检查条件,条件结果从通过变失败或反过来时才触发

Observer Aborts

None: 不中断任何东西,条件只在首次进入时判断一次

Self: 条件不满足后,中断自己所在分支正在执行的子节点,让 Selector/Sequence 重新评估

Lower Priority: 条件重新满足时,中断右边(优先级更低)分支正在执行的节点,切回自己

Both: Self + Lower Priority 的组合。条件不满足→中断自己;条件重新满足→中断右边抢回来

AI感知不到我

AI Controller 的感知 敌人,中立,朋友

先默认全是敌人

ETeamAttitude::Type AEqZeroPlayerBotController::GetTeamAttitudeTowards(const AActor& Other) const
{
    if (const APawn* OtherPawn = Cast<APawn>(&Other)) 
    {
        return ETeamAttitude::Hostile; // 默认全是敌人
    }

    return ETeamAttitude::Neutral;
}

看到了,红球是下面的伤害感知

这里也确实能找得到目标了

添加伤害感知

/EqZeroCore/Game/B_Hero_EqZMannequin.B_Hero_EqZMannequin

在角色里面加

上一篇
下一篇