EQS
EQS Testing Pawn
能够设置所需要查看的EQS生成点和效果,对于调试来说非常便利,且仅需拖动即可刷新
Generator
可以通过设置Projection Data将某些生成点映射到附近的NavMesh上
Path Exist Test
可以判断生成点是否有路径可以到达Context,开销大,需谨慎考虑
Game Mode
定义了玩家的类和其Controller的类
UCurveFloat
可以通过此类在蓝图中配置曲线
Tips
GetAllActorsOfClass函数性能开销很大,将遍历场景中所有的Actor
使用TActorIterator遍历场景中的Actor效率高于GetAllActorsOfClass
for(TActorIterator<T> It(GetWorld()); It; ++It)
{
//...
}
C++实现GameMode继承 GameModeBase
项目代码
GitHub: https://github.com/yufeige4/ActionRoguelike
实现在玩家周围根据条件刷怪的逻辑
EQS判断与玩家距离是否够大和是否存在路径能够到达玩家
GameMode调用
// GGameModeBase.h
// ...
UCLASS()
class ACTIONROGUELIKE_API AGGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
AGGameModeBase();
virtual void StartPlay() override;
protected:
UPROPERTY(VisibleAnywhere, Category = "Components")
UGSpawnControlComponent* SpawnControlComp;
};
// GGameModeBase.cpp
// ...
AGGameModeBase::AGGameModeBase()
{
SpawnControlComp = CreateDefaultSubobject<UGSpawnControlComponent>("SpawnControlComp");
}
void AGGameModeBase::StartPlay()
{
Super::StartPlay();
SpawnControlComp->SpawnObjects();
}
通过组件实现,并挂在GM下 (个人实现方法)
// GSpawnControlComponent.h
// ...
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ACTIONROGUELIKE_API UGSpawnControlComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UGSpawnControlComponent();
protected:
UPROPERTY(EditDefaultsOnly, Category = "AI")
int32 SpawnNumLimit;
UPROPERTY(EditDefaultsOnly, Category = "AI")
float SpawnInterval;
UPROPERTY(EditDefaultsOnly, Category = "AI")
TSubclassOf<AActor> SpawnClass;
UPROPERTY(EditDefaultsOnly, Category = "AI")
UEnvQuery* EnvQueryTemplate;
FTimerHandle TimerHandle_SpawnReset;
public:
UFUNCTION(BlueprintCallable, Category = "AI")
void SpawnObjects();
UFUNCTION(BlueprintCallable, Category = "AI")
void StopSpawning();
protected:
UFUNCTION()
void SpawnReset_TimeElapsed();
UFUNCTION()
void OnQueryFinished(UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus);
};
// GSpawnControlComponent.cpp
// ...
UGSpawnControlComponent::UGSpawnControlComponent()
{
SpawnInterval = 5.0;
SpawnNumLimit = 3;
}
void UGSpawnControlComponent::SpawnReset_TimeElapsed()
{
int32 CurrNumCount = 0;
for(TActorIterator<AGAICharacter> It(GetWorld()); It; ++It)
{
AGAICharacter* AICharacter = *It;
if(AICharacter)
{
auto AttributeComp = Cast<UGAttributeComponent>(AICharacter->GetComponentByClass(UGAttributeComponent::StaticClass()));
if(AttributeComp && AttributeComp->IsAlive())
{
++CurrNumCount;
}
}
}
int32 NumLimit = SpawnNumLimit;
if(DifficultyCurve)
{
DifficultyCurve->GetFloatValue(GetWorld()->TimeSeconds);
}
if(CurrNumCount>=NumLimit)
{
return;
}
if(ensureAlwaysMsgf(EnvQueryTemplate,TEXT("EnvQueryTemplate need to be select!!!")))
{
auto EQSInstance = UEnvQueryManager::RunEQSQuery(GetWorld(),EnvQueryTemplate,GetOwner(),EEnvQueryRunMode::RandomBest5Pct,nullptr);
if(EQSInstance)
{
EQSInstance->GetOnQueryFinishedEvent().AddDynamic(this,&UGSpawnControlComponent::OnQueryFinished);
}
}
}
void UGSpawnControlComponent::SpawnObjects()
{
GetWorld()->GetTimerManager().SetTimer(TimerHandle_SpawnReset,this,&UGSpawnControlComponent::SpawnReset_TimeElapsed,SpawnInterval,true);
}
void UGSpawnControlComponent::StopSpawning()
{
GetWorld()->GetTimerManager().ClearTimer(TimerHandle_SpawnReset);
}
void UGSpawnControlComponent::OnQueryFinished(UEnvQueryInstanceBlueprintWrapper* QueryInstance,
EEnvQueryStatus::Type QueryStatus)
{
if(QueryStatus!=EEnvQueryStatus::Success)
{
UE_LOG(LogTemp,Warning,TEXT("Spawn Object EQS Failed!"));
return;
}
TArray<FVector> Locations = QueryInstance->GetResultsAsLocations();
if(Locations.Num()>0)
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
if(ensureAlwaysMsgf(SpawnClass,TEXT("SpawnClass need to be select!!!")))
{
GetWorld()->SpawnActor<AActor>(SpawnClass,Locations[0],FRotator::ZeroRotator,SpawnParams);
}
}
}
实现怪物低血量逃跑脱战补血功能 - Assignment 4
Service - 检测低血量
EQS - 找到距离怪物最近的躲藏点
Task - 到达隐藏点补满HP
触发间隔为60秒