上一次修改时间:2019-08-13 02:01:21

UE4逗B的示例代码上

PikaGodStoneMacroLib.h

#pragma once
#include "Kismet/GameplayStatics.h"

#pragma region SaveSlot 

//关于我们自己的游戏储存
#define PIKA_CREATESAVE_SLOT( MyParameter) UGameplayStatics::CreateSaveGameObject(MyParameter)
#define PIKA_SAVE_SLOT( MyParameter1,MyParameter2,MyParameter3) UGameplayStatics::SaveGameToSlot(MyParameter1,MyParameter2,MyParameter3)
#define PIKA_ISSAVE_SLOT( MyParameter1, MyParameter2) UGameplayStatics::DoesSaveGameExist( MyParameter1, MyParameter2)
#define PIKA_LOADGAME_SLOT( MyParameter1, MyParameter2) UGameplayStatics::LoadGameFromSlot(MyParameter1, MyParameter2)
#define PIKA_DELETEGAME_SLOT( MyParameter1, MyParameter2) UGameplayStatics::DeleteGameInSlot(MyParameter1, MyParameter2)

#pragma endregion SaveSlot

//我们游戏存储的名字
#define PIKA_SAVE_SLOT_NAME "PikaSaveSlot"
#define PIKA_SAVE_GAME_NAME "PikaSaveGame"

//默认填充字体
#define PIKA_SAVE_PIKA "Pikaqiu"

//FOV间隔值
#define PIKA_FOVMAX_VALUE (200.0f)

#define Pika_Debug 1 //方便我们测试游戏

//定义UE4打印屏幕
#if Pika_Debug
#define PIKA_UE_PRINT(MyString) if(GEngine) { GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, MyString); }
#else
#define PIKA_UE_PRINT(MyString)
#endif

// 0
#define PIKA_ZERO (0)

#define PIKA_STANDARD_VALUE 1

//关于存储内容设定
#define Pika_SAVE_SaveBarNumber 20 //默认的条数量

//获取场景内所有Actor
#define PikaGETALLACTOR(MyParameter1,MyParameter2,MyParameter3) UGameplayStatics::GetAllActorsOfClass(MyParameter1,MyParameter2,MyParameter3)

//日志打印
#define PIKA_LOG(Cen) PikaLogPrintf(Cen)

//播放声音
#define PIKA_SPAWN_SOUND_LOCATION(MyParameter1,MyParameter2,MyParameter3,MyParameter4,MyParameter5,MyParameter6,MyParameter7,MyParameter8) UGameplayStatics::PlaySoundAtLocation(MyParameter1,MyParameter2,MyParameter3,MyParameter4,MyParameter5,MyParameter6,MyParameter7,MyParameter8)

#pragma region PikaSaveDataChannel

//作为一个单数据通道使用
struct PikaFSaveSlotDataChannel
{
    //存储游戏的区域
    //------------------------------------
    //PikaUIReadAndWrite循环创建PikaUISaveLoadBar时,
    //将循环的index存入PikaUISaveLoadBar类里的PikaSaveGameBoxNumber
    //点击某个PikaUISaveLoadBar类的按钮后,
    //通过读取PikaUISaveLoadBar类里的PikaSaveGameBoxNumber属性就能得到PikaCurrentSaveGameBoxNumber的值
    int32 PikaCurrentSaveGameBoxNumber;

    //存储游戏的编号
    //------------------------------------
    //在空的存档上新建存档时,在PikaUIReadAndWrite::PikaSaveArchiveSlot的新建逻辑里,
    //通过PikaGetGameInstance()->PikaGetCurrentSaveArchiveNumber获取已有存档的数量,
    //并用该数量来进行设置PikaCurrentGameArchiveNumber
    //------------------------------------
    //在已有的存档上进行覆盖存档时,由PikaUIReadAndWrite::PikaReadArchive读取的本地存档,
    //并将PikaCurrentGameArchiveNumber的值保存在PikaUISaveLoadBar里的PikaGameArchiveNumber属性里
    //点击某个PikaUISaveLoadBar类的按钮后,
    //通过读取PikaUISaveLoadBar类里的PikaGameArchiveNumber属性就能得到PikaCurrentGameArchiveNumber的值
    int32 PikaCurrentGameArchiveNumber;

    //构造赋值
    PikaFSaveSlotDataChannel(int32 SaveGameBoxNumber)
    {
        PikaCurrentSaveGameBoxNumber = SaveGameBoxNumber;
        PikaCurrentGameArchiveNumber = INDEX_NONE;
    }

    PikaFSaveSlotDataChannel(int32 SaveGameBoxNumber, int32 GameArchiveNumber)
    {
        PikaCurrentSaveGameBoxNumber = SaveGameBoxNumber;
        PikaCurrentGameArchiveNumber = GameArchiveNumber;
    }
};

#pragma endregion PikaSaveDataChannel

PikaGodStone.Build.cs

// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;

public class PikaGodStone : ModuleRules
{
    public PikaGodStone(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
    
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "UMG" , "MoviePlayer" });

        PrivateDependencyModuleNames.AddRange(new string[] {  });

        // Uncomment if you are using Slate UI
        PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
        
        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");

        // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    }
}

PikaGodStone.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaGodStoneMacroLib.h"

PikaGodStone.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaGodStone.h"
#include "Modules/ModuleManager.h"

IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, PikaGodStone, "PikaGodStone" );

PikaGodStoneGameModeBase.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "PikaGodStoneGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaGodStoneGameModeBase : public AGameModeBase
{
    GENERATED_BODY()
    
public:
    APikaGodStoneGameModeBase();
    
    virtual void BeginPlay()override;
};

PikaGodStoneGameModeBase.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaGodStoneGameModeBase.h"
#include "Public/PikaGodStoneGameStateBase.h"
#include "Public/PikaGodStonePlayerController.h"
#include "Public/PikaGodStoneCamera.h"
#include "Public/PikaGodStonePlayerState.h"
#include "Public/PikaHUDBase.h"
#include "Public/PikaGameUserSettings.h"




APikaGodStoneGameModeBase::APikaGodStoneGameModeBase()
{
    //加载GameState
    GameStateClass = APikaGodStoneGameStateBase::StaticClass();
    //加载Controller
    PlayerControllerClass = APikaGodStonePlayerController::StaticClass();
    //加载ContorllerPawn
    DefaultPawnClass = APikaGodStoneCamera::StaticClass();
    //加载PlayerState
    PlayerStateClass = APikaGodStonePlayerState::StaticClass();
    //加载HUD
    HUDClass = APikaHUDBase::StaticClass();
}

void APikaGodStoneGameModeBase::BeginPlay()
{
    Super::BeginPlay();

    //设置PikaGameUserSettings的UWorld
    if (UPikaGameUserSettings::PikaGetGameUserSettings())
    {
        UPikaGameUserSettings::PikaGetGameUserSettings()->PikaSetretrieveWorldContext(GetWorld());
    }
}

PikaAITargetPoint.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/TargetPoint.h"
#include "PikaAITargetPoint.generated.h"

/**
 * 
 */
UCLASS(Blueprintable)
class PIKAGODSTONE_API APikaAITargetPoint : public ATargetPoint
{
    GENERATED_BODY()

    //生成怪物时的特效
    UPROPERTY(EditDefaultsOnly, Category = "PikaTargetPoint")
    TArray<UParticleSystem*> PikaSPawnCharacterParticleArray;
    
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PikaTargetPoint")
    bool PikaTeam;
protected:

    virtual void BeginPlay() override;

public:

    APikaAITargetPoint();

    FORCEINLINE bool PikaGetTeam() { return PikaTeam; }
};

PikaAITargetPoint.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaAITargetPoint.h"
#include "Kismet/GameplayStatics.h"




void APikaAITargetPoint::BeginPlay()
{
    Super::BeginPlay();

    if (GetWorld())
    {
        if (!PikaTeam)
        {
            //特效的总数
            int32 PikaIndex_ParNumber = PikaSPawnCharacterParticleArray.Num();
            //特效的随机数
            int32 PikaRandomIndex = FMath::RandRange(0, PikaIndex_ParNumber - 1);
            //特效生成
            if (PikaSPawnCharacterParticleArray.IsValidIndex(PikaRandomIndex))
                UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), PikaSPawnCharacterParticleArray[PikaRandomIndex], GetActorLocation(), GetActorRotation());
        }
    }
}

APikaAITargetPoint::APikaAITargetPoint()
{
    PikaTeam = false;
}

PikaAllowBuildTrigger.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "PikaAllowBuildTrigger.generated.h"


UCLASS()
class PIKAGODSTONE_API APikaAllowBuildTrigger : public AActor
{
    GENERATED_BODY()
    //碰撞组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBox", meta = (AllowPrivateAccess = "true"))
    UBoxComponent * PikaBox;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBox", meta = (AllowPrivateAccess = "true"))
    USceneComponent * PikaRoot;
    
public:    
    // Sets default values for this actor's properties
    APikaAllowBuildTrigger();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;
};

PikaAllowBuildTrigger.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaAllowBuildTrigger.h"


// Sets default values
APikaAllowBuildTrigger::APikaAllowBuildTrigger()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    PikaBox = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaBox"));
    PikaRoot = CreateDefaultSubobject<USceneComponent>(TEXT("PikaRoot"));
    RootComponent = PikaRoot;
    //绑定
    PikaBox->SetupAttachment(PikaRoot);
    PikaBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);

}

// Called when the game starts or when spawned
void APikaAllowBuildTrigger::BeginPlay()
{
    Super::BeginPlay();
    
}

// Called every frame
void APikaAllowBuildTrigger::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

PikaAttackInterface.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaAttackInterface.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UPikaAttackInterface : public UInterface
{
    GENERATED_BODY()
};

/**
 * 
 */
class PIKAGODSTONE_API IPikaAttackInterface
{
    GENERATED_BODY()

    // Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
    //攻击
    virtual void PikaAttackTarget(AActor * Target) {}
    //持续性攻击接口
    virtual void PikaAttackTick() {}

    //接受对方的伤害
    //virtual float PikaGetDefenceDamage(AActor * OtherActor);
    
    //同伴升级的值传递
    virtual void PikaUpdateLevelCharacterArray(TArray<AActor*> MyActor) {};
};

PikaAttackInterface.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaAttackInterface.h"


// Add default functionality here for any IPikaAttackInterface functions that are not pure virtual.

PikaBTTask_RandomWalk.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
#include "PikaBTTask_RandomWalk.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaBTTask_RandomWalk : public UBTTask_BlackboardBase
{
    GENERATED_BODY()

public:
    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
    
public:
    /** Notify called after GameplayTask finishes initialization (not active yet) */
    virtual void OnGameplayTaskInitialized(UGameplayTask& Task) {}

    /** Notify called after GameplayTask changes state to Active (initial activation or resuming) */
    virtual void OnGameplayTaskActivated(UGameplayTask& Task) {}

    /** Notify called after GameplayTask changes state from Active (finishing or pausing) */
    virtual void OnGameplayTaskDeactivated(UGameplayTask& Task) {}
};

PikaBTTask_RandomWalk.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaBTTask_RandomWalk.h"
#include "Public/PikaMonsterAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h"




EBTNodeResult::Type UPikaBTTask_RandomWalk::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    Super::ExecuteTask(OwnerComp , NodeMemory);
    APikaMonsterAIController * PikaMyAIController = Cast<APikaMonsterAIController>(OwnerComp.GetOwner());
    UBlackboardComponent * PikaMyBlackBoard = (UBlackboardComponent *)OwnerComp.GetBlackboardComponent();

    if (PikaMyAIController == NULL) 
    {
        return EBTNodeResult::Failed;
    }
    else 
    {
        //获取随机点
        FVector PikaRandomPoint = PikaMyAIController->PikaGetRandomLoaction();
        //将随机点传出去
        if (BlackboardKey.SelectedKeyType == UBlackboardKeyType_Vector::StaticClass()) 
        {
            //BlackboardKey.SelectedKeyName表示在行为树的蓝图中,手动选择的变量
            PikaMyBlackBoard->SetValueAsVector(BlackboardKey.SelectedKeyName , PikaRandomPoint);
        }
        return EBTNodeResult::Succeeded;
    }

    return EBTNodeResult::Failed;
}

PikaBTTaskAttackTarget.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "PikaBTTaskAttackTarget.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaBTTaskAttackTarget : public UBTTaskNode
{
    GENERATED_BODY()
    
public:

    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;

    virtual void InitializeFromAsset(UBehaviorTree & Asset) override;

    //加载敌对目标
    UPROPERTY(EditAnywhere, Category = "PikaBlackboard")
    struct FBlackboardKeySelector PikaBlackboardActor;
    //敌对目标在行为树的黑板中的变量名,在蓝图中设置
    UPROPERTY(EditAnywhere, Category = "PikaBlackboard")
    FName PikaActorKeyName;

public:
    /** Notify called after GameplayTask finishes initialization (not active yet) */
    virtual void OnGameplayTaskInitialized(UGameplayTask& Task) {}

    /** Notify called after GameplayTask changes state to Active (initial activation or resuming) */
    virtual void OnGameplayTaskActivated(UGameplayTask& Task) {}

    /** Notify called after GameplayTask changes state from Active (finishing or pausing) */
    virtual void OnGameplayTaskDeactivated(UGameplayTask& Task) {}
};

PikaBTTaskAttackTarget.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaBTTaskAttackTarget.h"
#include "Public/PikaMonsterAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
#include "Public/PikaCharacterBase.h"



//PikaBlackboardActor调用前需要先初始化
EBTNodeResult::Type UPikaBTTaskAttackTarget::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    Super::ExecuteTask(OwnerComp, NodeMemory);
    APikaMonsterAIController * PikaMyAIController = Cast<APikaMonsterAIController>(OwnerComp.GetOwner());
    UBlackboardComponent * PikaMyBlackBoard = (UBlackboardComponent *)OwnerComp.GetBlackboardComponent();

    if (PikaMyAIController == NULL)
    {
        return EBTNodeResult::Failed;
    }
    else
    {
        if (PikaBlackboardActor.SelectedKeyType == UBlackboardKeyType_Object::StaticClass())
        {
            UObject * PikaTowerObject = PikaMyBlackBoard->GetValueAsObject(PikaActorKeyName);
            if (PikaTowerObject)
            {
                AActor * PikaTargetTower = (AActor *)PikaTowerObject;
                APikaCharacterBase * PikaMyPawn = Cast<APikaCharacterBase>(PikaMyAIController->GetPawn());
                if (PikaTargetTower && PikaMyPawn)
                {
                    PikaMyPawn->PikaAttackTarget(PikaTargetTower);

                    return EBTNodeResult::Succeeded;
                }
            }
        }
    }

    return EBTNodeResult::Failed;
}

//初始化PikaBlackboardActor
void UPikaBTTaskAttackTarget::InitializeFromAsset(UBehaviorTree & Asset)
{
    Super::InitializeFromAsset(Asset);

    UBlackboardData * PikaBlackboardAsset = GetBlackboardAsset();

    if (PikaBlackboardAsset)
    {
        PikaBlackboardActor.ResolveSelectedKey(*PikaBlackboardAsset);
    }
    else
    {
        UE_LOG(LogBehaviorTree, Warning, TEXT("Pika Can'Initialize task:%s"), *GetName());
    }
}

PikaBTTaskMoveToTarget.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "PikaBTTaskMoveToTarget.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaBTTaskMoveToTarget : public UBTTaskNode
{
    GENERATED_BODY()
public:
    UPikaBTTaskMoveToTarget();

    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;

    virtual void InitializeFromAsset(UBehaviorTree & Asset) override;

    //加载敌对目标
    UPROPERTY(EditAnywhere, Category = "PikaBlackboard")
    struct FBlackboardKeySelector PikaBlackboardActor;
    //接触半径
    UPROPERTY(EditAnywhere, Category = "PikaBlackboard")
    float PikaAcceptanceRadius;
    //敌对目标在行为树的黑板中的变量名,在蓝图中设置
    UPROPERTY(EditAnywhere, Category = "PikaBlackboard")
    FName PikaActorKeyName;

    //打印测试
    void PikaPrint(FString piksStr);
public:
    /** Notify called after GameplayTask finishes initialization (not active yet) */
    virtual void OnGameplayTaskInitialized(UGameplayTask& Task) {}

    /** Notify called after GameplayTask changes state to Active (initial activation or resuming) */
    virtual void OnGameplayTaskActivated(UGameplayTask& Task) {}

    /** Notify called after GameplayTask changes state from Active (finishing or pausing) */
    virtual void OnGameplayTaskDeactivated(UGameplayTask& Task) {}
};

PikaBTTaskMoveToTarget.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaBTTaskMoveToTarget.h"
#include "Public/PikaMonsterAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"




UPikaBTTaskMoveToTarget::UPikaBTTaskMoveToTarget()
{
    PikaAcceptanceRadius = 800.f;
}

//PikaBlackboardActor调用前需要先初始化
EBTNodeResult::Type UPikaBTTaskMoveToTarget::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    Super::ExecuteTask(OwnerComp, NodeMemory);
    APikaMonsterAIController * PikaMyAIController = Cast<APikaMonsterAIController>(OwnerComp.GetOwner());
    UBlackboardComponent * PikaMyBlackBoard = (UBlackboardComponent *)OwnerComp.GetBlackboardComponent();

    if (PikaMyAIController == NULL)
    {
        return EBTNodeResult::Failed;
    }
    else
    {
        if (PikaBlackboardActor.SelectedKeyType == UBlackboardKeyType_Object::StaticClass())
        {
            UObject * PikaTowerObject = PikaMyBlackBoard->GetValueAsObject(PikaActorKeyName);
            if (PikaTowerObject) 
            {
                AActor * PikaTargetTower = (AActor *)PikaTowerObject;
                if (PikaTargetTower)
                {
                    //PikaPrint("PikaMove");
                    /*FVector PikaTest = PikaTargetTower->GetActorLocation();
                    PikaPrint(FString::SanitizeFloat(PikaTest.X));
                    PikaPrint("x");
                    PikaPrint(FString::SanitizeFloat(PikaTest.Y));
                    PikaPrint("y");
                    PikaPrint(FString::SanitizeFloat(PikaTest.Z));
                    PikaPrint("z");*/
                    PikaMyAIController->MoveToLocation(PikaTargetTower->GetActorLocation() , PikaAcceptanceRadius);

                    return EBTNodeResult::Succeeded;
                }
            }
        }
    }

    return EBTNodeResult::Failed;
}

//初始化PikaBlackboardActor
void UPikaBTTaskMoveToTarget::InitializeFromAsset(UBehaviorTree & Asset)
{
    Super::InitializeFromAsset(Asset);

    UBlackboardData * PikaBlackboardAsset = GetBlackboardAsset();

    if (PikaBlackboardAsset)
    {
        PikaBlackboardActor.ResolveSelectedKey(*PikaBlackboardAsset);
    }
    else
    {
        UE_LOG(LogBehaviorTree, Warning, TEXT("Pika Can'Initialize task:%s"), *GetName());
    }
}

void UPikaBTTaskMoveToTarget::PikaPrint(FString piksStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.0f, FColor::Red, piksStr);
    }
}

PikaBulletBase.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/BoxComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Components/SphereComponent.h"
#include "Components/TimelineComponent.h"
#include "PikaBulletBase.generated.h"


class APikaGodStonePlayerController;


UENUM()
namespace PikaBulletType 
{
    enum Type 
    {
        PikaBULLET_Track,//跟踪子弹
        PikaBULLET_Line,//直线飞行子弹
        PikaBULLET_Range,//范围攻击型子弹
        PikaBULLET_Chain,//闪电型攻击
        PikaBULLET_MAX
    };
}

UCLASS()
class PIKAGODSTONE_API APikaBulletBase : public AActor
{
    GENERATED_BODY()
    //载入显示的粒子(子弹飞行中特效)
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    class UParticleSystemComponent * PikaParticleMesh;
    //碰撞盒子
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    //class UBoxComponent * PikaBoxDamage;
    class USphereComponent * PikaBoxDamage;
    //根组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    class USceneComponent * PikaRootBullet;
    //具有移动属性的组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    class UProjectileMovementComponent * PikaProjectileMovement;

    //子弹的施法者
    AActor * PikaCasterCharacter;

    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    TSubclassOf<AActor> PikaDamageValueActor;

    //开火的声音
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    TArray <USoundBase*> PikaOpenFireSound;

public:    
    // Sets default values for this actor's properties
    APikaBulletBase();

    //蓝图中可以设置的子弹类型
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    TEnumAsByte<PikaBulletType::Type> PikaBulletTypeBP;
    //蓝图中设置的子弹伤害特效
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    UParticleSystem * PikaDamageParticle;
    //蓝图中设置的子弹开火特效
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    UParticleSystem * PikaOpenFireParticle;

    //定时销毁子弹
    float PikaDestoryTime;
    FTimerHandle PikaDestoryBulletTimeHandle;
    void PikaDestoryBullet();

    //范围攻击,该曲线由蓝图载入
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    UCurveFloat * PikaCurveFloat;
    UPROPERTY()
    FTimeline PikaRangeAttackTimeLine;
    UFUNCTION()
    void PikaTimeLineRangeAttack(float PikaValue);
    //范围攻击球体的半径
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    float PikaBoxDamageTargetRadius;
    //攻击特效
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    UParticleSystem * PikaRangeAttackType;
    UPROPERTY()
    TArray<ACharacter*> PikaTargetRangeArr;
    UFUNCTION()
    void PikaTimeLineFinished();


protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    //碰撞
    UFUNCTION()
    void PikaBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);


    void PikaPrint(FString PikaStr);

    //获取子弹的粒子特效
    FORCEINLINE UParticleSystemComponent * GetParticleMesh() {return PikaParticleMesh;}
    //具有移动跟踪属性的子弹
    FORCEINLINE UProjectileMovementComponent * GetProjectleMovment() { return PikaProjectileMovement; }
    //设置子弹的发射者
    void PikaSetCasterCharacter(AActor * MyCharacter) { PikaCasterCharacter = MyCharacter; }

    FORCEINLINE class APikaGodStonePlayerController* PikaGetPlayerController();
};

PikaBulletBase.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaBulletBase.h"
#include "Public/PikaTowers.h"
#include "Kismet/GameplayStatics.h"
#include "Public/PikaDamageValueText.h"


// Sets default values
APikaBulletBase::APikaBulletBase()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    PikaParticleMesh = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("PikaBulletParticleMesh"));
    //PikaBoxDamage = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaBoxDamage")); 
    PikaBoxDamage = CreateDefaultSubobject<USphereComponent>(TEXT("PikaBoxDamage"));
    PikaRootBullet = CreateDefaultSubobject<USceneComponent>(TEXT("PikaRootBullet"));
    PikaProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("PikaProjectileMovement"));

    RootComponent = PikaRootBullet;
    PikaBoxDamage->AttachToComponent(PikaRootBullet , FAttachmentTransformRules::KeepRelativeTransform);
    PikaParticleMesh->AttachToComponent(PikaBoxDamage , FAttachmentTransformRules::KeepRelativeTransform);
    //速度初始化
    PikaProjectileMovement->InitialSpeed = 800.f;
    PikaProjectileMovement->MaxSpeed = 2000.f;

    PikaProjectileMovement->UpdatedComponent = PikaBoxDamage;

    switch (PikaBulletTypeBP)
    {
    case PikaBulletType::PikaBULLET_Track://跟踪子弹
        PikaProjectileMovement->bIsHomingProjectile = true;
        PikaProjectileMovement->bRotationFollowsVelocity = true;
        
        break;
    case PikaBulletType::PikaBULLET_Line://直线飞行子弹
        //取消重力模拟
        PikaProjectileMovement->ProjectileGravityScale = 0.f;
        PikaProjectileMovement->InitialSpeed = 1200.f;
        break;
    case PikaBulletType::PikaBULLET_Range://范围攻击型子弹
        PikaProjectileMovement->InitialSpeed = 0;
        break;
    case PikaBulletType::PikaBULLET_Chain://闪电型攻击
        //取消重力模拟
        PikaProjectileMovement->ProjectileGravityScale = 0.f;
        //初速度
        PikaProjectileMovement->InitialSpeed = 0.f;
        //取消碰撞
        PikaBoxDamage->SetCollisionEnabled(ECollisionEnabled::NoCollision);
        break;
    default:
        break;
    }
    //定时销毁子弹的时间
    PikaDestoryTime = 6.5f;
}

void APikaBulletBase::PikaDestoryBullet()
{
    if (PikaDestoryBulletTimeHandle.IsValid() && GetWorld())
    {
        GetWorld()->GetTimerManager().ClearTimer(PikaDestoryBulletTimeHandle);
        Destroy(true);
    }
}

void APikaBulletBase::PikaTimeLineRangeAttack(float PikaValue)
{
    float PikaBoxDamageCurentRadius = FMath::Lerp(PikaBoxDamage->GetScaledSphereRadius() , PikaBoxDamageTargetRadius , PikaValue);
    PikaBoxDamage->SetSphereRadius(PikaBoxDamageCurentRadius);
}

//TimeLine结束时触发的事件
void APikaBulletBase::PikaTimeLineFinished()
{
    /*for (ACharacter * PikaMyCharacter:PikaTargetRangeArr)
    {
        APikaCharacterBase * PikaChar = Cast<APikaCharacterBase>(PikaMyCharacter);
        APikaCharacterBase * PikaCasterActor = Cast<APikaCharacterBase>(PikaCasterCharacter);
        if (PikaChar && PikaRangeAttackType && PikaCasterCharacter)
        {
            FVector PikaMyDistance = PikaChar->GetActorLocation() - PikaCasterActor->GetActorLocation();
            if (PikaChar->PikaIsActive() && PikaMyDistance.Size()<1000)
            {
                //产生伤害,获取状态
                PikaChar->PikaGetCharacterState(PikaChar->PikaGetDefenceDamage(PikaCasterActor));
                //更新血条栏
                PikaChar->PikaUpdateUMGHealth();
                //攻击特效
                UParticleSystemComponent * PikaParticleBullet = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), PikaRangeAttackType, PikaCasterActor->GetSpawnPoint()->GetComponentLocation());
                if (PikaParticleBullet) 
                {
                    PikaParticleBullet->SetBeamSourcePoint(0 , PikaCasterActor->GetHomingPoint()->GetComponentLocation(), 0);
                    PikaParticleBullet->SetBeamTargetPoint(0 , PikaChar->GetHomingPoint()->GetComponentLocation(), 0);
                }
            }
            else 
            {
                PikaTargetRangeArr.Remove(PikaChar);
            }
        }
    }*/
}

// Called when the game starts or when spawned
void APikaBulletBase::BeginPlay()
{
    Super::BeginPlay();

    //碰撞绑定
    if (PikaBoxDamage)
    {
        PikaBoxDamage->OnComponentBeginOverlap.AddUniqueDynamic(this, &APikaBulletBase::PikaBeginOverlapping);
    }

    if (GetWorld()) 
    {
        //开始执行销毁
        GetWorld()->GetTimerManager().SetTimer(PikaDestoryBulletTimeHandle , this , &APikaBulletBase::PikaDestoryBullet , PikaDestoryTime);
        //生成开火特效
        if (PikaOpenFireParticle)
        {
            UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), PikaOpenFireParticle, GetActorLocation());
        }

        //曲线存在,通过代理绑定处理函数
        if (PikaCurveFloat && PikaBulletTypeBP == PikaBulletType::PikaBULLET_Range)
        {
            FOnTimelineFloat PikaTimeLineDelegate;
            FOnTimelineEvent PikaFinishedEvent;
            PikaFinishedEvent.BindUFunction(this, FName("PikaTimeLineFinished"));
            PikaTimeLineDelegate.BindUFunction(this, FName("PikaTimeLineRangeAttack"));
            //添加曲线
            PikaRangeAttackTimeLine.AddInterpFloat(PikaCurveFloat, PikaTimeLineDelegate);
            //设置停止循环
            PikaRangeAttackTimeLine.SetLooping(false);
            //开始播放
            PikaRangeAttackTimeLine.PlayFromStart();
            //TimeLine结束时触发的事件
            PikaRangeAttackTimeLine.SetTimelineFinishedFunc(PikaFinishedEvent);
        }
    }

    //播放开火声音
    if (PikaGetPlayerController())
        PikaGetPlayerController()->PikaPlaySoundAtVetor(PikaOpenFireSound, GetActorLocation(), PikaETowerDefenceSoundType::SOUND_EFFECT_SOUND);
}

