Created
March 17, 2024 16:13
-
-
Save SkylakeOfficial/743be7152c21207498539a347fa04a81 to your computer and use it in GitHub Desktop.
MyEnemySpawner_v2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Skylake 2023 all rights reserved | |
| #include "AFPEnemySpawnerActor.h" | |
| #include "GameFramework/Pawn.h" | |
| #include "Engine/World.h" | |
| #include "Engine.h" | |
| #include "Kismet/KismetMathLibrary.h" | |
| #include "Kismet/KismetSystemLibrary.h" | |
| #include "UObject/ConstructorHelpers.h" | |
| #include "TimerManager.h" | |
| #include "ArknightsFP/AFPGameInstance.h" | |
| #include "ArknightsFP/Gameplay/GameDirector/AFPGameDirectorSubsystem.h" | |
| DEFINE_LOG_CATEGORY(AFPEnemySpawner); | |
| AAFPEnemySpawnerActor::AAFPEnemySpawnerActor() | |
| { | |
| PrimaryActorTick.bCanEverTick = false; | |
| SceneRoot = CreateDefaultSubobject<UBillboardComponent>(TEXT("Root Comp")); | |
| SetRootComponent(SceneRoot); | |
| static ConstructorHelpers::FObjectFinder<UTexture2D> BillboardTex(TEXT("/Game/GameLogic/Spawner/Tx_Billboard")); | |
| if (BillboardTex.Succeeded()) | |
| { | |
| SceneRoot->Sprite = BillboardTex.Object; | |
| } | |
| } | |
| void AAFPEnemySpawnerActor::BeginPlay() | |
| { | |
| Super::BeginPlay(); | |
| UAFPGameInstance* GameInstance = Cast<UAFPGameInstance>(UGameplayStatics::GetGameInstance(this)); | |
| EAFPMapType RoomType = GameInstance->GetSubsystem<UAFPGameDirectorSubsystem>()->GetRoomType(); | |
| if (RoomType == EAFPMapType::EmergencyCombat) | |
| { | |
| SpawnProfileList = SpawnProfileListEmergency; | |
| } | |
| if (!SpawnProfileList.IsEmpty()) | |
| { | |
| SpawnProfile = SpawnProfileList[UKismetMathLibrary::RandomInteger(SpawnProfileList.Num())]; | |
| } | |
| else | |
| { | |
| UE_LOG(AFPEnemySpawner, Warning, TEXT("Spawn profile list null, spawner destroyed! ")); | |
| GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, TEXT("Spawn profile list null, spawner destroyed!")); | |
| Destroy(); | |
| } | |
| if (bAutoStart) StartSpawning(); | |
| } | |
| void AAFPEnemySpawnerActor::StartSpawning() | |
| { | |
| if (SpawnStarted || !SpawnProfile) return; | |
| SpawnStarted = true; | |
| if (SpawnProfile->Streaks.IsEmpty()) | |
| { | |
| UE_LOG(AFPEnemySpawner, Warning, TEXT("未填入波次信息,放弃生成!")); | |
| GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, TEXT("未填入波次信息,放弃生成!")); | |
| return; | |
| } | |
| UAFPGameDirectorSubsystem* DirectorSys = UGameplayStatics::GetGameInstance(this)->GetSubsystem<UAFPGameDirectorSubsystem>(); | |
| DirectorSys->OnBattleStartFunc(); | |
| OnEnemyEliminated.AddDynamic(this, &AAFPEnemySpawnerActor::OnBattleEnd); | |
| MakeStreakNodes(SpawnProfile->Streaks); | |
| for (UStreakNode* Node : StreakNodes) | |
| { | |
| if (Node->DirectStart) | |
| { | |
| SpawnNode(Node); | |
| } | |
| } | |
| } | |
| bool AAFPEnemySpawnerActor::SpawnSpecificStreak(FName StreakName) | |
| { | |
| if (StreakNodes.IsEmpty()) | |
| { | |
| return false; | |
| } | |
| for (int32 i = 0; i != StreakNodes.Num(); i++) | |
| { | |
| if (StreakNodes[i]->ID == StreakName&&(!StreakNodes[i]->IsSpawning)) | |
| { | |
| SpawnNode(StreakNodes[i]); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| TArray<FName> AAFPEnemySpawnerActor::GetListOfExternCallableStreaks() | |
| { | |
| TArray<FName> Result; | |
| for(auto Node : StreakNodes) | |
| { | |
| if (!Node->IsSpawning&&Node->ExternalCall) | |
| { | |
| Result.Add(Node->ID); | |
| } | |
| } | |
| return Result; | |
| } | |
| //返回存活的敌人 | |
| TArray<AAFPEnemyBase*> AAFPEnemySpawnerActor::GetSurvivingEnemies() | |
| { | |
| TArray<AAFPEnemyBase*> Result; | |
| if (StreakNodes.IsEmpty()) | |
| { | |
| return Result; | |
| } | |
| for (int32 i = 0; i != StreakNodes.Num(); i++) | |
| { | |
| Result.Append(StreakNodes[i]->SpawnedEnemies); | |
| } | |
| return Result; | |
| } | |
| //构造函数 | |
| void AAFPEnemySpawnerActor::OnConstruction(const FTransform& Transform) | |
| { | |
| } | |
| //Generate streak nodes | |
| void AAFPEnemySpawnerActor::MakeStreakNodes(TArray<FEnemyStreak> InStreak) | |
| { | |
| for (FEnemyStreak Streak : InStreak) | |
| { | |
| if (Streak.EnemyCount < 1) | |
| { | |
| continue; | |
| } | |
| UStreakNode* Node = NewObject<UStreakNode>(); | |
| Node->ID = Streak.StreakName; | |
| Node->Delay = Streak.StreakDelay; | |
| Node->DistributeTime = Streak.DistributeTime; | |
| Node->EnemyType = Streak.EnemyType; | |
| //TransformStuff | |
| FVector Scale = Streak.EnemyTransform.GetScale3D() * FVector(1.0, 1.0, 0.0); | |
| FVector Loc = Streak.EnemyTransform.GetLocation(); | |
| FRotator Rot = FRotator(Streak.EnemyTransform.GetRotation()); | |
| for (int i = 0; i != Streak.EnemyCount; ++i) | |
| { | |
| FVector2D OffsetXY = SobolVec2D(i); //生成二维sobol序列 | |
| FVector LocOffset = Streak.EnemyCount == 1 ? FVector(0, 0, 0) : UKismetMathLibrary::MakeVector(OffsetXY.X - 0.5f, OffsetXY.Y - 0.5f, 0) * Scale * 80; | |
| FVector Location = Loc + LocOffset; | |
| FTransform SelfTransform = GetTransform(); | |
| SelfTransform.SetScale3D(FVector(1, 1, 1)); | |
| Node->Transforms.Add(UKismetMathLibrary::MakeTransform(Location, Rot).operator+(SelfTransform)); | |
| } | |
| //Link corresponding nodes | |
| for (FEnemyStreak OtherStreak : InStreak) | |
| { | |
| if (OtherStreak.RefStreak == Node->ID && OtherStreak.StreakName != Node->ID) | |
| { | |
| if (OtherStreak.SpawnMethod == ESpawnMethod::StartAfter) | |
| { | |
| Node->CompleteExecute.Add(OtherStreak.StreakName); | |
| } | |
| else if (OtherStreak.SpawnMethod == ESpawnMethod::StartAfterEliminate) | |
| { | |
| Node->EliminateExecute.Add(OtherStreak.StreakName); | |
| } | |
| } | |
| } | |
| //Start condition | |
| Node->DirectStart = Streak.SpawnMethod == ESpawnMethod::DirectStart; | |
| Node->ExternalCall = Streak.SpawnMethod == ESpawnMethod::ExternalCall; | |
| StreakNodes.Add(Node); | |
| } | |
| } | |
| void AAFPEnemySpawnerActor::SpawnNode(UStreakNode* Node) | |
| { | |
| if (!Node) | |
| { | |
| return; | |
| } | |
| if (Node->IsSpawning) | |
| { | |
| return; | |
| } | |
| float DistributeTime = Node->DistributeTime; | |
| float StreakDelay = Node->Delay; | |
| if (DistributeTime <= 0) | |
| { | |
| DistributeTime = 0.5F; | |
| } | |
| if (StreakDelay < 0) | |
| { | |
| StreakDelay = 1; | |
| } | |
| FTimerHandle SpawnNodeHandle; | |
| FTimerDelegate SpawnNodeDelegate = FTimerDelegate::CreateUObject(this, &AAFPEnemySpawnerActor::SpawnNodeOnce, Node); | |
| GetWorldTimerManager().SetTimer(SpawnNodeHandle, SpawnNodeDelegate, DistributeTime / (Node->Transforms.Num() - 1), true, StreakDelay); | |
| Node->SpawnTimer = SpawnNodeHandle; | |
| Node->IsSpawning = true; | |
| } | |
| void AAFPEnemySpawnerActor::SpawnNodeOnce(UStreakNode* Node) | |
| { | |
| if (!Node) | |
| { | |
| return; | |
| } | |
| if (Node->Transforms.IsEmpty()) | |
| { | |
| return; | |
| } | |
| AAFPEnemyBase* Spawned = SpawnSingle(Node->Transforms[0], Node->EnemyType); | |
| Node->SpawnedEnemies.Add(Spawned); | |
| EnemyBelongsTo.Add(Spawned, Node->ID); | |
| Node->Transforms.RemoveAt(0); | |
| //Node完成生成时 | |
| if (Node->Transforms.IsEmpty()) | |
| { | |
| GetWorldTimerManager().ClearTimer(Node->SpawnTimer); | |
| OnStreakSpawnComplete.Broadcast(Node->ID); | |
| if (!Node->CompleteExecute.IsEmpty()) | |
| { | |
| for (FName OtherNodeID : Node->CompleteExecute) | |
| { | |
| SpawnNode(FindNodeByID(OtherNodeID)); | |
| } | |
| } | |
| } | |
| } | |
| //生成Pawn | |
| AAFPEnemyBase* AAFPEnemySpawnerActor::SpawnSingle(FTransform Transform, TSubclassOf<AAFPEnemyBase> EnemyType) | |
| { | |
| //位置 | |
| FVector SpawnLoc = Transform.GetLocation(); | |
| FRotator SpawnRot = FRotator(Transform.GetRotation()); | |
| //检测地面 | |
| //const TArray<TEnumAsByte<EObjectTypeQuery>> TraceQueryType = {EObjectTypeQuery::ObjectTypeQuery1, EObjectTypeQuery::ObjectTypeQuery2}; | |
| //FHitResult Result = FHitResult(); | |
| //TArray<AActor*> ActorsToIgnore; | |
| //UKismetSystemLibrary::LineTraceSingleForObjects(GetWorld(), SpawnLoc, SpawnLoc.operator-(FVector(0, 0, 10000.0)), TraceQueryType, false, ActorsToIgnore, EDrawDebugTrace::None, Result, true); | |
| //相关config | |
| FActorSpawnParameters SpawnParams; | |
| SpawnParams.Owner = this; | |
| SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; | |
| //生成 | |
| AAFPEnemyBase* SpawnedPawn = GetWorld()->SpawnActor<AAFPEnemyBase>(EnemyType, SpawnLoc, SpawnRot, SpawnParams); | |
| //吸附到地面 | |
| //FVector ActorBounds; | |
| //SpawnedPawn->GetActorBounds(true, SpawnLoc, ActorBounds, false); | |
| //FVector DestLocation = Result.Location.operator+(FVector(0, 0, ActorBounds.Z)); | |
| //SpawnedPawn->SetActorLocation(DestLocation, true, nullptr, ETeleportType::TeleportPhysics); | |
| /* 吸附地面的逻辑就不做了。因为加入了飞行敌人*/ | |
| //绑定到actor摧毁的委托 | |
| SpawnedPawn->OnDestroyed.AddDynamic(this, &AAFPEnemySpawnerActor::OnEnemyDestroy); | |
| return SpawnedPawn; | |
| } | |
| //pawn销毁调用 | |
| void AAFPEnemySpawnerActor::OnEnemyDestroy(AActor* DestroyedActor) | |
| { | |
| AAFPEnemyBase* Destroyed = Cast<AAFPEnemyBase>(DestroyedActor); | |
| if (EnemyBelongsTo.Contains(Destroyed)) | |
| { | |
| FName ID = EnemyBelongsTo[Destroyed]; | |
| UStreakNode* NodeBelongsTo = nullptr; | |
| for (int32 i = 0; i != StreakNodes.Num(); i++) | |
| { | |
| if (StreakNodes[i]->ID == ID) | |
| { | |
| NodeBelongsTo = StreakNodes[i]; | |
| break; | |
| } | |
| } | |
| if (NodeBelongsTo) | |
| { | |
| NodeBelongsTo->SpawnedEnemies.Remove(Destroyed); | |
| //如果销毁敌人的所属波次被清空了 | |
| if (NodeBelongsTo->SpawnedEnemies.IsEmpty() && NodeBelongsTo->Transforms.IsEmpty()) | |
| { | |
| OnStreakEliminate.Broadcast(ID); | |
| if (!NodeBelongsTo->EliminateExecute.IsEmpty()) | |
| { | |
| for (FName OtherNode : NodeBelongsTo->EliminateExecute) | |
| { | |
| SpawnNode(FindNodeByID(OtherNode)); | |
| } | |
| } | |
| StreakNodes.Remove(NodeBelongsTo); | |
| //检查是不是所有波次都被清空了 | |
| if (StreakNodes.IsEmpty()) | |
| { | |
| OnEnemyEliminated.Broadcast(); | |
| } | |
| //或者剩余的波次都是外部调用生成,且还没开始 —— 这种情况下也视为清空了 | |
| else | |
| { | |
| bool HasNonExternCallNode = false; | |
| for (int32 i = 0; i != StreakNodes.Num(); i++) | |
| { | |
| if (!StreakNodes[i]->ExternalCall)//不是外部调用? | |
| { | |
| HasNonExternCallNode = true; | |
| break; | |
| } | |
| else if (StreakNodes[i]->IsSpawning)//已经在生成中? | |
| { | |
| HasNonExternCallNode = true; | |
| break; | |
| } | |
| } | |
| if (!HasNonExternCallNode) | |
| { | |
| OnEnemyEliminated.Broadcast(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void AAFPEnemySpawnerActor::OnBattleEnd() | |
| { | |
| UAFPGameDirectorSubsystem* DirectorSys = UGameplayStatics::GetGameInstance(this)->GetSubsystem<UAFPGameDirectorSubsystem>(); | |
| DirectorSys->OnBattleEndFunc(); | |
| } | |
| //生成第d个维度的第i个sobol数 | |
| float AAFPEnemySpawnerActor::Sobol(uint32 d, uint32 i) | |
| { | |
| const uint32 Matrix[8 * 32] = { | |
| 2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, | |
| 2147483648, 3221225472, 2684354560, 4026531840, 2281701376, 3422552064, 2852126720, 4278190080, 2155872256, 3233808384, 2694840320, 4042260480, 2290614272, 3435921408, 2863267840, 4294901760, 2147516416, 3221274624, 2684395520, 4026593280, 2281736192, 3422604288, 2852170240, 4278255360, 2155905152, 3233857728, 2694881440, 4042322160, 2290649224, 3435973836, 2863311530, 4294967295, | |
| 2147483648, 3221225472, 1610612736, 2415919104, 3892314112, 1543503872, 2382364672, 3305111552, 1753219072, 2629828608, 3999268864, 1435500544, 2154299392, 3231449088, 1626210304, 2421489664, 3900735488, 1556135936, 2388680704, 3314585600, 1751705600, 2627492864, 4008611328, 1431684352, 2147543168, 3221249216, 1610649184, 2415969680, 3892340840, 1543543964, 2382425838, 3305133397, | |
| 2147483648, 3221225472, 536870912, 1342177280, 4160749568, 1946157056, 2717908992, 2466250752, 3632267264, 624951296, 1507852288, 3872391168, 2013790208, 3020685312, 2181169152, 3271884800, 546275328, 1363623936, 4226424832, 1977167872, 2693105664, 2437829632, 3689389568, 635137280, 1484783744, 3846176960, 2044723232, 3067084880, 2148008184, 3222012020, 537002146, 1342505107, | |
| 2147483648, 1073741824, 536870912, 2952790016, 4160749568, 3690987520, 2046820352, 2634022912, 1518338048, 801112064, 2707423232, 4038066176, 3666345984, 1875116032, 2170683392, 1085997056, 579305472, 3016343552, 4217741312, 3719483392, 2013407232, 2617981952, 1510979072, 755882752, 2726789248, 4090085440, 3680870432, 1840435376, 2147625208, 1074478300, 537900666, 2953698205, | |
| 2147483648, 1073741824, 1610612736, 805306368, 2818572288, 335544320, 2113929216, 3472883712, 2290089984, 3829399552, 3059744768, 1127219200, 3089629184, 4199809024, 3567124480, 1891565568, 394297344, 3988799488, 920674304, 4193267712, 2950604800, 3977188352, 3250028032, 129093376, 2231568512, 2963678272, 4281226848, 432124720, 803643432, 1633613396, 2672665246, 3170194367, | |
| 2147483648, 3221225472, 2684354560, 3489660928, 1476395008, 2483027968, 1040187392, 3808428032, 3196059648, 599785472, 505413632, 4077912064, 1182269440, 1736704000, 2017853440, 2221342720, 3329785856, 2810494976, 3628507136, 1416089600, 2658719744, 864310272, 3863387648, 3076993792, 553150080, 272922560, 4167467040, 1148698640, 1719673080, 2009075780, 2149644390, 3222291575, | |
| 2147483648, 1073741824, 2684354560, 1342177280, 2281701376, 1946157056, 436207616, 2566914048, 2625634304, 3208642560, 2720006144, 2098200576, 111673344, 2354315264, 3464626176, 4027383808, 2886631424, 3770826752, 1691164672, 3357462528, 1993345024, 3752330240, 873073152, 2870150400, 1700563072, 87021376, 1097028000, 1222351248, 1560027592, 2977959924, 23268898, 437609937 | |
| }; | |
| uint32 result = 0; | |
| uint32 offset = d * 32; | |
| for (uint32 j = 0; i; i >>= 1, j++) | |
| if (i & 1) | |
| result ^= Matrix[j + offset]; | |
| return float(result) * (1.0f / float(0xFFFFFFFFU)); | |
| } | |
| UStreakNode* AAFPEnemySpawnerActor::FindNodeByID(FName ID) | |
| { | |
| for (auto Node : StreakNodes) | |
| { | |
| if (Node->ID == ID) | |
| { | |
| return Node; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| //生成二维sobol坐标 | |
| FVector2D AAFPEnemySpawnerActor::SobolVec2D(uint32 i) | |
| { | |
| float u = Sobol(1, i ^ (i >> 1)); | |
| float v = Sobol(2, i ^ (i >> 1)); | |
| return FVector2D(u, v); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Skylake 2023 all rights reserved | |
| #pragma once | |
| #include "CoreMinimal.h" | |
| #include "AFPEnemySpawnProfile.h" | |
| #include "Components/BillboardComponent.h" | |
| #include "Engine/EngineTypes.h" | |
| #include "TimerManager.h" | |
| #include "ArknightsFP/Stats/AFPGameStatsSubsystem.h" | |
| #include "AFPEnemySpawnerActor.generated.h" | |
| DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAFPEnemyEliminatedSignature); | |
| DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAFPSpawnCompleteSignature, FName, StreakName); | |
| DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAFPEliminateStreakSignature, FName, StreakName); | |
| DECLARE_LOG_CATEGORY_EXTERN(AFPEnemySpawner,Log,All); | |
| UCLASS() | |
| class UStreakNode : public UObject | |
| { | |
| public: | |
| GENERATED_BODY() | |
| FName ID; | |
| float Delay = 1.0; | |
| float DistributeTime = 0.5; | |
| TSubclassOf<AAFPEnemyBase> EnemyType; | |
| TArray<FTransform> Transforms; | |
| TArray<FName> CompleteExecute; | |
| TArray<FName> EliminateExecute; | |
| bool DirectStart = false; | |
| bool ExternalCall = false; | |
| bool IsSpawning = false; | |
| UPROPERTY() | |
| TArray<AAFPEnemyBase*> SpawnedEnemies; | |
| FTimerHandle SpawnTimer; | |
| }; | |
| UCLASS() | |
| class ARKNIGHTSFP_API AAFPEnemySpawnerActor : public AActor | |
| { | |
| GENERATED_BODY() | |
| public: | |
| AAFPEnemySpawnerActor(); | |
| UPROPERTY(EditAnywhere, Category = "AFPSpawn") | |
| bool bAutoStart = false; | |
| UPROPERTY(EditAnywhere, Category = "AFPSpawn" ) | |
| TArray< UAFPEnemySpawnProfile*> SpawnProfileList; | |
| UPROPERTY(EditAnywhere, Category = "AFPSpawn" ) | |
| TArray< UAFPEnemySpawnProfile*> SpawnProfileListEmergency; | |
| UFUNCTION(BlueprintCallable, Category = "AFPSpawn") | |
| void StartSpawning(); | |
| UFUNCTION(BlueprintCallable, Category = "AFPSpawn") | |
| bool SpawnSpecificStreak(FName StreakName); | |
| /** | |
| * 返回尚没被外部调用的波次 | |
| * @return 现在可从外部启动的敌人波次列表 | |
| */ | |
| UFUNCTION(BlueprintCallable, BlueprintPure, Category = "AFPSpawn") | |
| TArray<FName> GetListOfExternCallableStreaks(); | |
| UFUNCTION(BlueprintCallable, BlueprintPure, Category = "AFPSpawn") | |
| TArray<AAFPEnemyBase*> GetSurvivingEnemies(); | |
| UPROPERTY(BlueprintAssignable, Category = "AFPSpawn") | |
| FAFPEnemyEliminatedSignature OnEnemyEliminated; | |
| UPROPERTY(BlueprintAssignable, Category = "AFPSpawn") | |
| FAFPSpawnCompleteSignature OnStreakSpawnComplete; | |
| UPROPERTY(BlueprintAssignable, Category = "AFPSpawn") | |
| FAFPEliminateStreakSignature OnStreakEliminate; | |
| protected: | |
| UPROPERTY(VisibleAnywhere, Category = "AFPSpawn") | |
| UBillboardComponent* SceneRoot; | |
| virtual void BeginPlay() override; | |
| virtual void OnConstruction(const FTransform& Transform) override; | |
| void MakeStreakNodes(TArray<FEnemyStreak> InStreak); | |
| void SpawnNode(UStreakNode* Node); | |
| FVector2D SobolVec2D(uint32 i); | |
| float Sobol(uint32 d, uint32 i); | |
| UStreakNode* FindNodeByID(FName ID); | |
| UFUNCTION() | |
| void SpawnNodeOnce(UStreakNode* Node); | |
| AAFPEnemyBase* SpawnSingle(FTransform Transform, TSubclassOf<AAFPEnemyBase> EnemyType); | |
| UFUNCTION() | |
| void OnEnemyDestroy(AActor* DestroyedActor); | |
| UFUNCTION() | |
| void OnBattleEnd(); | |
| UPROPERTY() | |
| UAFPEnemySpawnProfile* SpawnProfile = nullptr; | |
| bool SpawnStarted = false; | |
| int32 CurrentStreak = 0; | |
| UPROPERTY() | |
| FTimerHandle EnemySpawnTimer; | |
| FTimerHandle TimeLimitTimer; | |
| UPROPERTY() | |
| TArray<UStreakNode*> StreakNodes; | |
| UPROPERTY() | |
| TMap<AAFPEnemyBase*, FName> EnemyBelongsTo; | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Skylake 2023 all rights reserved | |
| #include "AFPEnemySpawnerEditor.h" | |
| #include "Kismet/KismetSystemLibrary.h" | |
| #include "Kismet/KismetMathLibrary.h" | |
| #include "UObject/ConstructorHelpers.h" | |
| #include "Materials/Material.h" | |
| #include "DrawDebugHelpers.h" | |
| // Sets default values | |
| AAFPEnemySpawnerEditor::AAFPEnemySpawnerEditor() | |
| { | |
| PrimaryActorTick.bCanEverTick = true; | |
| LastStoredProfile = StoredProfile; | |
| SceneRoot = CreateDefaultSubobject<UBillboardComponent>(TEXT("Root Comp")); //SceneRoot | |
| SetRootComponent(SceneRoot); | |
| AActor::SetActorHiddenInGame(true); | |
| static ConstructorHelpers::FObjectFinder<UMaterial> TextMat(TEXT("/Engine/EngineMaterials/DefaultTextMaterialOpaque")); | |
| if (TextMat.Succeeded()) | |
| { | |
| TextMaterial = TextMat.Object; | |
| } | |
| static ConstructorHelpers::FObjectFinder<UTexture2D> BillboardTex(TEXT("/Game/GameLogic/Spawner/Tx_Billboard")); | |
| if (BillboardTex.Succeeded()) | |
| { | |
| SceneRoot->Sprite = BillboardTex.Object; | |
| } | |
| DebugColors = {FColor::FromHex("8A24FF"), FColor::FromHex("2323FA"), FColor::FromHex("308DFF"), FColor::FromHex("24E9FF"), FColor::FromHex("24FFAC")}; | |
| } | |
| void AAFPEnemySpawnerEditor::Apply() | |
| { | |
| if (!StoredProfile) | |
| { | |
| GEditor->AddOnScreenDebugMessage(0, 5.0, FColor::Red,TEXT("没有编辑中的Profile!")); | |
| return; | |
| } | |
| else | |
| { | |
| StoredProfile->Streaks = Streaks; | |
| } | |
| RerunConstructionScripts(); | |
| } | |
| void AAFPEnemySpawnerEditor::OnConstruction(const FTransform& Transform) | |
| { | |
| int32 LastStreakNum = 0; | |
| if (LastStoredProfile) | |
| { | |
| LastStreakNum = LastStoredProfile->Streaks.Num(); | |
| } | |
| if (!StoredProfile) | |
| { | |
| Streaks = TArray<FEnemyStreak>(); | |
| } | |
| if (StoredProfile) | |
| { | |
| if (LastStoredProfile != StoredProfile) | |
| { | |
| Streaks = StoredProfile->Streaks; | |
| } | |
| } | |
| if (Streaks.Num()>LastStreakNum&&LastStoredProfile) | |
| { | |
| Streaks.Last().StreakName = FName(FString::FromInt(Streaks.Num()-1)); | |
| } | |
| LastStoredProfile = StoredProfile; | |
| UKismetSystemLibrary::FlushPersistentDebugLines(GetWorld()); | |
| TSubclassOf<UActorComponent> TextCompClass = UTextRenderComponent::StaticClass(); | |
| TArray<UActorComponent*> TextsGen; | |
| GetComponents(TextCompClass, TextsGen); | |
| for (int i = 0; i < TextsGen.Num(); i++) | |
| { | |
| if (TextsGen[i]) | |
| { | |
| TextsGen[i]->UnregisterComponent(); | |
| TextsGen[i]->DestroyComponent(); | |
| } | |
| } | |
| if (!Streaks.IsEmpty()) | |
| { | |
| for (int i = 0; i != Streaks.Num(); i++) | |
| { | |
| FVector Loc = Streaks[i].EnemyTransform.GetLocation().operator+(GetActorLocation()).operator+(FVector(0, 0, 30)); | |
| FVector Scale = Streaks[i].EnemyTransform.GetScale3D().operator*(FVector(40, 40, 0)).operator+(FVector(0, 0, 30)); | |
| FQuat Rot = Streaks[i].EnemyTransform.GetRotation(); | |
| DrawDebugBox(GetWorld(), Loc, Scale, Rot, DebugColors[i % DebugColors.Num()], true); | |
| FString ClassName = Streaks[i].EnemyType->GetName(); | |
| FString StreakName = Streaks[i].StreakName.ToString(); | |
| FString TextToDisplay = FString::Printf(TEXT("%s\n%s x %d"), *StreakName, *ClassName, Streaks[i].EnemyCount); | |
| UTextRenderComponent* Text = NewObject<UTextRenderComponent>(this); | |
| AddInstanceComponent(Text); | |
| Text->RegisterComponent(); | |
| Text->AttachToComponent(SceneRoot, FAttachmentTransformRules::KeepWorldTransform); | |
| if (TextMaterial) | |
| { | |
| Text->SetMaterial(0, TextMaterial); | |
| } | |
| Text->SetText(FText::FromString(TextToDisplay)); | |
| Text->SetTextRenderColor(FColor::Red); | |
| Text->SetWorldTransform(FTransform(Rot, Loc, FVector(1, 1, 1))); | |
| Text->SetHorizontalAlignment(EHorizTextAligment::EHTA_Center); | |
| Text->SetVerticalAlignment(EVerticalTextAligment::EVRTA_TextCenter); | |
| } | |
| } | |
| } | |
| void AAFPEnemySpawnerEditor::BeginPlay() | |
| { | |
| Super::BeginPlay(); | |
| } | |
| void AAFPEnemySpawnerEditor::Tick(float DeltaTime) | |
| { | |
| Super::Tick(DeltaTime); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Skylake 2023 all rights reserved | |
| #pragma once | |
| #include "CoreMinimal.h" | |
| #include "GameFramework/Actor.h" | |
| #include "ArknightsFP/Spawn/AFPEnemySpawnProfile.h" | |
| #include "Components/TextRenderComponent.h" | |
| #include "Components/BillboardComponent.h" | |
| #include "AFPEnemySpawnerEditor.generated.h" | |
| UCLASS() | |
| class ARKNIGHTSFPEDITOR_API AAFPEnemySpawnerEditor : public AActor | |
| { | |
| GENERATED_BODY() | |
| public: | |
| // Sets default values for this actor's properties | |
| AAFPEnemySpawnerEditor(); | |
| UPROPERTY(EditAnywhere, Category = "AFPSpawn", meta = (DisplayName = "ProfileEditing")) | |
| UAFPEnemySpawnProfile* StoredProfile; | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn") | |
| TArray<FEnemyStreak> Streaks; | |
| UFUNCTION(CallInEditor, Category = "AFPSpawn") | |
| void Apply(); | |
| virtual void OnConstruction(const FTransform& Transform) override; | |
| protected: | |
| UPROPERTY(VisibleAnywhere, Category = "AFPSpawn") | |
| UBillboardComponent* SceneRoot; | |
| virtual void BeginPlay() override; | |
| UPROPERTY() | |
| UAFPEnemySpawnProfile* LastStoredProfile; | |
| UPROPERTY() | |
| UMaterial* TextMaterial; | |
| TArray<FColor> DebugColors; | |
| public: | |
| // Called every frame | |
| virtual void Tick(float DeltaTime) override; | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Skylake 2023 all rights reserved | |
| #pragma once | |
| #include "Engine/DataAsset.h" | |
| #include "ArknightsFP/Gameplay/Enemies/AFPEnemyBase.h" | |
| #include "AFPEnemySpawnProfile.generated.h" | |
| UENUM(BlueprintType, Blueprintable, Category = "AFPSpawn", DisplayName = "触发模式") | |
| enum class ESpawnMethod : uint8 | |
| { | |
| DirectStart UMETA(DisplayName="直接开始"), | |
| StartAfter UMETA(DisplayName="波次生成后开始"), | |
| StartAfterEliminate UMETA(DisplayName="波次歼灭后开始"), | |
| ExternalCall UMETA(DisplayName="外部调用开始") | |
| }; | |
| USTRUCT(BlueprintType, Blueprintable, Category = "AFPSpawn", meta = (MakeEditWidget)) | |
| struct FEnemyStreak | |
| { | |
| GENERATED_BODY() | |
| /** | |
| * 这个波次的名称 | |
| */ | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "波次名称") | |
| FName StreakName; | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "参考波次") | |
| FName RefStreak; | |
| /** | |
| * 波次的开始方式。 | |
| * 直接开始:在生成器开始时开始。考虑生成延迟。 | |
| * 波次生成后开始:在参考波次生成完毕后开始。需要有效参考波次名称。 | |
| * 波次歼灭后开始:在参考波次被消灭后开始。需要有效参考波次名称。 | |
| * 外部调用开始:接受外部调用而开始。 | |
| */ | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "触发模式") | |
| ESpawnMethod SpawnMethod = ESpawnMethod::DirectStart; | |
| /** | |
| * 该敌人波次的生成延迟 | |
| */ | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "初始延迟") | |
| float StreakDelay = 1.0; | |
| /** | |
| * 敌人的生成并不在一帧内完成。此数决定该波次敌人的生成用时 | |
| */ | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "分布时间") | |
| float DistributeTime = 0.5; | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", meta = (MakeEditWidget), DisplayName = "生成位置") | |
| FTransform EnemyTransform = FTransform(); | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "敌人类型") | |
| TSubclassOf<AAFPEnemyBase> EnemyType = AAFPEnemyBase::StaticClass(); | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn", DisplayName = "敌人数量") | |
| int32 EnemyCount = 1; | |
| }; | |
| UCLASS(BlueprintType, Category = "AFPSpawn") | |
| class ARKNIGHTSFP_API UAFPEnemySpawnProfile : public UDataAsset | |
| { | |
| GENERATED_BODY() | |
| public: | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AFPSpawn") | |
| TArray<FEnemyStreak> Streaks; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment