前言
由于我思考到这每个角色都是绿色的血条,那岂不是很难分辨谁是队友,谁是队友了吗。因此我想把敌人的血条改成红的。
按照我自己的思路对友方血条和敌方血条的颜色进行修改
ValueGauge
类中让修改BarColor
颜色
void SetBarColor(FLinearColor BarColor);
void UValueGauge::SetBarColor(FLinearColor NewBarColor)
{
// 设置进度条颜色
BarColor = NewBarColor;
ProgressBar->SetFillColorAndOpacity(NewBarColor);
}
OverHeadStatsGauge
在头部血条的这个类中添加一个修改血条颜色的函数以及需要改变的敌方血条颜色。
// 设置血条颜色
void SetHealthBarColor(ETeamAttitude::Type TargetTeam);
private:
// 我方血条颜色
UPROPERTY(EditAnywhere, Category = "Visual")
FLinearColor FriendlyColor;
// 敌方血条颜色
UPROPERTY(EditAnywhere, Category = "Visual")
FLinearColor HostileColor;
void UOverHeadStatsGauge::SetHealthBarColor(ETeamAttitude::Type TargetTeam)
{
if (HealthBar)
{
switch (TargetTeam)
{
case ETeamAttitude::Friendly:
HealthBar->SetBarColor(FriendlyColor);
break;
case ETeamAttitude::Hostile:
HealthBar->SetBarColor(HostileColor);
break;
default:
HealthBar->SetBarColor(FLinearColor::Yellow);
break;
}
}
}
需要在角色中获取本地玩家然后判断阵容,传入到颜色设置中从而设置颜色
// 获取到本地玩家角色
APawn* LocalPlayerPawn = UGameplayStatics::GetPlayerPawn(this, 0);
if (LocalPlayerPawn && LocalPlayerPawn->GetClass()->ImplementsInterface(UGenericTeamAgentInterface::StaticClass()))
{
// 获取本地玩家角色的GenericTeamAgentInterface接口
const IGenericTeamAgentInterface* LocalTeamInterface = Cast<IGenericTeamAgentInterface>(LocalPlayerPawn);
if (LocalTeamInterface)
{
// 设置头顶UI组件的血条颜色
OverheadStatsGuage->SetHealthBarColor(GetTeamAttitudeTowards(*LocalPlayerPawn));
}
}
在配置UI中完成即可
void ACCharacter::ConfigureOverHeadStatusWidget()
{
// 检查头顶UI组件是否存在,如果不存在则直接返回
if (!OverHeadWidgetComponent)
{
return;
}
// 如果角色由本地玩家控制
if (IsLocallyControlledByPlayer())
{
// 隐藏头顶UI组件
OverHeadWidgetComponent->SetHiddenInGame(true);
return;
}
// 将头顶UI组件的用户控件对象转换为UOverHeadStatsGauge类型
UOverHeadStatsGauge* OverheadStatsGuage = Cast<UOverHeadStatsGauge>(OverHeadWidgetComponent->GetUserWidgetObject());
if (OverheadStatsGuage)
{
// 使用能力系统组件配置头顶统计量表
OverheadStatsGuage->ConfigureWithASC(GetAbilitySystemComponent());
// 获取到本地玩家角色
APawn* LocalPlayerPawn = UGameplayStatics::GetPlayerPawn(this, 0);
if (LocalPlayerPawn && LocalPlayerPawn->GetClass()->ImplementsInterface(UGenericTeamAgentInterface::StaticClass()))
{
// 获取本地玩家角色的GenericTeamAgentInterface接口
const IGenericTeamAgentInterface* LocalTeamInterface = Cast<IGenericTeamAgentInterface>(LocalPlayerPawn);
if (LocalTeamInterface)
{
// 设置头顶UI组件的血条颜色
OverheadStatsGuage->SetHealthBarColor(GetTeamAttitudeTowards(*LocalPlayerPawn));
}
}
// 显示头顶UI组件
OverHeadWidgetComponent->SetHiddenInGame(false);
// 清除之前的定时器,防止重复调用
GetWorldTimerManager().ClearTimer(HeadStatGaugeVisibilityUpdateTimerHandle);
// 设置新的定时器,周期性调用 UpdateHeadGaugeVisibility 方法
// 用于持续检测角色与玩家之间的距离并更新头顶UI可见性状态
GetWorldTimerManager().SetTimer(
HeadStatGaugeVisibilityUpdateTimerHandle, // 定时器句柄
this, // 绑定对象
&ACCharacter::UpdateHeadGaugeVisibility, // 每一定时间调用的函数
HeadStatGaugeVisibilityCheckUpdateGap, // 更新间隔时间(秒)
true // 是否循环调用true为循环,false为单次
);
}
}
在蓝图中调制一个好看的颜色
此时我发现代码不能成功,我发现是ValueGauge
类的NativePreConstruct
函数从中作祟,具体原因我也不了解,有懂的教教我。
void UValueGauge::NativePreConstruct()
{
Super::NativePreConstruct();
// 设置进度条颜色
ProgressBar->SetFillColorAndOpacity(BarColor);
if (ValueText)
{
// 获取当前字体设置
FSlateFontInfo FontInfo = ValueText->GetFont();
// 更新字体大小
FontInfo.Size = TextSize;
// 应用新的字体设置到文本组件
ValueText->SetFont(FontInfo);
}
}
于是我稍作调整
添加AI
添加AI控制器
添加AI感知组件和视觉感知,初步配置
// 幻雨喜欢小猫咪
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "Perception/AISenseConfig_Sight.h"
#include "CAIController.generated.h"
/**
*
*/
UCLASS()
class ACAIController : public AAIController
{
GENERATED_BODY()
public:
ACAIController();
virtual void OnPossess(APawn* InPawn) override;
private:
// AI感知组件(用于感知敌人等)
UPROPERTY(VisibleDefaultsOnly, Category = "Perception")
TObjectPtr<UAIPerceptionComponent> AIPerceptionComponent;
// 视觉感知配置(用于设置视野范围、角度等)
UPROPERTY(VisibleDefaultsOnly, Category = "Perception")
TObjectPtr<UAISenseConfig_Sight> SightConfig;
};
// 幻雨喜欢小猫咪
#include "CAIController.h"
#include "Perception/AIPerceptionComponent.h"
ACAIController::ACAIController()
{
AIPerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerceptionComponent"));
SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SightConfig"));
// 配置视觉感知的关联检测设置:
// - 检测敌人:启用
// - 检测中立目标:禁用
// - 检测友方单位:禁用
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = false;
SightConfig->DetectionByAffiliation.bDetectFriendlies = false;
// 设置视觉感知参数:
// - 视野半径
SightConfig->SightRadius = 1000.f;
// - 失去视野半径(当目标超出此范围时AI会认为失去目标)
SightConfig->LoseSightRadius = 1200.f;
// - 感知信息的最大保存时间:5秒
SightConfig->SetMaxAge(5.f);
// - 周边视觉角度:180度
SightConfig->PeripheralVisionAngleDegrees = 180.f;
// 配置AI感知组件的视觉感知
AIPerceptionComponent->ConfigureSense(*SightConfig);
}
void ACAIController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
// 随便设置的队伍
SetGenericTeamId(0);
IGenericTeamAgentInterface* PawnTeamInterface = Cast<IGenericTeamAgentInterface>(InPawn);
if (PawnTeamInterface)
{
PawnTeamInterface->SetGenericTeamId(GetGenericTeamId());
}
}
创建AI控制器蓝图
再创建一个CCharacter
的蓝图
在ai角色中配置一下ai
添加基础技能和GE
死亡蒙太奇
点击Enter
左边的按键再去小数字键盘按下5
就能看见调试信息,感知系统只会锁定敌对单位。
创建行为树和黑板
蓝图中添加行为树
再创建一个黑板
行为树中这个黑板就自动的设置上去了
在CAIController
添加行为树和运行行为树
virtual void BeginPlay() override;
private:
UPROPERTY(EditDefaultsOnly, Category = "AI Behavior")
TObjectPtr<UBehaviorTree> BehaviorTree;
void ACAIController::BeginPlay()
{
Super::BeginPlay();
// 运行行为树
RunBehaviorTree(BehaviorTree);
}
在蓝图AI控制器中配置行为树
行为树中尚未配置任何行为,因此去黑板中添加一些健
添加感知目标更新
private:
// 黑板中用于存储目标的Key名
UPROPERTY(EditDefaultsOnly, Category = "AI Behavior")
FName TargetBlackboardKeyName = "Target";
// 行为树
UPROPERTY(EditDefaultsOnly, Category = "AI Behavior")
TObjectPtr<UBehaviorTree> BehaviorTree;
// AI感知组件(用于感知敌人等)
UPROPERTY(VisibleDefaultsOnly, Category = "Perception")
TObjectPtr<UAIPerceptionComponent> AIPerceptionComponent;
// 视觉感知配置(用于设置视野范围、角度等)
UPROPERTY(VisibleDefaultsOnly, Category = "Perception")
TObjectPtr<UAISenseConfig_Sight> SightConfig;
// 感知到目标时的回调
UFUNCTION()
void TargetPerceptionUpdated(AActor* TargetActor, FAIStimulus Stimulus);
// 获取当前黑板中的目标对象
const UObject* GetCurrentTarget() const;
// 设置当前目标到黑板
void SetCurrentTarget(AActor* NewTarget);
ACAIController::ACAIController()
{
// 感知目标更新
AIPerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(this, &ACAIController::TargetPerceptionUpdated);
}
void ACAIController::TargetPerceptionUpdated(AActor* TargetActor, FAIStimulus Stimulus)
{
// 检查是否成功感知到目标
if (Stimulus.WasSuccessfullySensed())
{
// 如果当前没有目标,则设置当前目标为感知到的目标
if (!GetCurrentTarget())
{
SetCurrentTarget(TargetActor);
}
}else
{
// 如果当前目标是感知到的目标,但感知不成功,则清除当前目标
if (GetCurrentTarget() == TargetActor)
SetCurrentTarget(nullptr);
}
}
const UObject* ACAIController::GetCurrentTarget() const
{
// 获取黑板组件
const UBlackboardComponent* BlackboardComponent = GetBlackboardComponent();
if (BlackboardComponent)
{
// 从黑板组件中根据键名获取目标对象
return GetBlackboardComponent()->GetValueAsObject(TargetBlackboardKeyName);
}
// 如果黑板组件不存在,返回nullptr
return nullptr;
}
void ACAIController::SetCurrentTarget(AActor* NewTarget)
{
// 获取黑板组件
UBlackboardComponent* BlackboardComponent = GetBlackboardComponent();
// 如果黑板组件为空,则不执行任何操作
if (!BlackboardComponent) return;
// 如果新目标不为空,则在黑板上设置新目标
if (NewTarget)
{
BlackboardComponent->SetValueAsObject(TargetBlackboardKeyName, NewTarget);
}
// 如果新目标为空,则从黑板上清除当前目标
else
{
BlackboardComponent->ClearValue(TargetBlackboardKeyName);
}
}
AI成功的将感知到的对象赋值到了健中
添加感知目标遗忘
添加感知目标遗忘以及选择下一个敌人的函数
// 感知目标遗忘时的回调
UFUNCTION()
void TargetForgotten(AActor* ForgottenActor);
// 获取下一个感知到的敌人(用于目标切换)
AActor* GetNextPerceivedActor() const;
ACAIController::ACAIController()
{
// 感知目标遗忘
AIPerceptionComponent->OnTargetPerceptionForgotten.AddDynamic(this, &ACAIController::TargetForgotten);
}
void ACAIController::TargetPerceptionUpdated(AActor* TargetActor, FAIStimulus Stimulus)
{
// 检查是否成功感知到目标
if (Stimulus.WasSuccessfullySensed())
{
// 如果当前没有目标,则设置当前目标为感知到的目标
if (!GetCurrentTarget())
{
SetCurrentTarget(TargetActor);
}
}else
{
}
}
void ACAIController::TargetForgotten(AActor* ForgottenActor)
{
if (!ForgottenActor) return;
// 如果丢失的是当前的目标,则切换到下一个感知到的目标
if (GetCurrentTarget() == ForgottenActor)
{
SetCurrentTarget(GetNextPerceivedActor());
}
}
AActor* ACAIController::GetNextPerceivedActor() const
{
if (PerceptionComponent)
{
// 存储感知到的Actor
TArray<AActor*> Actors;
// 获取感知到的敌对Actor并存储到数组中
AIPerceptionComponent->GetPerceivedHostileActors(Actors);
// 如果敌对Actor的数量不为零
if (Actors.Num() != 0)
{
// 返回第一个
return Actors[0];
}
}
return nullptr;
}
打开AI的遗忘功能
走开后没有立马丢失目标,过了一定时间后遗忘目标
感知的目标角色离开遗忘后感知另外一个目标