// Called every frame
void APikaBulletBase::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    if (PikaBulletTypeBP == PikaBulletType::PikaBULLET_Range) 
    {
        PikaRangeAttackTimeLine.TickTimeline(DeltaTime);
    }
}
/*
 * OverlappedComponent:本类哪个组件触发的碰撞
 * OtherActor:和本类触发碰撞的Actor
 * OtherComp:和本类触发碰撞的Actor的组件
 */
void APikaBulletBase::PikaBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    if (!PikaCasterCharacter) 
    {
        return;
    }
    APikaCharacterBase * PikaEnemyCharacter = Cast<APikaCharacterBase>(OtherActor);
    APikaCharacterBase * PikaCasterActor = Cast<APikaCharacterBase>(PikaCasterCharacter);
    if (!OtherActor || !PikaEnemyCharacter || !PikaCasterActor)
    {
        return;
    }
    //判断是否为同一个队伍
    if (PikaCasterActor->PikaCharDataPre->PikaCharBaseData.PikaTeam != PikaEnemyCharacter->PikaCharDataPre->PikaCharBaseData.PikaTeam) 
    {
        if (PikaEnemyCharacter->PikaIsActive() && PikaBulletTypeBP != PikaBulletType::PikaBULLET_Chain && PikaDamageParticle && GetWorld())
        {
            UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), PikaDamageParticle, SweepResult.Location);
            PikaParticleMesh->SetActive(false);
            PikaBoxDamage->SetCollisionEnabled(ECollisionEnabled::NoCollision);
        
            /*if (PikaBulletTypeBP == PikaBulletType::PikaBULLET_Range) 
            {
                PikaTargetRangeArr.AddUnique(PikaEnemyCharacter);
                
                return;
            }*/
            
            //产生伤害,获取状态
            float PikaDamageValue = PikaEnemyCharacter->PikaGetDefenceDamage(PikaCasterActor);
            PikaEnemyCharacter->PikaGetCharacterState(PikaDamageValue);
            //更新血条栏
            PikaEnemyCharacter->PikaUpdateUMGHealth();
            //停止移动组件
            PikaProjectileMovement->StopMovementImmediately(); 
            //被子弹打中后,损失的生命数值的显示
            if (PikaDamageValue > 0 && PikaDamageValueActor) 
            {
                APikaDamageValueText * PikaValueText = GetWorld()->SpawnActor<APikaDamageValueText>(PikaDamageValueActor, PikaEnemyCharacter->GetActorLocation(), FRotator::ZeroRotator);
                if (PikaValueText)
                    PikaValueText->PikaDrawDamage(PikaDamageValue , 1.f);
            }
        }
    }
}


void APikaBulletBase::PikaPrint(FString PikaStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.f, FColor::Red, PikaStr);
    }
}

FORCEINLINE class APikaGodStonePlayerController* APikaBulletBase::PikaGetPlayerController()
{
    return GetWorld() ? ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController()) : NULL;
}

PikaCharacterBase.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PikaCharacterData.h"
#include "WidgetComponent.h"
#include "PikaAttackInterface.h"
#include "PikaBulletBase.h"
#include "Components/ArrowComponent.h"
#include "PikaGameResource.h"
#include "Sound/SoundBase.h"
#include "PikaCharacterBase.generated.h"



class APikaGodStonePlayerController;



UCLASS()
class PIKAGODSTONE_API APikaCharacterBase : public ACharacter , public IPikaAttackInterface
{
    GENERATED_BODY()

    //跟踪点
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttribute", meta = (AllowPrivateAccess = "true"))
    class USceneComponent * PikaHomingPoint;

    //血条显示
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttribute", meta = (AllowPrivateAccess = "true"))
    class UWidgetComponent * PikaShowUMG;

    //属性显示
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttribute", meta = (AllowPrivateAccess = "true"))
    class UWidgetComponent * PikaShowAttribute;

    //开火点
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttribute", meta = (AllowPrivateAccess = "true"))
    class UArrowComponent * PikaOpenFirePoint;

    //载入显示的粒子(子弹飞行中特效)
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    class UParticleSystemComponent * PikaParticleMesh;

    //捕捉射线,用来显示角色信息
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    class UBoxComponent * PikaTraceShowCharacterInfo;

    //受到攻击的声音
    UPROPERTY(EditDefaultsOnly, Category = "PikaBulletBase")
    TArray<USoundBase*> PikaHitSound;

    int32 PikaCharacterSpawnID;

public:
    //实始化角色信息
    void PikaInitCharacterInformation();
    // Sets default values for this character's properties
    APikaCharacterBase();
    //创建一个指向角色数据类的指针
    UPROPERTY()
    UPikaCharacterData * PikaCharDataPre;

    //加载子弹,该子弹实例从蓝图中手动指定
    UPROPERTY(EditDefaultsOnly , Category = "PikaBullet")
    TSubclassOf<APikaBulletBase> PikaCharacterBullet;

    
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    TSubclassOf<AActor> PikaDeathRangeCharacter;

    //攻击目标
    UPROPERTY()
    AActor * PikaEnemyTarget;

    //角色死亡后,该范围内的敌人角色升级
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    float PikaUpdateLevelRange;
    
    /********蒙太奇start********/
    //游戏开始蒙太奇
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PikaMontage")
    UAnimMontage* PikaGameBeignPlayMontage;

    //攻击蒙太奇
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PikaMontage")
    TArray<UAnimMontage*>  PikaCharacterAttackSkillMontage;

    //死亡蒙太奇
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PikaMontage")
    TArray<UAnimMontage*>  PikaCharacterDeathMontage;

    //角色蒙太奇播放
    void PikaCharacterMontage_Play(TArray<UAnimMontage*> CharacterMontageArray);

    //开始播放动画
    void PikaPlayAnimationForBegin(bool IsSaveCharater = false);

    //蒙太奇开始播放通知蓝图
    UFUNCTION(BlueprintImplementableEvent)
    void PikaBlueprintMontageBegin(UAnimMontage* Montage);

    //蒙太奇结束播放通知蓝图
    UFUNCTION(BlueprintImplementableEvent)
    void PikaBlueprintMontageEnd(UAnimMontage* Montage, bool bInterrupted);

    //如果是读取文件则执行该函数
    UFUNCTION(BlueprintImplementableEvent)
    void PikaEventSaveGameEnd();

protected:
    UFUNCTION()
    void PikaMontageBegin(UAnimMontage* Montage);

    UFUNCTION()
    void PikaMontageEnd(UAnimMontage* Montage, bool bInterrupted);

    /********蒙太奇end********/
protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    //获取游戏的控制
    FORCEINLINE APikaGodStonePlayerController* PikaGetPlayerController() { return GetWorld() ? ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController()) : NULL; }


public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

    void PikaPrint(FString PikaStr);
    
    //获取当前等级
    FORCEINLINE int32 PikaGetCharacterLevel()const { return PikaCharDataPre != NULL ? PikaCharDataPre->PikaCharBaseData.PikaCharacterLevel : INDEX_NONE; }
    //获取开火点和跟踪点
    FORCEINLINE UArrowComponent * GetSpawnPoint() {return PikaOpenFirePoint; }
    FORCEINLINE USceneComponent * GetHomingPoint() { return PikaHomingPoint; }
    //获取特效
    FORCEINLINE UParticleSystemComponent * GetParticleMesh() { return PikaParticleMesh; }

    //获取角色的生命百分比
    float PikaGetHealthPercentage();
    //接受对方的伤害
    float PikaGetDefenceDamage(AActor * OtherActor);
    //更新血条栏
    void PikaUpdateUMGHealth();
    //获取角色死亡状态
    bool PikaGetCharacterState(float Damage);
    //是否存活
    UFUNCTION(BlueprintPure , Category = "PikaAnim")
    bool PikaIsActive();
    //是否死亡
    UFUNCTION(BlueprintPure, Category = "PikaAnim")
    bool PikaIsDeathBP();

    //延时销毁时间句柄
    FTimerHandle PikaDestoryActorHandle;
    //销毁角色
    virtual void PikaDestoryCharacter(float DelayTime = 0);
    //延时销毁角色
    void PikaDelayDestoryCharacter();

    //队伍判断
    bool PikaGetTeam();

    //判断是不是一个队伍
    bool PikaIsTeam();

    //是否升级
    void PikaIsUpdateLevel(float Exp);

    //同伴升级的值传递
    virtual void PikaUpdateLevelCharacterArray(TArray<AActor*> MyActor) override;

    //是否显示角色信息
    void PikaCharacterAttributeDisplay(bool IsDisplay);

    //记录世界生成角色的ID
    int32 PikaGetCharacterSpawnID() const { return PikaCharacterSpawnID; }

    //记录世界生成角色的ID
    void PikaSetCharacterSpawnID(int32 NewSpawnID) { PikaCharacterSpawnID = NewSpawnID; }

    UPROPERTY()
    UPikaGameResource*  PikaTest2112;
};

PikaCharacterBase.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaCharacterBase.h"
#include "Public/PikaUIHealthShow.h"
#include "Engine/Engine.h"
#include "Public/PikaToolRangeOfCharacter.h"
#include "Public/PikaUICharacterAttribute.h"
#include "EngineUtils.h"


void APikaCharacterBase::PikaInitCharacterInformation()
{
    //更新角色信息
    UUserWidget* PikaTest = PikaShowAttribute->GetUserWidgetObject();
    if (PikaCharDataPre && PikaShowAttribute->GetUserWidgetObject())
    {
        UPikaUICharacterAttribute* PikaNewCharacterAttribute = Cast<UPikaUICharacterAttribute>(PikaShowAttribute->GetUserWidgetObject());
        UPikaUIHealthShow * PikaHealthShow = Cast<UPikaUIHealthShow>(PikaShowUMG->GetUserWidgetObject());
        //面板信息变化
        if (PikaNewCharacterAttribute)
        {
            //注册
            PikaNewCharacterAttribute->PikaSetGameCharacterData(PikaCharDataPre);
            PikaNewCharacterAttribute->PikaDrawUIToScreen();
            //更新面板
            PikaNewCharacterAttribute->PikaUpdataAll();
        }
        //血条变化
        if (PikaHealthShow) 
        {
            PikaHealthShow->PikaSetGameCharacterData(PikaCharDataPre);
            PikaHealthShow->PikaDrawUIToScreen();
            PikaHealthShow->PikaUpdateCharacterInformation();
        }
    }
}

// Sets default values
APikaCharacterBase::APikaCharacterBase()
{
     // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    PikaHomingPoint = CreateDefaultSubobject<USceneComponent>(TEXT("PikaHomingPoint"));
    PikaShowUMG = CreateDefaultSubobject<UWidgetComponent>(TEXT("PikaShowUMG"));
    PikaShowAttribute = CreateDefaultSubobject<UWidgetComponent>(TEXT("PikaShowAttribute"));
    PikaOpenFirePoint = CreateDefaultSubobject<UArrowComponent>(TEXT("PikaOpenFirePoint"));
    PikaParticleMesh = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("PikaParticleMesh"));
    PikaTraceShowCharacterInfo = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaTraceShowCharacterInfoComponent"));

    //绑定
    PikaOpenFirePoint->AttachToComponent(RootComponent , FAttachmentTransformRules::KeepRelativeTransform);
    PikaShowAttribute->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    PikaShowUMG->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    PikaHomingPoint->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    PikaParticleMesh->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    PikaTraceShowCharacterInfo->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    //设置预设通道
    PikaTraceShowCharacterInfo->SetCollisionProfileName("PikaScaning");
    PikaTraceShowCharacterInfo->SetBoxExtent(FVector(50 , 50 , 100));

    PikaCharacterSpawnID = -1;

    PikaUpdateLevelRange = 1200.f;
}


void APikaCharacterBase::PikaCharacterMontage_Play(TArray<UAnimMontage*> CharacterMontageArray)
{
    if (GetMesh() && GetMesh()->SkeletalMesh && GetMesh()->GetAnimInstance())
    {
        if (CharacterMontageArray.Num() > 0)
        {
            int32 PikaIndexM = FMath::RandRange(0, (CharacterMontageArray.Num() - 1));
            GetMesh()->GetAnimInstance()->Montage_Play(CharacterMontageArray[PikaIndexM]);
        }
    }
}

void APikaCharacterBase::PikaPlayAnimationForBegin(bool IsSaveCharater /*= false*/)
{
    //动画
    if (GetMesh() && GetMesh()->SkeletalMesh && GetMesh()->GetAnimInstance())
    {
        //对蒙太奇进行记录绑定
        GetMesh()->GetAnimInstance()->OnMontageStarted.AddDynamic(this, &APikaCharacterBase::PikaMontageBegin);
        GetMesh()->GetAnimInstance()->OnMontageEnded.AddDynamic(this, &APikaCharacterBase::PikaMontageEnd);

        //播放起始蒙太奇(如果该数据是存储中导入不需要执行起始蒙太奇)
        if (!IsSaveCharater)
        {
            if (PikaGameBeignPlayMontage)
                GetMesh()->GetAnimInstance()->Montage_Play(PikaGameBeignPlayMontage);
        }
        else
        {
            //执行蓝图内容
            //EventSaveGameEnd();
        }
    }
}


void APikaCharacterBase::PikaMontageBegin(UAnimMontage* Montage)
{
    PikaBlueprintMontageBegin(Montage);
}

void APikaCharacterBase::PikaMontageEnd(UAnimMontage* Montage, bool bInterrupted)
{
    PikaBlueprintMontageEnd(Montage, bInterrupted);
}

// Called when the game starts or when spawned
void APikaCharacterBase::BeginPlay()
{
    Super::BeginPlay();
    //默认状态下隐藏角色信息
    if(PikaShowAttribute && PikaShowAttribute->GetUserWidgetObject())
        PikaShowAttribute->GetUserWidgetObject()->SetVisibility(ESlateVisibility::Hidden);
    //生成角色需要生成一个默认的控制
    if (!GetController()) 
    {
        SpawnDefaultController();
    }

    //为角色数据分配位置
    TSubclassOf<UPikaCharacterData> PikaSubclassOfChartData = UPikaCharacterData::StaticClass();
    if (GetWorld() && PikaSubclassOfChartData)
        PikaCharDataPre = NewObject<UPikaCharacterData>(GetWorld(), PikaSubclassOfChartData);
    
}

// Called every frame
void APikaCharacterBase::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    if (!PikaGetPlayerController() && !PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon)
        return;

    //上传我们的数据,如果角色有速度变化时,更新角色在地图中的坐标
    //GetVelocity().Size()表示速度变化的大小,没有移动时,该值为0
    if (GetVelocity().Size())
    {
        int32 PikaCharacterIndex = PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaGetCurrentCharacterSpawnIDIndex(PikaCharacterSpawnID);
        if (PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaIsActive(PikaCharacterIndex))
        {
            PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterLocation[PikaCharacterIndex] = GetActorLocation();
            PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterRotator[PikaCharacterIndex] = GetActorRotation();
        }
    }
}

// Called to bind functionality to input
void APikaCharacterBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}

void APikaCharacterBase::PikaPrint(FString PikaStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.0f, FColor::Red, PikaStr);
    }
}

float APikaCharacterBase::PikaGetHealthPercentage()
{
    return PikaCharDataPre != NULL ? PikaCharDataPre->PikaCharBaseData.PikaGetHealthValuePercentage()  : 1 ;
}

float APikaCharacterBase::PikaGetDefenceDamage(AActor * OtherActor)
{
    if (OtherActor) 
    {
        APikaCharacterBase * PikaEnemy = (APikaCharacterBase *)OtherActor;
        if (PikaEnemy)
        {
            return PikaEnemy->PikaCharDataPre->PikaCharBaseData.PikaPhysicalAttack / ((PikaEnemy->PikaCharDataPre->PikaCharBaseData.PikaArmor / 10) + 1);
        }
    }
    return 0;
}

//更新血条栏
void APikaCharacterBase::PikaUpdateUMGHealth()
{
    if (PikaShowUMG) 
    {
        UPikaUIHealthShow * PikaHealthShow = Cast<UPikaUIHealthShow>(PikaShowUMG->GetUserWidgetObject());
        UPikaUICharacterAttribute* PikaNewCharacterAttribute = Cast<UPikaUICharacterAttribute>(PikaShowAttribute->GetUserWidgetObject());
        
        //更新血条显示
        if (PikaHealthShow) 
            PikaHealthShow->PikaUpdateCharacterInformation();
        
        //更新面板显示
        if (PikaNewCharacterAttribute)
            PikaNewCharacterAttribute->PikaUpdateCharacterInformation();
    }
}

bool APikaCharacterBase::PikaGetCharacterState(float Damage)
{
    //播放角色受伤的声音
    if (PikaGetPlayerController())
        PikaGetPlayerController()->PikaPlaySoundAtVetor(PikaHitSound, GetActorLocation(), PikaETowerDefenceSoundType::SOUND_EFFECT_SOUND);
    //产生伤害
    PikaCharDataPre->PikaCharBaseData.PikaCurrentHealth -= Damage;
    if (PikaCharDataPre->PikaCharBaseData.PikaCurrentHealth > 0) 
    {
        //上传我们血值信息
        if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon)
        {
            int32 PikaCharacterIndex = PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaGetCurrentCharacterSpawnIDIndex(PikaCharacterSpawnID);
            if (PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaIsActive(PikaCharacterIndex))
                PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterCurrentHealth[PikaCharacterIndex] = PikaCharDataPre->PikaCharBaseData.PikaCurrentHealth;
        }

        return PikaCharDataPre->PikaCharBaseData.PikaDeath = false;
    }
    
    PikaCharDataPre->PikaCharBaseData.PikaDeath = true;
    //角色死亡,销毁角色
    PikaDestoryCharacter(2);
    return true;
}

//是否存活
bool APikaCharacterBase::PikaIsActive()
{
    if (PikaCharDataPre) 
        return !PikaCharDataPre->PikaCharBaseData.PikaDeath;
        
    return true;
}

bool APikaCharacterBase::PikaIsDeathBP()
{
    if (PikaCharDataPre)
        return PikaCharDataPre->PikaCharBaseData.PikaDeath;

    return false;
}

void APikaCharacterBase::PikaDestoryCharacter(float DelayTime)
{
    if (GetWorld()) 
    {
        if (DelayTime > 0) 
        {
            GetWorld()->GetTimerManager().SetTimer(PikaDestoryActorHandle , this , &APikaCharacterBase::PikaDelayDestoryCharacter , DelayTime);
        }
        else 
        {
            GetWorld()->GetTimerManager().SetTimer(PikaDestoryActorHandle, this, &APikaCharacterBase::PikaDelayDestoryCharacter, 0.1f);
        }

        //该角色死亡后对角色范围内的非队友进行经验值增加
        for (TActorIterator<APikaCharacterBase>it(GetWorld(), APikaCharacterBase::StaticClass()); it; ++it)
        {
            APikaCharacterBase* PikaNewATCharacter = *it;
            if (PikaNewATCharacter && PikaNewATCharacter->PikaIsTeam() != PikaIsTeam())
            {
                if ((PikaNewATCharacter->GetActorLocation() - GetActorLocation()).Size() <= PikaUpdateLevelRange)
                {
                    PikaNewATCharacter->PikaIsUpdateLevel(PikaCharDataPre->PikaCharBaseData.PikaAddExp);
                }
            }
        }

        /*if (PikaDeathRangeCharacter)
        {
            APikaToolRangeOfCharacter * PikaCompanion = GetWorld()->SpawnActor<APikaToolRangeOfCharacter>(PikaDeathRangeCharacter , GetActorLocation() , GetActorRotation());
        }*/

        if (PikaGetPlayerController())
        {
            //播放我们的死亡声音
            //GetTowerDefencePlayerController()->PlaySoundAtVetor(DeathSound, GetActorLocation(), ETowerDefenceSoundType::SOUND_EFFECT_SOUND);

            //上传我们的死亡信息
            if (PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon)
            {
                int32 PikaCharacterIndex = PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaGetCurrentCharacterSpawnIDIndex(PikaCharacterSpawnID);
                if (PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaIsActive(PikaCharacterIndex))
                {
                    PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterActive[PikaCharacterIndex] = false;
                    PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterCurrentHealth[PikaCharacterIndex] = 0;
                }
            }
        
            /*//通知Control添加奖励玩家金币
            if (GameCharacterData)
            {
                if (!IsTeam())
                {
                    //添加金币
                    GetTowerDefencePlayerController()->Confo->TDGameData.AddGlobalGold(GameCharacterData->CharacterDataBase.RewardGlod);

                    //显示增加金币数字
                    ATool_DamageValueText* MyValueText = GetWorld()->SpawnActor<ATool_DamageValueText>(DamageValueActor, GetActorLocation(), FRotator::ZeroRotator);
                    if (MyValueText)
                        MyValueText->DrawGold(GameCharacterData->CharacterDataBase.RewardGlod, .7f);
                }

                //条件是否成功
                GetTowerDefencePlayerController()->GameCondition(ELevelCondition::ExceedingSpiritualForce);

                //播放我们的金币声音
                GetTowerDefencePlayerController()->PlaySoundAtVetor(GetTowerDefencePlayerController()->TDGameResources->GoldSound, GetActorLocation(), ETowerDefenceSoundType::SOUND_EFFECT_SOUND);

            }*/
        }
    }
}

//延时销毁角色
void APikaCharacterBase::PikaDelayDestoryCharacter()
{
    if (PikaDestoryActorHandle.IsValid() && GetWorld())
    {
        GetWorld()->GetTimerManager().ClearTimer(PikaDestoryActorHandle);
    }
    if (PikaCharDataPre)
    {
        PikaCharDataPre->ConditionalBeginDestroy();
        PikaCharDataPre = NULL;
    }
    UPikaGameResource *  PikaTest1 = PikaGetPlayerController()->PikaGameResourcePtr;
    if (PikaGetPlayerController())
        PikaGetPlayerController()->PikaClearTmpRuleOfTheCharacter();
    
    Destroy(true);
}

bool APikaCharacterBase::PikaGetTeam()
{
    return PikaCharDataPre->PikaCharBaseData.PikaTeam;
}

bool APikaCharacterBase::PikaIsTeam()
{
    return PikaCharDataPre != NULL ? PikaCharDataPre->PikaCharBaseData.PikaTeam : false;
}

void APikaCharacterBase::PikaIsUpdateLevel(float Exp)
{
    PikaCharDataPre->PikaCharBaseData.PikaCurrentExperienceValue += Exp;
    if (PikaCharDataPre->PikaCharBaseData.PikaCurrentExperienceValue >= PikaCharDataPre->PikaCharBaseData.PikaMaxExperienceValue) 
    {
        PikaCharDataPre->PikaCharBaseData.PikaUpdateLevel();

        UPikaUICharacterAttribute* PikaNewCharacterAttribute = Cast<UPikaUICharacterAttribute>(PikaShowAttribute->GetUserWidgetObject());
        // 更新面板
        if (PikaNewCharacterAttribute)
            PikaNewCharacterAttribute->PikaUpdateLevelCharacterElement();
    }
}

void APikaCharacterBase::PikaUpdateLevelCharacterArray(TArray<AActor*> MyActor)
{
    for (AActor* PikaOurActor : MyActor) 
    {
        APikaCharacterBase * PikaTempCharacterBase = Cast<APikaCharacterBase>(PikaOurActor);
        if (PikaTempCharacterBase) 
        {
            PikaTempCharacterBase->PikaIsUpdateLevel(PikaCharDataPre->PikaCharBaseData.PikaAddExp);
        }
    }
}

void APikaCharacterBase::PikaCharacterAttributeDisplay(bool IsDisplay)
{
    if (PikaShowAttribute && PikaShowAttribute->GetUserWidgetObject())
    {
        if (IsDisplay) 
        {
            PikaShowAttribute->GetUserWidgetObject()->SetVisibility(ESlateVisibility::Visible);
        }
        else 
        {
            PikaShowAttribute->GetUserWidgetObject()->SetVisibility(ESlateVisibility::Hidden);
        }
    }
}

PikaCharacterData.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "PikaCharacterData.generated.h"

//角色基础数据结构体
USTRUCT()
struct FPikaCharacterDataAttribute 
{
    GENERATED_USTRUCT_BODY()

    //角色名字
    UPROPERTY()
    FName PikaCharacterName;
    //角色ID
    UPROPERTY()
    int32 PikaCharacterID;
    //角色等级
    UPROPERTY()
    int32 PikaCharacterLevel;
    //最大生命值
    UPROPERTY()
    float PikaMaxHealth;
    //当前生命值
    UPROPERTY()
    float PikaCurrentHealth;
    //死亡状态
    UPROPERTY()
    bool PikaDeath;
    //角色组:True为友方,False为敌对方
    UPROPERTY()
    bool PikaTeam;
    //物理攻击
    UPROPERTY()
    float PikaPhysicalAttack;
    //护甲
    UPROPERTY()
    float PikaArmor;
    //最大经验值
    UPROPERTY()
    float PikaMaxExperienceValue;
    //当前经验值
    UPROPERTY()
    float PikaCurrentExperienceValue;
    //攻击速度
    UPROPERTY()
    float PikaAttackSpeed; 
    //最小攻击速度
    UPROPERTY()
    float PikaAttackSpeedMin;
    //建造时需要消耗的金币数
    UPROPERTY()
    float PikaComsumeGold;
    //消灭时获得的金币数
    UPROPERTY()
    float PikaAddComsume;

    //升级(每级加多少)
    //添加生命值
    UPROPERTY()
    float PikaAddHealth;
    //添加攻击力
    UPROPERTY()
    float PikaAddPhsicalAttack;
    //添加护甲
    UPROPERTY()
    float PikaAddArmor;
    //添加经验值
    UPROPERTY()
    float PikaAddExp;
    //添加攻击速度
    UPROPERTY()
    float PikaAddAttackSpeed;
    //添加金币
    UPROPERTY()
    float PikaAddGlod;
    //奖励金币
    UPROPERTY()
    float PikaRewardGlod;


    //属性初始化
    FPikaCharacterDataAttribute();
    //获取经验值百分比
    float PikaGetEmpircalValuePercentage() const { return PikaMaxExperienceValue ? PikaCurrentExperienceValue / PikaMaxExperienceValue : 0; }

    //生命值百分比
    float PikaGetHealthValuePercentage()  const { return PikaMaxHealth ? PikaCurrentHealth / PikaMaxHealth : 0; }

    //角色注册后主动调用一次,用导入的csv数据进行初始化
    void PikaInitCharacterAttrubute(float PikaMyMaxHealth , float PikaMyPhysicalAttack , float PikaMyAttackSpeed , float PikaMyMaxExperienceValue , float PikaMyArmor)
    {
        PikaMaxHealth = PikaMyMaxHealth;
        PikaPhysicalAttack = PikaMyPhysicalAttack;
        PikaAttackSpeed = PikaMyAttackSpeed;
        PikaMaxExperienceValue = PikaMyMaxExperienceValue;
        PikaArmor = PikaMyArmor;

        PikaCurrentExperienceValue = 0;
        PikaCurrentHealth = PikaMaxHealth;
        PikaDeath = false;
        //保存初始速度
        PikaAttackSpeedMin = PikaAttackSpeed;
        PikaCharacterLevel = 0;
    }
    //角色注册后主动调用一次
    void PikaInitAddCharacterAttribute(float PikaMyAddHealth , float PikaMyAddPhsicalAttack , float PikaMyAddArmor , float PikaMyAddExp , float PikaMyAddAttackSpeed)
    {
        PikaAddHealth = PikaMyAddHealth;
        PikaAddPhsicalAttack = PikaMyAddPhsicalAttack;
        PikaAddArmor = PikaMyAddArmor;
        PikaAddExp = PikaMyAddExp;
        PikaAddAttackSpeed = PikaMyAddAttackSpeed;
    }
    //角色升级
    void PikaUpdateLevel() 
    {
        PikaCharacterLevel += 1;
        PikaMaxHealth += (PikaCharacterLevel - 1)*PikaAddHealth;
        PikaPhysicalAttack += (PikaCharacterLevel - 1)*PikaAddPhsicalAttack;
        PikaAttackSpeed += (PikaCharacterLevel - 1)*PikaAddAttackSpeed;
        //PikaMaxExperienceValue += (PikaCharacterLevel - 1)*PikaAddExp;
        PikaArmor += (PikaCharacterLevel - 1)*PikaAddArmor;

        PikaCurrentExperienceValue = 0;
        PikaCurrentHealth = PikaMaxHealth;

        UE_LOG(LogTemp, Log, TEXT("Pika:UpdateLevel Successfully!"));
        
        return;
    }
};

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaCharacterData : public UObject
{
    GENERATED_BODY()

public:
    FPikaCharacterDataAttribute PikaCharBaseData;
};

PikaCharacterData.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaCharacterData.h"




FPikaCharacterDataAttribute::FPikaCharacterDataAttribute()
{
    //角色名字
    PikaCharacterName = "NoOne";
    //角色ID
    PikaCharacterID = 1;
    //角色等级
    PikaCharacterLevel = 1;
    //最大生命值
    PikaMaxHealth = 1000;
    //当前生命值
    PikaCurrentHealth = 1000;
    //死亡状态
    PikaDeath = false;
    //角色组:True为友方,False为敌对方
    PikaTeam = false;
    //物理攻击
    PikaPhysicalAttack = 100.f;
    //护甲
    PikaArmor = 10.f;
    //最大经验值
    PikaMaxExperienceValue = 6000.f;
    //当前经验值
    PikaCurrentExperienceValue = 0.f;
    //增加经验值
    PikaAddExp = 100.f;
    //攻击速度
    PikaAttackSpeed = PikaAttackSpeedMin = 0.66;
    //建造时需要消耗的金币数
    PikaComsumeGold = 100.f;
    //消灭时获得的金币数
    PikaAddComsume = 10.f;
}

PikaCharacterInfoDataTable.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "PikaCharacterInfoDataTable.generated.h"

UENUM()
namespace EPikaCharacterInfoDataTableType
{
    enum Type
    {
        // 基础数据
        DATA_PikaCharacterHealth,
        DATA_PikaCharacterAttack,
        DATA_PikaCharacterArm,
        DATA_PikaCharacterEmpircalValue,
        DATA_PikaCharacterAttackSpeed,
        DATA_PikaCharacterComsumeGlod,
        DATA_PikaCharacterConstructionTime,

        //增加数据
        DATA_PikaCharacterAddGlod,
        DATA_PikaCharacterAddHealth,
        DATA_PikaCharacterAddPhysicalAttack,
        DATA_PikaCharacterAddArmor,
        DATA_PikaCharacterAddEmpiricalValue,
        DATA_PikaCharacterAddAttackSpeed,
        DATA_PikaCharacterMax
    };
}

/**
 * 
 */
USTRUCT(BlueprintType)
struct FPikaCharacterInfoDataTable : public FTableRowBase
{
    GENERATED_BODY()
    
public:
    //角色ID
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    int32 PikaCharacterID;
    //角色图片
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    TAssetPtr<UTexture2D> PikaIcon;
    //角色蓝图实例
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    TAssetSubclassOf<AActor> PikaCharacterBlueprintKey;
    

    //基础属性
    //最大生命值
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaMaxHealth;
    //物理攻击
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaPhysicalAttack;
    //护甲
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaArmor;
    //最大经验值
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaMaxExperienceValue;
    //攻击速度
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAttackSpeed;
    //建造时需要消耗的金币数
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaComsumeGold;
    //建造时需要的时间
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaConstructionTime;
    //角色简介
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    FText PikaComment;
    

    //添加
    //增加经验值
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAddExperienceValue;
    //消灭时获得的金币数
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAddComsume;
    //增加生命值
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAddHealth;
    //增加攻击力
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAddPhysicalAttack;
    //增加护甲
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAddArmor;
    //添加攻击速度
    UPROPERTY(EditDefaultsOnly, Category = "PikaDataTable")
    float PikaAddAttackSpeed;
    
    
    FPikaCharacterInfoDataTable();
};

PikaCharacterInfoDataTable.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaCharacterInfoDataTable.h"




FPikaCharacterInfoDataTable::FPikaCharacterInfoDataTable():
//角色ID
PikaCharacterID(1),
//最大生命值
PikaMaxHealth(1000),
//物理攻击
PikaPhysicalAttack(100.f),
//护甲
PikaArmor(10.f),
//最大经验值
PikaMaxExperienceValue(6000.f),
//增加经验值
PikaAddExperienceValue(100.f),
//攻击速度
PikaAttackSpeed(0.66),
//建造时需要消耗的金币数
PikaComsumeGold(100.f),
//消灭时获得的金币数
PikaAddComsume(10.f)
{

}

PikaDamageValueText.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "WidgetComponent.h"
#include "Components/SceneComponent.h"
#include "Components/TimelineComponent.h"
#include "PikaDamageValueText.generated.h"


UCLASS()
class PIKAGODSTONE_API APikaDamageValueText : public AActor
{
    GENERATED_BODY()

    UPROPERTY(VisibleAnywhere , BlueprintReadOnly , Category="PikaDamage" , meta=(AllowPrivateAccess = "true"))
    class UWidgetComponent * PikaDamageValueUMG;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaDamage", meta = (AllowPrivateAccess = "true"))
    class USceneComponent * PikaRootPoint;

    UPROPERTY(EditDefaultsOnly,  Category = "PikaDamage")
    float PikaTextHeight;
    
public:    
    // Sets default values for this actor's properties
    APikaDamageValueText();

    //绘制伤害
    void PikaDrawDamage(float DamageValue, float DamagePercentage);

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // 范围攻击,该曲线由蓝图载入
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    UCurveFloat * PikaCurveFloat;
    UPROPERTY()
    FTimeline PikaRangeAttackTimeLine;
    //范围攻击球体的半径
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    float PikaBoxDamageTargetRadius;
    UFUNCTION()
    void PikaTimeLineRangeAttack(float PikaValue);
    UFUNCTION()
    void PikaTimeLineFinished();
    
};

PikaDamageValueText.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaDamageValueText.h"
#include "Public/PikaUI_InformationWeight.h"


// Sets default values
APikaDamageValueText::APikaDamageValueText()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    PikaDamageValueUMG = CreateDefaultSubobject<UWidgetComponent>(TEXT("PikaParticleMesh"));
    PikaRootPoint = CreateDefaultSubobject<USceneComponent>(TEXT("PikaHomingPoint"));
    RootComponent = PikaRootPoint;
    PikaDamageValueUMG->AttachToComponent(PikaRootPoint, FAttachmentTransformRules::KeepRelativeTransform); 
}

//绘制伤害
void APikaDamageValueText::PikaDrawDamage(float DamageValue, float DamagePercentage)
{
    if (PikaDamageValueUMG && PikaDamageValueUMG->GetUserWidgetObject())
    {
        UPikaUI_InformationWeight* PikaMyInformationWidget = Cast<UPikaUI_InformationWeight>(PikaDamageValueUMG->GetUserWidgetObject());
        if (PikaMyInformationWidget)
        {
            PikaMyInformationWidget->PikaDrawUIToScreen();
            PikaMyInformationWidget->PikaDrawSubtractionHealthInformation(DamageValue, DamagePercentage);
        }
    }
}

// Called when the game starts or when spawned
void APikaDamageValueText::BeginPlay()
{
    Super::BeginPlay();
    
    if (GetWorld())
    {
        //曲线存在,通过代理绑定处理函数
        if (PikaCurveFloat)
        {
            FOnTimelineFloat PikaTimeLineDelegate;
            FOnTimelineEvent PikaFinishedEvent;
            PikaFinishedEvent.BindUFunction(this, FName("PikaTimeLineFinished"));
            PikaTimeLineDelegate.BindUFunction(this, FName("PikaTimeLineRangeAttack"));
            //添加曲线
            PikaRangeAttackTimeLine.AddInterpFloat(PikaCurveFloat, PikaTimeLineDelegate);
            //设置停止循环
            PikaRangeAttackTimeLine.SetLooping(false);
            //开始播放
            PikaRangeAttackTimeLine.PlayFromStart();
            //TimeLine结束时触发的事件
            PikaRangeAttackTimeLine.SetTimelineFinishedFunc(PikaFinishedEvent);
        }
    }
}

// Called every frame
void APikaDamageValueText::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    if (PikaCurveFloat)
    {
        PikaRangeAttackTimeLine.TickTimeline(DeltaTime);
    }
}

void APikaDamageValueText::PikaTimeLineRangeAttack(float PikaValue)
{
    if (PikaDamageValueUMG && PikaTextHeight>0)
    {
        FVector PikaBoxDamageCurrentRadius = FMath::Lerp(FVector::ZeroVector , FVector(0 , 0 , PikaTextHeight) , PikaValue);
        PikaDamageValueUMG->AddRelativeLocation(PikaBoxDamageCurrentRadius);
    }
}

void APikaDamageValueText::PikaTimeLineFinished()
{
    Destroy(true);
}

PikaDisplaceTrigger.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "PikaGodStoneCamera.h"
#include "PikaDisplaceTrigger.generated.h"


UCLASS()
class PIKAGODSTONE_API APikaDisplaceTrigger : public AActor
{
    GENERATED_BODY()

    //碰撞组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBox", meta = (AllowPrivateAccess = "true"))
    UBoxComponent * PikaBox_1;

    //根组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBox", meta = (AllowPrivateAccess = "true"))
    USceneComponent * PikaRoot;

    //存储Camera的指针
    UPROPERTY()
    APikaGodStoneCamera * PikaCameraPawn;
    
public:    
    // Sets default values for this actor's properties
    APikaDisplaceTrigger();

    //蓝图中可以编辑的变量
    UPROPERTY(EditAnywhere , BlueprintReadWrite , Category = "PikaUI")
    float PikaAddDistance;

    UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "PikaEventAddDistance"), Category = "PikaEvent")
    void PikaAddDistanceCamera(AActor * OtherActor);

    UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "PikaEventSubstractDistance"), Category = "PikaEvent")
    void PikaSubstractDistanceCamera(AActor * OtherActor);

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    UFUNCTION()
    void PikaBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

    UFUNCTION()
    void PikaEndOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex);

    //打印测试
    UFUNCTION()
    void PikaPrint(FString piksStr);
};

PikaDisplaceTrigger.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaDisplaceTrigger.h"


// Sets default values
APikaDisplaceTrigger::APikaDisplaceTrigger()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    //创建碰撞组件
    PikaBox_1 = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaDisplacementTrigger_1"));
    PikaRoot = CreateDefaultSubobject<USceneComponent>(TEXT("PikaRoot_1"));
    RootComponent = PikaRoot;
    //绑定
    PikaBox_1->AttachToComponent(PikaRoot, FAttachmentTransformRules::KeepRelativeTransform); 
}


// Called when the game starts or when spawned
void APikaDisplaceTrigger::BeginPlay()
{
    Super::BeginPlay();
    
    //绑定碰撞的事件
    PikaBox_1->OnComponentBeginOverlap.AddUniqueDynamic(this, &APikaDisplaceTrigger::PikaBeginOverlapping);
    PikaBox_1->OnComponentEndOverlap.AddUniqueDynamic(this, &APikaDisplaceTrigger::PikaEndOverlapping);

    if (GetWorld()) 
    {
        //获取MainCamera的指针
        PikaCameraPawn = Cast<APikaGodStoneCamera>(GetWorld()->GetFirstPlayerController()->GetPawn());
        
    }
}

// Called every frame
void APikaDisplaceTrigger::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

void APikaDisplaceTrigger::PikaBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    PikaAddDistanceCamera(OtherActor);
}

void APikaDisplaceTrigger::PikaEndOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{
    PikaSubstractDistanceCamera(OtherActor);
}

void APikaDisplaceTrigger::PikaPrint(FString piksStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.0f, FColor::Red, piksStr);
    }
}

PikaDragDropOperation.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/DragDropOperation.h"
#include "PikaUIWidgetBase.h"
#include "PikaDragDropOperation.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaDragDropOperation : public UDragDropOperation
{
    GENERATED_BODY()
    
public:
    //PikaUI_InventorySlot类的指针
    UPROPERTY()
    UPikaUIWidgetBase* PikaInventorySlot;
};

PikaDragDropOperation.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaDragDropOperation.h"

PikaGameErrorHint.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "TextBlock.h"
#include "PikaGameErrorHint.generated.h"



class UPikaMainInterface;
class UPikaUIMainHall;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameErrorHint : public UPikaUIWidgetBase
{
    GENERATED_BODY()

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaGameLogName;
    
public:
    UPikaGameErrorHint();
    float PikaLogDisplayTime;
    bool PikaCheckTime;

    //相当于BeginPlay,用于解决BeginPlay执行时,有些资源可能还没加载完成的问题
    virtual void PikaDrawUIToScreen() override;
    //负责打印GameLOG
    UFUNCTION()
    virtual void PikaLogPrintf(FString MyString) override;
    
    //NativeTick函数里会调用蓝图里的Tick
    virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
    
    UPikaMainInterface* PikaGetMainInterface() const { return PikaMainInterface; }
    void PikaSetMainInterface(UPikaMainInterface* NewWidget) { PikaMainInterface = NewWidget; }

    UPikaUIMainHall* PikaGetMainHall() const { return PikaMainHall; }
    void PikaSetMainHall(UPikaUIMainHall* NewWidget) { PikaMainHall = NewWidget; }
private:
    UPikaMainInterface* PikaMainInterface;
    UPikaUIMainHall* PikaMainHall;
    UTextBlock* PikaGameLog;
};

PikaGameErrorHint.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameErrorHint.h"




UPikaGameErrorHint::UPikaGameErrorHint()
{
    PikaLogDisplayTime = 0.f;
    PikaCheckTime = false;
}

void UPikaGameErrorHint::PikaDrawUIToScreen()
{
    PikaGameLog = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaGameLogName));
    if (PikaGetMainInterface())
        PikaGetMainInterface()->PikaLog.AddDynamic(this , &UPikaGameErrorHint::PikaLogPrintf);
    if (PikaGameLog)
        PikaGameLog->SetVisibility(ESlateVisibility::Hidden);
}

void UPikaGameErrorHint::PikaLogPrintf(FString MyString)
{
    if (PikaGameLog) 
    {
        PikaGameLog->SetText(FText::FromString(MyString));
        PikaGameLog->SetVisibility(ESlateVisibility::Visible);
    }
        
    if (PikaUIDataPtr)
        PikaLogDisplayTime = PikaUIDataPtr->PikaLogDisplayTime;
}

void UPikaGameErrorHint::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
    Super::NativeTick(MyGeometry , InDeltaTime);

    if (PikaLogDisplayTime > 0) 
    {
        PikaLogDisplayTime -= InDeltaTime;
        PikaCheckTime = true;
        return;
    }
    if (PikaCheckTime && PikaGameLog) 
    {
        PikaCheckTime = false;
        PikaGameLog->SetVisibility(ESlateVisibility::Hidden);
    }
}

PikaGameManager.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/CheatManager.h"
#include "Public/PikaGodStonePlayerController.h"
#include "Public/PikaGodStonePlayerState.h"
#include "Public/PikaGodStoneCamera.h"
#include "PikaGameManager.generated.h"



/**
 * 
 */
UCLASS(Within = PikaGodStonePlayerController)
class PIKAGODSTONE_API UPikaGameManager : public UCheatManager
{
    GENERATED_BODY()
    
public:
    UFUNCTION(exec)
    void PikaLogPrint();

    //添加一个塔
    UFUNCTION(exec)
    void PikaAddTowers(int32 TowerID , int32 TowerLevel , int32 TowerCount);
    
    APikaGodStonePlayerController * PikaGetPC();

    APikaGodStonePlayerState * PikaGetTowersPlayerState();

    APikaGodStoneCamera * PikaGetTowerCamera();
};

PikaGameManager.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameManager.h"




void UPikaGameManager::PikaLogPrint()
{
    FString PiksStr = "pikapika";
    float PikaFloat = 0.22;
    int32 PikaInt = 123;

    UE_LOG(LogTemp , Log , TEXT("PikaOK"));
    UE_LOG(LogTemp , Warning , TEXT("%s , %f , %i") , *PiksStr , PikaFloat , PikaInt);
    UE_LOG(LogTemp, Error, TEXT("%s , %f , %i"), *PiksStr, PikaFloat, PikaInt);
}

//添加一个塔
void UPikaGameManager::PikaAddTowers(int32 TowerID, int32 TowerLevel, int32 TowerCount)
{
    if (!(TowerCount >= 1)) 
    {
        UE_LOG(LogTemp , Warning ,TEXT("%i < 1") , TowerCount);
        return;
    }
    for (int32 i=0 ; i<TowerCount ; i++)
    {
        PikaGetPC()->PikaSpawnTowers(TowerID , TowerLevel);
    }
}

APikaGodStonePlayerController * UPikaGameManager::PikaGetPC()
{
    return ((APikaGodStonePlayerController *)GetOuterAPlayerController());
}

APikaGodStonePlayerState * UPikaGameManager::PikaGetTowersPlayerState()
{
    return PikaGetPC()->PikaGetTowerPlayerState();
}

APikaGodStoneCamera * UPikaGameManager::PikaGetTowerCamera()
{
    return PikaGetPC()->PikaGetTowreCamera();
}

PikaGameResource.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Engine/Texture2D.h"
#include "Sound/SoundAttenuation.h"
#include "PikaGameResource.generated.h"


class APikaGodStonePlayerController;

USTRUCT()
struct FPikaMiniMap
{
    GENERATED_BODY()

    //Minimap的地图
    UPROPERTY(EditDefaultsOnly, Category = "PikaMiniMap")
    UTexture2D* PikaMiniMapTexture;
    //地图的宽
    UPROPERTY(EditDefaultsOnly, Category = "PikaMiniMap")
    float PikaMiniMapSizeX;
    //地图的高
    UPROPERTY(EditDefaultsOnly, Category = "PikaMiniMap")
    float PikaMiniMapSizeY;
    //地图的宽与X轴屏幕的间隔
    UPROPERTY(EditDefaultsOnly, Category = "PikaMiniMap")
    float PikaMiniMapSizeXInterval;
    //地图的高与Y轴屏幕的间隔
    UPROPERTY(EditDefaultsOnly, Category = "PikaMiniMap")
    float PikaMiniMapSizeYInterval;
    //真实地图的大小
    UPROPERTY(EditDefaultsOnly, Category = "PikaMap")
    float PikaMapSizeX;
    //真实地图的大小
    UPROPERTY(EditDefaultsOnly, Category = "PikaMap")
    float PikaMapSizeY;
    FPikaMiniMap()
    {
        PikaMiniMapSizeX = 256;
        PikaMiniMapSizeY = 256;
        PikaMiniMapSizeXInterval = 30;
        PikaMiniMapSizeYInterval = 30;
        //PikaMapSizeX和PikaMapSizeY的值需要在UE编辑器里的非透视图里,按下鼠标中键进行测量
        PikaMapSizeX = 15000;
        PikaMapSizeY = 15000;
    }
};


/**
 * 
 */
UCLASS(Blueprintable)
class PIKAGODSTONE_API UPikaGameResource : public UObject
{
    GENERATED_BODY()
    
public:
    UPikaGameResource();

    /*******************MapStart********************/
    UPROPERTY(EditDefaultsOnly, Category = "Mini Map")
    TArray<FPikaMiniMap> PikaLevelMiniMap;

    UPROPERTY(EditDefaultsOnly, Category = "Mini Map")
    UTexture2D* PikaNewCameraBoard;

    UPROPERTY(EditDefaultsOnly, Category = "Mini Map")
    FVector2D PikaNewCameraBoardSize;
    /*******************MapEnd********************/

    //hover声音
    UPROPERTY(EditDefaultsOnly, Category = "PikaSound")
    TArray <USoundBase*> PikaHoverSound;

    //unHover声音
    UPROPERTY(EditDefaultsOnly, Category = "PikaSound")
    TArray <USoundBase*> PikaUnHoverSound;
    
    
    //初始化我们的资源
    void PikaInitializationResource(class APikaGodStonePlayerController* MyController);

    FVector2D PikaPlayerLocConverMiniMapCoord(int32 CurrentLevel, AActor* NewTargetActor, APawn* Player);

    //战斗的声音衰减体积框
    UPROPERTY(EditDefaultsOnly, Category = "PikaSoundVolume")
    USoundAttenuation* PikaFightingSoundVolume;
};

PikaGameResource.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameResource.h"




UPikaGameResource::UPikaGameResource()
{

}

void UPikaGameResource::PikaInitializationResource(class APikaGodStonePlayerController* MyController)
{

}

FVector2D UPikaGameResource::PikaPlayerLocConverMiniMapCoord(int32 CurrentLevel, AActor* NewTargetActor, APawn* Player)
{
    FVector2D PikaMiniMapSize = FVector2D::ZeroVector;

    if (Player && NewTargetActor)
    {
        FVector PikaPlayerLocation = (-1 * (NewTargetActor->GetActorRotation())).RotateVector(Player->GetActorLocation() - NewTargetActor->GetActorLocation());
        PikaMiniMapSize = FVector2D(PikaPlayerLocation.X / PikaLevelMiniMap[CurrentLevel].PikaMapSizeX, PikaPlayerLocation.Y / (PikaLevelMiniMap[CurrentLevel].PikaMapSizeY));
    }
    return PikaMiniMapSize;
}

PikaGameSaveData.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/SaveGame.h"
#include "PikaGodStoneMacroLib.h"
#include "PikaGameSaveData.generated.h"

#define  LevelMax (5)

//读存数据表
USTRUCT()
struct  FPikaReadGameData
{
    GENERATED_USTRUCT_BODY()

    //我们存了多少个档,存储的内容和日期
    UPROPERTY(SaveGame)
    TArray<FString> PikaSaveDataText;

    //对应有存档的角标
    UPROPERTY(SaveGame)
    TArray<int32> PikaSaveGameBoxNumberArray;

    //关卡完成度
    UPROPERTY(SaveGame)
    TArray<float> PikaDegreeOfCompletion;

    //最大关卡
    UPROPERTY(SaveGame)
    int32 PikaMaxLevel;

    //还原某个数据
    void PikaClearReadGameDataAt(int32 IndexNumber)
    {
        if (PikaSaveDataText.IsValidIndex(IndexNumber))
        {
            PikaSaveDataText[IndexNumber] = PIKA_SAVE_PIKA;
            int32 PikaNewIndexGameBox = PikaSaveGameBoxNumberArray.Find(IndexNumber);
            if (PikaNewIndexGameBox != INDEX_NONE)
            {
                PikaSaveGameBoxNumberArray[PikaNewIndexGameBox] = -1;
            }
            else
            {
                PIKA_UE_PRINT(FString::Printf(TEXT("SaveGameBoxNumberArray[NewIndexGameBox] = !&&**%&^@$ ,NewIndexGameBox = %i"), PikaNewIndexGameBox));
            }
        }
        else
        {
            PIKA_UE_PRINT(FString::Printf(TEXT("Nothingness %i"), IndexNumber));
        }
    }

    //清空数据
    void PikaClearData()
    {
        PikaSaveDataText.Empty();
        PikaSaveGameBoxNumberArray.Empty();
        PikaDegreeOfCompletion.Empty();
    }

    //初始化数据尺寸
    void PikaInitDataSzie()
    {
        PikaSaveDataText.SetNum(Pika_SAVE_SaveBarNumber);
        PikaSaveGameBoxNumberArray.SetNum(Pika_SAVE_SaveBarNumber);
        PikaDegreeOfCompletion.SetNum(LevelMax);
        PikaMaxLevel = 0;
        for (int32 i = 0; i < Pika_SAVE_SaveBarNumber; i++)
        {
            PikaSaveDataText[i] = PIKA_SAVE_PIKA;
            PikaSaveGameBoxNumberArray[i] = INDEX_NONE;
        }
        for (int32 i = 0; i < LevelMax; i++)
        {
            PikaDegreeOfCompletion[i] = 0;
        }
    }

    //获取PIKA_SAVE_SLOT_NAME存档里第一个空位置的index
    int32 PikaGetSerialNumber()
    {
        int32 PikaSaveBarNumber = Pika_SAVE_SaveBarNumber;
        if (!((PikaSaveDataText.Num() & PikaSaveGameBoxNumberArray.Num())) == PikaSaveBarNumber)
            return INDEX_NONE;
        int32 PikaIntIndex = INDEX_NONE;
        for (int32 i = 0; i < Pika_SAVE_SaveBarNumber; i++)
        {
            if (PikaSaveDataText[i] == PIKA_SAVE_PIKA)
            {
                PikaIntIndex = i;
                break;
            }
        }
        return PikaIntIndex;
    }

    FPikaReadGameData()
    {
        if (PikaSaveGameBoxNumberArray.Num() == 0)
        {
            PikaInitDataSzie();
        }
    }

    //需要载入operator=,如果是基础的数据元素,不需要,但是其中有TArray<>,该类需要operator=
    FPikaReadGameData& operator=(const FPikaReadGameData& object)
    {
        PikaSaveDataText = object.PikaSaveDataText;
        PikaSaveGameBoxNumberArray = object.PikaSaveGameBoxNumberArray;
        PikaDegreeOfCompletion = object.PikaDegreeOfCompletion;
        PikaMaxLevel = PikaMaxLevel;
        return *this;
    }
};


//此数据是我们游戏角色在场景内的缓存
USTRUCT()
struct  FPikaTemporaryCacheData
{
    GENERATED_USTRUCT_BODY()

    //角色生成的混合ID
    UPROPERTY(SaveGame)
    TArray<int32> PikaCharacterSpawnID;

    //角色队伍
    UPROPERTY(SaveGame)
    TArray<bool> PikaCharacterTeam;

    //角色存活
    UPROPERTY(SaveGame)
    TArray<bool> PikaCharacterActive;

    //角色位置
    UPROPERTY(SaveGame)
    TArray<FVector> PikaCharacterLocation;

    //角色旋转
    UPROPERTY(SaveGame)
    TArray<FRotator> PikaCharacterRotator;

    //角色名字(通过角色的名字寻找到该角色)
    UPROPERTY(SaveGame)
    TArray<FName> PikaCharacterName;

    //当前生命值
    UPROPERTY(SaveGame)
    TArray<float> PikaCharacterCurrentHealth;

    //当前经验值
    UPROPERTY(SaveGame)
    TArray<float> PikaCharacterCurrentExp;

    //当前等级
    UPROPERTY(SaveGame)
    TArray<int32> PikaCharacterCurrentLevel;

    FPikaTemporaryCacheData()
    {
    }
    //全部移除
    ~FPikaTemporaryCacheData()
    {
        for (int32 IndexNumber = 0; IndexNumber < PikaCharacterSpawnID.Num(); IndexNumber++)
        {
            PikaRemoveIt(IndexNumber);
        }
    }
    //获取当前的角标
    int32 PikaGetCurrentCharacterSpawnIDIndex(int32 PawnSpawnID)
    {
        return PikaCharacterSpawnID.Find(PawnSpawnID);
    }
    //判断角标是不是存在数组范围内 判断当前是不是锁住状态
    bool PikaIsActive(int32 Index)
    {
        return (Index >= 0 && Index < PikaCharacterSpawnID.Num()) ? true : false;
    }

    //判断ID是否有相同的
    bool PikaIsExistSpawnID(int32 SpawnID)
    {
        return (PikaCharacterSpawnID.Find(SpawnID) != INDEX_NONE) ? true : false;
    }

    //移除单个
    void PikaRemoveIt(int32 Index = INDEX_NONE)
    {
        if (PikaIsActive(Index))
        {
            if(PikaCharacterSpawnID.IsValidIndex(Index))
                PikaCharacterSpawnID.RemoveAt(Index);

            if (PikaCharacterTeam.IsValidIndex(Index))
                PikaCharacterTeam.RemoveAt(Index);

            if (PikaCharacterActive.IsValidIndex(Index))
                PikaCharacterActive.RemoveAt(Index);

            if (PikaCharacterLocation.IsValidIndex(Index))
                PikaCharacterLocation.RemoveAt(Index);

            if (PikaCharacterRotator.IsValidIndex(Index))
                PikaCharacterRotator.RemoveAt(Index);

            if (PikaCharacterName.IsValidIndex(Index))
                PikaCharacterName.RemoveAt(Index);

            if (PikaCharacterCurrentHealth.IsValidIndex(Index))
                PikaCharacterCurrentHealth.RemoveAt(Index);

            if (PikaCharacterCurrentExp.IsValidIndex(Index))
                PikaCharacterCurrentExp.RemoveAt(Index);

            if (PikaCharacterCurrentLevel.IsValidIndex(Index))
                PikaCharacterCurrentLevel.RemoveAt(Index);
        }
    }
    FPikaTemporaryCacheData & operator=(const FPikaTemporaryCacheData &object)
    {
        PikaCharacterSpawnID = object.PikaCharacterSpawnID;
        PikaCharacterTeam = object.PikaCharacterTeam;
        PikaCharacterActive = object.PikaCharacterActive;
        PikaCharacterLocation = object.PikaCharacterLocation;
        PikaCharacterRotator = object.PikaCharacterRotator;
        PikaCharacterName = object.PikaCharacterName;
        PikaCharacterCurrentHealth = object.PikaCharacterCurrentHealth;
        PikaCharacterCurrentExp = object.PikaCharacterCurrentExp;
        PikaCharacterCurrentLevel = object.PikaCharacterCurrentLevel;
        return *this;
    }
};

USTRUCT()
struct  FPikaTowersData
{
    GENERATED_USTRUCT_BODY()

        float PikaTimeInterval;

    FPikaTowersData()
    {
        PikaTimeInterval = .5;
    }
};

//关卡管理
USTRUCT()
struct  FPikaGameLevelManangenment
{
    GENERATED_USTRUCT_BODY()

    //该关卡内怪物数量
    UPROPERTY(SaveGame)
    int32 PikaNumberOfMonster;

    //怪物等级(和游戏难度有关)
    UPROPERTY(SaveGame)
    int32 PikaAddMonsterLevel;

    //如果所有的主塔死亡,该值为true,并且禁止建造任何建筑
    UPROPERTY(SaveGame)
    uint8 PikaIsAllMainTowerDie : 1;

    //所有塔死亡,游戏结束
    UPROPERTY(Transient)
    uint8 PikaGameOver : 1;

    //所有塔死亡,则游戏结束
    UPROPERTY(Transient)
    uint8 PikaCurrentLevelMissionSuccess : 1;

    //塔等级
    UPROPERTY(SaveGame)
    int32 PikaTowersLevel;

    //生成怪物阶段
    UPROPERTY(SaveGame)
    int32 PikaSpawnMonsterStage;

    //当前关卡
    UPROPERTY(SaveGame)
    int32 PikaCurrentLevel;

    //时间间隔(多久生成一次怪物)
    UPROPERTY(SaveGame)
    float PikaTimeInterval;

    //生成怪物的时间记录
    UPROPERTY(SaveGame)
    float PikaSetTimeMnanagenment;

    //第几部分内容
    UPROPERTY(SaveGame)
    int32 PikaParts;

    //有多少波怪物
    UPROPERTY(SaveGame)
    int32 PikaMaxStagesAreMonsters;

    //当前是哪一波怪物
    UPROPERTY(SaveGame)
    int32 PikaCurrentStagesAreMonsters;

    //每波怪物当前数量
    UPROPERTY(SaveGame)
    TArray<int32> PikaPerNumberOfMonsters;

    //判定怪物全部消灭没
    void PikaDataSubtraction()
    {
        if (PikaCurrentStagesAreMonsters < PikaPerNumberOfMonsters.Num())
        {
            if (PikaPerNumberOfMonsters[PikaCurrentStagesAreMonsters] > 0)
            {
                PikaPerNumberOfMonsters[PikaCurrentStagesAreMonsters]--;
            }
            else
            {
                PikaLock();
            }
        }
        else
        {
            PikaCurrentLevelMissionSuccess = true;
            PikaLock();
        }
    }

    //分配数量梯度,数量从少变多
    void PikaAssignedMonsterAmount()
    {
        //检查构成条件,怪物数量需要大于阶段数量,负责会按照阶段数生成至少一个怪物
        if (PikaNumberOfMonster <= PikaMaxStagesAreMonsters)
            PikaNumberOfMonster = PikaMaxStagesAreMonsters;

        //多少的怪物
        int32 PikaCurrentMonsterNumber = PikaNumberOfMonster;
        //最大阶段
        int32 PikaCurrentStagesNumber = PikaMaxStagesAreMonsters;
        //每阶段的数量
        int32 PikaCurrentAssignedNum = INDEX_NONE;

        for (int32 i = 0; i < PikaMaxStagesAreMonsters; i++)
        {
            if (PikaCurrentStagesNumber > 0)
            {
                //每阶段的平均值
                int32 PikaStagesNumber = PikaCurrentMonsterNumber / PikaCurrentStagesNumber;
                if (PikaCurrentMonsterNumber > 1)
                {
                    //减去该阶段
                    PikaCurrentStagesNumber--;
                    if (PikaCurrentStagesNumber != 0)
                    {
                        //随机阶段,从平均值一般到平均值分配
                        PikaCurrentAssignedNum = FMath::RandRange(PikaStagesNumber / 2, PikaStagesNumber);
                    }
                    else
                    {
                        PikaCurrentAssignedNum = PikaStagesNumber;
                    }
                    PikaPerNumberOfMonsters.Add(PikaCurrentAssignedNum);
                    //减去分配的数量
                    PikaCurrentMonsterNumber -= PikaCurrentAssignedNum;
                }
                else if (PikaCurrentMonsterNumber == 1)
                {
                    //对于最后一个阶段
                    PikaPerNumberOfMonsters.Add(PikaCurrentAssignedNum);
                    break;
                }
            }
        }
    }

    //是否大于生成怪物的时间间隔
    bool PikaIsTimeRight() { return PikaSetTimeMnanagenment > PikaTimeInterval; }

    FPikaGameLevelManangenment()
    {
        PikaNumberOfMonster = 455;
        PikaAddMonsterLevel = 2;
        PikaSpawnMonsterStage = 3;
        PikaTimeInterval = 6;
        PikaSetTimeMnanagenment = 0;
        PikaTowersLevel = 1;
        PikaCurrentLevel = 0;
        PikaIsAllMainTowerDie = false;
        PikaGameOver = false;
        //PikaMaxStagesAreMonsters = INDEX_NONE;
        PikaMaxStagesAreMonsters = 6;
        PikaCurrentStagesAreMonsters = 0;
        PikaLockSpanwData = false;
        PikaParts = 0;
    }
    FPikaGameLevelManangenment& operator=(const FPikaGameLevelManangenment& NewObj)
    {
        PikaNumberOfMonster = NewObj.PikaNumberOfMonster;
        PikaAddMonsterLevel = NewObj.PikaAddMonsterLevel;
        PikaSpawnMonsterStage = NewObj.PikaSpawnMonsterStage;
        PikaSetTimeMnanagenment = NewObj.PikaSetTimeMnanagenment;
        PikaTowersLevel = NewObj.PikaTowersLevel;
        PikaCurrentLevel = NewObj.PikaCurrentLevel;
        PikaTimeInterval = NewObj.PikaTimeInterval;
        PikaIsAllMainTowerDie = NewObj.PikaIsAllMainTowerDie;
        PikaPerNumberOfMonsters = NewObj.PikaPerNumberOfMonsters;
        PikaMaxStagesAreMonsters = NewObj.PikaMaxStagesAreMonsters;
        PikaCurrentStagesAreMonsters = NewObj.PikaCurrentStagesAreMonsters;
        PikaLockSpanwData = NewObj.PikaLockSpanwData;
        PikaParts = NewObj.PikaParts;
        return *this;
    }

    //数据锁
    FORCEINLINE void PikaLock() { PikaLockSpanwData = true; }
    FORCEINLINE void PikaUnLock() { PikaLockSpanwData = false; }
    FORCEINLINE bool PikaGetLock() const { return PikaLockSpanwData; }
    FORCEINLINE bool PikaIsExistDataMonster() const { return PikaPerNumberOfMonsters.IsValidIndex(PikaCurrentStagesAreMonsters) ? (PikaPerNumberOfMonsters[PikaCurrentStagesAreMonsters] > 0) : false; }
private:
    //锁住数据
    UPROPERTY(SaveGame)
    uint8 PikaLockSpanwData : 1;
};

//故事
struct FPikaGameStory
{
    TArray<FString> PikaStoryNor;

    TArray<FString> PikaStorySuc;

    TArray<FString> PikaStoryFai;
};

//游戏本地的玩家数据
USTRUCT() 
struct FPikaGodStoneSaveGameData
{
    GENERATED_BODY()

    //总时间的倒计时
    UPROPERTY(SaveGame)
    float PikaGameCount;

    //总时间的倒计时
    UPROPERTY(SaveGame)
    float PikaMaxGameCount;

    UPROPERTY(Transient)
    float PikaGoldGrowthTime;

    UPROPERTY(Transient)
    float PikaGoldGrowthMaxTime;

    //杀死敌人总数量
    UPROPERTY(SaveGame)
    float PikaKillSoldierNumber;

    //杀死Boss数量
    UPROPERTY(SaveGame)
    float PikaKillBossNumber;

    //塔死亡总数
    UPROPERTY(SaveGame)
    float PikaTowersDeathNumber;

    //主塔死亡数
    UPROPERTY(SaveGame)
    float PikaMainTowersDeathNumber;

    //准备建造塔的数量
    UPROPERTY(SaveGame)
    TArray<int32> PikaTowerPrepareBuildingNumber;
    //已完成的塔的数量
    UPROPERTY(SaveGame)
    TArray<int32> PikaTowerConstructionNumber;
    
    //已经完成的数量
    UPROPERTY(SaveGame)
    TArray<int32> PikaTowerLikeID;

    //显示数字的CD
    UPROPERTY(SaveGame)
    TArray<float> PikaTowersCurrentCDNumber;

    //是否更新TowerCD
    UPROPERTY(SaveGame)
    TArray<bool> PikaIsUpdateTowersCDArray;

    /*****************玩家技能********************/
    //当前技能(4个技能)
    UPROPERTY(SaveGame)
    TArray<float> PikaCurrentSkillCDArray;

    //处于技能中状态
    UPROPERTY(SaveGame)
    TArray<bool> PikaIsSkillCDArray;

    //技能数量
    UPROPERTY(SaveGame)
    TArray<int32> PikaSkillNumberArray;

    //最大技能值
    UPROPERTY(SaveGame)
    TArray<int32> PikaMaxSkillCDArray;

    FPikaGodStoneSaveGameData(int32 Set_Size, bool IsSkill = false)
    {
        if (IsSkill)
        {
            PikaReSkillSzie(Set_Size);
        }
        else
        {
            PikaReSzie(Set_Size);
        }
    }

    FPikaGodStoneSaveGameData()
    {
        PikaMaxGameCount = 3600;
        PikaGameCount = 1800;
        PikaKillSoldierNumber = 0;
        PikaGold = 6000;
        PikaTowersDeathNumber = 0;
        PikaTowersCurrentCDNumber.Empty();
        PikaIsUpdateTowersCDArray.Empty();
        PikaTowerLikeID.Empty();
        PikaTowerConstructionNumber.Empty();
        PikaNumSize = 0;
        PikaSkillNumSize = 0;
        PikaCurrentSkillCDArray.Empty();
        PikaIsSkillCDArray.Empty();
        PikaSkillNumberArray.Empty();
        PikaMaxSkillCDArray.Empty();
        PikaGoldGrowthMaxTime = 1.0;
        PikaGoldGrowthTime = 0;
    }

    FPikaGodStoneSaveGameData& operator=(const FPikaGodStoneSaveGameData& NewObj)
    {
        PikaGameCount = NewObj.PikaGameCount;
        PikaMaxGameCount = NewObj.PikaMaxGameCount;
        PikaKillSoldierNumber = NewObj.PikaKillSoldierNumber;
        PikaTowersDeathNumber = NewObj.PikaTowersDeathNumber;
        PikaTowerPrepareBuildingNumber = NewObj.PikaTowerPrepareBuildingNumber;
        PikaTowerConstructionNumber = NewObj.PikaTowerConstructionNumber;
        PikaTowersCurrentCDNumber = NewObj.PikaTowersCurrentCDNumber;
        PikaGold = NewObj.PikaGetGlobalGold();
        PikaTowerLikeID = NewObj.PikaTowerLikeID;
        PikaIsUpdateTowersCDArray = NewObj.PikaIsUpdateTowersCDArray;
        PikaNumSize = NewObj.PikaNumSize;
        PikaSkillNumSize = NewObj.PikaSkillNumSize;
        PikaCurrentSkillCDArray = NewObj.PikaCurrentSkillCDArray;
        PikaIsSkillCDArray = NewObj.PikaIsSkillCDArray;
        PikaSkillNumberArray = NewObj.PikaSkillNumberArray;
        PikaMaxSkillCDArray = NewObj.PikaMaxSkillCDArray;
        return *this;
    }

    //获取金币
    int32 PikaGetGlobalGold()const { return PikaGold; }
    //添加金币
    void PikaAddGlobalGold(int32 AddGold) { PikaGold += AddGold; }
    //减去金币
    void PikaConsumeGlobalGold(int32 ConsumeGold) { PikaGold -= ConsumeGold;}

    //判断金币增长时间
    bool PikaIsJudgement() const { return PikaGoldGrowthTime > PikaGoldGrowthMaxTime; }

    //关卡进度百分比
    float PikaGetLevelPercentage() { return (PikaGameCount >= 0) ? PikaGameCount / PikaMaxGameCount : 0; }

    //清除某个数据
    void PikaClearIndexData(int32 PIndex)
    {
        check(PikaTowerPrepareBuildingNumber.IsValidIndex(PIndex));
        PikaTowerPrepareBuildingNumber[PIndex] = 0;
        PikaTowerConstructionNumber[PIndex] = 0;
        PikaTowersCurrentCDNumber[PIndex] = 0;
        PikaTowerLikeID[PIndex] = INDEX_NONE;
        PikaIsUpdateTowersCDArray[PIndex] = false;
    }

    //移动数据
    void PikaDataMoveTo(int32 Index, int32 TargetIndex)
    {
        if (Index != TargetIndex)
        {
            PikaTowerPrepareBuildingNumber[TargetIndex] = PikaTowerPrepareBuildingNumber[Index];
            PikaTowerConstructionNumber[TargetIndex] = PikaTowerConstructionNumber[Index];
            PikaTowersCurrentCDNumber[TargetIndex] = PikaTowersCurrentCDNumber[Index];
            PikaTowerLikeID[TargetIndex] = PikaTowerLikeID[Index];
            PikaIsUpdateTowersCDArray[TargetIndex] = PikaIsUpdateTowersCDArray[Index];

            PikaClearIndexData(Index);
        }
    }

    //是否有意义(和塔相关)
    bool IsActive(int32 Index)
    {
        return (PikaNumSize != 0) ? (Index >= 0 && Index < PikaNumSize) : false;
    }

    //是否有意义(和技能相关)
    bool IsSkillActive(int32 Index)
    {
        return (PikaSkillNumSize != 0) ? (Index >= 0 && Index < PikaSkillNumSize) : false;
    }

    //交换数据
    //@:Index        需要交换的数据
    //@:TargetIndex  交换的目标数据
    void SwapMyData(int32 Index, int32 TargetIndex)
    {
        if (IsActive(Index) && IsActive(TargetIndex) && Index != TargetIndex)
        {
            FPikaGodStoneSaveGameData PikaTmpData(PikaNumSize);

            PikaTmpData.PikaTowerPrepareBuildingNumber[TargetIndex] = PikaTowerPrepareBuildingNumber[TargetIndex];
            PikaTmpData.PikaTowerConstructionNumber[TargetIndex] = PikaTowerConstructionNumber[TargetIndex];
            PikaTmpData.PikaTowersCurrentCDNumber[TargetIndex] = PikaTowersCurrentCDNumber[TargetIndex];
            PikaTmpData.PikaTowerLikeID[TargetIndex] = PikaTowerLikeID[TargetIndex];
            PikaTmpData.PikaIsUpdateTowersCDArray[TargetIndex] = PikaIsUpdateTowersCDArray[TargetIndex];

            PikaTowerPrepareBuildingNumber[TargetIndex] = PikaTowerPrepareBuildingNumber[Index];
            PikaTowerConstructionNumber[TargetIndex] = PikaTowerConstructionNumber[Index];
            PikaTowersCurrentCDNumber[TargetIndex] = PikaTowersCurrentCDNumber[Index];
            PikaTowerLikeID[TargetIndex] = PikaTowerLikeID[Index];
            PikaIsUpdateTowersCDArray[TargetIndex] = PikaIsUpdateTowersCDArray[Index];

            PikaTowerPrepareBuildingNumber[Index] = PikaTmpData.PikaTowerPrepareBuildingNumber[TargetIndex];
            PikaTowerConstructionNumber[Index] = PikaTmpData.PikaTowerConstructionNumber[TargetIndex];
            PikaTowersCurrentCDNumber[Index] = PikaTmpData.PikaTowersCurrentCDNumber[TargetIndex];
            PikaTowerLikeID[Index] = PikaTmpData.PikaTowerLikeID[TargetIndex];
            PikaIsUpdateTowersCDArray[Index] = PikaTmpData.PikaIsUpdateTowersCDArray[TargetIndex];
        }
    }

    //重新设置尺寸
    void PikaReSzie(int32 MySize)
    {
        for (int32 i = 0; i < MySize; i++)
        {
            PikaTowerPrepareBuildingNumber.Add(0);
            PikaTowerConstructionNumber.Add(0);
            PikaTowersCurrentCDNumber.Add(0.0f);
            PikaTowerLikeID.Add(INDEX_NONE);
            PikaIsUpdateTowersCDArray.Add(false);
        }

        //赋值
        PikaNumSize = MySize;
    }

    //重新Skill设置尺寸
    void PikaReSkillSzie(int32 MySize)
    {
        for (int32 i = 0; i < MySize; i++)
        {
            PikaCurrentSkillCDArray.Add(0.f);
            PikaIsSkillCDArray.Add(false);
            PikaSkillNumberArray.Add(0);
            PikaMaxSkillCDArray.Add(0);
        }
        //赋值
        PikaSkillNumSize = MySize;
    }
private:
    //金币
    UPROPERTY(SaveGame)
    int32 PikaGold;

    //Towers数据数量
    UPROPERTY(SaveGame)
    int32 PikaNumSize;

    //Skill数据数量
    UPROPERTY(SaveGame)
    int32 PikaSkillNumSize;
};


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameSaveData : public USaveGame
{
    GENERATED_BODY()
    
public:
    UPROPERTY()
    FPikaGodStoneSaveGameData PikaGodStongSaveData;
    
    UPROPERTY(SaveGame)
    uint64 PikaSaveCount;

    UPikaGameSaveData();

    UPROPERTY(SaveGame)
    bool PikaIsSave;

    //UPROPERTY(SaveGame)
    //FMissionState MissionState;


    UPROPERTY()
    FPikaGameLevelManangenment PikaGameLevelManangenment;

    UPROPERTY()
    FPikaTowersData PikaTowersData;

    UPROPERTY()
    FPikaTemporaryCacheData PikaTemporaryCacheData;

    UPROPERTY()
    FPikaReadGameData PikaReadGameData;

    void AddStorageTime() { PikaSaveCount++; }
    
};

PikaGameSaveData.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameSaveData.h"

UPikaGameSaveData::UPikaGameSaveData()
{
    PikaIsSave = false;
}

PikaGameSettingsAudio.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "TextBlock.h"
#include "PikaGameSettingsAudio.generated.h"


class UPikaUIHallGameSetting;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameSettingsAudio : public UPikaUIWidgetBase
{
    GENERATED_BODY()

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaSoundControllSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaSoundEffectSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaMusicSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaAudioSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaSoundControllSliderTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaSoundEffectSliderTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaMusicSliderTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaAudioSliderTextName;
public:
    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    void PikaSetHallGameSettings(UPikaUIHallGameSetting* NewWidget) { PikaHallGameSettings = NewWidget; }

    virtual void PikaChangedValue(float Val) override;

    virtual    void PikaUpdateAttibe() override;

    FORCEINLINE UPikaUIHallGameSetting* PikaGetHallGameSettings() const { return PikaHallGameSettings; }
    FORCEINLINE USlider* PikaGetSoundControllSlider() const { return PikaSoundControllSlider; }
    FORCEINLINE USlider* PikaGetSoundEffectSlider() const { return PikaSoundEffectSlider; }
    FORCEINLINE USlider* PikaGetAudioSlider() const { return PikaAudioSlider; }
    FORCEINLINE USlider* PikaGetMusicSlider() const { return PikaMusicSlider; }
private:
    //总声音控件
    USlider* PikaSoundControllSlider;
    //特效音乐
    USlider* PikaSoundEffectSlider;
    //背景音乐
    USlider* PikaMusicSlider;
    //按钮音效
    USlider* PikaAudioSlider;

    UTextBlock* PikaSoundControllSliderText;
    UTextBlock* PikaSoundEffectSliderText;
    UTextBlock* PikaMusicSliderText;
    UTextBlock* PikaAudioSliderText;

    UPikaUIHallGameSetting* PikaHallGameSettings;
};

PikaGameSettingsAudio.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameSettingsAudio.h"

void UPikaGameSettingsAudio::PikaDrawUIToScreen()
{
    PikaSoundControllSlider = PikaGetSliderFormBlueprint(PikaSoundControllSliderName);
    PikaSoundEffectSlider = PikaGetSliderFormBlueprint(PikaSoundEffectSliderName);
    PikaMusicSlider = PikaGetSliderFormBlueprint(PikaMusicSliderName);
    PikaAudioSlider = PikaGetSliderFormBlueprint(PikaAudioSliderName);
    PikaSoundControllSliderText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaSoundControllSliderTextName));
    PikaSoundEffectSliderText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaSoundEffectSliderTextName));
    PikaMusicSliderText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaMusicSliderTextName));
    PikaAudioSliderText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaAudioSliderTextName));
}

void UPikaGameSettingsAudio::PikaChangedValue(float Val)
{
    PikaUpdateAttibe();
}

void UPikaGameSettingsAudio::PikaUpdateAttibe()
{
    if (PikaSoundControllSliderText && PikaSoundControllSlider)
    {
        uint16 PikaIndexNumber = PikaSoundControllSlider->GetValue() * 10;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaSoundControllSliderText->SetText(FText::FromString(PikaString_Text));
    }
    if (PikaSoundEffectSliderText && PikaSoundEffectSlider)
    {
        uint16 PikaIndexNumber = PikaSoundEffectSlider->GetValue() * 10;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaSoundEffectSliderText->SetText(FText::FromString(PikaString_Text));
    }
    if (PikaMusicSliderText && PikaMusicSlider)
    {
        uint16 PikaIndexNumber = PikaMusicSlider->GetValue() * 10;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaMusicSliderText->SetText(FText::FromString(PikaString_Text));
    }
    if (PikaAudioSliderText && PikaAudioSlider)
    {
        uint16 PikaIndexNumber = PikaAudioSlider->GetValue() * 10;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaAudioSliderText->SetText(FText::FromString(PikaString_Text));
    }
}

PikaGameSettingsGameSettings.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaGameSettingsGameSettings.generated.h"

class UPikaUIHallGameSetting;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameSettingsGameSettings : public UPikaUIWidgetBase
{
    GENERATED_BODY()
public:
    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    void PikaSetHallGameSettings(UPikaUIHallGameSetting* NewWidget) { PikHallGameSettings = NewWidget; }
private:
    UPikaUIHallGameSetting* PikHallGameSettings;
};

PikaGameSettingsGameSettings.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameSettingsGameSettings.h"

void UPikaGameSettingsGameSettings::PikaDrawUIToScreen()
{

}

PikaGameSettingsOtherSettings.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaGameSettingsOtherSettings.generated.h"

class UPikaUIHallGameSetting;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameSettingsOtherSettings : public UPikaUIWidgetBase
{
    GENERATED_BODY()
public:
    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    void PikaSetHallGameSettings(UPikaUIHallGameSetting* NewWidget) { PikHallGameSettings = NewWidget; }
private:
    UPikaUIHallGameSetting* PikHallGameSettings;
};

PikaGameSettingsOtherSettings.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameSettingsOtherSettings.h"

void UPikaGameSettingsOtherSettings::PikaDrawUIToScreen()
{

}

PikaGameSettingsVideo.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "TextBlock.h"
#include "PikaGameSettingsVideo.generated.h"


class UPikaUIHallGameSetting;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameSettingsVideo : public UPikaUIWidgetBase
{
    GENERATED_BODY()

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaResolutionBoxStringName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaFullScreenCheckBoxName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaWindowScreenCheckBoxName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaOverallScalabilityLevelSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTextureQualitySliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaShadowQualitySliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaAntiAliasingSliderName; 

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaPostProcessingSliderName;
    
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaEffectsSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaFoliageSliderName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaViewDistanceSliderName;
    
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaFOVSliderName; 
    
    //----------------------------------------------------------------
    
    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaOverallScalabilityLevelSliderTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaTextureQualityTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaShadowQualityTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaAntiAliasingTextName; 

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaPostProcessingTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaEffectsTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaFoliageTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaViewDistanceTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaFOVTextName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUIText")
    FName PikaLanguageStringName;
public:
    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    virtual void PikaUpdateAttibe() override;

    virtual void PikaSelectionChangedBySelf(UWidget* MyWidget, FString SelectedItem, ESelectInfo::Type SelectionType) override;

    virtual void PikaChangedValue(float Val) override;

    virtual void PikaClickedCheckBox(UWidget* MyWidget, bool ClickedWidget);

    void PikaSetHallGameSettings(UPikaUIHallGameSetting* NewWidget) { PikaHallGameSettings = NewWidget; }

    FORCEINLINE UPikaUIHallGameSetting* PikaGetHallGameSettings() const { return PikaHallGameSettings; }
    FORCEINLINE UComboBoxString* PikaGetResolutionBoxString() const { return PikaResolutionBoxString; }
    //FORCEINLINE UComboBoxString* PikaGetLanguageString() const { return PikaLanguageString; }
    FORCEINLINE USlider* PikaGetAntiAliasingSlider() const { return PikaAntiAliasingSlider; }
    FORCEINLINE USlider* PikaGetShadowQualitySlider() const { return PikaShadowQualitySlider; }
    FORCEINLINE USlider* PikaGetTextureQualitySlider() const { return PikaTextureQualitySlider; }
    FORCEINLINE USlider* PikaGetFOVSlider() const { return PikaFOVSlider; }
    FORCEINLINE UCheckBox* PikaGetFullScreenCheckBox() const { return PikaFullScreenCheckBox; }
    FORCEINLINE UCheckBox* PikaGetWindowScreenCheckBox() const { return PikaWindowScreenCheckBox; }

    FORCEINLINE USlider* PikaGetPostProcessingSlider() const { return PikaPostProcessingSlider; }
    FORCEINLINE USlider* PikaGetEffectsSlider() const { return PikaEffectsSlider; }
    FORCEINLINE USlider* PikaGetFoliageSlider() const { return PikaFoliageSlider; }
    FORCEINLINE USlider* PikaGetViewDistanceSlider() const { return PikaViewDistanceSlider; }
    FORCEINLINE USlider* PikaGetOverallScalabilityLevelSlider() const { return PikaOverallScalabilityLevelSlider; }
private:
    //分辨率
    UComboBoxString*  PikaResolutionBoxString;
    //全屏checkBox
    UCheckBox* PikaFullScreenCheckBox;
    //窗口checkBox
    UCheckBox* PikaWindowScreenCheckBox;
    //总级别控制
    USlider* PikaOverallScalabilityLevelSlider;
    //贴图
    USlider* PikaTextureQualitySlider;
    //阴影
    USlider* PikaShadowQualitySlider;
    //抗锯齿
    USlider* PikaAntiAliasingSlider;
    //后期
    USlider* PikaPostProcessingSlider;
    //特效
    USlider* PikaEffectsSlider;
    //Foliage叶子
    USlider* PikaFoliageSlider;
    //视距
    USlider* PikaViewDistanceSlider;
    //摄像机FOV
    USlider* PikaFOVSlider;
    //语言
    //UComboBoxString*  PikaLanguageString;
    
    //总级别控制数字显示
    UTextBlock* PikaOverallScalabilityLevelSliderText;
    //贴图数字显示
    UTextBlock* PikaTextureQualityText;
    //阴影数字显示
    UTextBlock* PikaShadowQualityText;
    //抗锯齿数字显示
    UTextBlock* PikaAntiAliasingText;
    //后期数字显示
    UTextBlock* PikaPostProcessingText;
    //特效数字显示
    UTextBlock* PikaEffectsText;
    //Foliage叶子数字显示
    UTextBlock* PikaFoliageText;
    //视距数字显示
    UTextBlock* PikaViewDistanceText;
    //摄像机FOV数字显示
    UTextBlock* PikaFOVText;
    

    UPikaUIHallGameSetting* PikaHallGameSettings;
};

PikaGameSettingsVideo.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameSettingsVideo.h"
#include "Public/PikaWorldSettings.h"

void UPikaGameSettingsVideo::PikaDrawUIToScreen()
{
    PikaResolutionBoxString = PikaGetComboBoxStringFormBlueprint(PikaResolutionBoxStringName);
    PikaFullScreenCheckBox = PikaGetCheckBoxFormBlueprint(PikaFullScreenCheckBoxName);
    PikaWindowScreenCheckBox = PikaGetCheckBoxFormBlueprint(PikaWindowScreenCheckBoxName);
    PikaOverallScalabilityLevelSlider = PikaGetSliderFormBlueprint(PikaOverallScalabilityLevelSliderName);
    PikaTextureQualitySlider = PikaGetSliderFormBlueprint(PikaTextureQualitySliderName);
    PikaShadowQualitySlider = PikaGetSliderFormBlueprint(PikaShadowQualitySliderName);
    PikaAntiAliasingSlider = PikaGetSliderFormBlueprint(PikaAntiAliasingSliderName);
    PikaPostProcessingSlider = PikaGetSliderFormBlueprint(PikaPostProcessingSliderName);
    PikaEffectsSlider = PikaGetSliderFormBlueprint(PikaEffectsSliderName);
    PikaFoliageSlider = PikaGetSliderFormBlueprint(PikaFoliageSliderName);
    PikaViewDistanceSlider = PikaGetSliderFormBlueprint(PikaViewDistanceSliderName);
    PikaFOVSlider = PikaGetSliderFormBlueprint(PikaFOVSliderName);
    //PikaLanguageString = GetComboBoxStringFormBlueprint(LanguageStringName);
    
    PikaOverallScalabilityLevelSliderText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaOverallScalabilityLevelSliderTextName));
    //贴图数字显示
    PikaTextureQualityText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTextureQualityTextName));
    //阴影数字显示
    PikaShadowQualityText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaShadowQualityTextName));
    //抗锯齿数字显示
    PikaAntiAliasingText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaAntiAliasingTextName));
    //后期数字显示
    PikaPostProcessingText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaPostProcessingTextName));
    //特效数字显示
    PikaEffectsText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaEffectsTextName));
    //Foliage叶子数字显示
    PikaFoliageText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaFoliageTextName));
    //视距数字显示
    PikaViewDistanceText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaViewDistanceTextName));
    //摄像机FOV数字显示
    PikaFOVText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaFOVTextName));
}

void UPikaGameSettingsVideo::PikaUpdateAttibe()
{
    //材质质量级别
    if (PikaOverallScalabilityLevelSliderText && PikaOverallScalabilityLevelSlider)
    {
        uint16 PikaIndexNumber = PikaOverallScalabilityLevelSlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaOverallScalabilityLevelSliderText->SetText(FText::FromString(PikaString_Text));
    }

    //贴图
    if (PikaTextureQualityText && PikaTextureQualitySlider)
    {
        uint16 PikaIndexNumber = PikaTextureQualitySlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaTextureQualityText->SetText(FText::FromString(PikaString_Text));
    }
    
    //阴影
    if (PikaShadowQualityText && PikaShadowQualitySlider)
    {
        uint16 PikaIndexNumber = PikaShadowQualitySlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaShadowQualityText->SetText(FText::FromString(PikaString_Text));
    }

    //锯齿
    if (PikaAntiAliasingText && PikaAntiAliasingSlider)
    {
        uint16 PikaIndexNumber = PikaAntiAliasingSlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaAntiAliasingText->SetText(FText::FromString(PikaString_Text));
    }
    
    //后期
    if (PikaPostProcessingText && PikaPostProcessingSlider)
    {
        uint16 PikaIndexNumber = PikaPostProcessingSlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaPostProcessingText->SetText(FText::FromString(PikaString_Text));
    }

    //特效
    if (PikaEffectsText && PikaEffectsSlider)
    {
        uint16 PikaIndexNumber = PikaEffectsSlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaEffectsText->SetText(FText::FromString(PikaString_Text));
    }

    //Foliage
    if (PikaFoliageText && PikaFoliageSlider)
    {
        uint16 PikaIndexNumber = PikaFoliageSlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaFoliageText->SetText(FText::FromString(PikaString_Text));
    }

    //视距
    if (PikaViewDistanceText && PikaViewDistanceSlider)
    {
        uint16 PikaIndexNumber = PikaViewDistanceSlider->GetValue() * 5;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), PikaIndexNumber);
        PikaViewDistanceText->SetText(FText::FromString(PikaString_Text));
    }
    
    //相机FOV
    if (PikaFOVText && PikaFOVSlider)
    {
        //最大值
        float PikaMaxValue = 240;

        float PikaIndexNumber = PikaFOVSlider->GetValue() * PIKA_FOVMAX_VALUE + 40.0f;
        FString PikaString_Text = FString::Printf(TEXT("%02d"), (int32)PikaIndexNumber);
        PikaFOVText->SetText(FText::FromString(PikaString_Text));
    }
}

void UPikaGameSettingsVideo::PikaSelectionChangedBySelf(UWidget* MyWidget, FString SelectedItem, ESelectInfo::Type SelectionType)
{
    //关于分辨率的一些设置
    if (MyWidget == PikaResolutionBoxString)
    {

    }
}

void UPikaGameSettingsVideo::PikaChangedValue(float Val)
{
    PikaUpdateAttibe();
}

void UPikaGameSettingsVideo::PikaClickedCheckBox(UWidget* MyWidget, bool ClickedWidget)
{
    if (MyWidget == PikaFullScreenCheckBox)
    {
        if (PikaWindowScreenCheckBox && PikaWindowScreenCheckBox->GetCheckedState() == ECheckBoxState::Checked)
        {
            PikaWindowScreenCheckBox->SetCheckedState(ECheckBoxState::Unchecked);
        }
    }
    if (MyWidget == PikaWindowScreenCheckBox)
    {
        if (PikaFullScreenCheckBox && PikaFullScreenCheckBox->GetCheckedState() == ECheckBoxState::Checked)
        {
            PikaFullScreenCheckBox->SetCheckedState(ECheckBoxState::Unchecked);
        }
    }
}

PikaGameUserSettings.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameUserSettings.h"
#include "Sound/SoundClass.h"
#include "PikaGameUserSettings.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameUserSettings : public UGameUserSettings
{
    GENERATED_BODY()

public:
    //用UPROPERTY(config)形式定义的变量,会自动存储到ini设置里
    //背景音乐
    UPROPERTY(config)
    float PikaBackgroundSoundControl;

    //特效音乐
    UPROPERTY(config)
    float PikaEffectsSoundControl;

    //分辨率
    UPROPERTY(config)
    float PikaResolution_HControl;
    UPROPERTY(config)
    float PikaResolution_WControl;

    //屏幕模式
    //0-窗口 1-全屏
    UPROPERTY(config)
    float PikaScreenModeControl;

    //总控制
    UPROPERTY(config)
    float PikaOverallScalabilityLevelControl;

    //贴图
    UPROPERTY(config)
    float PikaTextureQualityControl;

    //阴影
    UPROPERTY(config)
    float PikaShadowQualityControl;

    //抗锯齿
    UPROPERTY(config)
    float PikaAntiAliasingControl;

    //后期
    UPROPERTY(config)
    float PikaPostProcessingControl;

    //特效
    UPROPERTY(config)
    float PikaEffectsControl;

    //Foliage叶子
    UPROPERTY(config)
    float PikaFoliageControl;

    //视距
    UPROPERTY(config)
    float PikaViewDistanceControl;
    
    //FOV
    UPROPERTY(config)
    float PikaCameraFOV;

    UPikaGameUserSettings();

    static UPikaGameUserSettings* PikaGetGameUserSettings();

    virtual void ApplySettings(bool bCheckForCommandLineOverrides)override;

    TArray<USoundClass*> PikaGetAllMusicClass();

    virtual void LoadSettings(bool bForceReload = false) override;

    virtual void SaveSettings() override;

    void PikaSeveIni();

    bool PikaLoadIni();

    virtual void SetToDefaults()override;

    void PikaPlayBGM();

    //设置屏幕模式和分辨率
    void PikaSetVideo();

    /*PiksWorldStart*/
    virtual class UWorld* GetWorld() const;

    void PikaSetretrieveWorldContext(UWorld* NewWorld);
    /*PiksWorldEnd*/
private:

    class UWorld * PikaWorld;
};

PikaGameUserSettings.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameUserSettings.h"
#include "Sound/AmbientSound.h"
#include "Components/AudioComponent.h"



//PikaGameUserSettings属于ini配置类,该构造函数只会在代码编译时调用一次,在运行中不会再次执行
UPikaGameUserSettings::UPikaGameUserSettings()
{
    if (!PikaLoadIni())
    {
        SetToDefaults();
    }

}

UPikaGameUserSettings* UPikaGameUserSettings::PikaGetGameUserSettings()
{
    return GEngine != NULL ? ((UPikaGameUserSettings*)GEngine->GetGameUserSettings()) : NULL;
}

void UPikaGameUserSettings::ApplySettings(bool bCheckForCommandLineOverrides)
{
    Super::ApplySettings(bCheckForCommandLineOverrides);

    //背景音乐
    if (GetWorld())
    {
        TArray<USoundClass*> PikaTmpSound = PikaGetAllMusicClass();

        for (USoundClass* PikaNewSoundClass : PikaTmpSound)
        {
            if (PikaNewSoundClass->GetFullName().Contains(TEXT("Master")))
            {
                //总体音量
                PikaNewSoundClass->Properties.Volume = PikaBackgroundSoundControl * PikaqiuTotalSoundControl;
            }
        }
        TArray<AActor*> PikaClassArrayTmp;
        TArray<USoundClass*> PikaSoundClassArray;

        PikaGETALLACTOR(GetWorld(), AAmbientSound::StaticClass(), PikaClassArrayTmp);
        for (AActor* PikaSound_m : PikaClassArrayTmp)
        {
            AAmbientSound* PikaAmbient_sound = Cast<AAmbientSound>(PikaSound_m);
            PikaAmbient_sound->AdjustVolume(1, PikaBackgroundSoundControl * PikaqiuTotalSoundControl);
        }

    }

    // Apply the new settings and save them
    Scalability::SetQualityLevels(ScalabilityQuality);
    Scalability::SaveState(GGameUserSettingsIni);

    //存储到配置
    PikaSeveIni();
}

TArray<USoundClass*> UPikaGameUserSettings::PikaGetAllMusicClass()
{
    TArray<AActor*> PikaClassArrayTmp;
    TArray<USoundClass*> PikaSoundClassArray;

    PikaGETALLACTOR(GetWorld(), AAmbientSound::StaticClass(), PikaClassArrayTmp);
    for (AActor* PikaSound_m : PikaClassArrayTmp)
    {
        AAmbientSound* PikaAmbient_sound = Cast<AAmbientSound>(PikaSound_m);
        if (PikaAmbient_sound &&
            PikaAmbient_sound->GetAudioComponent() &&
            PikaAmbient_sound->GetAudioComponent()->Sound &&
            PikaAmbient_sound->GetAudioComponent()->Sound->DefaultSoundClassObject
            )
        {
            PikaSoundClassArray.Add(PikaAmbient_sound->GetAudioComponent()->Sound->DefaultSoundClassObject);
        }
    }
    return PikaSoundClassArray;

}

void UPikaGameUserSettings::LoadSettings(bool bForceReload /*= false*/)
{
    Super::LoadSettings(bForceReload);

    PikaLoadIni();
}

void UPikaGameUserSettings::SaveSettings()
{
    Super::SaveSettings();
}

void UPikaGameUserSettings::PikaSeveIni()
{
    const TCHAR* PikaSection = TEXT("PikaGodStoneINI");

    // looks like cvars but here we just use the name for the ini
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_BackgroundSoundControl"), PikaBackgroundSoundControl, GGameUserSettingsIni);
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_EffectsSoundControl"), PikaEffectsSoundControl, GGameUserSettingsIni);
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_ButtonSoundControl"), PikaqiuButtonSoundControl, GGameUserSettingsIni);
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_TotalSoundControl"), PikaqiuTotalSoundControl, GGameUserSettingsIni);
    /*//图像设置
    //分辨率
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_Resolution_H"), PikaResolution_HControl, GGameUserSettingsIni);
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_Resolution_W"), PikaResolution_WControl, GGameUserSettingsIni);
    //屏幕模式
    //0-窗口 1-全屏
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_ScreenMode"), PikaScreenModeControl, GGameUserSettingsIni);
    //总控制
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_OverallScalabilityLevel"), PikaOverallScalabilityLevelControl, GGameUserSettingsIni);
    //贴图
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_TextureQuality"), PikaTextureQualityControl, GGameUserSettingsIni);
    //阴影
    GConfig->SetFloat(PikaSection, TEXT("sg.ShadowQuality"), PikaShadowQualityControl, GGameUserSettingsIni);
    //抗锯齿
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_AntiAliasing"), PikaAntiAliasingControl, GGameUserSettingsIni);
    //后期
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_PostProcessing"), PikaPostProcessingControl, GGameUserSettingsIni);
    //特效
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_Effects"), PikaEffectsControl, GGameUserSettingsIni);
    //Foliage叶子
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_Foliage"), PikaFoliageControl, GGameUserSettingsIni);
    //视距
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_ViewDistance"), PikaViewDistanceControl, GGameUserSettingsIni);
    //FOV
    GConfig->SetFloat(PikaSection, TEXT("PikaIni_CameraFOV"), PikaCameraFOV, GGameUserSettingsIni);*/
    //GConfig->SetString(PikaSection, TEXT("sg.TDLanguage"), *GetCurrentLanguageType(), GGameUserSettingsIni);
}

bool UPikaGameUserSettings::PikaLoadIni()
{
    const TCHAR* PikaSection = TEXT("PikaGodStoneINI");
    FString PikaLanguageStr = "";
    bool PikaIsLoadSuc =
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_BackgroundSoundControl"), PikaBackgroundSoundControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_EffectsSoundControl"), PikaEffectsSoundControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_ButtonSoundControl"), PikaqiuButtonSoundControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_TotalSoundControl"), PikaqiuTotalSoundControl, GGameUserSettingsIni);

        /*GConfig->GetFloat(PikaSection, TEXT("PikaIni_Resolution_H"), PikaResolution_HControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_Resolution_W"), PikaResolution_WControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_ScreenMode"), PikaScreenModeControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_OverallScalabilityLevel"), PikaOverallScalabilityLevelControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_TextureQuality"), PikaTextureQualityControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_ShadowQuality"), PikaShadowQualityControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_AntiAliasing"), PikaAntiAliasingControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_PostProcessing"), PikaPostProcessingControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_Effects"), PikaEffectsControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_Foliage"), PikaFoliageControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_ViewDistance"), PikaViewDistanceControl, GGameUserSettingsIni) &&
        GConfig->GetFloat(PikaSection, TEXT("PikaIni_CameraFOV"), PikaCameraFOV, GGameUserSettingsIni);*/
        //GConfig->GetString(PikaSection, TEXT("sg.TDLanguage"), PikaLanguageStr, GGameUserSettingsIni) &&

    //设置语言
    //if (LanguageStr != "")
        //SetCuurrLanguage(LanguageStr);

    //return PikaIsLoadSuc;
    return true;
}

void UPikaGameUserSettings::SetToDefaults()
{
    Super::SetToDefaults();
    //背景音乐
    PikaBackgroundSoundControl = 1.0f;
    //特效音乐
    PikaEffectsSoundControl = 1.0f;
    PikaqiuTotalSoundControl = 1.0f;
    PikaqiuButtonSoundControl = 1.0f;
    //图像设置
    //分辨率
    PikaResolution_HControl = 800;
    PikaResolution_WControl = 600;

    //屏幕模式
    PikaScreenModeControl = 1.0f;

    //总控制
    PikaOverallScalabilityLevelControl = 0.5f;

    //贴图
    PikaTextureQualityControl = 0.5f;

    //阴影
    PikaShadowQualityControl = 0.5f;

    //抗锯齿
    PikaAntiAliasingControl = 0.5f;

    //后期
    PikaPostProcessingControl = 0.5f;

    //特效
    PikaEffectsControl = 0.5f;

    //Foliage叶子
    PikaFoliageControl = 0.5f;

    //视距
    PikaViewDistanceControl = 0.5f;

    //FOV
    PikaCameraFOV = 90.f;
}

void UPikaGameUserSettings::PikaPlayBGM()
{
    //背景音乐,由gameMode调用
    if (GetWorld())
    {
        TArray<AActor*> PikaClassArrayTmp;
        TArray<USoundClass*> PikaSoundClassArray;

        PikaGETALLACTOR(GetWorld(), AAmbientSound::StaticClass(), PikaClassArrayTmp);
        for (AActor* PikaSound_m : PikaClassArrayTmp)
        {
            AAmbientSound* PikaAmbient_sound = Cast<AAmbientSound>(PikaSound_m);
            PikaAmbient_sound->AdjustVolume(1, PikaBackgroundSoundControl * PikaqiuTotalSoundControl);
        }

    }
}

//由gameMode调用
void UPikaGameUserSettings::PikaSetVideo()
{
    //分辨率
    SetScreenResolution(GetScreenResolution());
    //屏幕模式
    SetFullscreenMode(GetLastConfirmedFullscreenMode());
}

class UWorld* UPikaGameUserSettings::GetWorld() const
{
    return PikaWorld;
}

void UPikaGameUserSettings::PikaSetretrieveWorldContext(UWorld* NewWorld)
{
    PikaWorld = NewWorld;
}

PikaGameViewportClient.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameViewportClient.h"
#include "SlateDynamicImageBrush.h"
#include "SCompoundWidget.h"
#include "PikaGameViewportClient.generated.h"

struct PikaFShooterGameLoadingScreenBrush : public FSlateDynamicImageBrush, public FGCObject
{
    PikaFShooterGameLoadingScreenBrush(const FName InTextureName, const FVector2D& InImageSize)
        : FSlateDynamicImageBrush(InTextureName, InImageSize)
    {
        ResourceObject = LoadObject<UObject>(NULL, *InTextureName.ToString());
    }

    virtual void AddReferencedObjects(FReferenceCollector& Collector)
    {
        if (ResourceObject)
        {
            Collector.AddReferencedObject(ResourceObject);
        }
    }
};


class PikaSShooterLoadingScreen : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(PikaSShooterLoadingScreen) {}
    SLATE_END_ARGS()

    void Construct(const FArguments& InArgs);

private:
    EVisibility PikaGetLoadIndicatorVisibility() const
    {
        return EVisibility::Visible;
    }

    /** loading screen image brush */
    TSharedPtr<FSlateDynamicImageBrush> PikaLoadingScreenBrush;
};


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGameViewportClient : public UGameViewportClient
{
    GENERATED_BODY()
public:
    UPikaGameViewportClient();

    void PikaShowLoadingScreen();
    void PikaHideLoadingScreen();
private:
    void PikaHideExistingWidgets();
    void PikaShowExistingWidgets();
    TSharedPtr<PikaSShooterLoadingScreen>    PikaLoadingScreenWidget;
    TArray<TSharedRef<class SWidget>>    PikaHiddenViewportContentStack;
    TArray<TSharedRef<class SWidget>>    PikaViewportContentStack;
};

PikaGameViewportClient.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGameViewportClient.h"

UPikaGameViewportClient::UPikaGameViewportClient()
{

}

void UPikaGameViewportClient::PikaShowLoadingScreen()
{
    if (PikaLoadingScreenWidget.IsValid())
    {
        return;
    }
    PikaLoadingScreenWidget = SNew(PikaSShooterLoadingScreen);

    AddViewportWidgetContent(PikaLoadingScreenWidget.ToSharedRef());
}

void UPikaGameViewportClient::PikaHideLoadingScreen()
{
    if (!PikaLoadingScreenWidget.IsValid())
    {
        return;
    }

    //销毁LoadingScreenWidget
    RemoveViewportWidgetContent(PikaLoadingScreenWidget.ToSharedRef());

    PikaLoadingScreenWidget = NULL;
}

void UPikaGameViewportClient::PikaHideExistingWidgets()
{
    check(PikaHiddenViewportContentStack.Num() == 0);

    TArray<TSharedRef<class SWidget>> PikaCopyOfViewportContentStack = PikaViewportContentStack;

    for (int32 i = PikaViewportContentStack.Num() - 1; i >= 0; i--)
    {
        RemoveViewportWidgetContent(PikaViewportContentStack[i]);
    }

    PikaHiddenViewportContentStack = PikaCopyOfViewportContentStack;
}

void UPikaGameViewportClient::PikaShowExistingWidgets()
{
    check(PikaViewportContentStack.Num() == 0);

    for (int32 i = 0; i < PikaHiddenViewportContentStack.Num(); i++)
    {
        AddViewportWidgetContent(PikaHiddenViewportContentStack[i]);
    }

    check(PikaViewportContentStack.Num() == PikaHiddenViewportContentStack.Num());

    PikaHiddenViewportContentStack.Empty();
}

void PikaSShooterLoadingScreen::Construct(const FArguments& InArgs)
{
    //static const FName PikaLoadingScreenName(TEXT("/Game/Movies/PikaLoading"));

    //PikaLoadingScreenBrush = MakeShareable(new PikaFShooterGameLoadingScreenBrush(PikaLoadingScreenName, FVector2D(1920, 1080)));

    /*ChildSlot
        [
            SNew(SOverlay)
            + SOverlay::Slot()
        .HAlign(HAlign_Fill)
        .VAlign(VAlign_Fill)
        [
            SNew(SImage)
            .Image(PikaLoadingScreenBrush.Get())
        ]
    + SOverlay::Slot()
        .HAlign(HAlign_Fill)
        .VAlign(VAlign_Fill)
        [
            SNew(SSafeZone)
            .VAlign(VAlign_Bottom)
        .HAlign(HAlign_Right)
        .Padding(10.0f)
        .IsTitleSafe(true)
        [
            SNew(SThrobber)
            .Visibility(this, &PikaSShooterLoadingScreen::GetLoadIndicatorVisibility)
        ]
        ]
        ];*/
}

PikaGlobalConfig.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "PikaGameSaveData.h"
#include "PikaGlobalConfig.generated.h"


//鼠标全局配置结构体
USTRUCT()
struct FPikaMouseSettingConfiguration 
{
    GENERATED_USTRUCT_BODY()
    //蓝图的配置方式
    //UPROPERTY(EditDefaultsOnly, Category = "PikaGlobal")
    //float MouseSpeed;
public:
    //c++的配置方式
    float MouseSpeed;
    //QE键模型的旋转角度
    float PikaAngleOfRotation;
    FPikaMouseSettingConfiguration();

};
/**
 * 
 */
//蓝图下的配置方式
//UCLASS(Blueprintable)
UCLASS()
class PIKAGODSTONE_API UPikaGlobalConfig : public UObject
{
    GENERATED_BODY()

    UPROPERTY(Transient)
    bool PikaSkillPermissions;
public:
    UPikaGlobalConfig();
    //蓝图的配置方式
    //UPROPERTY(EditDefaultsOnly , Category = "GlobalConfig")
    //FPikaMouseSettingConfiguration PikaMouseSetConf;
    //c++的配置方式
    UPROPERTY()
    FPikaMouseSettingConfiguration PikaMouseSetConf;

    //游戏UI相关数据,包括游戏快捷栏内物品,玩家技能等
    UPROPERTY()
    FPikaGodStoneSaveGameData PikaGameData;
    
    //是否是存储的数据经过读取而来,默认是非读取状态
    bool PikaIsSave;

    //鼠标缩放数据记录
    float PikaMouseZoom;

    //游戏存储
    uint64 PikaSaveCount;

    //关卡游戏规则相关的数据
    UPROPERTY()
    FPikaGameLevelManangenment PikaGameLevelManangenment;

    //
    UPROPERTY()
    FPikaTowersData PikaTowersData;

    //游戏场景内角色的数据
    UPROPERTY()
    FPikaTemporaryCacheData PikaTemporaryCacheData;

    //描述数据的数据
    //UPROPERTY()
    //FPikaReadGameData PikaReadGameData;

    //GameStory
    FPikaGameStory PikaGameStory;

    //UPROPERTY()
    //FCurrentLevelCondition CurrentLevelCondition;

    //UPROPERTY()
    //FMissionState MissionState;

    void PikaSetSkillPermissions(bool IsPermissions) { PikaSkillPermissions = IsPermissions; }

    //数据初始化完毕
    FORCEINLINE void PikaInitializationDataComplete() { PikaIsSave = false; }

    //使用技能的权限
    FORCEINLINE bool PikaGetPlayerSkillPermissions() const { return PikaSkillPermissions; }
};

PikaGlobalConfig.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGlobalConfig.h"

FPikaMouseSettingConfiguration::FPikaMouseSettingConfiguration()
{
    //c++方式配置的实始化
    MouseSpeed = 50.f;
    PikaAngleOfRotation = 15.f;

}

UPikaGlobalConfig::UPikaGlobalConfig() :
    PikaSkillPermissions(true),
    PikaIsSave(false)
{
    PikaSaveCount = 0;
    PikaMouseZoom = 1;
}

PikaGodStoneCamera.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/BoxComponent.h"
#include "PikaGodStoneCamera.generated.h"



UCLASS()
class PIKAGODSTONE_API APikaGodStoneCamera : public APawn
{
    GENERATED_BODY()

    //摄像机组件
    //根组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    USceneComponent* PikaCameraRootComponent;
    //控制摄像机离场景的距离
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    USpringArmComponent * PikaCameraBoom;
    //主摄像机
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    UCameraComponent * PikaMainCamera;

    //摄像机的碰撞标识
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
        UBoxComponent * PikaMarkBox;

public:
    // Sets default values for this pawn's properties
    APikaGodStoneCamera();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

    //摄像机缩放,MouseSpeed该值由playerController传进来
    void PikaCameraZoom(bool ZoomDirection, float MouseSpeed);

    void PikaSetCameraFOV(float NewFOV);

    //获取摄像机
    FORCEINLINE UCameraComponent* PikaGetMainCamera() const { return PikaMainCamera; }
    //获取FOV
    FORCEINLINE float PikaGetCameraFOV() const { return PikaGetMainCamera() ? PikaGetMainCamera()->FieldOfView : 0; }
};

PikaGodStoneCamera.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGodStoneCamera.h"


// Sets default values
APikaGodStoneCamera::APikaGodStoneCamera()
{
    // Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    //创建组件
    //CreateDefaultSubobject该创建object的方法只能用在构造函数里,非构造函数要用其它创建方式,如newObject
    PikaCameraRootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("PikaCameraRootComponent"));
    PikaCameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("PikaCameraBoom"));
    PikaMainCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("PikaMainCamera"));
    PikaMarkBox = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaHitSign"));
    //赋值根组件
    RootComponent = PikaCameraRootComponent;
    //绑定组件
    PikaCameraBoom->AttachToComponent(PikaCameraRootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    PikaMainCamera->AttachToComponent(PikaCameraBoom, FAttachmentTransformRules::KeepRelativeTransform);
    PikaMarkBox->AttachToComponent(PikaCameraRootComponent, FAttachmentTransformRules::KeepRelativeTransform);
    //设置摄像机角距离地面的长度
    PikaCameraBoom->TargetArmLength = 800.f;
    PikaCameraBoom->RelativeRotation = FRotator(-60.f, 0, 0);

    //碰撞,QueryOnly表示只能有碰撞和重叠
    PikaMarkBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    //忽略所有通道,然后单独开启ECC_Visibility和ECC_WorldDynamic这两个通道
    PikaMarkBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
    PikaMarkBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Overlap);
    PikaMarkBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldDynamic, ECollisionResponse::ECR_Overlap);
}

// Called when the game starts or when spawned
void APikaGodStoneCamera::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void APikaGodStoneCamera::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void APikaGodStoneCamera::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}

//缩放控制
void APikaGodStoneCamera::PikaCameraZoom(bool ZoomDirection, float MouseSpeed)
{
    if (ZoomDirection)
    {
        //判断摄像机组件是否存在
        if (PikaCameraBoom)
        {
            //PikaMinZoomVal为缩放的最小值
            int32 PikaMinZoomVal = 400;
            if (PikaCameraBoom->TargetArmLength > PikaMinZoomVal)
            {
                PikaCameraBoom->TargetArmLength -= (MouseSpeed * 2);
            }
        }
    }
    else
    {
        if (PikaCameraBoom)
        {
            //PikaMaxZoomVal为缩放的最大值
            int32 PikaMaxZoomVal = 1600;
            if (PikaCameraBoom->TargetArmLength < PikaMaxZoomVal)
            {
                PikaCameraBoom->TargetArmLength += (MouseSpeed * 2);
            }
        }
    }
}

void APikaGodStoneCamera::PikaSetCameraFOV(float NewFOV)
{
    if (PikaGetMainCamera())
    {
        PikaGetMainCamera()->SetFieldOfView(NewFOV);
    }
}

PikaGodStoneGameInstance.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "PikaGameSaveData.h"
#include "PikaGlobalConfig.h"
#include "PikaGodStoneGameInstance.generated.h"


#define PikaSTANDARD_VALUE 1

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaGodStoneGameInstance : public UGameInstance
{
    GENERATED_BODY()

    UPROPERTY()
    FString PikaCurrentSaveSlotName;

    //Loading界面
    //Loading需要在PikaGodStone.Build.cs里添加"MoviePlayer"模块
    TSubclassOf<UUserWidget>    PikaUI_LoadingTemplate;
    UUserWidget* PikaLoadingWidget;

    UPikaGameSaveData* PikaGodStoneGaveSaveDataPtr;
public:
    UPikaGodStoneGameInstance();

    virtual void Init();

    //解决UPikaGodStoneGameInstance的构造执行时,GetWorld还没有初始化完成的问题
    virtual FGameInstancePIEResult InitializeForPlayInEditor(int32 PIEInstanceIndex, const FGameInstancePIEParameters& Params) override;
    
    UPikaGameSaveData* PikaGetGameSaveData()  { return PikaGodStoneGaveSaveDataPtr;}

    //游戏全局的时间增量的设置
    void PikaSetGlobalGameTime(float TimeDilation = 0);

    //Loading
    void PikaShowLoadingScreen();

    UFUNCTION()
    virtual void BeginLoadingScreen(const FString& MapName);

    UFUNCTION()
    virtual void EndLoadingScreen(UWorld* NewWorld);

    bool PikaIsLoadingFinsh();

    //////////////////////////////////////读取和存储////////////////////////////////////
    //作为临时读取之用
    int32 PikaTmpArchive;
    //验证存档,如果没有就创建一个
    UPikaGameSaveData* PikaVerificationSaveData()const;

    //存储我们的游戏数据
    bool PikaSaveGameData(UPikaGlobalConfig* EnvironmentConfiguration, PikaFSaveSlotDataChannel SaveSlotDataChannel);

    //读取存档的序列号
    void PikaSetCurrentSaveSloatNmber(int32 NewNumber);

    FORCEINLINE FString PikaGetCurrentGameSaveName() const { return PikaCurrentSaveSlotName; }

    //是否可以读取进度
    bool PikaIsLoadingGameData(int32 SaveNumber);

    //获取当前存储了多少个存档,返回值将做为下一次创建新存档时的index
    int32 PikaGetCurrentSaveArchiveNumber()const;

    //删除存档
    bool PikaDeleteGameData(int32 SaveNumber);
private:
    //关联我们的游戏数据搭配存储点
    bool PikaCorrelationGameData(UPikaGlobalConfig* EnvironmentConfiguration, PikaFSaveSlotDataChannel SaveSlotDataChannel);
};

PikaGodStoneGameInstance.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGodStoneGameInstance.h"
#include "ConstructorHelpers.h"
#include "../MoviePlayer/Public/MoviePlayer.h"
#include "UserWidget.h"
#include "Public/PikaGameViewportClient.h"




UPikaGodStoneGameInstance::UPikaGodStoneGameInstance()
{
    //载入Loading
    ConstructorHelpers::FClassFinder<UUserWidget> PikaTmpLoading(TEXT("/Game/HUD/OtherUI/LoadingBP"));
    PikaUI_LoadingTemplate = PikaTmpLoading.Class;

    PikaTmpArchive = INDEX_NONE;
}

void UPikaGodStoneGameInstance::Init()
{
    Super::Init();

    //对两个Loading代理进行绑定
    FCoreUObjectDelegates::PreLoadMap.AddUObject(this, &UPikaGodStoneGameInstance::BeginLoadingScreen);
    FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &UPikaGodStoneGameInstance::EndLoadingScreen);
}


FGameInstancePIEResult UPikaGodStoneGameInstance::InitializeForPlayInEditor(int32 PIEInstanceIndex, const FGameInstancePIEParameters& Params)
{
    FGameInstancePIEResult PIEResult = Super::InitializeForPlayInEditor(PIEInstanceIndex  , Params);

    TSubclassOf<UPikaGameSaveData> PikaMySaveGame = UPikaGameSaveData::StaticClass();
    if (GetWorld() && PikaMySaveGame)
    {
        PikaGodStoneGaveSaveDataPtr = NewObject<UPikaGameSaveData>(GetWorld(), PikaMySaveGame);
    }

    return PIEResult;
}

//游戏全局的时间增量的设置
void UPikaGodStoneGameInstance::PikaSetGlobalGameTime(float TimeDilation /*= 0*/)
{
    if (GetWorld() && GetWorld()->GetWorldSettings())
    {
        float const PikaActualTimeDilation = GetWorld()->GetWorldSettings()->SetTimeDilation(TimeDilation);
        /*if (TimeDilation != PikaActualTimeDilation)
        {
            UE_LOG(LogBlueprintUserMessages, Warning, TEXT("Time Dilation must be between %f and %f.  Clamped value to that range."), GetWorld()->GetWorldSettings()->MinGlobalTimeDilation, GetWorld()->GetWorldSettings()->MaxGlobalTimeDilation);
        }*/
    }
}

void UPikaGodStoneGameInstance::PikaShowLoadingScreen()
{
    if (!IsRunningDedicatedServer())
    {
        FLoadingScreenAttributes PikaLoadingScreen;
        PikaLoadingScreen.bAutoCompleteWhenLoadingCompletes = true;
        PikaLoadingScreen.bMoviesAreSkippable = true;//任意键打断如果加载完成
        PikaLoadingScreen.bWaitForManualStop = false;//
        PikaLoadingScreen.PlaybackType = EMoviePlaybackType::MT_Looped;

        if (!PikaLoadingWidget)
            PikaLoadingWidget = CreateWidget<UUserWidget>(this, PikaUI_LoadingTemplate);

        if (PikaLoadingWidget)
        {
            PikaLoadingScreen.WidgetLoadingScreen = PikaLoadingWidget->TakeWidget();
            
            //添加mp4文件,位置为Content\Movies\PikaLoading.mp4
            PikaLoadingScreen.MoviePaths.Add("PikaLoading");
            GetMoviePlayer()->SetupLoadingScreen(PikaLoadingScreen);
        }
    }
    UPikaGameViewportClient * PikaShooterViewport = Cast<UPikaGameViewportClient>(GetGameViewportClient());

    if (PikaShooterViewport != NULL)
    {
        PikaShooterViewport->PikaShowLoadingScreen();
    }
}

void UPikaGodStoneGameInstance::BeginLoadingScreen(const FString& MapName)
{
    PikaShowLoadingScreen();
    if (GetGameViewportClient())
        GetGameViewportClient()->SetDisableSplitscreenOverride(false);
}

void UPikaGodStoneGameInstance::EndLoadingScreen(UWorld* NewWorld)
{
    UPikaGameViewportClient * PikaShooterViewport = Cast<UPikaGameViewportClient>(GetGameViewportClient());

    if (PikaShooterViewport != NULL)
    {
        if (PikaLoadingWidget)
        {
            PikaLoadingWidget = nullptr;
        }
        //进行销毁工作
        PikaShooterViewport->PikaHideLoadingScreen();
    }
}

bool UPikaGodStoneGameInstance::PikaIsLoadingFinsh()
{
    return false;
}

//验证存档,如果没有就创建一个
UPikaGameSaveData* UPikaGodStoneGameInstance::PikaVerificationSaveData() const
{
    //读取游戏存档在本地
    UPikaGameSaveData* PikaGameSaveData = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(PIKA_SAVE_SLOT_NAME, 0));
    //获取存档信息
    if (!PikaGameSaveData)
    {
        PikaGameSaveData = Cast<UPikaGameSaveData>(PIKA_CREATESAVE_SLOT(UPikaGameSaveData::StaticClass()));
        if (PikaGameSaveData)
            PIKA_SAVE_SLOT(PikaGameSaveData, PIKA_SAVE_SLOT_NAME, 0);
        else
            UE_LOG(LogTemp, Error, TEXT("There was a problem with the stored content at initialization time"));
    }
    return PikaGameSaveData;
}

bool UPikaGodStoneGameInstance::PikaSaveGameData(UPikaGlobalConfig* EnvironmentConfiguration, PikaFSaveSlotDataChannel SaveSlotDataChannel)
{
    if (EnvironmentConfiguration)
    {
        //存档,提取数据
        return PikaCorrelationGameData(EnvironmentConfiguration, SaveSlotDataChannel);
    }
    return false;
}

void UPikaGodStoneGameInstance::PikaSetCurrentSaveSloatNmber(int32 NewNumber)
{
    PikaCurrentSaveSlotName = PIKA_SAVE_GAME_NAME + FString::FromInt(NewNumber);
}

bool UPikaGodStoneGameInstance::PikaIsLoadingGameData(int32 SaveNumber)
{
    if (SaveNumber != INDEX_NONE)
    {
        //读取游戏存档在本地
        UPikaGameSaveData* PikaNewSaveDatas = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(PIKA_SAVE_GAME_NAME + FString::FromInt(SaveNumber), 0));
        if (PikaNewSaveDatas)
        {
            //还原游戏数据
            return true;
        }
    }
    return false;
}

//获取PIKA_SAVE_SLOT_NAME存档里第一个空位置的index,以该index还作为本次存档的index
int32 UPikaGodStoneGameInstance::PikaGetCurrentSaveArchiveNumber() const
{
    UPikaGameSaveData* PikaSaveSlot = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(PIKA_SAVE_SLOT_NAME, 0));
    if (PikaSaveSlot)
    {
        return PikaSaveSlot->PikaReadGameData.PikaGetSerialNumber();
    }
    else
    {
        PikaSaveSlot = PikaVerificationSaveData();
        if (PikaSaveSlot)
        {
            return PikaSaveSlot->PikaReadGameData.PikaGetSerialNumber();
        }
        else
        {
            UE_LOG(LogTemp, Error, TEXT("There was a problem with the stored content at initialization time"));
        }
    }
    return INDEX_NONE;
}

bool UPikaGodStoneGameInstance::PikaDeleteGameData(int32 SaveNumber)
{
    if (SaveNumber != INDEX_NONE)
    {
        if (PIKA_ISSAVE_SLOT(PIKA_SAVE_GAME_NAME + FString::FromInt(SaveNumber), 0))
        {
            //让数据默认显示,防止溢出
            UPikaGameSaveData* PikaNewSoltGameData = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(PIKA_SAVE_SLOT_NAME, 0));
            if (PikaNewSoltGameData)
            {
                //将PIKA_SAVE_SLOT_NAME存档里对应的数据项删除
                PikaNewSoltGameData->PikaReadGameData.PikaClearReadGameDataAt(SaveNumber);
                return (PIKA_DELETEGAME_SLOT(PIKA_SAVE_GAME_NAME + FString::FromInt(SaveNumber), 0) && PIKA_SAVE_SLOT(PikaNewSoltGameData, PIKA_SAVE_SLOT_NAME, 0));
            }
        }
    }
    return false;
}

//关联我们的游戏数据搭配存储点
bool UPikaGodStoneGameInstance::PikaCorrelationGameData(UPikaGlobalConfig* GlobalConfig, PikaFSaveSlotDataChannel SaveSlotDataChannel)
{
    //存储的序列号ID
    //PikaGameSaveSlot为游戏里整个存档数组里,存档时间和存档编号的信息
    //PikaGameSaveDatas为单个PikaUISaveLoadBar里对应的存档信息
    bool PikaIsGameSaveDatas = false;
    bool PikaIsGameSaveSlot = false;
    UPikaGameSaveData* PikaGameSaveDatas = Cast<UPikaGameSaveData>(PIKA_CREATESAVE_SLOT(UPikaGameSaveData::StaticClass()));
    UPikaGameSaveData* PikaGameSaveSlot = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(PIKA_SAVE_SLOT_NAME, 0));

    //////////////////////////////////数据判断////////////////////////////////////////
    //判断GameSaveSlot是否存在
    if (PikaGameSaveSlot)
    {
        PikaIsGameSaveSlot = true;
    }
    else
    {
        PikaGameSaveSlot = Cast<UPikaGameSaveData>(PIKA_CREATESAVE_SLOT(UPikaGameSaveData::StaticClass()));
        if (PikaGameSaveSlot)
        {
            PikaIsGameSaveSlot = true;
        }
    }
    //判断GameSaveDatas是否存在
    if (PikaGameSaveDatas)
    {
        PikaIsGameSaveDatas = true;
    }
    else
    {
        PikaGameSaveDatas = Cast<UPikaGameSaveData>(PIKA_CREATESAVE_SLOT(UPikaGameSaveData::StaticClass()));
        if (PikaGameSaveDatas)
            PikaIsGameSaveDatas = true;
    }
    //////////////////////////////////数据保存////////////////////////////////////////
    //执行GameSaveSlot保存
    if (PikaIsGameSaveSlot)
    {
        //PikaSaveGameBoxNumberArray数组里,index为循环出来的PikaUISaveLoadBar的编号
        //value为存档的编号,默认值为-1的那个,该值由UPikaUIReadAndWrite::PikaSaveArchiveSlot里的新建逻辑,获取当前存档数的最大值来设置
        if (PikaGameSaveSlot->PikaReadGameData.PikaSaveGameBoxNumberArray.IsValidIndex(SaveSlotDataChannel.PikaCurrentSaveGameBoxNumber))
            PikaGameSaveSlot->PikaReadGameData.PikaSaveGameBoxNumberArray[SaveSlotDataChannel.PikaCurrentSaveGameBoxNumber] = SaveSlotDataChannel.PikaCurrentGameArchiveNumber;
        //该存储条内的信息
        if (PikaGameSaveSlot->PikaReadGameData.PikaSaveDataText.IsValidIndex(SaveSlotDataChannel.PikaCurrentGameArchiveNumber))
            PikaGameSaveSlot->PikaReadGameData.PikaSaveDataText[SaveSlotDataChannel.PikaCurrentGameArchiveNumber] = FDateTime::Now().ToString();
    }
    //执行GameSaveDatas保存
    if (PikaIsGameSaveDatas)
    {
        //存储到我们的SavePoint
        //PikaGameSaveDatas->PikaMissionState = EnvironmentConfiguration->PikaMissionState;
        PikaGameSaveDatas->PikaGodStongSaveData = GlobalConfig->PikaGameData;
        PikaGameSaveDatas->PikaGameLevelManangenment = GlobalConfig->PikaGameLevelManangenment;
        PikaGameSaveDatas->PikaTemporaryCacheData = GlobalConfig->PikaTemporaryCacheData;
        PikaGameSaveDatas->PikaSaveCount = GlobalConfig->PikaSaveCount;
    }

    //////////////////////////////////////////////////////////////////////////
    PikaIsGameSaveDatas = PIKA_SAVE_SLOT(PikaGameSaveDatas, PIKA_SAVE_GAME_NAME + FString::FromInt(SaveSlotDataChannel.PikaCurrentGameArchiveNumber), 0);
    PikaIsGameSaveSlot = PIKA_SAVE_SLOT(PikaGameSaveSlot, PIKA_SAVE_SLOT_NAME, 0);

    return PikaIsGameSaveDatas && PikaIsGameSaveSlot;
}

PikaGodStoneGameStateBase.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "PikaGodStoneGameStateBase.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaGodStoneGameStateBase : public AGameStateBase
{
    GENERATED_BODY()
};

PikaGodStoneGameStateBase.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGodStoneGameStateBase.h"

PikaGodStonePlayerController.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "Public/PikaGlobalConfig.h"
#include "Public/PikaAITargetPoint.h"
#include "Engine/DataTable.h"
#include "Public/PikaGodStonePlayerState.h"
#include "Public/PikaGodStoneCamera.h"
#include "PikaTowerDoll.h"
#include "Public/PikaCharacterInfoDataTable.h"
#include "PikaCharacterBase.h"
#include "PikaMonster.h"
#include "PikaGameResource.h"
#include "PikaGodStoneGameInstance.h"
#include "PikaGodStonePlayerController.generated.h"

//带参数的代理
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPikaMouseMiddleButtonPressed, APikaGodStonePlayerController*, APikaGodStonePlayerController);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPikaMouseMiddleButtonReleased, APikaGodStonePlayerController*, APikaGodStonePlayerController);

/**
 *
 */
UCLASS()
class PIKAGODSTONE_API APikaGodStonePlayerController : public APlayerController
{
    GENERATED_BODY()

    //替身类指针
    UPROPERTY()
    APikaTowerDoll* PikaTowerDollPtr;

    //是否运行建造塔
    UPROPERTY()
    bool PikaIsAllowBuillingTowers;

    FHitResult PikaMouseTraceHit;

    //保存我们的指针
    APikaCharacterBase* PikaTempCharacterBasePtr;

    //uint8 BlockingProgram : 1;

    uint8 PikaIsSuccessLevel : 1;

    //uint8 IsFire : 1;

    //集中火力的目标
    UPROPERTY()
    APikaMonster* PikaMonstersTarget;

    UPROPERTY()
    ATargetPoint* PikaMapMarkPoint;

public:
    APikaGodStonePlayerController();

    virtual void Tick(float DeltaTime) override;

    virtual void BeginPlay() override;

    /****************************************代理Start****************************************/
    //按键响应在本类的SetupInputComponent里进行绑定
    //按下鼠标的代理
    FPikaMouseMiddleButtonPressed PikaEventMouseMiddlePressed;
    //松开鼠标的代理
    FPikaMouseMiddleButtonReleased PikaEventFMouseMiddleReleased;
    /****************************************代理End****************************************/

    /****************************************数据Start****************************************/
    //数据配置
    UDataTable * PikaAITowrerData;
    UDataTable * PikaAIMonsterData;
    UPikaGameResource * PikaGameResourcePtr;
    //指向全局配置的指针
    UPikaGlobalConfig * PikaGlobalConfigPrePlayerCon;
    //和上面一样,指向同一个内存地址
    UPikaGlobalConfig * PikaGlobalConfigPre;
    
    float** PikaTowersCharacterData;
    
    float** PikaMonsterCharacterData;

    //注册角色信息
    float** PikaRegisterCharacterData(UDataTable* CharacterData);
    /****************************************数据End****************************************/

    /****************************************塔相关函数Start****************************************/
    //获取塔的图片
    TArray<UTexture2D*> PikaGetTowerIocn();
    //名字
    TArray<FName> PikaGetTowersName();
    //简介
    TArray<FText> PikaGetTowersIntroduction();
    //获取塔的建造CD
    TArray<float> PikaGetBuildCD();
    //获取塔的Mesh,即特效内部的模型
    TArray<UStaticMesh*> PikaGetTowerMeshFromParticle(int32 TowerID , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo);
    //获取建造所需的金币
    TArray<int32> PikaGetTowerComsumeGold();
    //GM,读取数据表中的数据,并将其中的数据转换成蓝图实例
    //如果用该方法在场景里进行拖拽生成模型时,角色内部是没有数据的
    AActor* PikaSpawnTowers(int32 TowerID, int32 TowerLevel , bool PikaIsLoading = false, int32 CharacterIndex = INDEX_NONE , FVector CharacterLocation = FVector::ZeroVector);
    //生成替身类
    APikaTowerDoll* PikaSpawnTowerDoll(int32 SerialNum);
    //销毁替身类
    void PikaDestoryTowerDoll();
    //获取替换模型
    APikaTowerDoll* PikaGetTowersDoll() { return PikaTowerDollPtr; }
    //设置替换模型
    void PikaSetTowerDoll(APikaTowerDoll* NewTowerDoll) { PikaTowerDollPtr = NewTowerDoll; }
    //获取允许建筑的塔
    bool PikaGetAllowBuillingTowers() { return PikaIsAllowBuillingTowers; }
    //运行建造塔
    void PikaSetAllowBuillingTowers(bool AllowBuillingCondition) { PikaIsAllowBuillingTowers = AllowBuillingCondition; }
    //获取塔的数据
    TArray<float> PikaGetTowerData(EPikaCharacterInfoDataTableType::Type CharacterInformationDataType);

    //获取塔的当前等级
    int32 PikaGetCurrentTowersAverageLevel()const;

    //获取当前关卡所有Towers
    TArray<APikaCharacterBase*> PikaGetCurrentLevelTowers();

    /****************************************塔相关函数End****************************************/


    /****************************************怪物相关函数Start****************************************/
    //获取怪物图片
    TArray<UTexture2D*> PikaGetMonsterIocn();
    //简介
    TArray<FText> PikaGetMonsterIntroduction();
    //名字
    TArray<FName> PikaGetMonsterName();
    //获取怪物的Mesh,即特效内部的模型
    TArray<UStaticMesh*> PikaGetMonsterMeshFromParticle(int32 MonsterID , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo);
    //GM,读取数据表中的数据,并将其中的数据转换成蓝图实例
    //如果用该方法在场景里进行拖拽生成模型时,角色内部是没有数据的
    AActor* PikaSpawnMonster(int32 MonsterID, int32 MonsterLevel , bool PikaIsLoading = false, int32 CharacterIndex = INDEX_NONE , FVector CharacterLocation = FVector::ZeroVector);
    
    //获取怪物的数据
    TArray<float> PikaGetMonsterData(EPikaCharacterInfoDataTableType::Type CharacterInformationDataType);

    //获取当前关卡所有Monsters
    TArray<APikaCharacterBase*> PikaGetCurrentLevelMonsters();

    //获取手动目标
    FORCEINLINE APikaMonster* PikaGetTargetMonster() const { return PikaMonstersTarget; }
    /****************************************怪物相关函数End****************************************/

    /****************************************控制函数Start****************************************/
    //允许玩家自己更新自己的绑定响应
    virtual void SetupInputComponent() override;
    //鼠标上滚的响应函数
    void PikaMouseWheelUp();
    //鼠标下滚的响应函数
    void PikaMouseWheelDown();
    //判断鼠标移动的方向 
    void PikaCursorMoveDirection();
    //根据鼠标移动的方向移动摄像机
    void PikaCameraMove(bool Direction, bool Axle);
    //鼠标中键控制
    void PikaMouseMiddleButtonPressed();
    void PikaMouseMiddleButtonReleased();
    //键盘的Q和E旋转模型设置
    void PikaCounterClockWiseRotationKey();
    void PikaClockWiseRotationKey();
    /****************************************控制函数End****************************************/

    /****************************************其它函数Start****************************************/
    //获取场景中已经设置的目标点,目标点会作为GM命令的出生点(测试中)
    //获取场景所有出生点,作为怪物的生成点
    TArray<APikaAITargetPoint*> PikaGetAllOfTargetPoint();

    ATargetPoint* PikaGetAllOfLevelMapTargetPoint(TSubclassOf<ATargetPoint> MyTargetPointCalss);

    //获取玩家数据实例
    APikaGodStonePlayerState * PikaGetTowerPlayerState();
    //获取场景相机实例(Pawn)
    APikaGodStoneCamera * PikaGetTowreCamera();
    //测试打印函数
    void PikaPrint(FString PikaStr);
    //清除临时指针
    void PikaClearTmpRuleOfTheCharacter() { PikaTempCharacterBasePtr = NULL; }

    //获取当前关卡的地图
    UTexture2D* PikaGetCurrentLevelMiniMap() const;

    //获取地图标记点
    FVector PikaGetMapMarkPointLocation();

    FRotator PikaGetMapMarkPointRotator();

    FORCEINLINE AActor* PikaGetMapMarkPoint()const { return PikaMapMarkPoint; }

    UPikaGodStoneGameInstance* PikaGetGameInstance() const;

    //载入数据到场景角色(载入数据)
    bool PikaReductionGameData(bool IsReduction);

    //存储游戏数据的功能(储存数据)
    bool PikaSaveGameData(PikaFSaveSlotDataChannel SaveSlotDataChannel);

    //获取读取信息
    FText PikaGetSaveInformation(int32 NumData);


    //播放随机声音
    void PikaPlaySoundAtVetor(TArray<USoundBase*> CharacterSound, FVector CharcterLocation = FVector::ZeroVector, PikaETowerDefenceSoundType TowerDefenceSoundType = PikaETowerDefenceSoundType::SOUND_NONE);

    //播放声音
    void PikaPlaySoundAtVetor(USoundBase* CharacterSound, FVector CharcterLocation = FVector::ZeroVector, PikaETowerDefenceSoundType TowerDefenceSoundType = PikaETowerDefenceSoundType::SOUND_NONE);
    /****************************************其它函数End****************************************/

private:
    //数据导入,GM,读取数据表中的数据,并将其中的数据转换成蓝图实例
    AActor* PikaCreateAndDataImport(int32 CharacterID, int32 CharacterLevel, UDataTable* CharacterData , bool PikaIsLoading = false, int32 CharacterIndex = INDEX_NONE , FVector CharacterLocation = FVector::ZeroVector);
    //获取物品栏图标
    TArray<UTexture2D*> PikaGetCharacterIocn(UDataTable* CharacterData);
    //获取特效中的Mesh
    TArray<UStaticMesh*> PikaGetDataStaticMeshFormPartcle(UDataTable* MyCharacterDataTable , int32 CharacterID , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo);
    //名字
    TArray<FName> PikaGetCharacterName(UDataTable* CharacterData);
    //简介
    TArray<FText> PikaGetCharacterIntroduction(UDataTable* CharacterData);
    //获取角色数据
    TArray<float> PikaGetCharacterData(UDataTable* CharacterData, float** NewFloatArray, EPikaCharacterInfoDataTableType::Type CharacterInformationDataType);
    //释放掉我们的角色数据
    void PikaReleaseCharacterData();
    //获取场景内一个标准的等级(全部塔的平均等级)
    int32 PikaGetCurrentCharacterAverageLevel(bool CharacterClass = true) const;
};

PikaGodStonePlayerController.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGodStonePlayerController.h"
#include "Public/PikaGodStonePlayerState.h"
#include "Public/PikaGodStoneCamera.h"
#include "Public/PikaGameManager.h"
#include "Engine.h"
#include "ConstructorHelpers.h"
#include "Particles/ParticleLODLevel.h"
#include "Particles/TypeData/ParticleModuleTypeDataMesh.h"
#include "Particles/Size/ParticleModuleSize.h"
#include "Particles/Location/ParticleModuleLocation.h"
#include "Particles/Rotation/ParticleModuleMeshRotation.h"
#include "Public/PikaMonster.h"
#include "Public/PikaTowers.h"
#include "Public/PikaGameUserSettings.h"




APikaGodStonePlayerController::APikaGodStonePlayerController()
{
    //显示鼠标
    bShowMouseCursor = true;
    //初始GM类
    CheatClass = UPikaGameManager::StaticClass();
    
    
    static ConstructorHelpers::FClassFinder<ATargetPoint> PikaMapPointClass(TEXT("/Game/ToolActor/MapPointBP"));
    if (GetWorld())
    {
        //地图标记点
        if (PikaMapPointClass.Class)
        {
            PikaMapMarkPoint = PikaGetAllOfLevelMapTargetPoint(PikaMapPointClass.Class);
        }
    }
    

    PikaIsAllowBuillingTowers = true;

    PikaMonstersTarget = nullptr;


}

void APikaGodStonePlayerController::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    //获取鼠标移动的方向
    PikaCursorMoveDirection();
    //皮卡射线
    if (PikaTowerDollPtr) 
    {
        if (PikaMouseTraceHit.Location != FVector::ZeroVector)
            PikaMouseTraceHit.Location = FVector::ZeroVector;
        FHitResult PikaTraceOutHit;
        //ECC_EngineTraceChannel4为自定义的射线检测的通道
        GetHitResultUnderCursor(ECollisionChannel::ECC_GameTraceChannel4, true, PikaTraceOutHit);
        //利用射线设置替身类的坐标
        PikaTowerDollPtr->SetActorLocation(PikaTraceOutHit.Location);
        
    }
    else 
    {
        //鼠标扫描场景,通道名称为PikaScanTrace
        GetHitResultUnderCursor(ECollisionChannel::ECC_GameTraceChannel7, true, PikaMouseTraceHit);
    }

    if (PikaMouseTraceHit.Actor.IsValid())
    {
        APikaCharacterBase* PikaNewCharacterBase = Cast<APikaCharacterBase>(PikaMouseTraceHit.Actor.Get());
        if (PikaNewCharacterBase) 
        {
            PikaTempCharacterBasePtr = PikaNewCharacterBase;
            PikaTempCharacterBasePtr->PikaCharacterAttributeDisplay(true);
        }
    }
    else if (PikaTempCharacterBasePtr && PikaTempCharacterBasePtr->IsA(APikaCharacterBase::StaticClass()))
    {
        PikaTempCharacterBasePtr->PikaCharacterAttributeDisplay(false);
    }


    //关卡中角色控制
    if (PikaGlobalConfigPre && !PikaIsSuccessLevel && !PikaGlobalConfigPre->PikaGameLevelManangenment.PikaGameOver)
    {
        /*//添加灵力
        Confo->TDGameData.GoldGrowthTime += DeltaSeconds;
        if (Confo->TDGameData.IsJudgement())
        {
            Confo->TDGameData.GoldGrowthTime = 0;
            Confo->TDGameData.AddGlobalGold(2);
            //验证条件否成功
            GameCondition(ELevelCondition::ExceedingSpiritualForce);
        }*/

        //时间倒计时
        if (PikaGlobalConfigPre->PikaGameData.PikaGameCount > 0)
        {
            PikaGlobalConfigPre->PikaGameData.PikaGameCount -= DeltaTime;
        }
        else
        {
            //GameOver();
        }

        //检测玩家是否死亡
        //存在数量
        int32 PikaExistNumber = 0;
        int32 PikaTowersNum = 0;
        TArray<APikaCharacterBase*> PikaARuleOfTheTowers = PikaGetCurrentLevelTowers();
        for (APikaCharacterBase* PikaAloneTower : PikaARuleOfTheTowers)
        {
            if (PikaAloneTower->PikaIsActive())
            {
                PikaExistNumber++;

                APikaTowers* PikaTowers = Cast<APikaTowers>(PikaAloneTower);
                if (PikaTowers)
                    PikaTowersNum++;
            }
        }

        //场景内没有塔,玩家输
        if (PikaTowersNum == 0)
        {
            //GameOver();
        }
        
        //当前关卡是否胜利
        if (!PikaGlobalConfigPre->PikaGameLevelManangenment.PikaCurrentLevelMissionSuccess)
        {
            //输掉比赛没
            if (!PikaGlobalConfigPre->PikaGameLevelManangenment.PikaGameOver)
            {
                //准备开始怪物登场
                if (!PikaGlobalConfigPre->PikaGameLevelManangenment.PikaGetLock())
                {
                    if (PikaGlobalConfigPre->PikaGameLevelManangenment.PikaPerNumberOfMonsters.Num())
                    {
                        PikaGlobalConfigPre->PikaGameLevelManangenment.PikaSetTimeMnanagenment += DeltaTime;
                        //是否大于生成怪物的时间间隔
                        if (PikaGlobalConfigPre->PikaGameLevelManangenment.PikaIsTimeRight())
                        {
                            PikaGlobalConfigPre->PikaGameLevelManangenment.PikaSetTimeMnanagenment = PIKA_ZERO;
                            //获取怪物生成点
                            TArray<APikaAITargetPoint*> PikaSpawnPointArr = PikaGetAllOfTargetPoint();
                            TArray<APikaAITargetPoint*> PikaSpawnMonsterPoint;
                            if (PikaSpawnPointArr.Num() > 0)
                            {
                                for (APikaAITargetPoint* PikaMySpawnPoint : PikaSpawnPointArr)
                                {
                                    if (!PikaMySpawnPoint->PikaTeam)
                                    {
                                        PikaSpawnMonsterPoint.Add(PikaMySpawnPoint);
                                    }
                                }
                            }

                            //生成怪物
                            int32 PikaSpawnPoint = FMath::RandRange(0, PikaSpawnMonsterPoint.Num() - 1);

                            if (PikaSpawnPoint != INDEX_NONE && PikaSpawnMonsterPoint.IsValidIndex(PikaSpawnPoint))
                            {
                                //数据中是否存在角色
                                if (PikaGlobalConfigPre->PikaGameLevelManangenment.PikaIsExistDataMonster())
                                {
                                    int32 PikaCurrentTowersLevelAverage = PikaGetCurrentTowersAverageLevel();
                                    FVector PikaMonsterLocation = PikaSpawnMonsterPoint[PikaSpawnPoint]->GetActorLocation();
                                    AActor* PikaMyMonster = PikaSpawnMonster(FMath::RandRange(0, 3), PikaGlobalConfigPre->PikaGameLevelManangenment.PikaAddMonsterLevel + PikaCurrentTowersLevelAverage , false , -1 , PikaMonsterLocation);
                                    if ((APikaCharacterBase*)PikaMyMonster)
                                    {
                                        PikaMyMonster->SetActorLocation(PikaMonsterLocation);
                                        //((APikaCharacterBase*)PikaMyMonster)->PikaPlayBornSound();
                                    }

                                }
                                //对数据进行操作
                                PikaGlobalConfigPre->PikaGameLevelManangenment.PikaDataSubtraction();
                            }
                        }
                    }
                }
                else
                {
                    //存在数量
                    int32 PikaExistNumber = 0;
                    //获取怪物
                    TArray<APikaCharacterBase*> PikaCurrentMonters = PikaGetCurrentLevelMonsters();
                    //是否活着
                    for (APikaCharacterBase* PikaRuleOfTheMonster : PikaCurrentMonters)
                    {
                        if (PikaRuleOfTheMonster->PikaIsActive())
                        {
                            PikaExistNumber++;
                        }
                    }
                    if (PikaExistNumber == 0)
                    {
                        //解锁,开始下一波怪物登场
                        PikaGlobalConfigPre->PikaGameLevelManangenment.PikaUnLock();
                        //准备下一个阶段
                        PikaGlobalConfigPre->PikaGameLevelManangenment.PikaCurrentStagesAreMonsters++;
                    }
                }
            }
        }
        else
        {
            //GameSuccess();
        }
    }
    
}

void APikaGodStonePlayerController::BeginPlay()
{
    Super::BeginPlay();
    //获取playerState类中的蓝图配置
    APikaGodStonePlayerState * PikaPlayerStatePre = Cast<APikaGodStonePlayerState>(PlayerState);
    if (PikaPlayerStatePre)
    {
        PikaGlobalConfigPrePlayerCon = PikaPlayerStatePre->PikaGlobalConfigPre;
        if (PikaGlobalConfigPrePlayerCon)
        {
            
        }
    }
    //PikaGetTowerIocn();

    //初始化主塔
    if (PikaGlobalConfigPre)
    {
        if (!PikaGlobalConfigPre->PikaIsSave)
        {
            TArray<APikaAITargetPoint*> PikaSpawnPointArr = PikaGetAllOfTargetPoint();

            if (PikaSpawnPointArr.Num() > 0)
            {
                for (APikaAITargetPoint* MySpawnPoint : PikaSpawnPointArr)
                {
                    if (MySpawnPoint->PikaTeam)
                    {
                        APikaCharacterBase* PikaMianTowers = Cast<APikaCharacterBase>(PikaSpawnTowers(4, PikaGlobalConfigPre->PikaGameLevelManangenment.PikaTowersLevel));
                        if (PikaMianTowers)
                            PikaMianTowers->SetActorLocation(MySpawnPoint->GetActorLocation());

                        //上传我们的位置信息数据
                        if (PikaGlobalConfigPrePlayerCon)
                        {
                            int32 PikaCharacterIndex = PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaGetCurrentCharacterSpawnIDIndex(PikaMianTowers->PikaGetCharacterSpawnID());
                            if (PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaIsActive(PikaCharacterIndex))
                            {
                                PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterLocation[PikaCharacterIndex] = PikaMianTowers->GetActorLocation();
                                PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterRotator[PikaCharacterIndex] = PikaMianTowers->GetActorRotation();
                            }
                        }
                    }
                }
            }

            //初始化怪物梯度
            PikaGlobalConfigPre->PikaGameLevelManangenment.PikaAssignedMonsterAmount();
        }
        else
        {
            /*//数据读取完毕
            Confo->InitializationDataComplete();

            //将数据读取完毕告诉数据
            if (GetTowerDefenceGameInstance())
                GetTowerDefenceGameInstance()->InitializationDataComplete();

            //解锁
            UnTDLock();*/
        }
    }
}

void APikaGodStonePlayerController::PikaPrint(FString PikaStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, PikaStr);
    }
}

UTexture2D* APikaGodStonePlayerController::PikaGetCurrentLevelMiniMap() const
{
    if (PikaGameResourcePtr && PikaGlobalConfigPrePlayerCon && PikaGameResourcePtr->PikaLevelMiniMap.IsValidIndex(PikaGlobalConfigPrePlayerCon->PikaGameLevelManangenment.PikaCurrentLevel))
    {
        int32 PikaCurrentLevel = PikaGlobalConfigPrePlayerCon->PikaGameLevelManangenment.PikaCurrentLevel;
        if (PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapTexture)
            return PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapTexture;
    }
    return NULL;
}

FVector APikaGodStonePlayerController::PikaGetMapMarkPointLocation()
{
    return PikaMapMarkPoint != NULL ? PikaMapMarkPoint->GetActorLocation() : FVector::ZeroVector;
}

FRotator APikaGodStonePlayerController::PikaGetMapMarkPointRotator()
{
    return PikaMapMarkPoint != NULL ? PikaMapMarkPoint->GetActorRotation() : FRotator::ZeroRotator;
}

UPikaGodStoneGameInstance* APikaGodStonePlayerController::PikaGetGameInstance() const
{
    return GetWorld() != NULL ? ((UPikaGodStoneGameInstance*)GetWorld()->GetGameInstance()) : NULL;
}

bool APikaGodStonePlayerController::PikaSaveGameData(PikaFSaveSlotDataChannel SaveSlotDataChannel)
{
    if (PikaGetGameInstance())
    {
        if (PikaGlobalConfigPrePlayerCon)
            return PikaGetGameInstance()->PikaSaveGameData(PikaGlobalConfigPrePlayerCon, SaveSlotDataChannel);
    }
    return false;
}

FText APikaGodStonePlayerController::PikaGetSaveInformation(int32 NumData)
{
    FText PikaNewText;
    UPikaGameSaveData* PikaNewGameSaveData = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(PIKA_SAVE_SLOT_NAME, 0));
    if (PikaNewGameSaveData)
    {
        if (PikaNewGameSaveData->PikaReadGameData.PikaSaveDataText.IsValidIndex(NumData))
            PikaNewText = FText::FromString(PikaNewGameSaveData->PikaReadGameData.PikaSaveDataText[NumData]);
    }
    return PikaNewText;
}

//播放随机声音
void APikaGodStonePlayerController::PikaPlaySoundAtVetor(TArray<USoundBase*> CharacterSound, FVector CharcterLocation /*= FVector::ZeroVector*/, PikaETowerDefenceSoundType TowerDefenceSoundType /*= PikaETowerDefenceSoundType::SOUND_NONE*/)
{
    //随机序列
    int32 PikaRandomIndex = FMath::RandRange(0, (CharacterSound.Num() - 1));
    if (!CharacterSound.IsValidIndex(PikaRandomIndex))
        return;

    //播放声音
    PikaPlaySoundAtVetor(CharacterSound[PikaRandomIndex], CharcterLocation, TowerDefenceSoundType);
}

//播放声音
void APikaGodStonePlayerController::PikaPlaySoundAtVetor(USoundBase* CharacterSound, FVector CharcterLocation /*= FVector::ZeroVector*/, PikaETowerDefenceSoundType TowerDefenceSoundType /*= PikaETowerDefenceSoundType::SOUND_NONE*/)
{
    if (GetWorld() && CharacterSound)
    {
        //设置塔防播放类型
        CharacterSound->PikaqiuTowerDefenceSoundType = TowerDefenceSoundType;

        //总声音收益
        float PikaCurrentSound = 1;

        //声音大小调节
        if (UPikaGameUserSettings::PikaGetGameUserSettings())
        {
            switch (TowerDefenceSoundType)
            {
            case PikaETowerDefenceSoundType::SOUND_MUSIC:
                PikaCurrentSound = UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuTotalSoundControl * UPikaGameUserSettings::PikaGetGameUserSettings()->PikaBackgroundSoundControl;
                break;
            case PikaETowerDefenceSoundType::SOUND_EFFECT_SOUND:
                PikaCurrentSound = UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuTotalSoundControl * UPikaGameUserSettings::PikaGetGameUserSettings()->PikaEffectsSoundControl;
                break;
            case PikaETowerDefenceSoundType::SOUND_KEY_SOUND:
                PikaCurrentSound = UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuTotalSoundControl * UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuButtonSoundControl;
                break;
            }
        }

        //播放声音
        if (PikaCurrentSound > 0)
        {
            float pika = PikaCurrentSound > 1 ? 1 : PikaCurrentSound;
            //是否有衰减
            if (CharcterLocation != FVector::ZeroVector)
            {
                USoundAttenuation* PikaFightingSoundVolume = nullptr;

                //获取战斗衰减框
                if (PikaGameResourcePtr)
                    PikaFightingSoundVolume = PikaGameResourcePtr->PikaFightingSoundVolume;

                if (PikaFightingSoundVolume != nullptr)
                    PIKA_SPAWN_SOUND_LOCATION(GetWorld(), CharacterSound, CharcterLocation, PikaCurrentSound > 1 ? 1 : PikaCurrentSound, 1, 0, PikaFightingSoundVolume, nullptr);
            }
            else
            {
                PIKA_SPAWN_SOUND_LOCATION(GetWorld(), CharacterSound, CharcterLocation, PikaCurrentSound > 1 ? 1 : PikaCurrentSound, 1, 0, nullptr, nullptr);
            }
        }
    }
}

void APikaGodStonePlayerController::PikaCursorMoveDirection()
{
    //屏幕尺寸
    int32 PikaScreenSize_X, PikaScreenSize_Y;
    //鼠标位置
    float PikaMousePosi_X, PikaMousePosi_Y;
    //获取屏幕尺寸
    GetViewportSize(PikaScreenSize_X, PikaScreenSize_Y);
    //获取鼠标位置
    GetMousePosition(PikaMousePosi_X, PikaMousePosi_Y);
    //IsNearlyEqual的第三个参数为误差宽容度
    float ErrorTolerance = 6;
    if (FMath::IsNearlyEqual(PikaMousePosi_X, PikaScreenSize_X, ErrorTolerance))
    {
        PikaCameraMove(0, 0);
        return;
    }
    else if (FMath::IsNearlyEqual(PikaMousePosi_X, 0.0f, ErrorTolerance))
    {
        PikaCameraMove(1, 0);
        return;
    }

    if (FMath::IsNearlyEqual(PikaMousePosi_Y, PikaScreenSize_Y, ErrorTolerance))
    {
        PikaCameraMove(1, 1);
        return;
    }
    else if (FMath::IsNearlyEqual(PikaMousePosi_Y, 0.0f, ErrorTolerance))
    {
        PikaCameraMove(0, 1);
        return;
    }
}

void APikaGodStonePlayerController::PikaCameraMove(bool Direction, bool Axle)
{
    if (Direction)
    {
        if (Axle)
        {
            //x轴正方向
            FVector PikaTmpLocation = FVector(PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.MouseSpeed, 0.0f, 0.0f);
            //视角移动
            GetPawn()->AddActorWorldOffset(PikaTmpLocation);
        }
        else
        {
            //y轴正方向
            FVector PikaTmpLocation = FVector(0.0f, PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.MouseSpeed, 0.0f);
            GetPawn()->AddActorWorldOffset(PikaTmpLocation);
        }
    }
    else
    {
        if (Axle)
        {
            //x轴负方向
            FVector PikaTmpLocation = FVector(PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.MouseSpeed*(-1), 0.0f, 0.0f);
            GetPawn()->AddActorWorldOffset(PikaTmpLocation);
        }
        else
        {
            //y轴负方向
            FVector PikaTmpLocation = FVector(0.0f, PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.MouseSpeed*(-1), 0.0f);
            GetPawn()->AddActorWorldOffset(PikaTmpLocation);
        }
    }
}

void APikaGodStonePlayerController::PikaMouseMiddleButtonPressed()
{
    PikaEventMouseMiddlePressed.Broadcast(this);
}

void APikaGodStonePlayerController::PikaMouseMiddleButtonReleased()
{
    PikaEventFMouseMiddleReleased.Broadcast(this);
}

void APikaGodStonePlayerController::PikaCounterClockWiseRotationKey()
{
    if (PikaTowerDollPtr && PikaGlobalConfigPrePlayerCon)
        PikaTowerDollPtr->AddActorLocalRotation(FRotator(0, -1 * PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.PikaAngleOfRotation , 0));
}

void APikaGodStonePlayerController::PikaClockWiseRotationKey()
{
    if (PikaTowerDollPtr && PikaGlobalConfigPrePlayerCon)
        PikaTowerDollPtr->AddActorLocalRotation(FRotator(0, PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.PikaAngleOfRotation , 0));
}

void APikaGodStonePlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();
    /*按键绑定需要在UE4编辑器里的输入里设置绑定的键位信息*/
    //鼠标滚轮
    InputComponent->BindAction("PikaWheelUp", IE_Pressed, this, &APikaGodStonePlayerController::PikaMouseWheelUp);
    InputComponent->BindAction("PikaWheelDown", IE_Pressed, this, &APikaGodStonePlayerController::PikaMouseWheelDown);

    //鼠标中间绑定
    InputComponent->BindAction("PikaMiddleButtonPressed", IE_Pressed, this, &APikaGodStonePlayerController::PikaMouseMiddleButtonPressed);
    InputComponent->BindAction("PikaMiddleButtonReleased", IE_Released, this, &APikaGodStonePlayerController::PikaMouseMiddleButtonReleased);

    //键盘Q和E绑定
    InputComponent->BindAction("PikaCounterClockWiseRotation", IE_Pressed, this, &APikaGodStonePlayerController::PikaCounterClockWiseRotationKey);
    InputComponent->BindAction("PikaClockWiseRotation", IE_Pressed, this, &APikaGodStonePlayerController::PikaClockWiseRotationKey);
}

void APikaGodStonePlayerController::PikaMouseWheelUp()
{
    if (GetPawn())
    {
        APikaGodStoneCamera * PikaPartCamera = Cast<APikaGodStoneCamera>(GetPawn());
        if (PikaPartCamera)
        {
            PikaPartCamera->PikaCameraZoom(1, PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.MouseSpeed);
        }
    }
}

void APikaGodStonePlayerController::PikaMouseWheelDown()
{
    if (GetPawn())
    {
        APikaGodStoneCamera * PikaPartCamera = Cast<APikaGodStoneCamera>(GetPawn());
        if (PikaPartCamera)
        {
            PikaPartCamera->PikaCameraZoom(0, PikaGlobalConfigPrePlayerCon->PikaMouseSetConf.MouseSpeed);
        }
    }
}

AActor* APikaGodStonePlayerController::PikaCreateAndDataImport(int32 CharacterID, int32 CharacterLevel, UDataTable* CharacterData , bool PikaIsLoading , int32 CharacterIndex  , FVector CharacterLocation)
{
    AActor* PikaMyActor = NULL;
    if (CharacterData && CharacterID >= 0 && CharacterID < PikaAITowrerData->RowMap.Num())
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInfomationData;
        TArray<FName> PikaCharacterNameArray = PikaGetCharacterName(CharacterData);
        CharacterData->GetAllRows("PikaInfo", PikaInfomationData);
        //获取角色蓝图实例
        UClass * PikaMyClass = PikaInfomationData[CharacterID]->PikaCharacterBlueprintKey.LoadSynchronous();
        if (PikaMyClass)
        {
            //获取场景中已经设置的目标点
            //TArray<APikaAITargetPoint*> PikaMyPoint = PikaGetAllOfTargetPoint();
            //if (PikaMyPoint.Num() > 0)
            //{
                //PikaMyActor = GetWorld()->SpawnActor<AActor>(PikaMyClass, PikaMyPoint[0]->GetActorLocation(), FRotator::ZeroRotator);
                //生成模型
                PikaMyActor = GetWorld()->SpawnActor<AActor>(PikaMyClass, FVector::ZeroVector, FRotator::ZeroRotator);
                //更新模型数据到面板UI
                if (PikaMyActor)
                {
                    APikaCharacterBase * PikaMyCharacterBase = Cast<APikaCharacterBase>(PikaMyActor);
                    FName PikaCurrentCharacterName;
                    if (PikaMyCharacterBase)
                    {
                        //播放蒙太奇和绑定蒙太奇
                        PikaMyCharacterBase->PikaPlayAnimationForBegin(PikaIsLoading);
                        //获取角色ID和金币
                        PikaMyCharacterBase->PikaCharDataPre->PikaCharBaseData.PikaCharacterID = PikaInfomationData[CharacterID]->PikaCharacterID;
                        PikaMyCharacterBase->PikaCharDataPre->PikaCharBaseData.PikaComsumeGold = PikaInfomationData[CharacterID]->PikaComsumeGold;
                        //获取名字
                        if (PikaCharacterNameArray.IsValidIndex(CharacterLevel)) 
                        {
                            PikaMyCharacterBase->PikaCharDataPre->PikaCharBaseData.PikaCharacterName = PikaCharacterNameArray[CharacterID];
                            PikaCurrentCharacterName = PikaCharacterNameArray[CharacterID];
                        }
                            
                        //数据导入
                        PikaMyCharacterBase->PikaCharDataPre->PikaCharBaseData.PikaInitCharacterAttrubute(
                            PikaInfomationData[CharacterID]->PikaMaxHealth,
                            PikaInfomationData[CharacterID]->PikaPhysicalAttack,
                            PikaInfomationData[CharacterID]->PikaAttackSpeed,
                            PikaInfomationData[CharacterID]->PikaMaxExperienceValue,
                            PikaInfomationData[CharacterID]->PikaArmor
                        );
                        PikaMyCharacterBase->PikaCharDataPre->PikaCharBaseData.PikaInitAddCharacterAttribute(
                            PikaInfomationData[CharacterID]->PikaAddHealth,
                            PikaInfomationData[CharacterID]->PikaAddPhysicalAttack,
                            PikaInfomationData[CharacterID]->PikaAddAttackSpeed,
                            PikaInfomationData[CharacterID]->PikaAddExperienceValue,
                            PikaInfomationData[CharacterID]->PikaAddArmor
                        );
                    }

                    //自定义角色等级
                    if (CharacterLevel >= 1)
                    {
                        for (int32 i = 0; i < CharacterLevel; i++)
                        {
                            PikaMyCharacterBase->PikaCharDataPre->PikaCharBaseData.PikaUpdateLevel();
                        }
                    }
                    //更新角色数据到面板
                    PikaMyCharacterBase->PikaInitCharacterInformation();

                    //注册我们的生成序列
                    if (PikaGlobalConfigPrePlayerCon)
                    {
                        //是否是载入类型的spawn
                        if (PikaIsLoading)
                        {
                            if (CharacterIndex != INDEX_NONE)
                            {
                                /*//载入我们的数据
                                MyRuleOfTheCharacter->SetCharacterSpawnID(Confo->TemporaryCacheData.CharacterSpawnID[CharacterIndex]);
                                MyRuleOfTheCharacter->SetActorLocation(Confo->TemporaryCacheData.CharacterLocation[CharacterIndex] + FVector(0, 0, 100));//防止生成的角色掉下去
                                MyRuleOfTheCharacter->SetActorRotation(Confo->TemporaryCacheData.CharacterRotator[CharacterIndex]);
                                MyRuleOfTheCharacter->SetCharacterCurrentLevel(Confo->TemporaryCacheData.CharacterCurrentLevel[CharacterIndex]);
                                MyRuleOfTheCharacter->SetCharacterCurrentHealth(Confo->TemporaryCacheData.CharacterCurrentHealth[CharacterIndex]);
                                MyRuleOfTheCharacter->SetCharacterCurrentEXP(Confo->TemporaryCacheData.CharacterCurrentExp[CharacterIndex]);
                                MyRuleOfTheCharacter->SetCharacterActive(Confo->TemporaryCacheData.CharacterActive[CharacterIndex]);*/
                            }
                            else
                            {
                                UE_LOG(LogTemp, Error, TEXT(" Generation failed"));
                            }
                        }
                        else
                        {
                            int32 PikaMySpawnID = 10000 + FMath::RandRange(0, 10000);
                            while (PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaIsExistSpawnID(PikaMySpawnID))
                            {
                                PikaMySpawnID = 10000 + FMath::RandRange(0, 10000);
                            }

                            PikaMyCharacterBase->PikaSetCharacterSpawnID(PikaMySpawnID);

                            //上传信息到我们的数据包
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterSpawnID.Add(PikaMySpawnID);
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterActive.Add(true);
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterTeam.Add(PikaMyCharacterBase->PikaIsTeam());
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterLocation.Add(CharacterLocation);
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterRotator.Add(PikaMyCharacterBase->GetActorRotation());
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterName.Add(PikaCurrentCharacterName);
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterCurrentHealth.Add(PikaInfomationData[CharacterID]->PikaMaxHealth);
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterCurrentLevel.Add(CharacterLevel);
                            PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterCurrentExp.Add(0);
                        }
                    }
                }
            //}
        }
    }

    return PikaMyActor;
}

//GM,读取数据表中的数据,并将其中的数据转换成蓝图实例
AActor* APikaGodStonePlayerController::PikaSpawnTowers(int32 TowerID, int32 TowerLevel , bool PikaIsLoading , int32 CharacterIndex , FVector CharacterLocation)
{
    return PikaCreateAndDataImport(TowerID, TowerLevel, PikaAITowrerData , PikaIsLoading, CharacterIndex , CharacterLocation);
}

AActor* APikaGodStonePlayerController::PikaSpawnMonster(int32 MonsterID, int32 MonsterLevel , bool PikaIsLoading , int32 CharacterIndex , FVector CharacterLocation)
{
    return PikaCreateAndDataImport(MonsterID, MonsterLevel, PikaAIMonsterData , PikaIsLoading, CharacterIndex , CharacterLocation);
}


TArray<float> APikaGodStonePlayerController::PikaGetMonsterData(EPikaCharacterInfoDataTableType::Type CharacterInformationDataType)
{
    return PikaGetCharacterData(PikaAIMonsterData, PikaMonsterCharacterData, CharacterInformationDataType);
}

TArray<APikaCharacterBase*> APikaGodStonePlayerController::PikaGetCurrentLevelMonsters()
{
    TArray<APikaCharacterBase*> PikaCurrentLevelTowers;

    //临时
    TArray<AActor*> PikaTempCharacter;
    if (GetWorld())
    {
        PikaGETALLACTOR(GetWorld(), APikaCharacterBase::StaticClass(), PikaTempCharacter);
        for (AActor* PikaCurrentActor : PikaTempCharacter)
        {
            if (PikaCurrentActor->IsA(APikaMonster::StaticClass()))
            {
                PikaCurrentLevelTowers.Add(Cast<APikaCharacterBase>(PikaCurrentActor));
            }
        }
    }
    return PikaCurrentLevelTowers;
}

//获取场景中已经设置的目标点
TArray<APikaAITargetPoint*> APikaGodStonePlayerController::PikaGetAllOfTargetPoint()
{
    TArray<APikaAITargetPoint*> PikaTargetPointArr;
    if (!GetWorld()) 
    {
        UE_LOG(LogTemp , Error , TEXT("Pika:GetWorld() is fail"));
        return PikaTargetPointArr;
    }
    TSubclassOf<APikaAITargetPoint> PikaAITargetPointInstance = APikaAITargetPoint::StaticClass();
    if (PikaAITargetPointInstance) 
    {
        for (TActorIterator<APikaAITargetPoint>It(GetWorld() , PikaAITargetPointInstance); It; ++It)
        {
            APikaAITargetPoint * PikaMySpawnPoint = *It;
            if (PikaMySpawnPoint) 
            {
                PikaTargetPointArr.Add(PikaMySpawnPoint);
            }
        }
    }
    else 
    {
        UE_LOG(LogTemp , Error , TEXT("Pika:PikaTargetPointArr is fail"));
        return PikaTargetPointArr;
    }
    UE_LOG(LogTemp , Log , TEXT("Pika:Successfully"));
    return PikaTargetPointArr;
}

ATargetPoint* APikaGodStonePlayerController::PikaGetAllOfLevelMapTargetPoint(TSubclassOf<ATargetPoint> MyTargetPointCalss)
{
    if (MyTargetPointCalss)
    {
        for (TActorIterator<ATargetPoint>It(GetWorld(), MyTargetPointCalss); It; ++It)
        {
            ATargetPoint* MySpawnPoint = *It;
            if (MySpawnPoint && !MySpawnPoint->IsA(APikaAITargetPoint::StaticClass()))
                return MySpawnPoint;
        }
    }
    return NULL;
}

APikaGodStonePlayerState * APikaGodStonePlayerController::PikaGetTowerPlayerState()
{
    return ((APikaGodStonePlayerState*)PlayerState);
}

APikaGodStoneCamera * APikaGodStonePlayerController::PikaGetTowreCamera()
{
    return ((APikaGodStoneCamera*)GetPawn());
}

TArray<UTexture2D*> APikaGodStonePlayerController::PikaGetTowerIocn()
{
    return PikaGetCharacterIocn(PikaAITowrerData);
}

TArray<FName> APikaGodStonePlayerController::PikaGetTowersName()
{
    return PikaGetCharacterName(PikaAITowrerData);
}

TArray<FText> APikaGodStonePlayerController::PikaGetTowersIntroduction()
{
    return PikaGetCharacterIntroduction(PikaAITowrerData);
}

TArray<float> APikaGodStonePlayerController::PikaGetTowerData(EPikaCharacterInfoDataTableType::Type CharacterInformationDataType)
{
    return PikaGetCharacterData(PikaAITowrerData, PikaTowersCharacterData, CharacterInformationDataType);
}

int32 APikaGodStonePlayerController::PikaGetCurrentTowersAverageLevel() const
{
    return PikaGetCurrentCharacterAverageLevel();
}

TArray<APikaCharacterBase*> APikaGodStonePlayerController::PikaGetCurrentLevelTowers()
{
    TArray<APikaCharacterBase*> PikaCurrentLevelTowers;

    //临时
    TArray<AActor*> PikaTempCharacter;
    if (GetWorld())
    {
        PikaGETALLACTOR(GetWorld(), APikaCharacterBase::StaticClass(), PikaTempCharacter);
        for (AActor* PikaCurrentActor : PikaTempCharacter)
        {
            if (PikaCurrentActor->IsA(APikaTowers::StaticClass()))
            {
                PikaCurrentLevelTowers.Add(Cast<APikaCharacterBase>(PikaCurrentActor));
            }
        }
    }
    return PikaCurrentLevelTowers;
}

TArray<UTexture2D*> APikaGodStonePlayerController::PikaGetMonsterIocn()
{
    return PikaGetCharacterIocn(PikaAIMonsterData);
}

TArray<FText> APikaGodStonePlayerController::PikaGetMonsterIntroduction()
{
    return PikaGetCharacterIntroduction(PikaAIMonsterData);
}

TArray<FName> APikaGodStonePlayerController::PikaGetMonsterName()
{
    return PikaGetCharacterName(PikaAIMonsterData);
}

TArray<float> APikaGodStonePlayerController::PikaGetBuildCD()
{
    TArray<float> PikaArrayFloat;
    if (PikaAITowrerData)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInfomationData;
        PikaAITowrerData->GetAllRows("PikaInfo", PikaInfomationData);
        for (int32 i = 0; i < PikaAITowrerData->RowMap.Num(); i++)
        {
            float PikaFloat = PikaInfomationData[i]->PikaConstructionTime;
            PikaArrayFloat.Add(PikaFloat);
        }
    }
    return PikaArrayFloat;
}

TArray<int32> APikaGodStonePlayerController::PikaGetTowerComsumeGold()
{
    TArray<int32> PikaArrayGlod;
    if (PikaAITowrerData)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInfomationData;
        PikaAITowrerData->GetAllRows("PikaInfo", PikaInfomationData);
        for (int32 i = 0; i < PikaAITowrerData->RowMap.Num(); i++)
        {
            float PikaGlod = PikaInfomationData[i]->PikaComsumeGold;
            PikaArrayGlod.Add(PikaGlod);
        }
    }
    return PikaArrayGlod;
}

TArray<UStaticMesh*> APikaGodStonePlayerController::PikaGetTowerMeshFromParticle(int32 TowerID , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo)
{
    return PikaGetDataStaticMeshFormPartcle(PikaAITowrerData , TowerID , MyPikaComponentSpaceInfo);
}

TArray<UStaticMesh*> APikaGodStonePlayerController::PikaGetMonsterMeshFromParticle(int32 MonsterID , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo)
{
    return PikaGetDataStaticMeshFormPartcle(PikaAIMonsterData , MonsterID , MyPikaComponentSpaceInfo);
}

APikaTowerDoll* APikaGodStonePlayerController::PikaSpawnTowerDoll(int32 SerialNum)
{
    TSubclassOf<APikaTowerDoll> PikaMyDoll = APikaTowerDoll::StaticClass();
    //记录我们的空间位置
    FPikaComponentSpaceInfo PikaComponentSpaceInformation;
    if (GetWorld() && PikaMyDoll) 
    {
        PikaTowerDollPtr = GetWorld()->SpawnActor<APikaTowerDoll>(PikaMyDoll, FVector::ZeroVector, FRotator::ZeroRotator);
        if (PikaTowerDollPtr)
        {
            //注册
            PikaTowerDollPtr->PikaSetController(this);
            TArray<UStaticMesh*> PikaMeshArray = PikaGetTowerMeshFromParticle(SerialNum, PikaComponentSpaceInformation);
            if (PikaMeshArray.Num() < 1)
            {
                PikaMeshArray = PikaGetTowerMeshFromParticle(SerialNum, PikaComponentSpaceInformation);
                if (PikaMeshArray.Num() < 1)
                {
                    return nullptr;//无Mesh载入
                }
            }
            //生成替身的颜色
            PikaTowerDollPtr->PikaSetMeshAddToMeshComponet(PikaMeshArray , PikaComponentSpaceInformation);
            PikaTowerDollPtr->PikaSetMeshColor(true);
        }
        return PikaTowerDollPtr;
    }
    return NULL;
}

void APikaGodStonePlayerController::PikaDestoryTowerDoll()
{
    if (PikaTowerDollPtr)
        PikaTowerDollPtr->Destroyed();
    PikaTowerDollPtr = NULL;
}

TArray<UTexture2D*> APikaGodStonePlayerController::PikaGetCharacterIocn(UDataTable* CharacterData)
{
    TArray<UTexture2D*> PikaArrayTexture;
    if (CharacterData)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInfomationData;
        CharacterData->GetAllRows("PikaInfo", PikaInfomationData);
        for (int32 i = 0; i < CharacterData->RowMap.Num(); i++) 
        {
            UTexture2D* PikaTexture = PikaInfomationData[i]->PikaIcon.LoadSynchronous();
            if (PikaTexture) 
            {
                PikaArrayTexture.Add(PikaTexture);
            }
        }
    }
    return PikaArrayTexture;
}
//获取特效中的Mesh
TArray<UStaticMesh*> APikaGodStonePlayerController::PikaGetDataStaticMeshFormPartcle(UDataTable* MyCharacterDataTable, int32 CharacterID , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo)
{
    TArray<UStaticMesh*> PikaMyStaticMeshArray;
    if (MyCharacterDataTable)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInfomationData;
        MyCharacterDataTable->GetAllRows("PikaInfo", PikaInfomationData);
        if (CharacterID >= 0 && CharacterID < PikaInfomationData.Num()) 
        {
            //获取蓝图实例
            UClass* PikaMyClass = PikaInfomationData[CharacterID]->PikaCharacterBlueprintKey.LoadSynchronous();
            if (PikaMyClass && GetWorld())
            {
                APikaCharacterBase* PikaMyCharacter = GetWorld()->SpawnActor<APikaCharacterBase>(PikaMyClass, FVector::ZeroVector, FRotator::ZeroRotator);
                //PikaMyCharacter->GetParticleMesh()->Template为加载特效的地方,此处相当于获取了特效
                if (PikaMyCharacter && PikaMyCharacter->GetParticleMesh() && PikaMyCharacter->GetParticleMesh()->Template)
                {
                    //判断该特效中的发射器个数是否大于0
                    if (PikaMyCharacter->GetParticleMesh()->Template->Emitters.Num())
                    {
                        //从特效中的发射器中搜索Mesh
                        for (int32 i = 0; i < PikaMyCharacter->GetParticleMesh()->Template->Emitters.Num(); i++)
                        {
                            const UParticleEmitter* PikaMyEmitter = PikaMyCharacter->GetParticleMesh()->Template->Emitters[i];
                            if (PikaMyEmitter)
                            {
                                UParticleLODLevel* PikaMyLOD = PikaMyEmitter->LODLevels[0];
                                //PikaMyLOD->bEnabled表示当前LOD是否为启动状态
                                if (PikaMyLOD && PikaMyLOD->bEnabled)
                                {
                                    UParticleModuleTypeDataMesh* PikaMyParticleDataMesh = Cast<UParticleModuleTypeDataMesh>(PikaMyLOD->TypeDataModule);
                                    //获取特效中的Mesh
                                    if (PikaMyParticleDataMesh && PikaMyParticleDataMesh->Mesh)
                                    {
                                        //复制Mesh
                                        UStaticMesh* PikaMyParticleMeshCopy = Cast<UStaticMesh>(StaticDuplicateObject(PikaMyParticleDataMesh->Mesh, this));

                                        if (PikaMyParticleMeshCopy)
                                        {
                                            PikaMyStaticMeshArray.Add(PikaMyParticleMeshCopy);
                                            //获取特效的尺寸,用来控制生成绿色模型的大小
                                            //PikaMyLOD->Modules是指特效个每个发射器里的一行,如Lifetime、Inital Size等
                                            for (int32 IndexMesh = 0 ; IndexMesh < PikaMyLOD->Modules.Num() ; IndexMesh++)
                                            {
                                                bool PikaIsComponentSpaceInformation = false;
                                                UParticleModuleSize* PikaMeshSize = Cast<UParticleModuleSize>(PikaMyLOD->Modules[IndexMesh]);
                                                UParticleModuleLocation* PikaMeshLocation = Cast<UParticleModuleLocation>(PikaMyLOD->Modules[IndexMesh]);
                                                UParticleModuleMeshRotation* PikaMeshRotation = Cast<UParticleModuleMeshRotation>(PikaMyLOD->Modules[IndexMesh]);
                                                //判断是不是MeshSize
                                                if (PikaMeshSize)
                                                {
                                                    MyPikaComponentSpaceInfo.PikaComponentSize.Add(PikaMeshSize->StartSize.PikaqiuGetMaxValueVec());
                                                    PikaIsComponentSpaceInformation = true;
                                                }
                                                //判断是不是MeshLocation
                                                if (PikaMeshLocation)
                                                {
                                                    MyPikaComponentSpaceInfo.PikaComponentLocation.Add(PikaMeshLocation->StartLocation.PikaqiuGetMaxValueVec());
                                                    PikaIsComponentSpaceInformation = true;
                                                }
                                                //判断是不是MeshRotation
                                                if (PikaMeshRotation)
                                                {
                                                    MyPikaComponentSpaceInfo.PikaComponentRotation.Add((PikaMeshRotation->StartRotation.PikaqiuGetMaxValueVec()).Rotation());
                                                    PikaIsComponentSpaceInformation = true;
                                                }

                                                if (PikaIsComponentSpaceInformation)
                                                {
                                                    if (!MyPikaComponentSpaceInfo.PikaComponentRotation.IsValidIndex(MyPikaComponentSpaceInfo.PikaGetMax() - 1))
                                                    {
                                                        //MyPikaComponentSpaceInfo.PikaComponentRotation为无效值时,添加一个基本值
                                                        MyPikaComponentSpaceInfo.PikaComponentRotation.Add(FRotator::ZeroRotator);
                                                    }
                                                    if (!MyPikaComponentSpaceInfo.PikaComponentLocation.IsValidIndex(MyPikaComponentSpaceInfo.PikaGetMax() - 1))
                                                    {
                                                        //MyPikaComponentSpaceInfo.PikaComponentLocation为无效值时,添加一个基本值
                                                        MyPikaComponentSpaceInfo.PikaComponentLocation.Add(FVector::ZeroVector);
                                                    }
                                                    if (!MyPikaComponentSpaceInfo.PikaComponentSize.IsValidIndex(MyPikaComponentSpaceInfo.PikaGetMax() - 1))
                                                    {
                                                        //MyPikaComponentSpaceInfo.PikaComponentSize为无效值时,添加一个基本值
                                                        MyPikaComponentSpaceInfo.PikaComponentSize.Add(FVector(1, 1, 1));
                                                    }

                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    PikaMyCharacter->Destroy(true);
                    PikaMyCharacter = NULL;
                }
            }
        }
    }

    return PikaMyStaticMeshArray;
}

TArray<FName> APikaGodStonePlayerController::PikaGetCharacterName(UDataTable* CharacterData)
{
    TArray<FName> PikaTArrayFName;
    return CharacterData != NULL ? CharacterData->GetRowNames() : PikaTArrayFName;
}

TArray<FText> APikaGodStonePlayerController::PikaGetCharacterIntroduction(UDataTable* CharacterData)
{
    TArray<FText> PikaTArrayFText;
    TArray<FPikaCharacterInfoDataTable*>  PikaInformationData;
    CharacterData->GetAllRows("InformationData", PikaInformationData);
    for (int32 i = 0; i < PikaInformationData.Num(); i++)
    {
        PikaTArrayFText.Add(PikaInformationData[i]->PikaComment);
    }
    return PikaTArrayFText;
}

float** APikaGodStonePlayerController::PikaRegisterCharacterData(UDataTable* CharacterData)
{
    float** PikaSaveCharacterData = NULL;

    if (CharacterData)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInformationData;
        CharacterData->GetAllRows("InformationData", PikaInformationData);
        //申请行空间
        PikaSaveCharacterData = new float*[PikaInformationData.Num()];
        //申请列排空间
        for (int32 i = 0; i < PikaInformationData.Num(); i++)
        {
            PikaSaveCharacterData[i] = new float[EPikaCharacterInfoDataTableType::DATA_PikaCharacterMax];
        }
        for (int32 i = 0; i < PikaInformationData.Num(); i++)
        {
            for (int32 indexI = 0; indexI < PikaInformationData.Num(); indexI++)
            {
                switch (indexI)
                {
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterHealth:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaMaxHealth;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAttack:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaPhysicalAttack;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterArm:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaArmor;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterEmpircalValue:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaMaxExperienceValue;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAttackSpeed:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAttackSpeed;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterComsumeGlod:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaComsumeGold;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterConstructionTime:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaConstructionTime;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAddGlod:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAddComsume;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAddHealth:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAddHealth;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAddPhysicalAttack:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAddPhysicalAttack;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAddArmor:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAddArmor;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAddAttackSpeed:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAddAttackSpeed;
                    break;
                case EPikaCharacterInfoDataTableType::DATA_PikaCharacterAddEmpiricalValue:
                    PikaSaveCharacterData[i][indexI] = PikaInformationData[i]->PikaAddExperienceValue;
                    break;
                }
            }
        }
    }
    return PikaSaveCharacterData;
}

TArray<float> APikaGodStonePlayerController::PikaGetCharacterData(UDataTable* CharacterData, float** NewFloatArray, EPikaCharacterInfoDataTableType::Type CharacterInformationDataType)
{
    TArray<float> PikaBaseArrayCharacterData;
    if (CharacterData && NewFloatArray)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInformationData;
        CharacterData->GetAllRows("InformationData", PikaInformationData);
        for (int32 i = 0; i < PikaInformationData.Num(); i++)
        {
            PikaBaseArrayCharacterData.Add(NewFloatArray[i][CharacterInformationDataType]);
        }
    }
    return PikaBaseArrayCharacterData;
}

void APikaGodStonePlayerController::PikaReleaseCharacterData()
{
    if (PikaAITowrerData)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInformationData;
        PikaAITowrerData->GetAllRows("InformationData", PikaInformationData);

        for (int32 i = 0; i < PikaInformationData.Num(); i++)
        {
            delete[] PikaMonsterCharacterData[i];
        }
        delete[] PikaMonsterCharacterData;
    }
    if (PikaAIMonsterData)
    {
        TArray<FPikaCharacterInfoDataTable*> PikaInformationData;
        PikaAIMonsterData->GetAllRows("InformationData", PikaInformationData);

        for (int32 i = 0; i < PikaInformationData.Num(); i++)
        {
            delete[] PikaMonsterCharacterData[i];
        }
        delete[] PikaMonsterCharacterData;
    }
}

int32 APikaGodStonePlayerController::PikaGetCurrentCharacterAverageLevel(bool CharacterClass /*= true*/) const
{
    if (GetWorld())
    {
        TArray<AActor*> PikaOutArray;
        //总和
        int32 PikaTheSum = PIKA_ZERO;
        int32 PikaCharacterNumber = PIKA_ZERO;
        if (CharacterClass)
        {
            PikaGETALLACTOR(GetWorld(), APikaTowers::StaticClass(), PikaOutArray);
        }
        else
        {
            PikaGETALLACTOR(GetWorld(), APikaMonster::StaticClass(), PikaOutArray);
        }

        //获取数量
        PikaCharacterNumber = PikaOutArray.Num();
        for (AActor* PikaCharacterOriginal : PikaOutArray)
        {
            APikaCharacterBase* PikaCurrentRuleOfTheCharacter = (APikaCharacterBase*)PikaCharacterOriginal;
            if (PikaCurrentRuleOfTheCharacter)
            {
                //获取值
                PikaTheSum += PikaCurrentRuleOfTheCharacter->PikaGetCharacterLevel();
            }
        }
        if (PikaCharacterNumber != PIKA_ZERO)
        {
            return PikaTheSum / PikaCharacterNumber;
        }
    }
    return INDEX_NONE;
}

PikaGodStonePlayerState.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "PikaGlobalConfig.h"
#include "Public/PikaGodStoneGameInstance.h"
#include "PikaGameResource.h"
#include "PikaGodStonePlayerState.generated.h"

/**
 *
 */
UCLASS()
class PIKAGODSTONE_API APikaGodStonePlayerState : public APlayerState
{
    GENERATED_BODY()

public:
    //UPROPERTY()
    //TSubclassOf<UPikaGlobalConfig> PikaGlobalConfigInstance;

    UPROPERTY()
    UPikaGlobalConfig * PikaGlobalConfigPre;

    UPROPERTY()
    UPikaGameResource* PikaGameResources;

    APikaGodStonePlayerState();

    void Tick(float DeltaTime) override;

    UPikaGodStoneGameInstance* PikaGetGameInstance();

    void PikaPrint(FString PikaStr);
};

PikaGodStonePlayerState.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaGodStonePlayerState.h"
#include "ConstructorHelpers.h"
#include "Public/PikaGodStonePlayerController.h"

APikaGodStonePlayerState::APikaGodStonePlayerState()
{
    PrimaryActorTick.bCanEverTick = true;
    //加载蓝图中的全局配置
    //static ConstructorHelpers::FClassFinder<UPikaGlobalConfig>PikaGConfig(TEXT("/Game/GlobalConfig/PikaGlobalConfigBP"));
    //PikaGlobalConfigInstance = PikaGConfig.Class;
    //加载C++方式的全局配置
    //PikaGlobalConfigInstance = UPikaGlobalConfig::StaticClass();

    //在c++中引用蓝图中的全局配置
    /*if (GetWorld() && PikaGlobalConfigInstance)
    {
        PikaGlobalConfigPre = NewObject<UPikaGlobalConfig>(GetWorld(), PikaGlobalConfigInstance);
    }*/

    //数据配置
    static ConstructorHelpers::FClassFinder<UPikaGameResource> PikaMyGameResourceClass(TEXT("/Game/MinMap/BP/PikaGameResourceBP"));
    static ConstructorHelpers::FObjectFinder<UDataTable> PikaMyDataTable(TEXT("/Game/TowerAI/DataTable/PikaTower"));
    static ConstructorHelpers::FObjectFinder<UDataTable> PikaMyDataTableMonster(TEXT("/Game/MonsterAI/DataTable/PikaMonster"));
    

    if (GetWorld()) 
    {
        if (UPikaGlobalConfig::StaticClass())
        {
            PikaGlobalConfigPre = NewObject<UPikaGlobalConfig>(GetWorld(), UPikaGlobalConfig::StaticClass());

            //将存档进行赋值
            if (PikaGetGameInstance())
            {
                //获取当前正确的读取名字
                FString DataName = PikaGetGameInstance()->PikaGetCurrentGameSaveName(); 
                UPikaGameSaveData* PikaNewGameSaveData = Cast<UPikaGameSaveData>(PIKA_LOADGAME_SLOT(DataName, 0));
                if (PikaNewGameSaveData)
                {
                    PikaGlobalConfigPre->PikaGameData = PikaNewGameSaveData->PikaGodStongSaveData;
                    PikaGlobalConfigPre->PikaGameLevelManangenment = PikaNewGameSaveData->PikaGameLevelManangenment;
                    PikaGlobalConfigPre->PikaTowersData = PikaNewGameSaveData->PikaTowersData;
                    PikaGlobalConfigPre->PikaTemporaryCacheData = PikaNewGameSaveData->PikaTemporaryCacheData;
                    //PikaGlobalConfigPre->IsSave = NewGameSaveData->IsSave;
                    //PikaGlobalConfigPre->MissionState = NewGameSaveData->MissionState;
                }
            }

            //加载资源
            if (PikaMyGameResourceClass.Class)
            {
                PikaGameResources = NewObject<UPikaGameResource>(GetWorld(), PikaMyGameResourceClass.Class);

                /*//关卡条件载入
                if (PikaGlobalConfigPre &&
                    PikaGameResources &&
                    PikaGameResources->CLevel.IsValidIndex(EnvironmentConfiguration->TDGameLevelManangenment.CurrentLevel)
                    )
                {
                //PikaGlobalConfigPre->PikaCurrentLevelCondition = TDGameResources->GetCurrentCondition(EnvironmentConfiguration->TDGameLevelManangenment.CurrentLevel);
                }*/
            }

            //初始化Control数据
            if (((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController()))
            {
                //PikaAITowrerData = PikaMyDataTable.Object;
                //PikaAIMonsterData = PikaMyDataTableMonster.Object;
                //注册信息
                //PikaTowersCharacterData = PikaRegisterCharacterData(PikaAITowrerData);
                //PikaMonsterCharacterData = PikaRegisterCharacterData(PikaAIMonsterData);

                //载入角色
                ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaAITowrerData = PikaMyDataTable.Object;
                ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaAIMonsterData = PikaMyDataTableMonster.Object;

                //注册信息
                ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaTowersCharacterData = ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaRegisterCharacterData(PikaMyDataTable.Object);
                ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaMonsterCharacterData = ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaRegisterCharacterData(PikaMyDataTableMonster.Object);

                //数据
                ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaGlobalConfigPre = PikaGlobalConfigPre;
                ((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->PikaGameResourcePtr = PikaGameResources;
                //((APikaGodStonePlayerController*)GetWorld()->GetFirstPlayerController())->InitConditionUI();
            }
        }
    }
}

void APikaGodStonePlayerState::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    if (PikaGlobalConfigPre)
    {
        float PikaMouseSpeed = PikaGlobalConfigPre->PikaMouseSetConf.MouseSpeed;
        

        return;
    }
}

UPikaGodStoneGameInstance* APikaGodStonePlayerState::PikaGetGameInstance()
{
    return GetWorld() ? ((UPikaGodStoneGameInstance*)GetWorld()->GetGameInstance()) : NULL ;
}

void APikaGodStonePlayerState::PikaPrint(FString PikaStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.0f, FColor::Red, PikaStr);
    }
}

PikaHallGameMode.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Matinee/MatineeActor.h"
#include "PikaHallGameMode.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaHallGameMode : public AGameModeBase
{
    GENERATED_BODY()

    //描边时间
    //float PikaStrokeCurrentTime;
    
public:
    APikaHallGameMode();

    virtual void BeginPlay()override;
private:
    //Matinee
    TArray<TWeakObjectPtr<AMatineeActor>> PikaMatineeArray;
    TArray<TWeakObjectPtr<ACameraActor>> PikaCameraArray;
    //描边材质
    //UMaterialInstanceDynamic* PikaM_PostProcessBase;
    
};

PikaHallGameMode.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaHallGameMode.h"
#include "Public/PikaHallHUD.h"
#include "Public/PikaHallPawn.h"
#include "Public/PikaGameUserSettings.h"

APikaHallGameMode::APikaHallGameMode()
{
    //加载PlayerState
    PlayerStateClass = APikaGodStonePlayerState::StaticClass();
    HUDClass = APikaHallHUD::StaticClass();
    DefaultPawnClass = APikaHallPawn::StaticClass();
    
}

void APikaHallGameMode::BeginPlay()
{
    TArray<AActor*> PikaCurrentLevelActor;
    //获取场景内的ACameraActor
    PikaGETALLACTOR(GetWorld(), ACameraActor::StaticClass(), PikaCurrentLevelActor);
    //PikaCameraArray.SetNum(PikaCurrentLevelActor.Num());
    //初始化相机
    for (AActor* ElemCamera : PikaCurrentLevelActor)
    {
        ACameraActor* PikaCurrentCamera = Cast<ACameraActor>(ElemCamera);
        if (PikaCurrentCamera)
        {
            //游戏开始的相机
            if (PikaCurrentCamera->GetName() == "CameraNewGame")
            {
                GetWorld()->GetFirstPlayerController()->SetViewTargetWithBlend(PikaCurrentCamera, 0);
                //CameraArray[ECameraArray::Camera_NewGame] = CurrentCamera;
                //continue;
            }

        }
    }

    //获取场景内的Matinee
    PikaCurrentLevelActor.Empty();
    PikaGETALLACTOR(GetWorld(), AMatineeActor::StaticClass(), PikaCurrentLevelActor);
    PikaMatineeArray.SetNum(PikaCurrentLevelActor.Num());
    //初始化Matinee
    for (AActor* ElemMatinee : PikaCurrentLevelActor)
    {
        AMatineeActor* PikaCurrentMatinee = Cast<AMatineeActor>(ElemMatinee);
        if (PikaCurrentMatinee)
        {
            //播放进入
            if (PikaCurrentMatinee->GetName() == "MatineeNewGame")
            {
                PikaCurrentMatinee->Play();
                //PikaMatineeArray[EMatineeArray::Matinee_Coming] = CurrentMatinee;
                continue;
            }
        }
    }

    //设置PikaGameUserSettings的UWorld
    if (UPikaGameUserSettings::PikaGetGameUserSettings())
    {
        UPikaGameUserSettings::PikaGetGameUserSettings()->PikaSetretrieveWorldContext(GetWorld()); 
        //播放背景音乐
        UPikaGameUserSettings::PikaGetGameUserSettings()->PikaPlayBGM();
        //设置分辨率和窗口模式
        UPikaGameUserSettings::PikaGetGameUserSettings()->PikaSetVideo();
    }
}

PikaHallHUD.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "PikaUIMainHall.h"
#include "PikaHallHUD.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaHallHUD : public AHUD
{
    GENERATED_BODY()

public:
    APikaHallHUD();

    UPROPERTY()
    UPikaUIData* PikaUIDataPtr;

    //virtual void Tick(float DeltaSeconds)override;

    //释放
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
    UPikaUIMainHall* PikaMianHallPtr;
};

PikaHallHUD.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaHallHUD.h"
#include "ConstructorHelpers.h"

APikaHallHUD::APikaHallHUD()
{
    if (GetWorld())
    {
        //加载我们的mainHall
        PikaUIDataPtr = NewObject<UPikaUIData>(GetWorld(), UPikaUIData::StaticClass());
        static ConstructorHelpers::FClassFinder<UPikaUIMainHall> PikaMyGameInterface(TEXT("/Game/HUD/HallUI/PikaUIMainHallBP"));
        if (PikaMyGameInterface.Class && PikaUIDataPtr)
        {
            PikaMianHallPtr = CreateWidget<UPikaUIMainHall>(GetWorld(), PikaMyGameInterface.Class);
            PikaMianHallPtr->PikaUIDataPtr = PikaUIDataPtr;
            PikaMianHallPtr->AddToViewport();
            PikaMianHallPtr->PikaDrawUIToScreen();
        }

        //打开我们的鼠标箭头
        if (GetWorld()->GetFirstPlayerController())
            GetWorld()->GetFirstPlayerController()->bShowMouseCursor = true;
    }
}

void APikaHallHUD::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    if (PikaMianHallPtr)
        PikaMianHallPtr->RemoveFromViewport();
    PikaMianHallPtr = NULL;

    Super::EndPlay(EndPlayReason);
}

PikaHallPawn.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PikaHallPawn.generated.h"

UCLASS()
class PIKAGODSTONE_API APikaHallPawn : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties
    APikaHallPawn();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};

PikaHallPawn.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Public/PikaHallPawn.h"


// Sets default values
APikaHallPawn::APikaHallPawn()
{
     // Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void APikaHallPawn::BeginPlay()
{
    Super::BeginPlay();
    
}

// Called every frame
void APikaHallPawn::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void APikaHallPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}

11

22