上一次修改时间:2019-08-13 02:42:09

UE4逗B的示例代码下

PikaHUDBase.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "PikaMainInterface.h"
#include "PikaUIData.h"
#include "PikaGodStonePlayerController.h"
#include "PikaHUDBase.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaHUDBase : public AHUD
{
    GENERATED_BODY()
    
public:
    APikaHUDBase();
    virtual void BeginPlay() override;

    //该函数类似Tick函数,会被连续调用
    virtual void DrawHUD() override;

    FORCEINLINE APikaGodStonePlayerController* PikaGetPC();

    //UI数据
    UPikaUIData * PikaUIDataInstance;
private:
    //主界面指针
    UPikaMainInterface * PikaMainInterfacePre;
};

PikaHUDBase.cpp

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

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

APikaHUDBase::APikaHUDBase()
{
    //加载主界面蓝图
    TSubclassOf<UPikaUIData> MyUIData = UPikaUIData::StaticClass();
    if (GetWorld()) 
    {
        PikaUIDataInstance = NewObject<UPikaUIData>(GetWorld(), MyUIData);

        static ConstructorHelpers::FClassFinder<UPikaMainInterface> PikaMainInterfaceInstance(TEXT("/Game/HUD/GameUI/PikaMainInterfaceBP"));
        TSubclassOf<UPikaMainInterface> PikaInterfaceTsubclass = PikaMainInterfaceInstance.Class;
        if (PikaInterfaceTsubclass && PikaUIDataInstance)
        {
            PikaMainInterfacePre = CreateWidget<UPikaMainInterface>(GetWorld(), PikaInterfaceTsubclass);
            PikaMainInterfacePre->PikaUIDataPtr = PikaUIDataInstance;
            PikaMainInterfacePre->AddToViewport();
        }
    }
}

void APikaHUDBase::BeginPlay()
{
    Super::BeginPlay();
}

//该函数类似Tick函数,会被连续调用
void APikaHUDBase::DrawHUD()
{
    if (PikaGetPC() && PikaGetPC()->PikaGlobalConfigPrePlayerCon && PikaGetPC()->PikaGameResourcePtr)
    {
        //绘制我们的地图
        if (PikaGetPC()->PikaGetCurrentLevelMiniMap() && Canvas && PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap.IsValidIndex(PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaGameLevelManangenment.PikaCurrentLevel))
        {
            //********************************************************************************************
            int32 PikaCurrentLevel = PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaGameLevelManangenment.PikaCurrentLevel;
            //贴图占我们的720分辨率的百分比
            float PikaView_RatioY = PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapSizeY / 720.f;
            float PikaView_WHRatio = PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapSizeY / PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapSizeX;

            //我们的视野离边界的距离
            float PikaView_RatioIntervalY = PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapSizeYInterval / 720.f;
            float PikaView_IntervalWHRatio = PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapSizeYInterval / PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMiniMapSizeXInterval;

            //当前的贴图大小
            float PikaView_CurrentHeight = PikaView_RatioY * Canvas->SizeY;
            float PikaView_CurrentWidth = PikaView_CurrentHeight / PikaView_WHRatio;

            //当前的贴图与外边框的距离
            float PikaView_CurrentIntervalHeight = PikaView_RatioIntervalY * Canvas->SizeY;
            float PikaView_CurrentIntervalWidth = PikaView_CurrentIntervalHeight / PikaView_IntervalWHRatio;


            //获取绘制地图的x,y坐标
            float PikaView_MiniMapSizeX = Canvas->SizeX - (PikaView_CurrentWidth + PikaView_CurrentIntervalWidth);
            float PikaView_MiniMapSizeY = Canvas->SizeY - (PikaView_CurrentHeight + PikaView_CurrentIntervalHeight);

            //绘制地图
            DrawTexture(PikaGetPC()->PikaGetCurrentLevelMiniMap(), PikaView_MiniMapSizeX, PikaView_MiniMapSizeY, PikaView_CurrentWidth, PikaView_CurrentHeight, 0, 0, 1, 1);
            //********************************************************************************************

            //绘制玩家的位置坐标
            if (PikaGetPC()->PikaGlobalConfigPrePlayerCon)
            {
                //移除死亡的,不绘制
                for (int32 i = 0; i < PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterSpawnID.Num(); i++)
                {
                    if (!PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterActive[i])
                        PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaRemoveIt(i);
                }

                for (int32 i = 0; i < PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterSpawnID.Num(); i++)
                {
                    //所有角色的世界位置
                    FVector PikaCharacter_Pos = PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterLocation[i];
                    //获取该角色相对与我们的M(X,Y)的坐标的位置
                    FVector PikaVecLocation = (-1 * (PikaGetPC()->PikaGetMapMarkPointRotator())).RotateVector(PikaCharacter_Pos - PikaGetPC()->PikaGetMapMarkPointLocation());

                    //求出在真实场景内玩家在小地图上的距离
                    float PikaMiniMap_SizeX = (PikaVecLocation.X / PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMapSizeX) *  PikaView_CurrentWidth;
                    float PikaMiniMap_SizeY = (PikaVecLocation.Y / PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMapSizeY) *  PikaView_CurrentHeight;

                    //求出角色在我们地图上的坐标
                    float PikaChar_PosMiniMapX = PikaView_MiniMapSizeX + PikaMiniMap_SizeX;
                    float PikaChar_PosMiniMapY = PikaView_MiniMapSizeY + PikaMiniMap_SizeY;

                    //绘制点在我们的小地图上
                    if (PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterTeam[i])
                        Canvas->K2_DrawLine(FVector2D(PikaChar_PosMiniMapX, PikaChar_PosMiniMapY), FVector2D(PikaChar_PosMiniMapX + 1, PikaChar_PosMiniMapY + 1), 6, FLinearColor::Blue);
                    else
                        Canvas->K2_DrawLine(FVector2D(PikaChar_PosMiniMapX, PikaChar_PosMiniMapY), FVector2D(PikaChar_PosMiniMapX + 1, PikaChar_PosMiniMapY + 1), 6, FLinearColor::Red);
                }

                //绘制相机框
                if (PikaGetPC()->PikaGameResourcePtr->PikaNewCameraBoard && PikaGetPC()->GetPawn())
                {
                    //获取地图比值
                    FVector2D PikaCharacter_Pos = PikaGetPC()->PikaGameResourcePtr->PikaPlayerLocConverMiniMapCoord(PikaCurrentLevel, PikaGetPC()->PikaGetMapMarkPoint(), PikaGetPC()->GetPawn());

                    float PikaMiniMap_SizeX = FMath::Abs(PikaCharacter_Pos.X*  PikaView_CurrentWidth);
                    float PikaMiniMap_SizeY = FMath::Abs(PikaCharacter_Pos.Y*  PikaView_CurrentHeight);

                    //求出角色在我们地图上的坐标
                    float PikaChar_PosMiniMapX = PikaView_MiniMapSizeX + PikaMiniMap_SizeX;
                    float PikaChar_PosMiniMapY = PikaView_MiniMapSizeY + PikaMiniMap_SizeY;

                    //摄像机尺寸
                    float PikaCamraBoardX = ((PikaGetPC()->PikaGameResourcePtr->PikaNewCameraBoardSize.X / PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMapSizeX)* PikaView_CurrentWidth)*PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaMouseZoom;
                    float PikaCamraBoardY = ((PikaGetPC()->PikaGameResourcePtr->PikaNewCameraBoardSize.Y / PikaGetPC()->PikaGameResourcePtr->PikaLevelMiniMap[PikaCurrentLevel].PikaMapSizeY)* PikaView_CurrentHeight)*PikaGetPC()->PikaGlobalConfigPrePlayerCon->PikaMouseZoom;
                    DrawTexture(PikaGetPC()->PikaGameResourcePtr->PikaNewCameraBoard, PikaChar_PosMiniMapX - PikaCamraBoardX, PikaChar_PosMiniMapY - PikaCamraBoardY / 2, PikaCamraBoardX, PikaCamraBoardY, 0, 0, 1, 1);
                }
            }
        }
    }
}

FORCEINLINE APikaGodStonePlayerController* APikaHUDBase::PikaGetPC()
{
    return PlayerOwner != NULL ? ((APikaGodStonePlayerController*)PlayerOwner) : NULL;
}

PikaMainInterface.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaUI_Inventory.h"
#include "SizeBox.h"
#include "PikaUITowerInfo.h"
#include "PikaUIGameCount.h"
#include "PikaUIGameMenu.h"
#include "PikaGameErrorHint.h"
#include "WidgetSwitcher.h"
#include "PikaUIReadAndWrite.h"
#include "PikaUIHallGameSetting.h"
#include "PikaUIGameWarning.h"
#include "PikaMainInterface.generated.h"


DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FEventPikaLog, FString, MyString);

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

    //用来载入PikaUI_Inventory的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUI_Inventory> PikaInventoryClass;
    //用来载入UPikaUITowerInfo的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUITowerInfo> PikaUITowerInfoClass;
    //用来载入UPikaUIGameCount的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUIGameCount> PikaUIGameCountClass;
    //用来载入UPikaUIGameMenu的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUIGameMenu> PikaUIGameMenuClass;
    //用来载入UPikaGameErrorHint的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaGameErrorHint> PikaGameErrorHintClass;
    //用来载入UPikaUIReadAndWrite的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUIReadAndWrite> PikaReadAndWriteClass;
    //用来载入UPikaUIHallGameSetting的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUIHallGameSetting> PikaHallGameSettingsClass;
    //用来载入UPikaUIGameWarning的蓝图类
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUIGameWarning> PikaGameWarningClass;
    

    //BoxSize相关的名字
    //////////////////////////////////////////////////////////////////////////
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaAttributeInfoArrayName;
    //物品栏集合的名字,由蓝图传入
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaInventoryArrayName;
    //物品栏道具的信息,由蓝图传入
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaCharacterInfoArrayName;
    //游戏规则和游戏计时,由蓝图传入
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaUIGameCountArrayName;
    //游戏内部简易菜单,由蓝图传入
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaUIGameMenuArrayName;
    //错误提示,由蓝图传入
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaGameErrorHintArrayName;
    //打开游戏菜单
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaGameOpenMenuName;
    //设置和存储菜单
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaBoxSaveGameArrayName;
    //警告框
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaBoxGameWarningArrayName;



public:
    virtual void AddToViewport(int32 ZOrder = 0) override;

    //每一帧的屏幕信息
    FGeometry ScreenGeometry;
    //打印的代理
    FEventPikaLog PikaLog;

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString) override;

    virtual void PikaDrawUIToScreen() override;

    //Tick
    virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;

    //敲击
    virtual void PikaOnClickedWidget(UWidget* MyWidget)override;

    //屏蔽操作
    void PikaShieldedInteractiveOperation(bool InVisibility);

    //存档与游戏设置切换
    void PikaSetBoxListDisplayOrder(PikaBoxListType::Type BoxListType);

    PikaBoxListType::Type PikaGetBoxListDisplayOrder() const;

    virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None) override;

    ////////////////////////////////////内联/////////////////////////////////////
    FORCEINLINE USizeBox* PikaGetCharacterAttribute() const { return PikaAttributeInfoArray; }
    FORCEINLINE UPikaUITowerInfo* PikaGetUITowerInfo() const { return PikaUITowerInfoPtr; }
    FORCEINLINE UWidgetSwitcher* PikaGetBoxSaveGameArray()const { return PikaBoxSaveGameArray; }
    FORCEINLINE UPikaUIReadAndWrite* PikaGetReadAndWrite() const { return PikaReadAndWrite; }
    FORCEINLINE UPikaUIGameWarning* PikaGetGameWarning() const { return PikaGameWarningPtr; }

private:
    //UPikaUI_Inventory类的指针
    UPikaUI_Inventory * PikaUIInventoryPtr;
    //UPikaUITowerInfo类的指针,对应PikaAttributeInfoArray
    UPikaUITowerInfo * PikaUITowerInfoPtr;
    //作为临时角色数据显示
    FPikaCharacterInformationWidget* PikaTempCharacterInformationWidget;
    //游戏规则和游戏计时
    UPikaUIGameCount* PikaUIGameCountPtr;
    //游戏内部简易菜单
    UPikaUIGameMenu* PikaUIGameMenuPtr;
    //错误提示
    UPikaGameErrorHint* PikaGameErrorHintPtr;
    //保存和读取界面
    UPikaUIReadAndWrite* PikaReadAndWrite;
    //游戏设置
    UPikaUIHallGameSetting* PikaHallGameSettings;
    //提示界面
    UPikaUIGameWarning* PikaGameWarningPtr;

    //容器相关
    USizeBox * PikaInventorySlotArrayBox;
    USizeBox * PikaCharacterInfoArray;
    USizeBox* PikaAttributeInfoArray;
    USizeBox* PikaGameCountArray;
    USizeBox* PikaGameErrorHintArray;
    USizeBox* PikaGameMenuArray;
    UButton* PikaGameOpenMenu;
    UWidgetSwitcher* PikaBoxSaveGameArray;
    USizeBox* PikaBoxGameWarninArray;

    //存储我们的控件的上级
    TArray<UWidget*> PikaBoxSizeArray;

protected:
    //NativeOnDragDetected激活后,如果将图标从一个物品栏拖拽到另外一个物品栏则会触发该事件
    //接收拖拽信息,InOperation为空时,该函数不会被激活
    virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
};

PikaMainInterface.cpp

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

#include "Public/PikaMainInterface.h"
#include "Engine/Engine.h"
#include "Public/PikaDragDropOperation.h"


void UPikaMainInterface::AddToViewport(int32 ZOrder /*= 0*/)
{
    Super::AddToViewport(ZOrder);

    PikaInventorySlotArrayBox = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaInventoryArrayName));
    PikaCharacterInfoArray = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaCharacterInfoArrayName));
    PikaGameCountArray = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaUIGameCountArrayName));
    PikaGameErrorHintArray = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaGameErrorHintArrayName));
    PikaGameMenuArray = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaUIGameMenuArrayName));
    PikaGameOpenMenu = PikaGetButtonFromBlueprint(PikaGameOpenMenuName);
    PikaBoxSaveGameArray = Cast<UWidgetSwitcher>(PikaqiuGetBlueprintWidget(PikaBoxSaveGameArrayName));
    PikaBoxGameWarninArray = Cast<USizeBox>(GetWidgetFromName(PikaBoxGameWarningArrayName));
    //在基础UI里添加控件
    if (GetWorld())
    {
        //添加Inventory
        if (PikaInventoryClass  && PikaInventorySlotArrayBox)
        {
            PikaUIInventoryPtr = CreateWidget<UPikaUI_Inventory>(GetWorld(), PikaInventoryClass);
            PikaUIInventoryPtr->PikaUIDataPtr = PikaUIDataPtr;
            //添加进去
            PikaAddSizeBox(PikaInventorySlotArrayBox, PikaUIInventoryPtr);
            PikaUIInventoryPtr->PikaSetMainInterface(this);
            PikaUIInventoryPtr->PikaDrawUIToScreen();
        }
        //添加PikaUITowerInfo控件
        if (PikaUITowerInfoClass && PikaCharacterInfoArray)
        {
            PikaUITowerInfoPtr = CreateWidget<UPikaUITowerInfo>(GetWorld(), PikaUITowerInfoClass);
            PikaUITowerInfoPtr->PikaUIDataPtr = PikaUIDataPtr;
            //添加进去
            PikaAddSizeBox(PikaCharacterInfoArray, PikaUITowerInfoPtr);
            PikaUITowerInfoPtr->PikaDrawUIToScreen();
            //默认状态下隐藏
            PikaUITowerInfoPtr->SetVisibility(ESlateVisibility::Hidden);
        }
        //添加UPikaUIGameCount
        if (PikaUIGameCountClass  && PikaGameCountArray)
        {
            PikaUIGameCountPtr = CreateWidget<UPikaUIGameCount>(GetWorld(), PikaUIGameCountClass);
            PikaUIGameCountPtr->PikaUIDataPtr = PikaUIDataPtr;
            //添加进去
            PikaAddSizeBox(PikaGameCountArray, PikaUIGameCountPtr);
            PikaUIGameCountPtr->PikaSetMainInterface(this);
            PikaUIGameCountPtr->PikaDrawUIToScreen();
        }
        //添加UPikaUIGameMenu
        if (PikaUIGameMenuClass  && PikaGameMenuArray)
        {
            PikaUIGameMenuPtr = CreateWidget<UPikaUIGameMenu>(GetWorld(), PikaUIGameMenuClass);
            PikaUIGameMenuPtr->PikaUIDataPtr = PikaUIDataPtr;
            //添加进去
            PikaAddSizeBox(PikaGameMenuArray, PikaUIGameMenuPtr);
            PikaUIGameMenuPtr->PikaSetMainInterface(this);
            PikaUIGameMenuPtr->PikaDrawUIToScreen();
            //默认状态下隐藏
            PikaUIGameMenuPtr->SetVisibility(ESlateVisibility::Hidden);
        }
        //添加UPikaGameErrorHint
        if (PikaGameErrorHintClass  && PikaGameErrorHintArray)
        {
            PikaGameErrorHintPtr = CreateWidget<UPikaGameErrorHint>(GetWorld(), PikaGameErrorHintClass);
            PikaGameErrorHintPtr->PikaUIDataPtr = PikaUIDataPtr;
            //添加进去
            PikaAddSizeBox(PikaGameErrorHintArray, PikaGameErrorHintPtr);
            PikaGameErrorHintPtr->PikaSetMainInterface(this);
            PikaGameErrorHintPtr->PikaDrawUIToScreen();
        }
        //叠加界面
        if (PikaBoxSaveGameArray)
        {
            //隐藏自己
            PikaBoxSaveGameArray->SetVisibility(ESlateVisibility::Hidden);
            //添加我们的游戏存储界面
            if (PikaReadAndWriteClass)
            {
                PikaReadAndWrite = CreateWidget<UPikaUIReadAndWrite>(GetWorld(), PikaReadAndWriteClass);
                check(PikaReadAndWrite);
                PikaReadAndWrite->PikaUIDataPtr = PikaUIDataPtr;
                PikaBoxSaveGameArray->AddChild(PikaReadAndWrite);
                PikaReadAndWrite->PikaDrawUIToScreen();
                if (PikaUIGameMenuPtr)
                    PikaReadAndWrite->PikaSetGameMenu(PikaUIGameMenuPtr);
                PikaReadAndWrite->PikaSetButtonText(PikaReadAndWrite->Pika_WText[0]);
                PikaReadAndWrite->PikaSetNewButtonType(PikaETDButtonType::SaveGame);
            }
            //添加设置界面
            if (PikaHallGameSettingsClass)
            {
                PikaHallGameSettings = CreateWidget<UPikaUIHallGameSetting>(GetWorld(), PikaHallGameSettingsClass);
                check(PikaHallGameSettings);
                PikaHallGameSettings->PikaUIDataPtr = PikaUIDataPtr;
                PikaBoxSaveGameArray->AddChild(PikaHallGameSettings);
                PikaHallGameSettings->PikaDrawUIToScreen();
                if (PikaUIGameMenuPtr)
                    PikaHallGameSettings->PikaSetGameMenu(PikaUIGameMenuPtr);
            }
        }

        //加载游戏警告
        if (PikaGameWarningClass && PikaBoxGameWarninArray)
        {
            //隐藏自己
            PikaBoxGameWarninArray->SetVisibility(ESlateVisibility::Hidden);

            PikaGameWarningPtr = CreateWidget<UPikaUIGameWarning>(GetWorld(), PikaGameWarningClass);
            check(PikaGameWarningPtr);
            PikaGameWarningPtr->PikaUIDataPtr = PikaUIDataPtr;

            //添加进去
            PikaAddSizeBox(PikaBoxGameWarninArray, PikaGameWarningPtr);
            PikaGameWarningPtr->PikaSetMainInterface(this);
            PikaGameWarningPtr->PikaDrawUIToScreen();
            //隐藏
            //SetWarningWindowsState(false, 0.5f);
        }
    }
    //添加到数组,用在暂停游戏时,屏蔽掉所有不能和鼠标互动的组件
    if (PikaInventorySlotArrayBox)
        PikaBoxSizeArray.Add(PikaInventorySlotArrayBox);
    if (PikaGameOpenMenu)
        PikaBoxSizeArray.Add(PikaGameOpenMenu);
    if (PikaCharacterInfoArray)
        PikaBoxSizeArray.Add(PikaCharacterInfoArray);
    if (PikaGameCountArray)
        PikaBoxSizeArray.Add(PikaGameCountArray);
    if (PikaGameErrorHintArray)
        PikaBoxSizeArray.Add(PikaGameErrorHintArray);

}

void UPikaMainInterface::PikaLogPrintf(FString MyString)
{
    PikaLog.Broadcast(MyString);
}

void UPikaMainInterface::PikaDrawUIToScreen()
{
    ///////////////////////////////////控件///////////////////////////////////////
    //InventorySlotArray = Cast<USizeBox>(GetWidgetFromName(InventorySlotArrayName));
    //WidgetInformationArray = Cast<USizeBox>(GetWidgetFromName(WidgetInformationArrayName));
    //GameMenuArray = Cast<USizeBox>(GetWidgetFromName(GameMenuName));
    //GamePromptErrorArray = Cast<USizeBox>(GetWidgetFromName(GamePromptErrorName));
    //GameCountArray = Cast<USizeBox>(GetBlueprintWidget(GameCountName));
    //BoxSaveGameArray = Cast<UWidgetSwitcher>(GetBlueprintWidget(BoxSaveGameArrayName));
    //BoxGameWarninArray = Cast<USizeBox>(GetWidgetFromName(BoxGameWarningArrayName));
    //BoxCharacterList = Cast<USizeBox>(GetWidgetFromName(BoxCharacterButtonName));
    //BoxKeyList = Cast<USizeBox>(GetBlueprintWidget(BoxKeyListName));
    //ConditionsArray = Cast<USizeBox>(GetWidgetFromName(ConditionsArrayName));
    PikaAttributeInfoArray = Cast<USizeBox>(GetWidgetFromName(PikaAttributeInfoArrayName));
    //APointOnScreen = Cast<UImage>(GetBlueprintWidget(APointOnScreenName));
    //////////////////////////////////////////////////////////////////////////
}

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

    //获取角色数据并在界面上显示
    if (PikaUIInventoryPtr && PikaUITowerInfoPtr)
    {
        if (PikaUIInventoryPtr->PikaGetNewInventorySlot() && PikaUIInventoryPtr->PikaGetNewInventorySlot()->PikaBuildingTowerStruct.PikaIsActive())
        {
            //只要是我们的CharacterInformationWidget的值发生改变,才会显示改变的值
            if (!PikaTempCharacterInformationWidget || !(&PikaUIInventoryPtr->PikaGetNewInventorySlot()->PikaBuildingTowerStruct.PikaCharacterInformationWidget == PikaTempCharacterInformationWidget))
            {
                PikaUITowerInfoPtr->PikaCharacterInformationWidget = &PikaUIInventoryPtr->PikaGetNewInventorySlot()->PikaBuildingTowerStruct.PikaCharacterInformationWidget;
                //作为临时判断的界面信息
                PikaTempCharacterInformationWidget = &PikaUIInventoryPtr->PikaGetNewInventorySlot()->PikaBuildingTowerStruct.PikaCharacterInformationWidget;
                //更新界面显示
                PikaUITowerInfoPtr->PikaUpdateCharacterInformation();
            }
        }
    }

}

//onclick事件是通过父类UPikaUIWidgetBase里的PikaGetButtonFromBlueprint方法绑定的
void UPikaMainInterface::PikaOnClickedWidget(UWidget* MyWidget)
{
    if (MyWidget == PikaGameOpenMenu) 
    {
        if (PikaUIGameMenuPtr && PikaGetPlayerController())
        {
            PikaUIGameMenuPtr->SetVisibility(ESlateVisibility::Visible);
            PikaShieldedInteractiveOperation(false);
            //设置全局时间
            if (PikaGetGameInstance())
            {
                //设置全局时间的增量为0,默认值为0
                PikaGetGameInstance()->PikaSetGlobalGameTime();
            }
        }
    }
}

void UPikaMainInterface::PikaShieldedInteractiveOperation(bool InVisibility)
{
    for (UWidget* ElementWidger : PikaBoxSizeArray) 
    {
        ElementWidger->SetIsEnabled(InVisibility);
    }

    //锁住CD更新或继续CD更新
    if (PikaUIInventoryPtr)
        PikaUIInventoryPtr->PikaLockSlotCD(!InVisibility);

    //暂停游戏或继续游戏
    PikaGetPlayerController()->SetPause(!InVisibility);
}

void UPikaMainInterface::PikaSetBoxListDisplayOrder(PikaBoxListType::Type BoxListType)
{
    if (PikaBoxSaveGameArray)
        PikaBoxSaveGameArray->SetActiveWidgetIndex(BoxListType);
}

PikaBoxListType::Type UPikaMainInterface::PikaGetBoxListDisplayOrder() const
{
    if (PikaBoxSaveGameArray)
        return (PikaBoxListType::Type)PikaBoxSaveGameArray->GetActiveWidgetIndex();
    return (PikaBoxListType::Type)INDEX_NONE;
}

void UPikaMainInterface::PikaSetWarningWindowsState(bool IsVisibles, float StateTime /*= 0*/, FString MyWidget /*= ""*/, TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType /*= PikaEButtonSureType::BST_None*/)
{
    if (IsVisibles)
    {
        //显示警告框
        PikaGameWarningPtr->PikaSetText(MyWidget);
        PikaBoxGameWarninArray->SetVisibility(ESlateVisibility::Visible);
    }
    else
    {
        PikaBoxGameWarninArray->SetVisibility(ESlateVisibility::Hidden);
    }
}

bool UPikaMainInterface::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
    Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
    bool PikaIsDrop = false;

    return PikaIsDrop;
}

PikaMonster.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaCharacterBase.h"
#include "PikaBulletBase.h"
#include "PikaMonster.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaMonster : public APikaCharacterBase
{
    GENERATED_BODY()
    
public:
    //怪物的攻击逻辑
    virtual void PikaAttackTarget(AActor * Target) override;
    //接收对方的伤害
    virtual float PikaGetDefenceDamage(AActor * OtherActor);
    
    //子弹
    APikaBulletBase * PikaMyBullet;
    //怪物死亡函数
    virtual void PikaDestoryCharacter(float DelayTime = 0) override;

    virtual void BeginPlay() override;
};

PikaMonster.cpp

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

#include "Public/PikaMonster.h"
#include "Kismet/GameplayStatics.h"
#include "Public/PikaMonsterAIController.h"



//怪物的攻击逻辑,攻击目标Target由行为树传递过来
void APikaMonster::PikaAttackTarget(AActor * Target)
{
    Super::PikaAttackTarget(Target);

    if (PikaCharacterBullet && GetWorld() && GetSpawnPoint())
    {
        APikaCharacterBase * PikaEnemyTower = Cast<APikaCharacterBase>(Target);
        FVector PikaBulletLocation = GetSpawnPoint()->GetComponentLocation();
        //FRotator PikaBuletRotator = GetSpawnPoint()->GetComponentRotation();
        //销毁上一次的子弹
        /*if (PikaMyBullet)
        {
            PikaMyBullet->Destroy(true);
            PikaMyBullet = NULL;
        }*/
        PikaMyBullet = GetWorld()->SpawnActor<APikaBulletBase>(PikaCharacterBullet, PikaBulletLocation, FRotationMatrix::MakeFromX(PikaEnemyTower->GetHomingPoint()->GetComponentLocation() - PikaBulletLocation).Rotator());
        if (PikaMyBullet) 
        {
            //将自己赋值到子弹的施法者里
            PikaMyBullet->PikaSetCasterCharacter(this);
            //跟踪子弹
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Track)
            {
                APikaCharacterBase * PikaMyTarget = (APikaCharacterBase*)Target;
                if (PikaMyTarget)
                {
                    //跟踪加速度大小
                    PikaMyBullet->GetProjectleMovment()->HomingAccelerationMagnitude = 4000.f;
                    //跟踪组件
                    PikaMyBullet->GetProjectleMovment()->HomingTargetComponent = PikaMyTarget->GetHomingPoint();
                    //绑定伤害特效
                    /*if (PikaMyBullet->PikaDamageParticle)
                    {
                        UGameplayStatics::SpawnEmitterAttached(PikaMyBullet->PikaDamageParticle, PikaMyTarget->GetHomingPoint());
                    }*/
                }

                return;
            }

            //直线飞行子弹
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Line)
            {
                return;
            }

            //范围攻击型子弹
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Range)
            {
                return;
            }

            //闪电型攻击
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Chain)
            {
                
                return;
            }
        }
    }
}

float APikaMonster::PikaGetDefenceDamage(AActor * OtherActor)
{
    return 0;
}

void APikaMonster::PikaDestoryCharacter(float DelayTime)
{
    //上传死亡的数据
    if (PikaGetPlayerController())
        PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData.PikaKillSoldierNumber++;
    Super::PikaDestoryCharacter(DelayTime);

    APikaMonsterAIController * PikaMonAICon = Cast<APikaMonsterAIController>(Controller);
    //告诉行为树角色已死亡
    if (PikaMonAICon)
    {
        PikaMonAICon->PikaSetCharacterDeathInfoSendBehaviorTree(true);
    }
}

void APikaMonster::BeginPlay()
{
    Super::BeginPlay();
    //将塔的Team设置为true,初始化时为false
    if (PikaCharDataPre)
        PikaCharDataPre->PikaCharBaseData.PikaTeam = false;
}

PikaMonsterAIController.h

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

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "BehaviorTree/BehaviorTree.h"
#include "Public/PikaTowers.h"
#include "PikaMonsterAIController.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaMonsterAIController : public AAIController
{
    GENERATED_BODY()

    //创建行为树指针
    UPROPERTY(EditDefaultsOnly , Category = "PikaBehaviorTree")
    UBehaviorTree * PikaMonsterBeahaviorTreePre;
    
    UPROPERTY(EditDefaultsOnly, Category = "PikaBehaviorTree")
    FName  PikaDeathName;


public:
    virtual void BeginPlay() override;

    //寻找目标
    AActor * PikaFindTarget();

    //获取随机点
    FVector PikaGetRandomLoaction();
    
    //将角色的死亡信息传递到行为树
    void PikaSetCharacterDeathInfoSendBehaviorTree(bool BoolDeath);

    APikaTowers* PikaGetRecentlyTowers(TArray<APikaTowers*>MyTowersArr);
};

PikaMonsterAIController.cpp

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

#include "Public/PikaMonsterAIController.h"
#include "Public/PikaCharacterBase.h"
#include "EngineUtils.h"
#include "AI/Navigation/NavigationSystem.h"
#include "BehaviorTree/BlackboardComponent.h"

void APikaMonsterAIController::BeginPlay()
{
    Super::BeginPlay();
    //执行行为树
    if (PikaMonsterBeahaviorTreePre)
    {
        RunBehaviorTree(PikaMonsterBeahaviorTreePre);
    }
    //初始化死亡状态
    PikaSetCharacterDeathInfoSendBehaviorTree(false);
}

//寻找目标
AActor * APikaMonsterAIController::PikaFindTarget()
{
    if (!GetWorld() || !GetPawn()) { return NULL; }
    //存储主塔
    TArray<APikaTowers*> PikaTargetMainTarray;
    //存储需要攻击的目标
    TArray<APikaTowers*> PikaTargetTarray;

    APikaCharacterBase * PikaPawn = Cast<APikaCharacterBase>(GetPawn());
    //获取整个场景中APikaCharacterBase类的全部实例
    if (PikaPawn)
    {
        for (TActorIterator<APikaTowers>it(GetWorld() , APikaTowers::StaticClass()); it; ++it)
        {
            APikaTowers * PikaTheCharacter = *it;
            if (PikaTheCharacter && PikaTheCharacter->PikaIsActive())
            {
                if (PikaTheCharacter->PikaGetTowersType() == EPikaTowersType::PikaTOWERS_MainTowers)
                {
                    PikaTargetMainTarray.Add(PikaTheCharacter);
                }
                else if (PikaTheCharacter->PikaGetTowersType() == EPikaTowersType::PikaTOWERS_Towers)
                {
                    PikaTargetTarray.Add(PikaTheCharacter);
                }
            }
        }
    }
    //寻找离自己最近的主塔和普通塔,有主塔优先返回主塔为目标塔
    APikaTowers* PikaMainTowers = PikaGetRecentlyTowers(PikaTargetMainTarray);
    APikaTowers* PikaNormalTowers = PikaGetRecentlyTowers(PikaTargetTarray);

    if (PikaMainTowers)
    {
        return PikaMainTowers;
    }
    else if (PikaNormalTowers)
    {
        return PikaNormalTowers;
    }
    return NULL;
}

//获取随机点
FVector APikaMonsterAIController::PikaGetRandomLoaction()
{
    FVector PikaRandomPoint = FVector::ZeroVector;
    if (GetWorld()) 
    {
        //第四个参数表示在多大范围内进行随机
        UNavigationSystem::K2_GetRandomPointInNavigableRadius(GetWorld() , GetPawn()->GetActorLocation() , PikaRandomPoint , 2000.f);
    }

    return PikaRandomPoint;
}

//将角色的死亡信息传递到行为树
void APikaMonsterAIController::PikaSetCharacterDeathInfoSendBehaviorTree(bool BoolDeath)
{
    UBlackboardComponent * PikaMyBlackBoard = GetBlackboardComponent();
    if (PikaMyBlackBoard)
    {
        PikaMyBlackBoard->SetValueAsBool(PikaDeathName, BoolDeath);
    }
}

APikaTowers* APikaMonsterAIController::PikaGetRecentlyTowers(TArray<APikaTowers*>MyTowersArr)
{
    //寻找离自己最近的敌对目标
    if (MyTowersArr.Num() > 0)
    {
        float PikaTowerTargetDistance = 999999;
        int PikaTowerID = -2;
        //获取自己的位置
        FVector PikaMyLocation = GetPawn()->GetActorLocation();
        for (int32 i = 0; i < MyTowersArr.Num(); i++)
        {
            APikaTowers * PikaTowerChar = MyTowersArr[i];
            if (PikaTowerChar)
            {
                FVector PikaTowerLocation = PikaTowerChar->GetActorLocation();
                //距离
                FVector TempVector = PikaTowerLocation - PikaMyLocation;
                float PikaTowerAndMonsterDistance = TempVector.Size();
                if (PikaTowerAndMonsterDistance < PikaTowerTargetDistance)
                {
                    //获取容器中最近的那个
                    PikaTowerID = i;
                    //和下一个塔的位置做比较,寻找最近的那个
                    PikaTowerTargetDistance = PikaTowerAndMonsterDistance;
                }
            }
        }
        //找到的离自己最近的塔
        if (PikaTowerID > -1)
        {
            return MyTowersArr[PikaTowerID];
        }

    }
    return NULL;
}

PikaMonsterBTServiceFindTarget.h

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

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTService.h"
#include "PikaCharacterBase.h"
#include "PikaMonsterBTServiceFindTarget.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaMonsterBTServiceFindTarget : public UBTService
{
    GENERATED_BODY()

    APikaCharacterBase * PikaTargetTower;
    //是否终止循环
    bool PikaIsStop;
public:
    virtual void InitializeFromAsset(UBehaviorTree & Asset) override;

    /** update next tick interval
     * this function should be considered as const (don't modify state of object) if node is not instanced! */
    virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
    //下面的两个结构体会通过行为树的selector里选择和黑板对应的变量
    //tower
    UPROPERTY(EditAnywhere, Category = "PikaBlackBoard")
    struct FBlackboardKeySelector PikaBlackBoardKeyTarget;
    //tower离Monster之间的距离
    UPROPERTY(EditAnywhere, Category = "PikaBlackBoard")
    struct FBlackboardKeySelector PikaBlackBoardKeyDistance;

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

PikaMonsterBTServiceFindTarget.cpp

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

#include "PikaMonsterBTServiceFindTarget.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Public/PikaMonsterAIController.h"

void UPikaMonsterBTServiceFindTarget::InitializeFromAsset(UBehaviorTree & Asset)
{
    Super::InitializeFromAsset(Asset);

    PikaIsStop = false;
}

void UPikaMonsterBTServiceFindTarget::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
    Super::TickNode(OwnerComp , NodeMemory , DeltaSeconds);

    UBehaviorTreeComponent & PikaBehaveiorTree = OwnerComp;
    APikaMonsterAIController * PikaMonsAIController = Cast<APikaMonsterAIController>(PikaBehaveiorTree.GetOwner());
    UBlackboardComponent * PikaBlackBoard = OwnerComp.GetBlackboardComponent();

    if (!PikaBlackBoard) { return; }
    if (PikaMonsAIController)
    {
        //目标塔为空时
        if (!PikaTargetTower) 
        {
            //寻找离自己最近的目标塔
            PikaTargetTower = Cast<APikaCharacterBase>(PikaMonsAIController->PikaFindTarget());
            //存储目标塔到黑板里
            //PikaBlackBoard->SetValueAsObject(PikaBlackBoardKeyTarget.SelectedKeyName, PikaTargetTower);
        }
        else 
        {
            //目标塔死亡后,重新寻找目标
            if (PikaTargetTower->PikaCharDataPre->PikaCharBaseData.PikaDeath)
            {
                //PikaIsStop = false;
                PikaTargetTower = NULL;
            }
        }
        //存储目标塔到黑板里
        PikaBlackBoard->SetValueAsObject(PikaBlackBoardKeyTarget.SelectedKeyName, PikaTargetTower);
        //实时获取距离
        if (PikaTargetTower)
        {
            //AI自己的位置
            FVector PikaLocation = PikaMonsAIController->GetPawn()->GetActorLocation();
            FVector PikaDsitance = PikaLocation - PikaTargetTower->GetActorLocation();
        
            PikaBlackBoard->SetValueAsFloat(PikaBlackBoardKeyDistance.SelectedKeyName, PikaDsitance.Size());
            
        }
    }
}

PikaToolRangeOfCharacter.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "Components/TimelineComponent.h"
#include "PikaToolRangeOfCharacter.generated.h"


UCLASS()
class PIKAGODSTONE_API APikaToolRangeOfCharacter : public AActor
{
    GENERATED_BODY()
    //碰撞盒子
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBulletBase", meta = (AllowPrivateAccess = "true"))
    class USphereComponent * PikaBoxDamage;
    
public:    
    // Sets default values for this actor's properties
    APikaToolRangeOfCharacter();

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

    //范围攻击,该曲线由蓝图载入
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    UCurveFloat * PikaCurveFloat;
    UPROPERTY()
    FTimeline PikaRangeAttackTimeLine;
    //范围攻击球体的半径
    UPROPERTY(EditDefaultsOnly, Category = "PikaBullet")
    float PikaBoxDamageTargetRadius;
    UFUNCTION()
    void PikaTimeLineRangeAttack(float PikaValue);
    UFUNCTION()
    void PikaTimeLineFinished();
    
    //发起者(怪物死亡后,将自身的经验添加到离自己最近的塔上)
    UPROPERTY()
    AActor * PikaCastCharacter;
    //将检测到的敌人加入到容器
    TArray<AActor*> PikaCharacterArray;
};

PikaToolRangeOfCharacter.cpp

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

#include "Public/PikaToolRangeOfCharacter.h"
#include "Engine/Engine.h"
#include "Public/PikaCharacterBase.h"


// Sets default values
APikaToolRangeOfCharacter::APikaToolRangeOfCharacter()
{
     // 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;

    PikaBoxDamage = CreateDefaultSubobject<USphereComponent>(TEXT("PikaBoxDamage"));
    PikaBoxDamage->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); 

}


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

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

    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 APikaToolRangeOfCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

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

void APikaToolRangeOfCharacter::PikaBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    if (!OtherActor || !PikaCastCharacter) 
    {
        return;
    }
    APikaCharacterBase * PikaTargetActor = Cast<APikaCharacterBase>(OtherActor);
    APikaCharacterBase * PikaCasterActor = Cast<APikaCharacterBase>(PikaCastCharacter);
    if (PikaTargetActor && PikaCasterActor)
    {
        if (PikaTargetActor->PikaIsActive() && (PikaTargetActor->PikaGetTeam() == PikaCasterActor->PikaGetTeam())) 
        {
            PikaCharacterArray.AddUnique(PikaTargetActor);
        }
    }
}

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

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

void APikaToolRangeOfCharacter::PikaTimeLineFinished()
{
    APikaCharacterBase * PikaCasterActor = Cast<APikaCharacterBase>(PikaCastCharacter);
    if (PikaCasterActor)
    {
        PikaCasterActor->PikaUpdateLevelCharacterArray(PikaCharacterArray);
    }

    Destroy(true);
}

PikaTowerAIController.h

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

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "PikaTowerAIController.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaTowerAIController : public AAIController
{
    GENERATED_BODY()
};

PikaTowerAIController.cpp

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

#include "Public/PikaTowerAIController.h"

PikaTowerDoll.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "Components/SphereComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "PikaTowerDoll.generated.h"

class APikaGodStonePlayerController;

//记录空间信息
USTRUCT()
struct FPikaComponentSpaceInfo
{
    GENERATED_BODY()
public:
    UPROPERTY()
    TArray<FVector> PikaComponentSize;

    UPROPERTY()
    TArray<FVector> PikaComponentLocation;

    UPROPERTY()
    TArray<FRotator> PikaComponentRotation;

    FPikaComponentSpaceInfo() 
    {
        PikaComponentSize.Empty();
        PikaComponentLocation.Empty();
        PikaComponentRotation.Empty();
    }
    FPikaComponentSpaceInfo& operator=(const FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo) 
    {
        PikaComponentSize = MyPikaComponentSpaceInfo.PikaComponentSize;
        PikaComponentLocation = MyPikaComponentSpaceInfo.PikaComponentLocation;
        PikaComponentRotation = MyPikaComponentSpaceInfo.PikaComponentRotation;

        return *this;
    }

    //判断该序列号是不是在该容器内有效
    bool PikaIsValuable(int32 ID)
    {
        return (PikaComponentLocation.IsValidIndex(ID) && PikaComponentSize.IsValidIndex(ID) && PikaComponentRotation.IsValidIndex(ID)) ? true : false;
    }

    //获取FComponentSpaceInformation最大的数量
    //获取PikaComponentLocation.Num()、PikaComponentSize.Num()、PikaComponentLocation.Num()里的最大值
    int32 PikaGetMax()
    {
        return(PikaComponentLocation.Num() >= PikaComponentSize.Num() ? PikaComponentLocation.Num() : PikaComponentSize.Num()) >= PikaComponentRotation.Num() ? (PikaComponentLocation.Num() >= PikaComponentSize.Num() ? PikaComponentLocation.Num() : PikaComponentSize.Num()) : PikaComponentRotation.Num();
    }
};

UCLASS()
class PIKAGODSTONE_API APikaTowerDoll : public AActor
{
    GENERATED_BODY()
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttrubte", meta = (AllowPrivateAccess = "true"))
    class USceneComponent * PikaHomingPoint;
    //运行建造范围检测
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttrubte", meta = (AllowPrivateAccess = "true"))
    class USphereComponent * PikaAllowBuildRange;
    
    //StaticMeshComponent组件集
    TArray<UStaticMeshComponent*> PikaMeshComponentArray;
    //StaticMeshComponent基础材质集
    TArray<UMaterialInstanceDynamic*> PikaTowerDollAllMatrial;
    //指向Doll材质的指针
    UMaterialInterface* PikaTowerDollMaterial;

    //本地的游戏控制
    APikaGodStonePlayerController* PikaLocalPlayerController;
public:    
    // Sets default values for this actor's properties
    APikaTowerDoll();
    
    //将mesh添加到我们的MeshComponents中
    void PikaSetMeshAddToMeshComponet(TArray<UStaticMesh*> MyMesh , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo);
    void PikaSetMeshColor(bool PermitConstruction);
    
    virtual void Destroyed() override;

    void PikaSetController(APikaGodStonePlayerController* OurPlayerController) { PikaLocalPlayerController = OurPlayerController; }

    //测试打印函数
    void PikaPrint(FString PikaStr);

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

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

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

    APikaGodStonePlayerController* PikaGetTowerDefencePlayerController() { return PikaLocalPlayerController; }

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

private:
    //初始化MeshComponent数量
    void PikaSetMeshComponentSize(int32 ComponentNumber , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo);
};

PikaTowerDoll.cpp

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

#include "Public/PikaTowerDoll.h"
#include "ConstructorHelpers.h"
#include "Components/BoxComponent.h"


// Sets default values
APikaTowerDoll::APikaTowerDoll()
{
     // 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;

    static ConstructorHelpers::FObjectFinder<UMaterialInterface> PikaMyMaterial = (TEXT("/Game/TowerAI/TowerDollMat/MAT_TowersDoll"));
    PikaTowerDollMaterial = PikaMyMaterial.Object;
    PikaHomingPoint = CreateDefaultSubobject<USceneComponent>(TEXT("PikaTowerDollHominPoint"));
    PikaAllowBuildRange = CreateDefaultSubobject<USphereComponent>(TEXT("PikaTowerAllowBuildRange"));
    if (PikaHomingPoint && PikaAllowBuildRange) 
    {
        RootComponent = PikaHomingPoint;
        PikaAllowBuildRange->AttachToComponent(PikaHomingPoint, FAttachmentTransformRules::KeepRelativeTransform);
        //设置通道类型,名字为DefaultEngine.ini文件里通道的Name="PikaAllowBuild"
        PikaAllowBuildRange->SetCollisionProfileName("PikaAllowBuild");
        PikaAllowBuildRange->SetSphereRadius(250.0f);
        PikaAllowBuildRange->SetHiddenInGame(true);
    }
    
}

//获取Mesh的数量
void APikaTowerDoll::PikaSetMeshComponentSize(int32 ComponentNumber , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo)
{
    if (PikaHomingPoint) 
    {
        for (int32 i = 0; i < ComponentNumber; i++)
        {
            FString PikaStaticMeshComponentName = "Mesh_" + FString::FromInt(i);
            UStaticMeshComponent* PikaMyMeshComponent = NewObject<UStaticMeshComponent>(this , *PikaStaticMeshComponentName);
            if (PikaMyMeshComponent && MyPikaComponentSpaceInfo.PikaIsValuable(i))
            {
                PikaMyMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
                PikaMyMeshComponent->SetRelativeLocation(MyPikaComponentSpaceInfo.PikaComponentLocation[i]);
                PikaMyMeshComponent->SetRelativeRotation(MyPikaComponentSpaceInfo.PikaComponentRotation[i]);
                PikaMyMeshComponent->SetRelativeScale3D(MyPikaComponentSpaceInfo.PikaComponentSize[i]);
                PikaMyMeshComponent->AttachToComponent(PikaHomingPoint, FAttachmentTransformRules::KeepRelativeTransform);
                PikaMeshComponentArray.Add(PikaMyMeshComponent);
                //注册组件
                PikaMyMeshComponent->RegisterComponent();
            }
        }
    }
    
}

void APikaTowerDoll::PikaSetMeshAddToMeshComponet(TArray<UStaticMesh*> MyMesh  , FPikaComponentSpaceInfo& MyPikaComponentSpaceInfo)
{
    PikaSetMeshComponentSize(MyMesh.Num() , MyPikaComponentSpaceInfo);
    int32 i = 0;
    TArray<UMaterialInterface*> PikaTempMaterialInterfaceArray;
    if (PikaTowerDollMaterial) 
    {
        for (UStaticMesh* SimMesh : MyMesh)
        {
            if(PikaMeshComponentArray[i])
                PikaMeshComponentArray[i]->SetStaticMesh(SimMesh);
            //替换成我们自己的材质
            for (int32 MatIndex = 0 ; MatIndex < PikaMeshComponentArray[i]->GetNumMaterials() ; MatIndex++)
            {
                PikaMeshComponentArray[i]->SetMaterial(MatIndex , PikaTowerDollMaterial);
            }
            //获取所有材质
            for (int32 MeshIndex = 0 ; MeshIndex < PikaMeshComponentArray[i]->GetMaterials().Num(); MeshIndex++)
            {
                UMaterialInstanceDynamic* PikaMID = PikaMeshComponentArray[i]->CreateAndSetMaterialInstanceDynamic(MeshIndex);
                if (PikaMID)
                    PikaTowerDollAllMatrial.Add(PikaMID);
            }
            i++;
        }
    }
    
}

void APikaTowerDoll::PikaSetMeshColor(bool PermitConstruction)
{
    if (PermitConstruction)
    {
        for (UMaterialInstanceDynamic* PikaMyDynamicMat : PikaTowerDollAllMatrial)
        {
            FString test1 = PikaMyDynamicMat->GetName();
            if (PikaMyDynamicMat->GetName() != "None" && PikaMyDynamicMat->GetName() != "Invalid")
                //显示绿色,ColorStatus为TowerAI下MAT_TowersDoll材质里,节点参数化后的变量名
                PikaMyDynamicMat->SetScalarParameterValue("ColorStatus" , 0);
        }
    }
    else 
    {
        for (UMaterialInstanceDynamic* PikaMyDynamicMat : PikaTowerDollAllMatrial)
        {
            if (PikaMyDynamicMat->GetName() != "None" && PikaMyDynamicMat->GetName() != "Invalid")
                //显示红色,ColorStatus为TowerAI下MAT_TowersDoll材质里,节点参数化后的变量名
                PikaMyDynamicMat->SetScalarParameterValue("ColorStatus", 1);
        }
    }
}

void APikaTowerDoll::Destroyed()
{
    for (UStaticMeshComponent* MyStaticMeshComponent : PikaMeshComponentArray) 
    {
        if (MyStaticMeshComponent)
            MyStaticMeshComponent->DestroyComponent(true);
        MyStaticMeshComponent = NULL;
    }
    if (PikaTowerDollMaterial)
        PikaTowerDollMaterial->ConditionalBeginDestroy();
    PikaTowerDollMaterial = NULL;
    PikaAllowBuildRange->DestroyComponent(true);
    
    Super::Destroyed();
}

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

void APikaTowerDoll::PikaDollAllowBuildBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    if (OtherComp->IsA(UBoxComponent::StaticClass()) && PikaGetTowerDefencePlayerController())
    {
        if (OtherComp->GetCollisionObjectType() == ECollisionChannel::ECC_GameTraceChannel6)
        {
            PikaSetMeshColor(false);
            PikaGetTowerDefencePlayerController()->PikaSetAllowBuillingTowers(false);
        }
    }
}

void APikaTowerDoll::PikaDollAllowBuildEndOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{
    if (OtherComp->IsA(UBoxComponent::StaticClass()) && PikaGetTowerDefencePlayerController())
    {
        if (OtherComp->GetCollisionObjectType() == ECollisionChannel::ECC_GameTraceChannel6)
        {
            PikaSetMeshColor(true);
            PikaGetTowerDefencePlayerController()->PikaSetAllowBuillingTowers(true);
        }
    }
}

// Called when the game starts or when spawned
void APikaTowerDoll::BeginPlay()
{
    Super::BeginPlay();
    if (PikaAllowBuildRange) 
    {
        PikaAllowBuildRange->OnComponentBeginOverlap.AddUniqueDynamic(this, &APikaTowerDoll::PikaDollAllowBuildBeginOverlapping);
        PikaAllowBuildRange->OnComponentEndOverlap.AddUniqueDynamic(this, &APikaTowerDoll::PikaDollAllowBuildEndOverlapping);
    }
}

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

PikaTowerIcon.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "Public/PikaUI_InventorySlot.h"
#include "PikaTowerIcon.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaTowerIcon : public UPikaUIWidgetBase
{
    GENERATED_BODY()
    //控件实例的名字
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerImageName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerCDImageName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerPrepareBuildingNumTextName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerCompletionNumTextName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerCDValueTextName;

    //CD材质的名字,该值为CD材质里参数化的变量ControllerCD
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaCDMatName;

public:
    void PikaInitTowreIcon(UTexture2D* TowresIcon);
    //相当于BeginPlay,用于解决BeginPlay执行时,有些资源可能还没加载完成的问题
    //需要手动调用
    virtual void PikaDrawUIToScreen() override;

    //每一个物品栏对应的单个数据
    FPikaBulidingTowres PikaBuildingTowerStructIcon;

    //打印测试
    void PikaPrint(FString piksStr);
private:
    //控件实例
    UImage* PikaTowerImage;
    UImage* PikaTowerCDImage;
    UTextBlock* PikaTowerPrepareBuildingNumText;
    UTextBlock* PikaTowerCompletionNumText;
    UTextBlock* PikaTowerCDValueText;
    
    //显示字体和CD
    void PikaDisplayNumber(UTextBlock* MyTextNumberBlock, int32 MyTextNumber);
};

PikaTowerIcon.cpp

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

#include "Public/PikaTowerIcon.h"

void UPikaTowerIcon::PikaInitTowreIcon(UTexture2D* TowresIcon)
{
    if (PikaTowerImage && TowresIcon)
    {
        PikaTowerImage->SetBrushFromTexture(TowresIcon);
        PikaTowerImage->SetVisibility(ESlateVisibility::Visible);
    }
}

void UPikaTowerIcon::PikaDrawUIToScreen()
{
    PikaTowerImage = Cast<UImage>(PikaqiuGetBlueprintWidget(PikaTowerImageName));
    PikaTowerCDImage = Cast<UImage>(PikaqiuGetBlueprintWidget(PikaTowerCDImageName));
    PikaTowerPrepareBuildingNumText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowerPrepareBuildingNumTextName));
    PikaTowerCompletionNumText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowerCompletionNumTextName));
    PikaTowerCDValueText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowerCDValueTextName));

    //获取动态材质实例
    if (PikaTowerCDImage) 
    {
        UMaterialInstanceDynamic* PikaCDMaterialDynamic = PikaTowerCDImage->GetDynamicMaterial();
        if (PikaCDMaterialDynamic)
            PikaCDMaterialDynamic->SetScalarParameterValue(PikaCDMatName, PikaBuildingTowerStructIcon.PikaGetTowerConstructionTimePercentage());
    }
        
    //填充PikaTowerImage
    PikaInitTowreIcon(PikaBuildingTowerStructIcon.PikaTexture);

    //填充PikaTowerPrepareBuildingNumText
    if (PikaTowerPrepareBuildingNumText)
        PikaDisplayNumber(PikaTowerPrepareBuildingNumText , PikaBuildingTowerStructIcon.PikaTowerPerpareBuildingNumber);
    
    //填充PikaTowerCompletionNumText
    if (PikaTowerCompletionNumText)
        PikaDisplayNumber(PikaTowerCompletionNumText, PikaBuildingTowerStructIcon.PikaTowerConstructionNumber);
    
    //填充PikaTowerPrepareBuildingNumText
    if (PikaTowerCDValueText)
        PikaDisplayNumber(PikaTowerCDValueText, PikaBuildingTowerStructIcon.PikaCurrentConstructionTowerCD);
}

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

void UPikaTowerIcon::PikaDisplayNumber(UTextBlock* MyTextNumberBlock, int32 MyTextNumber)
{
    //物品栏上有道显示文本,没有道具则不显示文本
    if (MyTextNumber < 1 || !PikaBuildingTowerStructIcon.PikaIsActive())
    {
        MyTextNumberBlock->SetVisibility(ESlateVisibility::Hidden);
    }
    else
    {
        //格式输出里,%02d表示输出格式为00,%03d的输出格式为000
        MyTextNumberBlock->SetText(FText::FromString(FString::Printf(TEXT("%02d"), MyTextNumber)));
        MyTextNumberBlock->SetVisibility(ESlateVisibility::Visible);
    }
}

PikaTowers.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaCharacterBase.h"
#include "PikaBulletBase.h"
#include "Components/SphereComponent.h"
#include "PikaTowers.generated.h"

//塔类型
UENUM()
namespace EPikaTowersType
{
    enum Type
    {
        PikaTOWERS_Towers,
        PikaTOWERS_MainTowers,
        PikaTOWERS_Max
    };
}

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaTowers : public APikaCharacterBase
{
    GENERATED_BODY()

    //Box组件,用于设置产生碰撞的Box
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttack" , meta = (AllowPrivateAccess = "true"))
    class UBoxComponent * PikaBoxCollision;

    //Box组件,用于允许建设的设置
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PikaBaseAttack", meta = (AllowPrivateAccess = "true"))
    class UBoxComponent * PikaAllowBuildBox;

    //怪物数组
    UPROPERTY()
    TArray<APikaMonster*> PikaTarrayMonster;

    //塔类型
    UPROPERTY(EditDefaultsOnly, Category = "PikaTowerType")
    TEnumAsByte<EPikaTowersType::Type> PikaTowersType;

    //攻击范围
    UPROPERTY(EditDefaultsOnly, Category = "PikaTowerType")
    float PikaAattakRange;

    //被动技能范围
    UPROPERTY(EditDefaultsOnly, Category = "PikaTowerType")
    float PikaPassiveSkillRange;

public:
    APikaTowers();
    virtual void BeginPlay() override;

    virtual void Tick(float DeltaTime) override;
    
    //塔攻击逻辑
    virtual void PikaAttackTarget(AActor * Target) override;


    /******************攻击前摇start*********************/
    //攻击前摇时间
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PikaAttackFront")
    float  PikaAttackFrontDelayTimeBP;

    //攻击前摇时间句柄
    UPROPERTY()
    FTimerHandle PikaAttackFrontDelayTimeHandle;

    void PikaAttackType();
    /******************攻击前摇end**********************/

    //塔的持续性攻击,由定时器来模拟Tick
    //virtual void PikaAttackTick() override;
    
    //攻击时间句柄
    FTimerHandle PikaAttackHandle;

    //链击的时间句柄
    FTimerHandle PikaChainHandle;

    float PikaChainSave;

    //是否停止攻击检测
    bool PikaStopAttackCheck;

    //子弹
    APikaBulletBase * PikaMyBullet;

    //闪电攻击时,获取敌人的位置
    bool PikaCheckAttackTick;
    //闪电攻击时,目标位置纠正
    virtual void PiakGetParticlePostionCheckAttackTick();

    APikaCharacterBase * PikaGetTargetActor();

    //碰撞
    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 PikaAllowBuildBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

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

    //防止碰撞多次触发
    AActor * PikaLastActor;

    //链击伤害
    void PikaChainDamage();

    //塔死亡函数
    virtual void PikaDestoryCharacter(float DelayTime = 0) override;

    //获取塔类型
    FORCEINLINE EPikaTowersType::Type PikaGetTowersType() { return PikaTowersType; }

    /**********和范围相关Start**********/
    //获取攻击范围内的敌人
    void PikaBTServiceGetTarget();

    //获取范围内的同伴
    TArray<APikaTowers*> PikaBTServiceGetCompanion();
    /**********和范围相关End**********/

    /**********角色TickStart**********/
    //攻击检测
    virtual void PikaAttackTick() override;

    //被动技能
    //void PikaPassiveSkill(float DeltaTime);
    /**********角色TickEnd**********/

    //当前的时间
    float PikaTimeSet;

    //当前攻击记时
    float PikaCurrentAttackCountTime;
};

PikaTowers.cpp

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

#include "Public/PikaTowers.h"
#include "Public/PikaMonster.h"
#include "Kismet/GameplayStatics.h"
#include "RotationMatrix.h"
#include "EngineUtils.h"





APikaTowers::APikaTowers()
{
    PikaBoxCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaBoxCollision"));
    PikaAllowBuildBox = CreateDefaultSubobject<UBoxComponent>(TEXT("PikaAllowBuildBox"));
    if (PikaBoxCollision && PikaAllowBuildBox)
    {
        PikaBoxCollision->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
        PikaAllowBuildBox->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
        PikaAllowBuildBox->SetCollisionProfileName("PikaAllowBuild");
    }
    
    //是否停止攻击检测
    PikaStopAttackCheck = false;

    PikaMyBullet = NULL;
    PikaCheckAttackTick = false;
    //怪物数组初始化
    PikaTarrayMonster.Empty();

    PikaLastActor = nullptr;

    PikaChainSave = 0;

    //塔的攻击范围
    PikaAattakRange = 820;
    //被动技能范围
    PikaPassiveSkillRange = 1000.f;

    PikaTimeSet = 0;

    PikaAttackFrontDelayTimeBP = 0.1f;

}

void APikaTowers::BeginPlay()
{
    Super::BeginPlay();
    //将塔的Team设置为true,初始化时为false
    if(PikaCharDataPre)
        PikaCharDataPre->PikaCharBaseData.PikaTeam = true;

}


void APikaTowers::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    if (PikaEnemyTarget && PikaMyBullet && PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Chain)
    {
        //子弹攻击的目标
        //PikaMyBullet->GetParticleMesh()->SetBeamTargetPoint(0, PikaEnemyTarget->GetActorLocation(), 0);
        APikaCharacterBase * PikaMyTarget = (APikaCharacterBase*)PikaEnemyTarget;
        if (PikaMyTarget) 
        {
            PikaMyBullet->GetParticleMesh()->SetBeamTargetPoint(0 , PikaMyTarget->GetHomingPoint()->GetComponentLocation() , 0);
        }
    }

    //开始检测范围内周围敌人
    if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGlobalConfigPre)
    {
        if (PikaTimeSet <= PikaGetPlayerController()->PikaGlobalConfigPre->PikaTowersData.PikaTimeInterval)
        {
            PikaTimeSet += DeltaTime;
        }
        else
        {
            PikaTimeSet = 0;
            //获取攻击范围内的敌人
            PikaBTServiceGetTarget();
        }
    }

    if (PikaCharDataPre)
    {
        //开始攻击
        if (PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed != 0)
        {
            PikaCurrentAttackCountTime += DeltaTime;
            //攻击间隔
            float PikaCurrentAttackIntervalTime = 1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed;
            //当前攻击时间如果大于攻击间隔,则开始攻击,并清空当前攻击时间
            if (PikaCurrentAttackCountTime >= PikaCurrentAttackIntervalTime)
            {
                PikaCurrentAttackCountTime = 0;
                PikaAttackTick();
            }
        }

        /*//开始被动技能
        if (CurrentPassiveTime <= PASSIVE_SKILL_MAX_TIME)
        {
            CurrentPassiveTime += DeltaTime;
        }
        else
        {
            PassiveSkill(CurrentPassiveTime);
            CurrentPassiveTime = 0.f;
        }*/
    }
}

void APikaTowers::PikaAttackTarget(AActor * Target)
{
    Super::PikaAttackTarget(Target);
    //攻击动画
    PikaCharacterMontage_Play(PikaCharacterAttackSkillMontage);

    //攻击前摇
    if (GetWorld())
        GetWorld()->GetTimerManager().SetTimer(PikaAttackFrontDelayTimeHandle , this , &APikaTowers::PikaAttackType , PikaAttackFrontDelayTimeBP);
}

void APikaTowers::PikaAttackType()
{
    if (PikaAttackFrontDelayTimeHandle.IsValid())
        GetWorld()->GetTimerManager().ClearTimer(PikaAttackFrontDelayTimeHandle);
    APikaCharacterBase * PikaEnemyTargetBase = (APikaCharacterBase *)PikaEnemyTarget;
    if (PikaCharacterBullet && GetWorld() && GetSpawnPoint() && PikaIsActive())
    {
        FVector PikaBulletLocation = GetSpawnPoint()->GetComponentLocation();
        //FRotator PikaBuletRotator = GetSpawnPoint()->GetComponentRotation();
        //销毁上一次的子弹
        /*if (PikaMyBullet)
        {
            PikaMyBullet->Destroy(true);
            PikaMyBullet = NULL;
        }*/
        PikaMyBullet = GetWorld()->SpawnActor<APikaBulletBase>(PikaCharacterBullet, PikaBulletLocation, FRotationMatrix::MakeFromX(PikaEnemyTargetBase->GetHomingPoint()->GetComponentLocation() - PikaBulletLocation).Rotator());

        if (PikaMyBullet)
        {
            //将自己赋值到子弹的施法者里
            PikaMyBullet->PikaSetCasterCharacter(this);
            //跟踪子弹
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Track)
            {
                APikaCharacterBase * PikaMyTarget = (APikaCharacterBase*)PikaEnemyTarget;
                if (PikaMyTarget)
                {
                    //跟踪加速度大小
                    PikaMyBullet->GetProjectleMovment()->HomingAccelerationMagnitude = 4000.f;
                    //跟踪组件
                    PikaMyBullet->GetProjectleMovment()->HomingTargetComponent = PikaMyTarget->GetHomingPoint();
                    //绑定伤害特效
                    /*if (PikaMyBullet->PikaDamageParticle)
                    {
                        UGameplayStatics::SpawnEmitterAttached(PikaMyBullet->PikaDamageParticle, PikaMyTarget->GetHomingPoint());
                    }*/
                }

                return;
            }

            //直线飞行子弹
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Line)
            {

                return;
            }

            //范围攻击型子弹
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Range)
            {
                //设置范围攻击的转向为0向量
                //PikaMyBullet->SetActorRotation(FRotator::ZeroRotator);
                if (PikaMyBullet->GetParticleMesh())
                {
                    PikaMyBullet->GetParticleMesh()->SetWorldRotation(FRotator::ZeroRotator);
                }
                return;
            }

            //闪电型攻击
            if (PikaMyBullet->PikaBulletTypeBP == PikaBulletType::PikaBULLET_Chain)
            {
                //SetBeamSourcePoint函数里,第一个参数为粒子系统发射器的index,第三个参数为源index
                if (PikaMyBullet->GetParticleMesh())
                {
                    //设置闪电攻击的初始点,目标点写在Tick里
                    PikaMyBullet->GetParticleMesh()->SetBeamSourcePoint(0, PikaBulletLocation, 0);
                    //绑定伤害特效
                    if (PikaMyBullet->PikaDamageParticle && PikaEnemyTargetBase)
                    {
                        UGameplayStatics::SpawnEmitterAttached(PikaMyBullet->PikaDamageParticle, PikaEnemyTargetBase->GetHomingPoint());
                        //0.01表示即时执行该函数,如果换成1/攻击速度,特效上会有延迟
                        //持续费血
                        GetWorld()->GetTimerManager().SetTimer(PikaChainHandle, this, &APikaTowers::PikaChainDamage, 0.01, true);
                    }
                }

                return;
            }
        }
    }
}

//连续攻击
/*void APikaTowers::PikaAttackTick()
{
    Super::PikaAttackTick();
    
    if (PikaAttackHandle.IsValid())
    {
        //清除时间句柄
        GetWorld()->GetTimerManager().ClearTimer(PikaAttackHandle);
        //关闭Tick里的检测
        PikaCheckAttackTick = false;
    }
    //自身如果已经死亡,则返回空
    if (!PikaIsActive()) { return;  }
    //诊断攻击的目标是否死亡
    if (PikaEnemyTarget)
    {
        APikaCharacterBase * PikaIsDeathTarget = (APikaCharacterBase*)PikaEnemyTarget;
        if (PikaIsDeathTarget && PikaIsDeathTarget->PikaCharDataPre->PikaCharBaseData.PikaDeath)
        {
            //目标已死亡,移除死亡目标,并获取新的目标
            PikaTarrayMonster.Remove(PikaIsDeathTarget);
            PikaEnemyTarget = PikaGetTargetActor();
        }
    }

    //是否停止检测攻击
    //if (PikaStopAttackCheck) { return; }
    //没有敌对目标时停止检测
    if (!PikaEnemyTarget) 
    {
        //正在攻击的目标为null,但目标数组里还有值时,重新获取新的攻击目标
        if (PikaTarrayMonster.Num() > 0) 
        {
            PikaEnemyTarget = PikaGetTargetActor();
        }
        else 
        {
            return;
        }
    }

    if (PikaEnemyTarget)
    {
        //有敌对目标,进行攻击
        PikaCheckAttackTick = true;
        PikaAttackTarget(PikaEnemyTarget);
    }

    if (GetWorld())
    {
        float PikaAttackSpeedTime = 1 / (PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed);
        GetWorld()->GetTimerManager().SetTimer(PikaAttackHandle, this, &APikaTowers::PikaAttackTick, PikaAttackSpeedTime, true);
    }
}*/

void APikaTowers::PiakGetParticlePostionCheckAttackTick()
{
    
}

APikaCharacterBase * APikaTowers::PikaGetTargetActor()
{
    if (PikaTarrayMonster.Num() > 0)
    {
        if (1 == PikaTarrayMonster.Num()) 
        {
            return PikaTarrayMonster[0];
        }
        else 
        {
            //返回多个目标中离自己最近的目标
            int32 PikaMonsterIndex = -1;
            FVector PikaMyLocation = GetActorLocation();
            float PikaMyDistance = 999999999999;
            for (int32 i=0 ; i< PikaTarrayMonster.Num(); i++)
            {
                FVector PikaMonsterLocation = PikaTarrayMonster[i]->GetActorLocation();
                FVector PikaTempDistance = PikaMonsterLocation - PikaMyLocation;
                if (PikaTempDistance.Size() < PikaMyDistance) 
                {
                    PikaMyDistance = PikaTempDistance.Size();
                    PikaMonsterIndex = i;
                }
            }
            if (PikaMonsterIndex < 0)
            {
                return NULL;
            }
            else 
            {
                return PikaTarrayMonster[PikaMonsterIndex];
            }
        }
    }
    
    return NULL;
    
}

void APikaTowers::PikaBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    /*if (PikaLastActor == OtherActor)
    {
        PikaLastActor = nullptr;
        return;
    }*/
    //触发碰撞事件的对象是否为APikaMonster类的实例
    /*if (OtherActor->IsA(APikaMonster::StaticClass()))
    {
        APikaCharacterBase * PikaTempActor = (APikaCharacterBase*)OtherActor;
        //添加入侵攻击范围内的敌对目标
        if (PikaTempActor)
        {
            if (PikaTempActor->PikaIsActive()) 
            {
                PikaTarrayMonster.AddUnique(PikaTempActor);
            }
            
            //进入攻击范围的目标,每增加一个就进行一次查找当前离自己最近目标的逻辑
            //诊断攻击的目标是否死亡
            if (PikaEnemyTarget)
            {
                APikaCharacterBase * PikaIsDeathTarget = (APikaCharacterBase*)PikaEnemyTarget;
                if (PikaIsDeathTarget && PikaIsDeathTarget->PikaCharDataPre->PikaCharBaseData.PikaDeath)
                {
                    //目标已死亡,移除死亡目标,并获取新的目标
                    PikaTarrayMonster.Remove(PikaIsDeathTarget);
                    PikaEnemyTarget = PikaGetTargetActor();
                }
            }
            else 
            {
                PikaEnemyTarget = PikaGetTargetActor();
            }
            //PikaLastActor = OtherActor;
            PikaAttackTick();
        }
        
    }*/
    
}

void APikaTowers::PikaEndOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{
    /*if (OtherActor->IsA(APikaMonster::StaticClass()))
    {
        APikaCharacterBase * PikaTempActor = Cast<APikaCharacterBase>(OtherActor);
        //移除入侵攻击范围内的敌对目标
        if (PikaTempActor)
        {
            PikaTarrayMonster.Remove(PikaTempActor);
            //当前的攻击目标离开了攻击范围时,将当前攻击目标的值置为NULL
            if (PikaEnemyTarget == PikaTempActor)
            {
                PikaEnemyTarget = NULL;
            }
            
        }
        
    }*/
}

void APikaTowers::PikaAllowBuildBeginOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{

}

void APikaTowers::PikaAllowBuildEndOverlapping(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{

}

void APikaTowers::PikaChainDamage()
{
    if (!GetWorld()) { return; }
    if (PikaChainHandle.IsValid())
    {
        GetWorld()->GetTimerManager().ClearTimer(PikaChainHandle);
    }
    float PikaTempFloat = (1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed) * 0.2;
    //PikaChainSave += (PikaTempFloat + 0.1);
    //攻击修正
    PikaChainSave += ((1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed)*0.2 + 0.1*(1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed) / (1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeedMin));
    if (PikaEnemyTarget)
    {
        APikaCharacterBase * EnemyTarget = Cast<APikaCharacterBase>(PikaEnemyTarget);
        if (EnemyTarget)
        {
            //0.2表示将伤害分割成5次,1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed*0.2
            EnemyTarget->PikaGetCharacterState(EnemyTarget->PikaGetDefenceDamage(this) * 0.2);
            EnemyTarget->PikaUpdateUMGHealth();
        }
    }
    if (PikaChainSave < (1 / PikaCharDataPre->PikaCharBaseData.PikaAttackSpeed)) 
    {
        GetWorld()->GetTimerManager().SetTimer(PikaChainHandle, this, &APikaTowers::PikaChainDamage, PikaTempFloat, 1);
    }
    else 
    {
        PikaChainSave = 0;
    }
    
}

void APikaTowers::PikaDestoryCharacter(float DelayTime)
{
    //上传死亡的数据
    if (PikaGetPlayerController())
        PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData.PikaTowersDeathNumber++;
    Super::PikaDestoryCharacter(DelayTime);
}

//获取攻击范围内的敌人
void APikaTowers::PikaBTServiceGetTarget()
{
    PikaTarrayMonster.Empty();
    //获取我们全场景的ARuleOfTheCharacter的Actor
    for (TActorIterator<APikaMonster>it(GetWorld(), APikaMonster::StaticClass()); it; ++it)
    {
        APikaMonster* PikaTheCharacter = *it;
        if (PikaTheCharacter)
        {
            if (PikaTheCharacter->PikaIsActive())
            {
                FVector PikaTDistance = PikaTheCharacter->GetActorLocation() - GetActorLocation();
                if (PikaTDistance.Size() <= PikaAattakRange)
                    PikaTarrayMonster.AddUnique(PikaTheCharacter);
            }
        }
    }
}

//获取范围内的同伴
TArray<APikaTowers*> APikaTowers::PikaBTServiceGetCompanion()
{
    TArray<APikaTowers*> PikaTArrayATowers;
    //获取我们全场景的ARuleOfTheCharacter的Actor
    for (TActorIterator<APikaTowers>it(GetWorld(), APikaTowers::StaticClass()); it; ++it)
    {
        APikaTowers* PikaTheCharacter = *it;
        if (PikaTheCharacter && PikaTheCharacter != this)
        {
            if (PikaTheCharacter->PikaIsActive())
            {
                FVector PikaTDistance = PikaTheCharacter->GetActorLocation() - GetActorLocation();
                if (PikaTDistance.Size() <= PikaPassiveSkillRange)
                    PikaTArrayATowers.Add(PikaTheCharacter);
            }
        }
    }
    return PikaTArrayATowers;
}

//攻击检测
void APikaTowers::PikaAttackTick()
{
    Super::PikaAttackTick();

    //关闭检测
    PikaCheckAttackTick = false;

    //自己是否死亡
    if (!PikaIsActive())
    {
        return;
    }

    //检测玩家设定的目标敌人
    if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGetTargetMonster())
    {
        for (APikaMonster* PikaTargetMonsters : PikaTarrayMonster)
        {
            if (PikaTargetMonsters->PikaIsActive())
            {
                if (PikaGetPlayerController()->PikaGetTargetMonster() == PikaTargetMonsters)
                {
                    PikaEnemyTarget = PikaGetPlayerController()->PikaGetTargetMonster();
                    break;
                }
            }
            else if (PikaTargetMonsters == PikaEnemyTarget)
            {
                PikaEnemyTarget = nullptr;
            }
        }
    }

    //检查怪物是否死亡
    if (PikaEnemyTarget)
    {
        //判断该角色是否在范围内
        if ((PikaEnemyTarget->GetActorLocation() - GetActorLocation()).Size() > PikaAattakRange)
        {
            //重新确定目标
            PikaEnemyTarget = PikaGetTargetActor();
        }
        else
        {
            APikaMonster* PikaEnemyTargetPtr = Cast<APikaMonster>(PikaEnemyTarget);
            if (PikaEnemyTargetPtr)
            {
                if (!PikaEnemyTargetPtr->PikaIsActive())
                {
                    //移除目标死亡的怪物
                    PikaTarrayMonster.Remove(PikaEnemyTargetPtr);
                    //重新搜索怪物
                    PikaEnemyTarget = PikaGetTargetActor();
                }
            }
        }
    }
    else
    {
        //重新获取目标
        PikaEnemyTarget = PikaGetTargetActor();
    }

    //有没有目标
    if (PikaEnemyTarget)
    {
        //攻击目标
        PikaAttackTarget(PikaEnemyTarget);
    }
}

PikaUI_InformationWeight.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 "PikaUI_InformationWeight.generated.h"


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

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

    void PikaDrawSubtractionHealthInformation(float DamageValue, float DamagePercentage);

    void PikaDrawGoldformation(int32 GlodValue, float DamagePercentage);

    void PikaDrawAddHealthInformation(float AddHealthValue, float DamagePercentage);
private:
    UTextBlock* PikaHealthTextBlock;
};

PikaUI_InformationWeight.cpp

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

#include "Public/PikaUI_InformationWeight.h"

void UPikaUI_InformationWeight::PikaDrawUIToScreen()
{
    PikaHealthTextBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaHealthTextBlockName));
}

void UPikaUI_InformationWeight::PikaDrawSubtractionHealthInformation(float DamageValue, float DamagePercentage)
{
    int32 PikaMaxFontSize = 50;
    int32 PikaMinFontSize = 16;
    int32 PikaCurrentFontSize = PikaMinFontSize + (PikaMaxFontSize - PikaMinFontSize) * DamagePercentage;
    if (PikaHealthTextBlock && DamageValue != 0)
    {
        PikaHealthTextBlock->SetText(FText::FromString(FString::Printf(TEXT("-%0.f"), DamageValue)));
        PikaHealthTextBlock->Font.Size = PikaCurrentFontSize;

        //设置颜色为红色
        PikaHealthTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor::Red));
    }
}

void UPikaUI_InformationWeight::PikaDrawGoldformation(int32 GlodValue, float DamagePercentage)
{
    int32 PikaMaxFontSize = 50;
    int32 PikaMinFontSize = 16;
    int32 PikaCurrentFontSize = PikaMinFontSize + (PikaMaxFontSize - PikaMinFontSize) * DamagePercentage;
    if (PikaHealthTextBlock)
    {
        PikaHealthTextBlock->SetText(FText::FromString(FString::Printf(TEXT("+%d"), GlodValue)));
        PikaHealthTextBlock->Font.Size = PikaCurrentFontSize;

        //设置颜色为黄色
        PikaHealthTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor::Yellow));
    }
}

void UPikaUI_InformationWeight::PikaDrawAddHealthInformation(float AddHealthValue, float DamagePercentage)
{
    int32 PikaMaxFontSize = 50;
    int32 PikaMinFontSize = 16;
    int32 PikaCurrentFontSize = PikaMinFontSize + (PikaMaxFontSize - PikaMinFontSize) * DamagePercentage;
    if (PikaHealthTextBlock && AddHealthValue != 0)
    {
        PikaHealthTextBlock->SetText(FText::FromString(FString::Printf(TEXT("+%0.f"), AddHealthValue)));
        PikaHealthTextBlock->Font.Size = PikaCurrentFontSize;

        //设置颜色为绿色
        PikaHealthTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor::Green));
    }
}

PikaUI_Inventory.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaUI_InventorySlot.h"
#include "UniformGridPanel.h"
#include "UniformGridSlot.h"
#include "PikaUI_Inventory.generated.h"

class UPikaMainInterface;


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

    //用来载入PikaUI_InventorySlot的蓝图类
    UPROPERTY(EditDefaultsOnly , Category = "PikaUI")
    TSubclassOf<UPikaUI_InventorySlot> PikaInventorySlotClass;
    //从蓝图中获取soltArray的名字
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaSoltArrayInventoryName;
public:
    virtual void PikaDrawUIToScreen() override;
    
    void PikaLayoutInventorySlot(int32 PikaColumn , int32 PikaRow);
    /*********************************响应控制Start***************************************/
    //绑定我们的鼠标中键响应
    //以下两个函数是通过代理绑定的,需要加UFUNCTIO()
    UFUNCTION()
    void PikaSpawnTowerDollPressed(APikaGodStonePlayerController* MyPlayerController);
    UFUNCTION()
    void PikaSpawnTowerDollReleased(APikaGodStonePlayerController* MyPlayerController);
    /*********************************响应控制End***************************************/

    /*********************************临时InventorySlot变量Start***************************************/
    UPikaUI_InventorySlot* GetTempInventorySlot()const;
    void PikaSetTempInventorySlot(UPikaUI_InventorySlot* NewInventorySlot);
    //将InventorySlot置空
    void PikaCancelTempInventorySlot();
    //获取PikaTempInventorySlot
    UPikaUI_InventorySlot* PikaGetNewInventorySlot()const { return PikaTempInventorySlot; }
    /*********************************临时InventorySlot变量End***************************************/

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

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString)override;

    UPikaMainInterface* PikaGetMainInterface() const { return PikaMainInterfacePtr; }

    void PikaSetMainInterface(UPikaMainInterface* NewWidget) {PikaMainInterfacePtr = NewWidget;}

    //锁CD
    void PikaLockSlotCD(bool IsLock);
    
private:
    UUniformGridPanel * PikaSlotArrayInventory;
    TArray<UPikaUI_InventorySlot*> PikaInventorySlotArray;
    UPikaUI_InventorySlot* PikaTempInventorySlot;
    UPikaMainInterface* PikaMainInterfacePtr;
};

PikaUI_Inventory.cpp

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

#include "Public/PikaUI_Inventory.h"
#include "Public/PikaCharacterBase.h"
#include "Engine/Engine.h"

void UPikaUI_Inventory::PikaDrawUIToScreen()
{
    if (!GetWorld()) 
    {
        return;
    }
    PikaSlotArrayInventory = Cast<UUniformGridPanel>(PikaqiuGetBlueprintWidget(PikaSoltArrayInventoryName));
    //布局界面
    if (PikaUIDataPtr) 
    {
        PikaLayoutInventorySlot(PikaUIDataPtr->PikaInventoryColumn , PikaUIDataPtr->PikaInventoryRow);
    }

    //绑定我们的鼠标中建响应
    if (PikaGetPlayerController())
    {
        PikaGetPlayerController()->PikaEventMouseMiddlePressed.AddDynamic(this , &UPikaUI_Inventory::PikaSpawnTowerDollPressed);
        PikaGetPlayerController()->PikaEventFMouseMiddleReleased.AddDynamic(this, &UPikaUI_Inventory::PikaSpawnTowerDollReleased);
    }
}

void UPikaUI_Inventory::PikaLayoutInventorySlot(int32 PikaColumn, int32 PikaRow)
{
    if (PikaInventorySlotClass && PikaSlotArrayInventory)
    {
        for (int32 MyRow = 0 ; MyRow < PikaRow; MyRow++) 
        {
            for (int32 MyColumn=0 ; MyColumn< PikaColumn; MyColumn++)
            {
                UPikaUI_InventorySlot * PikaSlotWidget = CreateWidget<UPikaUI_InventorySlot>(GetWorld() , PikaInventorySlotClass);
                if(PikaSlotWidget)
                {
                    //需要将我们的UMG添加进去
                    UUniformGridSlot * PikaGridSlot = PikaSlotArrayInventory->AddChildToUniformGrid(PikaSlotWidget);
                    if (PikaGridSlot) 
                    {
                        PikaGridSlot->SetColumn(MyColumn);
                        PikaGridSlot->SetRow(MyRow);
                        PikaGridSlot->SetHorizontalAlignment(EHorizontalAlignment::HAlign_Fill);
                        PikaGridSlot->SetVerticalAlignment(EVerticalAlignment::VAlign_Fill);
                    }
                    PikaSlotWidget->PikaDrawUIToScreen();
                    //设置父类
                    PikaSlotWidget->PikaSetParentClass(this);
                    //添加到容器内
                    PikaInventorySlotArray.Add(PikaSlotWidget);
                }
            }
        }
        if (PikaGetPlayerController())
        {
            TArray<UTexture2D*> PikaMyTextureArray = PikaGetPlayerController()->PikaGetTowerIocn();
            //塔的建造CD
            TArray<float> PikaMyTowerCD = PikaGetPlayerController()->PikaGetBuildCD();
            //建造塔所需的金币
            TArray<int32> PikaMyGoldArray = PikaGetPlayerController()->PikaGetTowerComsumeGold();
            //信息的注册
            TArray<FName> PikaTowersNameArray = PikaGetPlayerController()->PikaGetTowersName();
            TArray<FText> PikaTowersIntroductionArray = PikaGetPlayerController()->PikaGetTowersIntroduction();
            TArray<float> PikaTowersAttackArray = PikaGetPlayerController()->PikaGetTowerData(EPikaCharacterInfoDataTableType::DATA_PikaCharacterAttack);
            TArray<float> PikaTowersArmorArray = PikaGetPlayerController()->PikaGetTowerData(EPikaCharacterInfoDataTableType::DATA_PikaCharacterArm);
            TArray<float> PikaTowersAttackSpeedArray = PikaGetPlayerController()->PikaGetTowerData(EPikaCharacterInfoDataTableType::DATA_PikaCharacterAttackSpeed);
            TArray<float> PikaTowersHealthArray = PikaGetPlayerController()->PikaGetTowerData(EPikaCharacterInfoDataTableType::DATA_PikaCharacterHealth);



            int32 i = 0;
            for (UTexture2D* MyTexture: PikaMyTextureArray)
            {
                if (PikaInventorySlotArray.IsValidIndex(i) && PikaMyTowerCD.IsValidIndex(i) && PikaMyGoldArray.IsValidIndex(i) 
                    && PikaTowersNameArray.IsValidIndex(i) && PikaTowersArmorArray.IsValidIndex(i) && PikaTowersAttackArray.IsValidIndex(i)
                    && PikaTowersAttackSpeedArray.IsValidIndex(i) && PikaTowersHealthArray.IsValidIndex(i) && PikaTowersIntroductionArray.IsValidIndex(i))
                {
                    //备份
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaTexture = MyTexture;
                    //激活
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaSetActive(true);
                    //初始化物品栏图片
                    PikaInventorySlotArray[i]->PikaInitTowerIcon();
                    //初始化塔的建造时间
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaMaxConstructionTowerCD = PikaMyTowerCD[i];
                    //消费金币更新
                    //PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaNeedGold = PikaMyGoldArray[i];

                    //注册信息
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterName = PikaTowersNameArray[i];
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterArmor = PikaTowersArmorArray[i];
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterAttack = PikaTowersAttackArray[i];
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterAttackSpeed = PikaTowersAttackSpeedArray[i];
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterHealth = PikaTowersHealthArray[i];
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaComsumeGlod = PikaMyGoldArray[i];
                    PikaInventorySlotArray[i]->PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterIntroduction = PikaTowersIntroductionArray[i];

                    //为读写存档做准备
                    if (1) 
                    {
                        //注册,保存建造的数量信息
                        PikaGetSaveGameData()->PikaTowerConstructionNumber.Add(0);
                        PikaGetSaveGameData()->PikaTowerPrepareBuildingNumber.Add(0);
                    }
                    else 
                    {
                    
                    }
                    
                }
                i++;
            }
        }
    }
}

void UPikaUI_Inventory::PikaSpawnTowerDollPressed(APikaGodStonePlayerController* MyPlayerController)
{
    if (MyPlayerController && PikaTempInventorySlot && PikaTempInventorySlot->PikaBuildingTowerStruct.PikaIsActive() && PikaTempInventorySlot->PikaGetTowersIcon())
    {
        if (PikaTempInventorySlot->PikaBuildingTowerStruct.PikaTowerConstructionNumber >= 1)
        {
            //通过图标寻找对应塔的序列位置
            int32 PikaTowersIndex = MyPlayerController->PikaGetTowerIocn().Find(PikaTempInventorySlot->PikaGetTowersIcon()->PikaqiuGetTexture2D());
            APikaTowerDoll* PikaTowersDoll = MyPlayerController->PikaSpawnTowerDoll(PikaTowersIndex);
        }
        else
            PikaLogPrintf("At least one tower to complete!");
    }
}

void UPikaUI_Inventory::PikaSpawnTowerDollReleased(APikaGodStonePlayerController* MyPlayerController)
{
    if (MyPlayerController && MyPlayerController->PikaGetTowersDoll() && MyPlayerController->PikaGetAllowBuillingTowers())
    {
        if (MyPlayerController->PikaGetTowersDoll()->GetActorLocation() != FVector::ZeroVector)
        {
            //通过图标寻找对应塔的序列位置
            int32 PikaTowersIndex = MyPlayerController->PikaGetTowerIocn().Find(PikaTempInventorySlot->PikaGetTowersIcon()->PikaqiuGetTexture2D());
            APikaCharacterBase* PikaTowersMesh = Cast<APikaCharacterBase>(MyPlayerController->PikaSpawnTowers(PikaTowersIndex, 1));
            if (PikaTowersMesh)
            {
                PikaTowersMesh->SetActorLocation(MyPlayerController->PikaGetTowersDoll()->GetActorLocation());
                PikaTowersMesh->SetActorRotation(MyPlayerController->PikaGetTowersDoll()->GetActorRotation());
                //减去建造数量
                PikaTempInventorySlot->PikaBuildingTowerStruct.PikaTowerConstructionNumber--;
                //上传到我们的存储
                PikaTempInventorySlot->PikaUpdateTowerCompletionNumText(PikaTempInventorySlot->PikaBuildingTowerStruct.PikaTowerConstructionNumber);

                //上传我们的位置信息数据
                if (MyPlayerController->PikaGlobalConfigPrePlayerCon)
                {
                    int32 PikaCharacterIndex = MyPlayerController->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaGetCurrentCharacterSpawnIDIndex(PikaTowersMesh->PikaGetCharacterSpawnID());
                    if (MyPlayerController->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaIsActive(PikaCharacterIndex))
                    {
                        MyPlayerController->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterLocation[PikaCharacterIndex] = PikaTowersMesh->GetActorLocation();
                        MyPlayerController->PikaGlobalConfigPrePlayerCon->PikaTemporaryCacheData.PikaCharacterRotator[PikaCharacterIndex] = PikaTowersMesh->GetActorRotation();
                    }
                }
                PikaLogPrintf("Tower Success");
            }
        }
        else
            PikaLogPrintf("Prohibition of construction in the region!");

        //销毁
        PikaCancelTempInventorySlot();
        MyPlayerController->PikaDestoryTowerDoll();
    }
    else
    {
        PikaLogPrintf("UPikaUI_Inventory::PikaSpawnTowerDollReleased Error");
        PikaCancelTempInventorySlot();
        MyPlayerController->PikaDestoryTowerDoll();
    }
}

UPikaUI_InventorySlot* UPikaUI_Inventory::GetTempInventorySlot() const
{
    return PikaTempInventorySlot;
}

void UPikaUI_Inventory::PikaSetTempInventorySlot(UPikaUI_InventorySlot* NewInventorySlot)
{
    PikaTempInventorySlot = NewInventorySlot;
}

void UPikaUI_Inventory::PikaCancelTempInventorySlot()
{
    if (PikaTempInventorySlot)
        PikaTempInventorySlot = NULL;
}

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

void UPikaUI_Inventory::PikaLogPrintf(FString MyString)
{
    if (PikaGetMainInterface())
        PikaGetMainInterface()->PikaLogPrintf(MyString);
}

void UPikaUI_Inventory::PikaLockSlotCD(bool IsLock)
{
    for (UPikaUI_InventorySlot* InventorySlot_E : PikaInventorySlotArray)
    {
        if (IsLock)
            InventorySlot_E->PikaCDLock();
        else
            InventorySlot_E->PikaUnCDLock();
    }
}

PikaUI_InventorySlot.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "Image.h"
#include "TextBlock.h"
#include "Widget.h"
#include "PikaUI_InventorySlot.generated.h"

//前向声明
class UPikaUI_Inventory;

USTRUCT()
struct FPikaBulidingTowres 
{
    GENERATED_BODY()
    //需要消费的金币
    int32 PikaNeedGold;
    //准备阶段塔的建造数量
    int32 PikaTowerPerpareBuildingNumber;
    //塔已经建造的数量
    int32 PikaTowerConstructionNumber;
    //当前塔最大建造CD
    float PikaMaxConstructionTowerCD;
    //当前建造中塔的CD
    float PikaCurrentConstructionTowerCD;
    //是否更新塔的CD
    bool PikaIsUpdateTowerCD;
    //作为图片指针
    UTexture2D* PikaTexture;
    //是否拖拽图标
    bool PikaIsDragIco;
    //锁
    bool PikaIsLockCD;
    
    
    FPikaBulidingTowres() 
    {
        PikaInitBuildingTower();
    }
    ~FPikaBulidingTowres()
    {
        if (PikaTexture)
            PikaTexture = NULL;
    }
    //获取当前建造时间与最大建造时间的百分比
    float PikaGetTowerConstructionTimePercentage() { return PikaMaxConstructionTowerCD ? PikaCurrentConstructionTowerCD / PikaMaxConstructionTowerCD : 0 ; }
    //重置CD
    void PikaSetCurrentCD() { PikaCurrentConstructionTowerCD = PikaMaxConstructionTowerCD; }
    //设置激活状态
    void PikaSetActive(bool DeathInformation) { PikaIsSlotActive = DeathInformation; }
    //判断物品栏是否激活
    bool PikaIsActive() const { return PikaIsSlotActive; }
    //存储角色信息
    FPikaCharacterInformationWidget PikaCharacterInformationWidget;
    //重载操作符
    FPikaBulidingTowres& operator = (const FPikaBulidingTowres& StructBuildingTower)
    {
        PikaNeedGold = StructBuildingTower.PikaNeedGold;
        PikaTowerPerpareBuildingNumber = StructBuildingTower.PikaTowerPerpareBuildingNumber;
        PikaTowerConstructionNumber = StructBuildingTower.PikaTowerConstructionNumber;
        PikaMaxConstructionTowerCD = StructBuildingTower.PikaMaxConstructionTowerCD;
        PikaCurrentConstructionTowerCD = StructBuildingTower.PikaCurrentConstructionTowerCD;
        PikaIsUpdateTowerCD = StructBuildingTower.PikaIsUpdateTowerCD;
        PikaSetActive(StructBuildingTower.PikaIsActive());
        PikaTexture = StructBuildingTower.PikaTexture;
        PikaCharacterInformationWidget = StructBuildingTower.PikaCharacterInformationWidget;

        return *this;
    }
    //初始化结构体
    void PikaInitBuildingTower() 
    {
        PikaNeedGold = 0;
        PikaTowerPerpareBuildingNumber = 0;
        PikaTowerConstructionNumber = 0;
        PikaMaxConstructionTowerCD = 0;
        PikaCurrentConstructionTowerCD = 0;
        PikaIsUpdateTowerCD = false;
        PikaIsSlotActive = false;
        PikaTexture = nullptr;
        PikaIsDragIco = false;
        PikaIsLockCD = false;
        //初始化我们的显示信息
        PikaCharacterInformationWidget.PikaClearCharacterInformationWidget();
    }
private:
    //判断本结构体是不是使用中
    bool PikaIsSlotActive;
};

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUI_InventorySlot : public UPikaUIWidgetBase
{
    GENERATED_BODY()
    //控件实例的名字
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerImageName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerCDImageName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerPrepareBuildingNumTextName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerCompletionNumTextName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaTowerCDValueTextName;
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaInventorySlotButtonName;
    //CD材质的名字,该值为CD材质里参数化的变量ControllerCD
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaCDMatName;
    //物品栏上是否有道具
    /*UPROPERTY()
    bool PikaIsExistItem;*/
    //CD材质
    UPROPERTY()
    UMaterialInstanceDynamic* PikaCDMaterialDynamic;
    
public:
    UPikaUI_InventorySlot();
    
    void PikaInitTowerIcon();
    //相当于BeginPlay,用于解决BeginPlay执行时,有些资源可能还没加载完成的问题
    //需要手动调用
    virtual void PikaDrawUIToScreen() override;

    virtual void PikaOnClickedWidget(UWidget* PikaWidget) override;
    //NativeTick函数里会调用蓝图里的Tick
    virtual void NativeTick(const FGeometry& MyGeometry , float InDeltaTime) override;
    //每一个物品栏对应的单个数据
    FPikaBulidingTowres PikaBuildingTowerStruct;
    
    //初始化,还原物品栏没有道具时的状态
    void PikaInitInventorySlot();
    
    //用当前的物品栏数据,刷新物品栏的状态,如图标、CD等
    void PikaUpdateInventorySlotData();
    //重新设置父类
    void PikaSetParentClass(UPikaUI_Inventory* MyParentClass);
    //打印测试
    void PikaPrint(FString piksStr);

    UImage* PikaGetTowersIcon() { return PikaTowerImage; }

    //更新物品栏上的文本
    void PikaUpdateTowerCompletionNumText(int32 TowerImageIndex);

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString)override;

    FORCEINLINE void PikaCDLock() { PikaBuildingTowerStruct.PikaIsLockCD = true; }
    FORCEINLINE void PikaUnCDLock() { PikaBuildingTowerStruct.PikaIsLockCD = false; }

private:
    //控件实例
    UImage* PikaTowerImage;
    UImage* PikaTowerCDImage;
    UTextBlock* PikaTowerPrepareBuildingNumText;
    UTextBlock* PikaTowerCompletionNumText;
    UTextBlock* PikaTowerCDValueText;
    UButton* PikaInventorySlotButton;
    UPikaUI_Inventory* PikaInventoryPtr;
    //显示字体和CD
    void PikaDisplayNumber(UTextBlock* MyTextNumberBlock , int32 MyTextNumber);
    //更新物品栏上的文本
    void PikaUpdateTowerPrepareBuildingNumText(int32 TowerImageIndex);
    void PikaUpdateTowerCDValueText(float MyTowerCDValueText);
    //绘制CD,CDParameter的取值范围为0-1
    void PikaUpdateTowerCDImage(float CDParameter);
    //构造塔的CD
    void PikaUpdateTowerCD(float InDeltaTime);

    //注册图标
    void PikaRegisterTowreIcon(UTexture2D* TowresIcon);
protected:
    /*开启Slate和SlateCore模块后start*/
    virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
    //NativeOnMouseButtonDown里增加的拖拽事件的响应函数
    virtual void NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& OutOperation) override;
    //NativeOnDragDetected激活后,如果将图标从一个物品栏拖拽到另外一个物品栏则会触发该事件
    //接收拖拽信息,InOperation为空时,该函数不会被激活
    virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
    //鼠标移动到物品栏时触发,前提条件是在没有按鼠标上任何键位时移动进入的,如拖拽时不会触发该函数
    virtual void NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
    //鼠标离开到物品栏时触发,前提条件是在没有按鼠标上任何键位时移动进入的,如拖拽时不会触发该函数
    virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent) override;
    /*开启Slate和SlateCore模块后end*/
};

PikaUI_InventorySlot.cpp

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

#include "Public/PikaUI_InventorySlot.h"
#include "Engine/Engine.h"
#include "Components/Image.h"
#include "Public/PikaTowerIcon.h"
#include "Public/PikaDragDropOperation.h"

UPikaUI_InventorySlot::UPikaUI_InventorySlot()
{
    //PikaIsExistItem = false;
}

void UPikaUI_InventorySlot::PikaRegisterTowreIcon(UTexture2D* TowresIcon)
{
    if (PikaTowerImage)
    {
        if (TowresIcon) 
        {
            PikaTowerImage->SetBrushFromTexture(TowresIcon);
            PikaTowerImage->SetVisibility(ESlateVisibility::Visible);
        }
        else 
        {
            PikaTowerImage->SetBrushFromTexture(TowresIcon);
            PikaTowerImage->SetVisibility(ESlateVisibility::Hidden);
        }
    }
}

void UPikaUI_InventorySlot::PikaUpdateInventorySlotData()
{
    //还原图标
    PikaInitTowerIcon();
    //还原CD材质
    PikaUpdateTowerCDImage(PikaBuildingTowerStruct.PikaCurrentConstructionTowerCD);
    //还原准备建造的塔
    PikaUpdateTowerPrepareBuildingNumText(PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber);
    //还原已经完成的塔
    PikaUpdateTowerCompletionNumText(PikaBuildingTowerStruct.PikaTowerConstructionNumber);
    //还原CD显示
    PikaUpdateTowerCDValueText(PikaBuildingTowerStruct.PikaGetTowerConstructionTimePercentage());
}

void UPikaUI_InventorySlot::PikaSetParentClass(UPikaUI_Inventory* MyParentClass)
{
    PikaInventoryPtr = MyParentClass;
}

void UPikaUI_InventorySlot::PikaInitTowerIcon()
{
    PikaRegisterTowreIcon(PikaBuildingTowerStruct.PikaTexture);
}

void UPikaUI_InventorySlot::PikaDrawUIToScreen()
{
    PikaTowerImage = Cast<UImage>(PikaqiuGetBlueprintWidget(PikaTowerImageName));
    PikaTowerCDImage = Cast<UImage>(PikaqiuGetBlueprintWidget(PikaTowerCDImageName));
    PikaTowerPrepareBuildingNumText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowerPrepareBuildingNumTextName));
    PikaTowerCompletionNumText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowerCompletionNumTextName));
    PikaTowerCDValueText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowerCDValueTextName));
    /*
     * Button的获取方式是UButton * UPikaUIWidgetBase::PikaGetButtonFromBlueprint(FName BlueprintWidgetName) const
     * 而除Button之外的为UWidget* UUserWidget::PikaqiuGetBlueprintWidget(FName PikaBlueprintWidgetName) const
     */
    PikaInventorySlotButton = PikaGetButtonFromBlueprint(PikaInventorySlotButtonName);

    //获取动态材质实例
    if (PikaTowerCDImage)
        PikaCDMaterialDynamic = PikaTowerCDImage->GetDynamicMaterial();
    //材质绘制CD旋转
    PikaUpdateTowerCDImage(0);
}

void UPikaUI_InventorySlot::PikaOnClickedWidget(UWidget* PikaWidget)
{
    if (PikaWidget == PikaInventorySlotButton) 
    {
        if (PikaGetSaveGameData() && PikaBuildingTowerStruct.PikaIsActive() && PikaTowerImage->PikaqiuGetTexture2D())
        {
            if (PikaGetSaveGameData()->PikaGetGlobalGold() >= PikaBuildingTowerStruct.PikaNeedGold) 
            {
                //获取图片对应的序列号
                int32 PikaTowerImageIndex = PikaGetPlayerController()->PikaGetTowerIocn().Find(PikaTowerImage->PikaqiuGetTexture2D());
                //消费金币
                PikaGetSaveGameData()->PikaConsumeGlobalGold(PikaBuildingTowerStruct.PikaNeedGold);
                //记录我们的塔
                PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber++;
                //更新到界面并且上传到存储
                PikaUpdateTowerPrepareBuildingNumText(PikaTowerImageIndex);
                //赋值CD
                if (PikaBuildingTowerStruct.PikaCurrentConstructionTowerCD <= 0)
                {
                    PikaBuildingTowerStruct.PikaSetCurrentCD();
                }
            }
            else 
            {
                PikaLogPrintf("Insufficient glod");
            }
            
        }
    }
}

void UPikaUI_InventorySlot::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
    Super::NativeTick(MyGeometry , InDeltaTime);
    //构造塔的CD
    if (!PikaBuildingTowerStruct.PikaIsLockCD)
    {
        if (!PikaBuildingTowerStruct.PikaIsDragIco)
            PikaUpdateTowerCD(InDeltaTime);
    }
    
}

void UPikaUI_InventorySlot::PikaUpdateTowerPrepareBuildingNumText(int32 TowerImageIndex)
{
    if (PikaTowerPrepareBuildingNumText)
    {
        //显示到界面
        PikaDisplayNumber(PikaTowerPrepareBuildingNumText , PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber);
        //上传数据到存储类
        if (PikaGetSaveGameData()->PikaTowerPrepareBuildingNumber.IsValidIndex(TowerImageIndex))
            PikaGetSaveGameData()->PikaTowerPrepareBuildingNumber[TowerImageIndex] = PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber;
    }
}

void UPikaUI_InventorySlot::PikaUpdateTowerCompletionNumText(int32 TowerImageIndex)
{
    if (PikaTowerCompletionNumText)
    {
        //显示到界面
        PikaDisplayNumber(PikaTowerCompletionNumText, PikaBuildingTowerStruct.PikaTowerConstructionNumber);
        //上传数据到存储类
        if (PikaGetSaveGameData()->PikaTowerConstructionNumber.IsValidIndex(TowerImageIndex))
            PikaGetSaveGameData()->PikaTowerConstructionNumber[TowerImageIndex] = PikaBuildingTowerStruct.PikaCurrentConstructionTowerCD;
    }

}

void UPikaUI_InventorySlot::PikaLogPrintf(FString MyString)
{
    PikaInventoryPtr->PikaLogPrintf(MyString);
}

void UPikaUI_InventorySlot::PikaUpdateTowerCDValueText(float MyTowerCDValueText)
{
    if (PikaTowerCDValueText)
    {
        PikaDisplayNumber(PikaTowerCDValueText, MyTowerCDValueText);
    }
}

//绘制CD,CDParameter的取值范围为0-1
void UPikaUI_InventorySlot::PikaUpdateTowerCDImage(float CDParameter)
{
    if (PikaCDMaterialDynamic) 
    {
        PikaCDMaterialDynamic->SetScalarParameterValue(PikaCDMatName, CDParameter);
    }
        
}

/**************************构建CD**************************/
void UPikaUI_InventorySlot::PikaUpdateTowerCD(float InDeltaTime)
{
    //当前CD是否小于0
    if (PikaBuildingTowerStruct.PikaCurrentConstructionTowerCD > 0)
    {
        //绘制旋转的材质CD
        PikaUpdateTowerCDImage(PikaBuildingTowerStruct.PikaGetTowerConstructionTimePercentage());
        //绘制数字CD显示
        PikaUpdateTowerCDValueText(PikaBuildingTowerStruct.PikaCurrentConstructionTowerCD);
        //用来呼叫完成事件
        PikaBuildingTowerStruct.PikaIsUpdateTowerCD = true;
        //数字变化
        PikaBuildingTowerStruct.PikaCurrentConstructionTowerCD -= InDeltaTime;
        return;
    }
    //等价于CD转完后呼叫一个完成事件
    if (PikaBuildingTowerStruct.PikaIsUpdateTowerCD)
    {
        PikaBuildingTowerStruct.PikaIsUpdateTowerCD = false;
        //PikaTowerImage->Brush.GetResourceObject()可以返回图片资源
        if (PikaTowerImage && PikaTowerImage->PikaqiuGetTexture2D())
        {
            //获取图片对应的序列号
            int32 PikaTowerImageIndex = PikaGetPlayerController()->PikaGetTowerIocn().Find(PikaTowerImage->PikaqiuGetTexture2D());

            //准备构建的塔
            PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber--;
            //建造完成的塔
            PikaBuildingTowerStruct.PikaTowerConstructionNumber++;
            PikaUpdateTowerPrepareBuildingNumText(PikaTowerImageIndex);
            PikaUpdateTowerCompletionNumText(PikaTowerImageIndex);
            //让我们的建造会一直根据我们的输入而建造
            if (PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber > 0)
                PikaBuildingTowerStruct.PikaSetCurrentCD();

            //打印塔建造完成的信息
            //获取游戏路径
            FString gameDir = FPaths::ProjectDir();
            FString path = gameDir + "111.txt";
            FString string;
            TArray<FString> stringArray;
            FFileHelper::LoadFileToString(string, *path);
            string.ParseIntoArray(stringArray, TEXT(","), false);

            if(stringArray.IsValidIndex(0) && stringArray.IsValidIndex(1) && stringArray.IsValidIndex(2))
                PikaLogPrintf(stringArray[0] + stringArray[1] + stringArray[2]);
            //PikaLogPrintf(PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterName.ToString() + string);
            //PikaLogPrintf(FString::Printf(TEXT("%s:%i has been done and %i 's in perparation for constriction!") , *PikaBuildingTowerStruct.PikaCharacterInformationWidget.PikaCharacterName.ToString() , PikaBuildingTowerStruct.PikaTowerConstructionNumber ,PikaBuildingTowerStruct.PikaTowerPerpareBuildingNumber));
        }
    }
}

FReply UPikaUI_InventorySlot::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
    Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
    //InMouseEvent.IsTouchEvent()为触摸屏的判断
    if (InMouseEvent.GetEffectingButton() == EKeys::RightMouseButton || InMouseEvent.IsTouchEvent())
    {
        //FReply::Handled()表示按键响应已经被处理
        FReply PikaReply = FReply::Handled();
        TSharedPtr<SWidget> PikaSlateWidgetDrag = GetCachedWidget();
        if (PikaSlateWidgetDrag.IsValid()) 
        {
            //PikaReply.DetectDrag()检测到拖拽后会触发一个UUserWidget类里的NativeOnDragDetected事件
            PikaReply.DetectDrag(PikaSlateWidgetDrag.ToSharedRef() , EKeys::RightMouseButton);
            return PikaReply;
        }
    }
    
    //FReply::Unhandled()表示按键响应未被处理
    return FReply::Unhandled();
}

//NativeOnMouseButtonDown里增加的拖拽事件的响应函数
void UPikaUI_InventorySlot::NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& OutOperation)
{
    if (PikaBuildingTowerStruct.PikaIsActive() && GetWorld())
    {
        TSubclassOf<UPikaTowerIcon> PikaIcoDragDrop = LoadClass<UPikaTowerIcon>(NULL , TEXT("WidgetBlueprint'/Game/HUD/GameUI/PikaTowerIconBP.PikaTowerIconBP_C'"));
        if (PikaIcoDragDrop) 
        {
            UPikaTowerIcon* PikaCharactersIcoDragDrop = CreateWidget<UPikaTowerIcon>(GetWorld() , PikaIcoDragDrop);
            if (PikaCharactersIcoDragDrop) 
            {
                //生成图标
                UPikaDragDropOperation* PikaTowerDragOperation = nullptr;
                TSubclassOf<UPikaDragDropOperation> PikaTowerDragOperationClass = UPikaDragDropOperation::StaticClass();
                if (PikaTowerDragOperationClass) 
                {
                    PikaTowerDragOperation = NewObject<UPikaDragDropOperation>(GetTransientPackage() , PikaTowerDragOperationClass);
                }
                else 
                {
                    PikaTowerDragOperation = NewObject<UPikaDragDropOperation>();
                }
                if (PikaTowerDragOperation)
                    PikaTowerDragOperation->SetFlags(RF_StrongRefOnFrame);//设置标识

                //注册图标显示信息
                PikaTowerDragOperation->PikaInventorySlot = this;
                PikaCharactersIcoDragDrop->PikaBuildingTowerStructIcon = PikaBuildingTowerStruct;
                PikaTowerDragOperation->DefaultDragVisual = PikaCharactersIcoDragDrop;
                OutOperation = PikaTowerDragOperation;

                //初始化我们的值(显示图标信息)
                PikaCharactersIcoDragDrop->PikaDrawUIToScreen();

                //处于拖拽状态中,停止所有CD
                PikaBuildingTowerStruct.PikaIsDragIco = true;
            }
        }
    }
    Super::NativeOnDragDetected(InGeometry, InMouseEvent, OutOperation);
}

//NativeOnDragDetected激活后,拖拽完成时,如果将图标从一个物品栏拖拽到另外一个物品栏时,则会触发该事件
//接收拖拽信息,InOperation为空时,该函数不会被激活
bool UPikaUI_InventorySlot::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
    this;
    Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
    bool PikaIsDrop = false;
    UPikaDragDropOperation* PikaDragDropOperation = Cast<UPikaDragDropOperation>(InOperation);
    if (PikaDragDropOperation)
    {
        //PikaMyInventorySlot为拖拽前的物品栏,类本身为拖拽的目标物品栏
        UPikaUI_InventorySlot* PikaMyInventorySlot = Cast<UPikaUI_InventorySlot>(PikaDragDropOperation->PikaInventorySlot);
        if (PikaMyInventorySlot)
        {
            PikaMyInventorySlot->PikaBuildingTowerStruct.PikaIsDragIco = false;
            PikaBuildingTowerStruct.PikaIsDragIco = false;
            if (PikaBuildingTowerStruct.PikaIsActive())
            {
                //拖拽的目标物品栏处于激活状态,做物品栏交换操作
                FPikaBulidingTowres PikaTempBulidTower;

                PikaTempBulidTower = PikaBuildingTowerStruct;
                PikaBuildingTowerStruct = PikaMyInventorySlot->PikaBuildingTowerStruct;
                PikaMyInventorySlot->PikaBuildingTowerStruct = PikaTempBulidTower;
                //更新数据显示
                PikaMyInventorySlot->PikaUpdateInventorySlotData();
                PikaUpdateInventorySlotData();
            }
            else
            {
                //拖拽的目标物品栏处于未激活状态,做物品栏移动操作
                PikaBuildingTowerStruct = PikaMyInventorySlot->PikaBuildingTowerStruct;
                //物品栏还原没有道具时的状态
                PikaMyInventorySlot->PikaInitInventorySlot();
                PikaUpdateInventorySlotData();
            }
            PikaIsDrop = true;
        }
        PikaIsDrop = false;
    }
    else
        PikaIsDrop = false;

    //销毁,引擎会自动释放,下面的释放代码可以不写
    /*if (InOperation)
    {
        if (InOperation->DefaultDragVisual)
            InOperation->DefaultDragVisual->RemoveFromParent();
        InOperation->DefaultDragVisual = NULL;
        InOperation->ConditionalBeginDestroy();
    }*/

    return PikaIsDrop;
}

void UPikaUI_InventorySlot::NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
    if (PikaInventoryPtr)
    {
        PikaInventoryPtr->PikaSetTempInventorySlot(this);
        //显示塔信息
        if (PikaInventoryPtr->PikaGetMainInterface() && PikaBuildingTowerStruct.PikaIsActive())
        {
            if (PikaInventoryPtr->PikaGetMainInterface()->PikaGetUITowerInfo())
                PikaInventoryPtr->PikaGetMainInterface()->PikaGetUITowerInfo()->SetVisibility(ESlateVisibility::Visible);
        }
    }
}

void UPikaUI_InventorySlot::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
{
    if (PikaInventoryPtr) 
    {
        //PikaInventoryPtr->PikaCancelTempInventorySlot();
        //隐藏塔信息
        if (PikaInventoryPtr->PikaGetMainInterface()) 
        {
            if (PikaInventoryPtr->PikaGetMainInterface()->PikaGetUITowerInfo()) 
                PikaInventoryPtr->PikaGetMainInterface()->PikaGetUITowerInfo()->SetVisibility(ESlateVisibility::Hidden);
        }
    }
}

void UPikaUI_InventorySlot::PikaInitInventorySlot()
{
    //初始化结构体
    PikaBuildingTowerStruct.PikaInitBuildingTower();
    PikaUpdateInventorySlotData();
    
}

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

//显示字体和CD
void UPikaUI_InventorySlot::PikaDisplayNumber(UTextBlock* MyTextNumberBlock, int32 MyTextNumber)
{
    //物品栏上有道显示文本,没有道具则不显示文本
    if (MyTextNumber < 1 || !PikaBuildingTowerStruct.PikaIsActive())
    {
        MyTextNumberBlock->SetVisibility(ESlateVisibility::Hidden);
    }
    else 
    {
        //格式输出里,%02d表示输出格式为00,%03d的输出格式为000
        MyTextNumberBlock->SetText(FText::FromString(FString::Printf(TEXT("%02d") , MyTextNumber)));
        MyTextNumberBlock->SetVisibility(ESlateVisibility::Visible);
    }
}

PikaUICharacterAttribute.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaCharacterData.h"
#include "TextBlock.h"
#include "ProgressBar.h"
#include "MultiLineEditableTextBox.h"
#include "Image.h"
#include "PikaMainInterface.h"
#include "PikaUICharacterAttribute.generated.h"

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

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

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

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

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

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

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

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

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

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

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

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

    //角色的信息数据
    UPikaCharacterData* PikaCharacterDataPtr;
public:
    //作为初始化实例
    virtual void NativePreConstruct()override;

    virtual void PikaDrawUIToScreen() override;

    //设置我们的数据
    void PikaSetGameCharacterData(UPikaCharacterData* NewCharacterData) { PikaCharacterDataPtr = NewCharacterData; }

    //只要角色值变动就会更新
    void PikaUpdateCharacterInformation();

    //角色升级后更新
    void PikaUpdateLevelCharacterElement();

    void PikaUpdataAll();

    //游戏初始化进行更新(只更新一次)
    void PikaUpdatecharacterElement();

    void PikaSetMianScreen(UPikaMainInterface* NewMianScreen) { PikaMianScreen = NewMianScreen; }
private:
    UTextBlock* PikaCharacterNameBlock;
    UTextBlock* PikaComsumeGlodBlock;
    UTextBlock* PikaCharacterLevelBlock;
    UTextBlock* PikaCharacterHealthBlock;
    UTextBlock* PikaCharacterAttackBlock;
    UTextBlock* PikaCharacterArmorBlock;
    UTextBlock* PikaCharacterAttackSpeedBlock;
    UTextBlock* PikaCharacterEmpiricalValueBlock;
    UProgressBar* PikaCharacterEmpiricalValueBar;
    UMultiLineEditableTextBox* PikaIntroduction;
    UImage* PikaTowersIco;
    UPikaMainInterface* PikaMianScreen;
protected:
    virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent)override;
};

PikaUICharacterAttribute.cpp

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

#include "Public/PikaUICharacterAttribute.h"

void UPikaUICharacterAttribute::NativePreConstruct()
{
    Super::NativePreConstruct();

    if (!PikaCharacterNameBlock)
        PikaCharacterNameBlock = Cast<UTextBlock>(GetWidgetFromName(PikaCharacterNameBlockName));

    if (!PikaComsumeGlodBlock)
        PikaComsumeGlodBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaComsumeGlodBlockName));

    if (!PikaCharacterLevelBlock)
        PikaCharacterLevelBlock = Cast<UTextBlock>(GetWidgetFromName(PikaCharacterLevelName));

    if (!PikaCharacterHealthBlock)
        PikaCharacterHealthBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterHealthBlockName));

    if (!PikaCharacterAttackBlock)
        PikaCharacterAttackBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterAttackBlockName));

    if (!PikaCharacterArmorBlock)
        PikaCharacterArmorBlock = Cast<UTextBlock>(GetWidgetFromName(PikaCharacterArmorBlockName));

    if (!PikaCharacterAttackSpeedBlock)
        PikaCharacterAttackSpeedBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterAttackSpeedBlockName));

    if (!PikaCharacterEmpiricalValueBlock)
        PikaCharacterEmpiricalValueBlock = Cast<UTextBlock>(GetWidgetFromName(PikaCharacterEmpiricalValueBlockName));

    if (!PikaCharacterEmpiricalValueBar)
        PikaCharacterEmpiricalValueBar = Cast<UProgressBar>(PikaqiuGetBlueprintWidget(PikaCharacterEmpiricalValueBarName));

    if (!PikaIntroduction)
        PikaIntroduction = Cast<UMultiLineEditableTextBox>(GetWidgetFromName(PikaIntroductionName));

    if (!PikaTowersIco)
        PikaTowersIco = Cast<UImage>(PikaqiuGetBlueprintWidget(PikaTowersIcoName));
}

void UPikaUICharacterAttribute::PikaDrawUIToScreen()
{
    if (!PikaCharacterNameBlock)
        PikaCharacterNameBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterNameBlockName));

    if (!PikaComsumeGlodBlock)
        PikaComsumeGlodBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaComsumeGlodBlockName));

    if (!PikaCharacterLevelBlock)
        PikaCharacterLevelBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterLevelName));

    if (!PikaCharacterHealthBlock)
        PikaCharacterHealthBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterHealthBlockName));

    if (!PikaCharacterAttackBlock)
        PikaCharacterAttackBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterAttackBlockName));

    if (!PikaCharacterArmorBlock)
        PikaCharacterArmorBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterArmorBlockName));

    if (!PikaCharacterAttackSpeedBlock)
        PikaCharacterAttackSpeedBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterAttackSpeedBlockName));

    if (!PikaCharacterEmpiricalValueBlock)
        PikaCharacterEmpiricalValueBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterEmpiricalValueBlockName));

    if (!PikaCharacterEmpiricalValueBar)
        PikaCharacterEmpiricalValueBar = Cast<UProgressBar>(PikaqiuGetBlueprintWidget(PikaCharacterEmpiricalValueBarName));

    if (!PikaIntroduction)
        PikaIntroduction = Cast<UMultiLineEditableTextBox>(PikaqiuGetBlueprintWidget(PikaIntroductionName));

    if (!PikaTowersIco)
        PikaTowersIco = Cast<UImage>(PikaqiuGetBlueprintWidget(PikaTowersIcoName));
}

//只要角色值变动就会更新
void UPikaUICharacterAttribute::PikaUpdateCharacterInformation()
{
    if (PikaCharacterDataPtr)
    {
        //生命值
        if (PikaCharacterHealthBlock)
            PikaCharacterHealthBlock->SetText(FText::FromString(FString::Printf(TEXT("%0.f / %0.f"), PikaCharacterDataPtr->PikaCharBaseData.PikaMaxHealth, PikaCharacterDataPtr->PikaCharBaseData.PikaCurrentHealth)));

        //经验数值
        if (PikaCharacterEmpiricalValueBlock)
            PikaCharacterEmpiricalValueBlock->SetText(FText::FromString(FString::Printf(TEXT("%0.f / %0.f"), PikaCharacterDataPtr->PikaCharBaseData.PikaMaxExperienceValue, PikaCharacterDataPtr->PikaCharBaseData.PikaCurrentExperienceValue)));

        //经验值百分百Bar
        if (PikaCharacterEmpiricalValueBar)
            PikaCharacterEmpiricalValueBar->SetPercent(PikaCharacterDataPtr->PikaCharBaseData.PikaGetEmpircalValuePercentage());
    }
}

//角色升级后更新
void UPikaUICharacterAttribute::PikaUpdateLevelCharacterElement()
{
    if (PikaCharacterDataPtr)
    {
        //攻击力
        if (PikaCharacterAttackBlock)
            PikaCharacterAttackBlock->SetText(FText::AsNumber(PikaCharacterDataPtr->PikaCharBaseData.PikaPhysicalAttack));

        //防御力
        if (PikaCharacterArmorBlock)
            PikaCharacterArmorBlock->SetText(FText::AsNumber(PikaCharacterDataPtr->PikaCharBaseData.PikaArmor));

        //攻击速度
        if (PikaCharacterAttackSpeedBlock)
            PikaCharacterAttackSpeedBlock->SetText(FText::AsNumber(PikaCharacterDataPtr->PikaCharBaseData.PikaAttackSpeed));

        //等级
        if (PikaCharacterLevelBlock)
            PikaCharacterLevelBlock->SetText(FText::FromString(FString::Printf(TEXT("Lv- %02d"), PikaCharacterDataPtr->PikaCharBaseData.PikaCharacterLevel)));

        //生命值
        if (PikaCharacterHealthBlock)
            PikaCharacterHealthBlock->SetText(FText::FromString(FString::Printf(TEXT("%0.f / %0.f"), PikaCharacterDataPtr->PikaCharBaseData.PikaMaxHealth, PikaCharacterDataPtr->PikaCharBaseData.PikaCurrentHealth)));

        //经验数值
        if (PikaCharacterEmpiricalValueBlock)
            PikaCharacterEmpiricalValueBlock->SetText(FText::FromString(FString::Printf(TEXT("%0.f / %0.f"), PikaCharacterDataPtr->PikaCharBaseData.PikaMaxExperienceValue, PikaCharacterDataPtr->PikaCharBaseData.PikaCurrentExperienceValue)));

        //经验值百分百Bar
        if (PikaCharacterEmpiricalValueBar)
            PikaCharacterEmpiricalValueBar->SetPercent(PikaCharacterDataPtr->PikaCharBaseData.PikaGetEmpircalValuePercentage());
    }
}

void UPikaUICharacterAttribute::PikaUpdataAll()
{
    this->PikaUpdatecharacterElement();
    this->PikaUpdateCharacterInformation();
    this->PikaUpdateLevelCharacterElement();
}

//游戏初始化进行更新(只更新一次)
void UPikaUICharacterAttribute::PikaUpdatecharacterElement()
{
    if (PikaGetPlayerController() && PikaCharacterDataPtr)
    {
        //更新名字
        if (PikaCharacterNameBlock)
            PikaCharacterNameBlock->SetText(FText::FromName(PikaCharacterDataPtr->PikaCharBaseData.PikaCharacterName));

        //花费
        if (PikaComsumeGlodBlock)
            PikaComsumeGlodBlock->SetText(FText::AsNumber(PikaCharacterDataPtr->PikaCharBaseData.PikaComsumeGold));

        if (PikaCharacterDataPtr->PikaCharBaseData.PikaTeam)
        {
            int32 PikaIndexTowerName = PikaGetPlayerController()->PikaGetTowersName().Find(PikaCharacterDataPtr->PikaCharBaseData.PikaCharacterName);
            if (PikaIndexTowerName >= 0 && PikaIndexTowerName < PikaGetPlayerController()->PikaGetTowersIntroduction().Num())
            {
                if (PikaTowersIco)
                    PikaTowersIco->SetBrushFromTexture(PikaGetPlayerController()->PikaGetTowerIocn()[PikaIndexTowerName]);


                if (PikaIntroduction)
                    PikaIntroduction->SetText(PikaGetPlayerController()->PikaGetTowersIntroduction()[PikaIndexTowerName]);
            }
        }
        else
        {
            int32 PikaIndexMonsterName = PikaGetPlayerController()->PikaGetMonsterName().Find(PikaCharacterDataPtr->PikaCharBaseData.PikaCharacterName);
            if (PikaIndexMonsterName >= 0 && PikaIndexMonsterName < PikaGetPlayerController()->PikaGetMonsterName().Num())
            {
                if (PikaTowersIco)
                    PikaTowersIco->SetBrushFromTexture(PikaGetPlayerController()->PikaGetMonsterIocn()[PikaIndexMonsterName]);

                if (PikaIntroduction)
                    PikaIntroduction->SetText(PikaGetPlayerController()->PikaGetMonsterIntroduction()[PikaIndexMonsterName]);
            }
        }
    }
}

void UPikaUICharacterAttribute::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
{
    //将自己隐藏
    if (PikaMianScreen && PikaMianScreen->PikaGetCharacterAttribute() && PikaMianScreen->PikaGetCharacterAttribute()->GetVisibility() != ESlateVisibility::Hidden)
    {
        PikaMianScreen->PikaGetCharacterAttribute()->SetVisibility(ESlateVisibility::Hidden);
    }
}

PikaUIData.h

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

#pragma once

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

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUIData : public UObject
{
    GENERATED_BODY()
    
public:
    //物品栏的格子数
    int32 PikaInventoryColumn, PikaInventoryRow;

    float PikaLogDisplayTime;

    UPikaUIData();
};

PikaUIData.cpp

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

#include "Public/PikaUIData.h"

UPikaUIData::UPikaUIData()
{
    PikaInventoryColumn = 3;
    PikaInventoryRow = 6;
    PikaLogDisplayTime = 2.f;
}

PikaUIGameCount.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "ProgressBar.h"
#include "TextBlock.h"
#include "PikaUIGameCount.generated.h"

class  UPikaMainInterface;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUIGameCount : public UPikaUIWidgetBase
{
    GENERATED_BODY()
    
    //控件示例的名字
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaGameGlobName;
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaTowersDeathNumberName;
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaGameCountName;
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaGameLevelScheduleName;
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaGameLevelScheduleSginName;
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaKillSoldierName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FString PikaBaseIamgeFadeInName;
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FString PikaBaseIamgeFadeOutName;

public:
    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen() override;

    //void PikaPlayIamgeAnimationState(bool IsBegin);

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString) override;

    void PikaUpdateInformation();

    //更新数字显示 25/5
    void PikaUpdateMonsterMaximum(int32 MaxValue, int32 Surplus);

    //获取时间格式
    FString PikaGetCurrentCount(float NewTimeCount);

    //Tick
    virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;

    UPikaMainInterface* PikaGetMianScreen()const { return PikaMainInterface; }

    void PikaSetMainInterface(UPikaMainInterface* NewWidget) { PikaMainInterface = NewWidget; }
private:
    UTextBlock* PikaGameGlob;
    UTextBlock* PikaTowersDeathNumber;
    UTextBlock* PikaGameCount;
    UTextBlock* PikaKillSoldier;
    UTextBlock* PikaGameLevelScheduleTexrSgin;
    UProgressBar* PikaGameLevelSchedule;
    UPikaMainInterface* PikaMainInterface;

    //动画
    //UWidgetAnimation* PikaBaseIamgeFadeIn;
    //UWidgetAnimation* PikaBaseIamgeFadeOut;
protected:
    //virtual void NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent);
    //virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent);
};

PikaUIGameCount.cpp

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

#include "Public/PikaUIGameCount.h"


void UPikaUIGameCount::PikaDrawUIToScreen()
{
    PikaGameGlob = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaGameGlobName));
    PikaTowersDeathNumber = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaTowersDeathNumberName));
    PikaGameLevelScheduleTexrSgin = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaGameLevelScheduleSginName));
    PikaGameCount = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaGameCountName));
    PikaKillSoldier = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaKillSoldierName));
    PikaGameLevelSchedule = Cast<UProgressBar>(PikaqiuGetBlueprintWidget(PikaGameLevelScheduleName));
    //PikaBaseIamgeFadeIn = GetNameWidgetAnimation(PikaBaseIamgeFadeInName);
    //PikaBaseIamgeFadeOut = GetNameWidgetAnimation(PikaBaseIamgeFadeOutName);
}

void UPikaUIGameCount::PikaLogPrintf(FString MyString)
{
    if (PikaMainInterface)
        PikaMainInterface->PikaLogPrintf(MyString);
}

void UPikaUIGameCount::PikaUpdateInformation()
{
    if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGlobalConfigPre)
    {
        //显示游戏的倒计时
        if (PikaGameCount)
            PikaGameCount->SetText(FText::FromString(PikaGetCurrentCount(PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData.PikaGameCount)));

        //显示金币
        if (PikaGameGlob)
            PikaGameGlob->SetText(FText::AsNumber(PikaGetSaveGameData()->PikaGetGlobalGold()));

        //显示塔死亡
        if (PikaTowersDeathNumber)
            PikaTowersDeathNumber->SetText(FText::AsNumber(PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData.PikaTowersDeathNumber));

        //显示击杀小兵信息
        if (PikaKillSoldier)
            PikaKillSoldier->SetText(FText::AsNumber(PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData.PikaKillSoldierNumber));

        //显示关卡进度
        if (PikaGameLevelSchedule)
            PikaGameLevelSchedule->SetPercent(PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData.PikaGetLevelPercentage());

        //关卡信号标记
        PikaUpdateMonsterMaximum(PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameLevelManangenment.PikaMaxStagesAreMonsters, PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameLevelManangenment.PikaCurrentStagesAreMonsters);
    }
}

void UPikaUIGameCount::PikaUpdateMonsterMaximum(int32 MaxValue, int32 Surplus)
{
    //调整系数
    Surplus += 1;
    if (Surplus > MaxValue)
        Surplus = MaxValue;

    //显示当前波段
    if (PikaGameLevelScheduleTexrSgin)
        PikaGameLevelScheduleTexrSgin->SetText(FText(FText::FromString(FString::Printf(TEXT("%2d / %2d"), MaxValue, Surplus))));
}

//获取时间格式
FString UPikaUIGameCount::PikaGetCurrentCount(float NewTimeCount)
{
    const int32 NewOurTime = FMath::Max(0, FMath::TruncToInt(NewTimeCount));
    const int32 NewMinrutes = NewOurTime / 60;
    const int32 NewSeconds = NewOurTime % 60;
    return FString::Printf(TEXT("%02d:%02d"), NewMinrutes, NewSeconds);
}

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

    //更新我们的显示数据
    PikaUpdateInformation();
}

PikaUIGameMenu.h

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

#pragma once

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

class UPikaMainInterface;

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

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FString PikaGameBeginAnimationName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FString PikaGameEndAnimationName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaReturnGameButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaSaveGameButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaGameSettingButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaGameQuitButtonName;
public:
    UPikaUIGameMenu();

    virtual void PikaDrawUIToScreen() override;

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString)override;

    //敲击
    virtual void PikaOnClickedWidget(UWidget* MyWidget)override;

    virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None) override;

    UPikaMainInterface* PikaGetMainInterface()const { return PikaMainInterface; }

    void PikaSetMainInterface(UPikaMainInterface* NewWidget) { PikaMainInterface = NewWidget; }

    /////////////////////////////////动画相关/////////////////////////////////////////
    void PikaGameMenuFadeInAndFadeOut(bool IsOut);
private:
    UButton* PikaReturnGameButton;
    UButton* PikaSaveGameButton;
    UButton* PikaGameSettingButtonButton;
    UButton* PikaGameQuitButtonButton;
    UPikaMainInterface* PikaMainInterface;

    //动画相关
    UWidgetAnimation* PikaGameBeginAnimation;
    UWidgetAnimation* PikaGameEndAnimation;
};

PikaUIGameMenu.cpp

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

#include "Public/PikaUIGameMenu.h"




UPikaUIGameMenu::UPikaUIGameMenu()
{

}

void UPikaUIGameMenu::PikaDrawUIToScreen()
{
    PikaReturnGameButton = PikaGetButtonFromBlueprint(PikaReturnGameButtonName);
    PikaSaveGameButton = PikaGetButtonFromBlueprint(PikaSaveGameButtonName);
    PikaGameSettingButtonButton = PikaGetButtonFromBlueprint(PikaGameSettingButtonName);
    PikaGameQuitButtonButton = PikaGetButtonFromBlueprint(PikaGameQuitButtonName);

    //PikaGameBeginAnimation = GetNameWidgetAnimation(GameBeginAnimationName);
    //PikaGameEndAnimation = GetNameWidgetAnimation(GameEndAnimationName);
}

void UPikaUIGameMenu::PikaLogPrintf(FString MyString)
{
    if (PikaMainInterface)
        PikaMainInterface->PikaLogPrintf(MyString);
}

//onclick事件是通过父类UPikaUIWidgetBase里的PikaGetButtonFromBlueprint方法绑定的
void UPikaUIGameMenu::PikaOnClickedWidget(UWidget* MyWidget)
{
    if (MyWidget == PikaReturnGameButton)
    {
        SetVisibility(ESlateVisibility::Hidden);
        
        //开始淡出
        if (PikaMainInterface && PikaGetPlayerController())
        {
            PikaMainInterface->PikaShieldedInteractiveOperation(true);
            
            //PikaMainInterface->GameMenuFadeInAndFadeOut(false);

            //设置全局时间
            if (PikaGetGameInstance())
            {
                //设置全局时间的增量为1
                PikaGetGameInstance()->PikaSetGlobalGameTime(PIKA_STANDARD_VALUE);
            }
        }
    }
    if (MyWidget == PikaSaveGameButton)
    {
        if (PikaMainInterface)
        {
            //打开存储列表
            PikaMainInterface->PikaSetBoxListDisplayOrder(PikaBoxListType::BOXLIST_SaveList);
            PikaMainInterface->PikaGetBoxSaveGameArray()->SetVisibility(ESlateVisibility::Visible);
            //PikaMainInterface->ClickedMenuFadeInAndFadeOut(true);
        }
    }
    if (MyWidget == PikaGameSettingButtonButton)
    {
        /*if (PikaMainInterface)
        {
            //打开设置列表列表
            PikaMainInterface->SetBoxListDisplayOrder(EBoxListType::BOXLIST_GameSettings);
            PikaMainInterface->ClickedMenuFadeInAndFadeOut(true);
        }*/
    }
    if (MyWidget == PikaGameQuitButtonButton)
    {
        /*//打开提示菜单
        SetWarningWindowsState(true, 0, FString::Printf(TEXT("Return to the menu, unsaved data will be lost. do you want to exit?")), EButtonSureType::BST_QuitMeun);

        //设置面板状态
        if (PikaMainInterface)
            PikaMainInterface->SetWindowsState(true);*/
    }
}

void UPikaUIGameMenu::PikaSetWarningWindowsState(bool IsVisibles, float StateTime /*= 0*/, FString MyWidget /*= ""*/, TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType /*= PikaEButtonSureType::BST_None*/)
{
    if (PikaMainInterface)
    {
        PikaMainInterface->PikaSetWarningWindowsState(IsVisibles, StateTime, MyWidget, MyButtonSureType);
    }
}

void UPikaUIGameMenu::PikaGameMenuFadeInAndFadeOut(bool IsOut)
{
    /*if (IsOut)
    {
        if (PikaGameBeginAnimation)
            PlayAnimation(PikaGameBeginAnimation);
    }
    else
    {
        if (PikaGameEndAnimation)
            PlayAnimation(PikaGameEndAnimation);
    }*/
}

PikaUIGameWarning.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "MultiLineEditableTextBox.h"
#include "PikaUIGameWarning.generated.h"


class UPikaMainInterface;
class UPikaUIMainHall;


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

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

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

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

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FString PikaOpenContorllerColorName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FString PikaCloseContorllerColorName;

public:
    UPikaUIGameWarning();

    //存储游戏的编号
    int32 PikaCurrentGameArchiveNumber;
    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    virtual void PikaOnClickedWidget(UWidget* MyWidget) override;

    //设置字体显示
    void PikaSetText(FString FSInformation);

    //设置MianScreen
    void PikaSetMainInterface(UPikaMainInterface* NewMianScreen);

    //设置MainHall
    void PikaSetMainHall(UPikaUIMainHall* NewMainHall);

    //virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None)override;

    //取消显示
    void PikaCancelWarningDisplay();

    ///////////////////////////////动画///////////////////////////////////////////
    //void GameWarningFadeInAndFadeOut(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<EButtonSureType::Type> MyButtonSureType = EButtonSureType::BST_None);
    //动画完成播放后执行该函数
    //virtual void OnAnimationFinished_Implementation(const UWidgetAnimation* Animation) override;
    //动画开始播放后执行该函数
    //virtual void OnAnimationStarted_Implementation(const UWidgetAnimation* Animation) override;
    
private:

    //界面
    UPikaMainInterface* PikaMianInterfacePtr;
    UPikaUIMainHall* PikaMainHallPtr;

    //按键
    UButton* PikaSureButton;
    UButton* PikaCancelButton;
    UMultiLineEditableTextBox* PikaEditableText;

    //动画相关
    UWidgetAnimation* PikaOpenContorllerColor;
    UWidgetAnimation* PikaCloseContorllerColor;
};

PikaUIGameWarning.cpp

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

#include "Public/PikaUIGameWarning.h"


UPikaUIGameWarning::UPikaUIGameWarning()
{
    PikaCurrentGameArchiveNumber = INDEX_NONE;
}

void UPikaUIGameWarning::PikaDrawUIToScreen()
{
    PikaSureButton = PikaGetButtonFromBlueprint(PikaSureButtonName);
    PikaCancelButton = PikaGetButtonFromBlueprint(PikaCancelButtonName);
    PikaEditableText = Cast<UMultiLineEditableTextBox>(GetWidgetFromName(PikaEditableTextName));

    /*//动画
    OpenContorllerColor = GetNameWidgetAnimation(OpenContorllerColorName);
    CloseContorllerColor = GetNameWidgetAnimation(CloseContorllerColorName);
    //默认关闭
    if (CloseContorllerColor)
        PlayAnimation(CloseContorllerColor, 0.5);*/
}

void UPikaUIGameWarning::PikaOnClickedWidget(UWidget* MyWidget)
{
    if (MyWidget == PikaSureButton)
    {
        /*//覆盖之发生在游戏中,不发生在大厅内
        if (PikaGameButtonSureType == PikaEButtonSureType::BST_CoverSave)
        {
            if (PikaMianInterfacePtr &&
                PikaMianInterfacePtr->GetReadAndWrite() &&
                GetTowerDefencePlayerController() &&
                GetTowerDefencePlayerController()->Confo)
            {
                //覆盖存档
                MianScreen->GetReadAndWrite()->SaveArchiveSlot(true);
            }
        }*/

        //游戏删除
        if (PikaGameButtonSureType == PikaEButtonSureType::BST_DeleteSave)
        {
            if (PikaCurrentGameArchiveNumber != INDEX_NONE)
            {
                //游戏场景内
                if (PikaMianInterfacePtr && PikaMianInterfacePtr->PikaGetReadAndWrite())
                {
                    //还原显示设置
                    PikaMianInterfacePtr->PikaGetReadAndWrite()->PikaClearNumberSave(PikaCurrentGameArchiveNumber);
                    //隐藏自己
                    PikaMianInterfacePtr->PikaSetWarningWindowsState(false);
                    if (PikaGetGameInstance())
                    {
                        bool DeleData = PikaGetGameInstance()->PikaDeleteGameData(PikaCurrentGameArchiveNumber);
                        if (DeleData)
                        {
                            PIKA_LOG("Delete Successfully");
                        }
                        else
                        {
                            PIKA_LOG("Delete Failure");
                        }
                    }
                }

                //大厅内
                if (PikaMainHallPtr && PikaMainHallPtr->PikaGetReadAndWrite())
                {
                    //清除显示并且还原
                    PikaMainHallPtr->PikaGetReadAndWrite()->PikaClearNumberSave(PikaCurrentGameArchiveNumber);
                    //隐藏自己
                    PikaMainHallPtr->PikaSetWarningWindowsState(false);
                    if (PikaGetGameInstance())
                    {
                        bool PikaDeleData = PikaGetGameInstance()->PikaDeleteGameData(PikaCurrentGameArchiveNumber);
                        if (PikaDeleData)
                        {
                            PIKA_LOG("Delete Successfully");
                        }
                        else
                        {
                            PIKA_LOG("Delete Failure");
                        }
                    }
                }
            }
            else
            {
                PIKA_LOG("CurrentGameArchiveNumber == INDEX_NONE");
            }
        }
        /*//返回主菜单
        if (GameButtonSureType == EButtonSureType::BST_QuitMeun)
        {
            //通知游戏菜单播放动画
            MianScreen->SetGameButtonSureType(EButtonSureType::BST_QuitMeun);
            MianScreen->MainUMGFadeInAndFadeOut(false, EAnimationState::BST_QuitMeun);
        }*/
        /*//退出游戏
        if (GameButtonSureType == EButtonSureType::BST_QuitGame)
        {
            ////考虑不同的场景所以使用这种办法
            if (MainHall)
            {
                MainHall->MainUMGFadeInAndFadeOut(false, EAnimationState::EA_GameQuit);
            }

            //播放离游戏开动画 //只能在菜单界面播放
            if (GetTowerDefenceHallGameMode())
            {
                GetTowerDefenceHallGameMode()->PlayCurrentCameraAnimation(ECameraArray::Camera_Leave, EMatineeArray::Matinee_Max, TD_CAMARE_BLENDTIME + 2);
            }
        }*/
    }
    if (MyWidget == PikaCancelButton)
    {
        //取消
        //隐藏自己
        if(PikaMianInterfacePtr)
            PikaMianInterfacePtr->PikaSetWarningWindowsState(false);
        //隐藏自己
        if(PikaMainHallPtr)
        {
            PikaMainHallPtr->PikaSetWarningWindowsState(false);
            PikaMainHallPtr->PikaWindowsFadeInAndFadeOut(false);
        }
            
    }
}

void UPikaUIGameWarning::PikaSetText(FString FSInformation)
{
    //更新显示字体
    if (PikaEditableText)
        PikaEditableText->SetText(FText::FromString(FSInformation));
}

void UPikaUIGameWarning::PikaSetMainInterface(UPikaMainInterface* NewMianScreen)
{
    PikaMianInterfacePtr = NewMianScreen;
}

void UPikaUIGameWarning::PikaSetMainHall(UPikaUIMainHall* NewMainHall)
{
    PikaMainHallPtr = NewMainHall;
}


void UPikaUIGameWarning::PikaCancelWarningDisplay()
{

}

PikaUIHallGameSetting.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaGameSettingsAudio.h"
#include "PikaGameSettingsGameSettings.h"
#include "PikaGameSettingsOtherSettings.h"
#include "PikaGameSettingsVideo.h"
#include "CheckBox.h"
#include "WidgetSwitcher.h"
#include "PikaUIGameMenu.h"
#include "PikaUIHallGameSetting.generated.h"



class UPikaUIMainHall;

UENUM()
namespace PikaGameSettingsType
{
    enum Type
    {
        GAMESETTINGS_Sound,
        GAMESTETTINGS_Graphics,
        GAMESTETTINGS_Game,
        GAMESTETTINGS_Other,
        GAMESETTINGS_Max
    };
}

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

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

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

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

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

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

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

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

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaGameSettingsAudio> PikaGameSettingsAudioClass;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaGameSettingsGameSettings> PikaGameSettingsGameSettingsClass;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaGameSettingsOtherSettings> PikaGameSettingsOtherSettingsClass;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaGameSettingsVideo> PikaGameSettingsVideoClass;
    
public:
    UPikaUIHallGameSetting();

    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    //返回和存储设置按钮
    virtual void PikaOnClickedWidget(UWidget* MyWidget)override;

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

    void PikaShowOneCheckBox(PikaGameSettingsType::Type GameSettingsType);

    void PikaSetMainHall(UPikaUIMainHall* NewWidget) { PikaMainHall = NewWidget; }

    void PikaSetGameMenu(UPikaUIGameMenu* NewWidget) { PikaMyGameMenu = NewWidget; }

    FORCEINLINE UPikaUIMainHall* PikaGetMainHall()const { return PikaMainHall; }

protected:

    void PikaSaveGameSettings();

    void PikaLoadGameSettings();

private:
    UCheckBox* PikaSoundSettingBox;
    UCheckBox* PikaGraphicsSettingsBox;
    UCheckBox* PikaGameSettingsBox;
    UCheckBox* PikaOtherGameSettingsBox;
    UButton* PikaSaveBarButton;
    UButton* PikaReturnMenuButton;
    //设置选项列表
    UWidgetSwitcher* PikaSettingsListWitcher;

    UPikaUIMainHall* PikaMainHall;
    UPikaUIGameMenu* PikaMyGameMenu;
    UPikaGameSettingsAudio* PikaGameSettingsAudio;
    UPikaGameSettingsGameSettings* PikaGameSettingsGameSettings;
    UPikaGameSettingsOtherSettings* PikaGameSettingsOtherSettings;
    UPikaGameSettingsVideo* PikaGameSettingsVideo;

    TArray<UCheckBox*> PikaCheckBoxArray;
};

PikaUIHallGameSetting.cpp

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

#include "Public/PikaUIHallGameSetting.h"
#include "Public/PikaGameUserSettings.h"
#include "Public/PikaWorldSettings.h"

UPikaUIHallGameSetting::UPikaUIHallGameSetting()
{

}

void UPikaUIHallGameSetting::PikaDrawUIToScreen()
{
    PikaSettingsListWitcher = Cast<UWidgetSwitcher>(GetWidgetFromName(PikaSettingsListWitcherName));

    PikaSoundSettingBox = PikaGetCheckBoxFormBlueprint(PikaSoundSettingBoxName);
    PikaGraphicsSettingsBox = PikaGetCheckBoxFormBlueprint(PikaGraphicsSettingsBoxName);
    PikaGameSettingsBox = PikaGetCheckBoxFormBlueprint(PikaGameSettingsBoxName);
    PikaOtherGameSettingsBox = PikaGetCheckBoxFormBlueprint(PikaOtherGameSettingsBoxName);
    PikaReturnMenuButton = PikaGetButtonFromBlueprint(PikaReturnMenuButtonName);
    PikaSaveBarButton = PikaGetButtonFromBlueprint(PikaSaveBarButtonName);

    if (PikaSettingsListWitcher && GetWorld())
    {
        //添加Audio
        if (PikaGameSettingsAudioClass)
        {
            PikaGameSettingsAudio = CreateWidget<UPikaGameSettingsAudio>(GetWorld(), PikaGameSettingsAudioClass);
            if (PikaGameSettingsAudio)
            {
                PikaSettingsListWitcher->AddChild(PikaGameSettingsAudio);
                PikaGameSettingsAudio->PikaDrawUIToScreen();
                PikaGameSettingsAudio->PikaSetHallGameSettings(this);
            }
        }
        //添加图像设置
        if (PikaGameSettingsVideoClass)
        {
            //添加图像设置
            PikaGameSettingsVideo = CreateWidget<UPikaGameSettingsVideo>(GetWorld(), PikaGameSettingsVideoClass);
            if (PikaGameSettingsVideo)
            {
                PikaSettingsListWitcher->AddChild(PikaGameSettingsVideo);
                PikaGameSettingsVideo->PikaDrawUIToScreen();
                PikaGameSettingsVideo->PikaSetHallGameSettings(this);
            }
        }
        //添加游戏设置
        if (PikaGameSettingsGameSettingsClass)
        {
            //添加游戏设置
            PikaGameSettingsGameSettings = CreateWidget<UPikaGameSettingsGameSettings>(GetWorld(), PikaGameSettingsGameSettingsClass);
            if (PikaGameSettingsGameSettings)
            {
                PikaSettingsListWitcher->AddChild(PikaGameSettingsGameSettings);
                PikaGameSettingsGameSettings->PikaDrawUIToScreen();
                PikaGameSettingsGameSettings->PikaSetHallGameSettings(this);
            }
        }
        //添加其他设置
        if (PikaGameSettingsOtherSettingsClass)
        {
            //添加其他设置
            PikaGameSettingsOtherSettings = CreateWidget<UPikaGameSettingsOtherSettings>(GetWorld(), PikaGameSettingsOtherSettingsClass);
            if (PikaGameSettingsOtherSettings)
            {
                PikaSettingsListWitcher->AddChild(PikaGameSettingsOtherSettings);
                PikaGameSettingsOtherSettings->PikaDrawUIToScreen();
                PikaGameSettingsOtherSettings->PikaSetHallGameSettings(this);
            }
        }
    }
    //添加我们的敲击盒子和绑定它
    if (PikaSoundSettingBox)
        PikaCheckBoxArray.Add(PikaSoundSettingBox);

    if (PikaGraphicsSettingsBox)
        PikaCheckBoxArray.Add(PikaGraphicsSettingsBox);

    if (PikaGameSettingsBox)
        PikaCheckBoxArray.Add(PikaGameSettingsBox);

    if (PikaOtherGameSettingsBox)
        PikaCheckBoxArray.Add(PikaOtherGameSettingsBox);

    //读取本地的配置
    PikaLoadGameSettings();
}

//返回和存储设置按钮
void UPikaUIHallGameSetting::PikaOnClickedWidget(UWidget* MyWidget)
{
    if (MyWidget == PikaSaveBarButton)
    {
        PikaSaveGameSettings();
    }
    if (MyWidget == PikaReturnMenuButton)
    {
        if (PikaGetMainHall() && PikaGetMainHall()->PikaGetBoxList())
            //PikaGetMainHall()->PikaGetBoxList()->ClearChildren();
            PikaGetMainHall()->PikaGetBoxList()->SetVisibility(ESlateVisibility::Hidden);
    }
}

void UPikaUIHallGameSetting::PikaClickedCheckBox(UWidget* MyWidget, bool ClickedWidget)
{
    if (MyWidget == PikaSoundSettingBox)
    {
        if (PikaSettingsListWitcher)
        {
            PikaSettingsListWitcher->SetActiveWidgetIndex(PikaGameSettingsType::GAMESETTINGS_Sound);
            PikaShowOneCheckBox(PikaGameSettingsType::GAMESETTINGS_Sound);
        }
    }
    if (MyWidget == PikaGraphicsSettingsBox)
    {
        if (PikaSettingsListWitcher)
        {
            PikaSettingsListWitcher->SetActiveWidgetIndex(PikaGameSettingsType::GAMESTETTINGS_Graphics);
            PikaShowOneCheckBox(PikaGameSettingsType::GAMESTETTINGS_Graphics);
        }
    }
    if (MyWidget == PikaGameSettingsBox)
    {
        if (PikaSettingsListWitcher)
        {
            PikaSettingsListWitcher->SetActiveWidgetIndex(PikaGameSettingsType::GAMESTETTINGS_Game);
            PikaShowOneCheckBox(PikaGameSettingsType::GAMESTETTINGS_Game);
        }
    }
    if (MyWidget == PikaOtherGameSettingsBox)
    {
        if (PikaSettingsListWitcher)
        {
            PikaSettingsListWitcher->SetActiveWidgetIndex(PikaGameSettingsType::GAMESTETTINGS_Other);
            PikaShowOneCheckBox(PikaGameSettingsType::GAMESTETTINGS_Other);
        }
    }
}

void UPikaUIHallGameSetting::PikaShowOneCheckBox(PikaGameSettingsType::Type GameSettingsType)
{
    for (int32 i = 0; i < PikaGameSettingsType::GAMESETTINGS_Max; i++)
    {
        if (i != GameSettingsType && PikaCheckBoxArray.IsValidIndex(i))
            PikaCheckBoxArray[i]->SetCheckedState(ECheckBoxState::Checked);
        else
            PikaCheckBoxArray[i]->SetCheckedState(ECheckBoxState::Unchecked);
    }
}

void UPikaUIHallGameSetting::PikaSaveGameSettings()
{
    if (UPikaGameUserSettings::PikaGetGameUserSettings())
    {
        //音乐存储
        if (PikaGameSettingsAudio)
        {
            if (PikaGameSettingsAudio->PikaGetSoundControllSlider())
                UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuTotalSoundControl = PikaGameSettingsAudio->PikaGetSoundControllSlider()->GetValue();
            if (PikaGameSettingsAudio->PikaGetMusicSlider())
                UPikaGameUserSettings::PikaGetGameUserSettings()->PikaBackgroundSoundControl = PikaGameSettingsAudio->PikaGetMusicSlider()->GetValue();
            if (PikaGameSettingsAudio->PikaGetSoundEffectSlider())
                UPikaGameUserSettings::PikaGetGameUserSettings()->PikaEffectsSoundControl = PikaGameSettingsAudio->PikaGetSoundEffectSlider()->GetValue();
            if (PikaGameSettingsAudio->PikaGetAudioSlider())
                UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuButtonSoundControl = PikaGameSettingsAudio->PikaGetAudioSlider()->GetValue();
        }

        if (PikaGameSettingsVideo)
        {
            //保存分辨率
            if (PikaGameSettingsVideo->PikaGetResolutionBoxString())
            {
                FString PikaResolution_H;
                FString PikaResolution_W;
                FString PikaResolutionString = PikaGameSettingsVideo->PikaGetResolutionBoxString()->GetSelectedOption();
                PikaResolutionString.Split(TEXT("x"), &PikaResolution_W, &PikaResolution_H);
                FIntPoint PikaResolutionInt;
                PikaResolutionInt.X = FCString::Atoi(*PikaResolution_W);
                PikaResolutionInt.Y = FCString::Atoi(*PikaResolution_H);
                UPikaGameUserSettings::PikaGetGameUserSettings()->SetScreenResolution(PikaResolutionInt);
            }

            /*//语言
            if (GameSettingsVideo->GetLanguageString())
            {
                FString LanguageTmp = GameSettingsVideo->GetLanguageString()->GetSelectedOption();
                UTowerDefenceGameUserSettings::GetTowerDefenceGameUserSettings()->SetCuurrLanguage(LanguageTmp);
            }*/
            //设置屏幕模式
            if (PikaGameSettingsVideo->PikaGetFullScreenCheckBox() && PikaGameSettingsVideo->PikaGetWindowScreenCheckBox())
            {
                if (PikaGameSettingsVideo->PikaGetFullScreenCheckBox()->IsChecked())
                {
                    if (!PikaGameSettingsVideo->PikaGetWindowScreenCheckBox()->IsChecked())
                    {
                        UPikaGameUserSettings::PikaGetGameUserSettings()->SetFullscreenMode(EWindowMode::WindowedFullscreen);
                    }
                }
                else if (PikaGameSettingsVideo->PikaGetWindowScreenCheckBox()->IsChecked())
                {
                    UPikaGameUserSettings::PikaGetGameUserSettings()->SetFullscreenMode(EWindowMode::Windowed);
                }
            }

            //默认级别
            int32 PikaScalabilityQualityLevel = 5.0f;

            //贴图质量
            if (PikaGameSettingsVideo->PikaGetTextureQualitySlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetTextureQualitySlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.TextureQuality = PikaCurrentQualityLevel;
            }

            //阴影质量
            if (PikaGameSettingsVideo->PikaGetShadowQualitySlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetShadowQualitySlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.ShadowQuality = PikaCurrentQualityLevel;
            }

            //锯齿质量
            if (PikaGameSettingsVideo->PikaGetAntiAliasingSlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetAntiAliasingSlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.AntiAliasingQuality = PikaCurrentQualityLevel;
            }

            //后期质量
            if (PikaGameSettingsVideo->PikaGetPostProcessingSlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetPostProcessingSlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.PostProcessQuality = PikaCurrentQualityLevel;
            }
            //特效质量
            if (PikaGameSettingsVideo->PikaGetEffectsSlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetEffectsSlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.EffectsQuality = PikaCurrentQualityLevel;
            }
            //Foliage质量
            if (PikaGameSettingsVideo->PikaGetFoliageSlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetFoliageSlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.FoliageQuality = PikaCurrentQualityLevel;
            }
            //视距离质量
            if (PikaGameSettingsVideo->PikaGetViewDistanceSlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetViewDistanceSlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.ViewDistanceQuality = PikaCurrentQualityLevel;
            }

            //摄像机FOV设置
            if (PikaGameSettingsVideo->PikaGetFOVSlider())
            {
                //载入摄像机FOV
                if (GetWorld() && GetWorld()->GetWorldSettings())
                {
                    APikaWorldSettings* PikaWorldSettings = Cast<APikaWorldSettings>(GetWorld()->GetWorldSettings());

                    if (PikaWorldSettings && PikaWorldSettings->PikaCurrentLevel != INDEX_NONE)
                    {
                        if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGetTowreCamera())
                        {
                            float PikaCurrentFOV = PikaGameSettingsVideo->PikaGetFOVSlider()->GetValue() * PIKA_FOVMAX_VALUE + 40;
                            if (PikaCurrentFOV >= 40 && PikaCurrentFOV <= 240)
                            {
                                //大厅里设置该值会崩
                                //PikaGetPlayerController()->PikaGetTowreCamera()->PikaSetCameraFOV(PikaCurrentFOV);
                                UPikaGameUserSettings::PikaGetGameUserSettings()->PikaCameraFOV = PikaCurrentFOV;
                            }
                        }
                    }
                }
            }

            //总级别控制,
            /*总级别控制这项一定要放到所有其它设置的最后面,否则读取不到值*/
            if (PikaGameSettingsVideo->PikaGetOverallScalabilityLevelSlider())
            {
                float PikaCurrentValue = PikaGameSettingsVideo->PikaGetOverallScalabilityLevelSlider()->GetValue();
                int32 PikaCurrentQualityLevel = PikaCurrentValue * PikaScalabilityQualityLevel;
                if (PikaCurrentQualityLevel != UPikaGameUserSettings::PikaGetGameUserSettings()->GetOverallScalabilityLevel())
                {
                    UPikaGameUserSettings::PikaGetGameUserSettings()->SetOverallScalabilityLevel(PikaCurrentQualityLevel);
                }
            }
        }


        UPikaGameUserSettings::PikaGetGameUserSettings()->ApplySettings(true);
    }
}

void UPikaUIHallGameSetting::PikaLoadGameSettings()
{
    if ((UPikaGameUserSettings::PikaGetGameUserSettings()))
    {
        //读取配置
        UPikaGameUserSettings::PikaGetGameUserSettings()->LoadSettings();

        //读取音乐配置
        if (PikaGameSettingsAudio)
        {
            if (PikaGameSettingsAudio->PikaGetSoundControllSlider())
                PikaGameSettingsAudio->PikaGetSoundControllSlider()->SetValue(UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuTotalSoundControl);
            if (PikaGameSettingsAudio->PikaGetMusicSlider())
                PikaGameSettingsAudio->PikaGetMusicSlider()->SetValue(UPikaGameUserSettings::PikaGetGameUserSettings()->PikaBackgroundSoundControl);
            if (PikaGameSettingsAudio->PikaGetSoundEffectSlider())
                PikaGameSettingsAudio->PikaGetSoundEffectSlider()->SetValue(UPikaGameUserSettings::PikaGetGameUserSettings()->PikaEffectsSoundControl);
            if (PikaGameSettingsAudio->PikaGetAudioSlider())
                PikaGameSettingsAudio->PikaGetAudioSlider()->SetValue(UPikaGameUserSettings::PikaGetGameUserSettings()->PikaqiuButtonSoundControl);

            //更新属性
            PikaGameSettingsAudio->PikaUpdateAttibe();
        }

        //关于显示设置
        if (PikaGameSettingsVideo)
        {

            //载入分辨率
            if (PikaGameSettingsVideo->PikaGetResolutionBoxString())
            {
                FIntPoint PikaResolutionPoint = UPikaGameUserSettings::PikaGetGameUserSettings()->GetScreenResolution();
                FString PikaResolutionString = FString::Printf(TEXT("%ix%i"), PikaResolutionPoint.X, PikaResolutionPoint.Y);
                PikaGameSettingsVideo->PikaGetResolutionBoxString()->SetSelectedOption(PikaResolutionString);
            }

            /*//本地语言
            if (GameSettingsVideo->GetLanguageString())
            {
                FString LanguageString = UTowerDefenceGameUserSettings::GetTowerDefenceGameUserSettings()->GetCurrentLanguageType();
                GameSettingsVideo->GetLanguageString()->SetSelectedOption(LanguageString);
            }*/

            //载入窗口模式
            if (PikaGameSettingsVideo->PikaGetFullScreenCheckBox() && PikaGameSettingsVideo->PikaGetWindowScreenCheckBox())
            {
                EWindowMode::Type PikaWindowMode = UPikaGameUserSettings::PikaGetGameUserSettings()->GetLastConfirmedFullscreenMode();
                switch (PikaWindowMode)
                {
                case EWindowMode::Fullscreen:
                    PikaGameSettingsVideo->PikaGetFullScreenCheckBox()->SetCheckedState(ECheckBoxState::Checked);
                    PikaGameSettingsVideo->PikaGetWindowScreenCheckBox()->SetCheckedState(ECheckBoxState::Unchecked);
                    break;
                case EWindowMode::Windowed:
                    PikaGameSettingsVideo->PikaGetFullScreenCheckBox()->SetCheckedState(ECheckBoxState::Unchecked);
                    PikaGameSettingsVideo->PikaGetWindowScreenCheckBox()->SetCheckedState(ECheckBoxState::Checked);
                    break;
                case EWindowMode::NumWindowModes:
                    break;
                case EWindowMode::WindowedFullscreen:
                    PikaGameSettingsVideo->PikaGetFullScreenCheckBox()->SetCheckedState(ECheckBoxState::Unchecked);
                    PikaGameSettingsVideo->PikaGetWindowScreenCheckBox()->SetCheckedState(ECheckBoxState::Checked);
                    break;
                }
            }

            //默认级别
            float PikaScalabilityQualityLevel = 5.0f;

            //贴图质量
            if (PikaGameSettingsVideo->PikaGetTextureQualitySlider())
            {
                float PikaTextureQualityLevel = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.TextureQuality;
                PikaGameSettingsVideo->PikaGetTextureQualitySlider()->SetValue(PikaTextureQualityLevel / PikaScalabilityQualityLevel);
            }

            //阴影质量
            if (PikaGameSettingsVideo->PikaGetShadowQualitySlider())
            {
                float PikaShadowQualityLevel = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.ShadowQuality;
                PikaGameSettingsVideo->PikaGetShadowQualitySlider()->SetValue(PikaShadowQualityLevel / PikaScalabilityQualityLevel);
            }

            //锯齿质量
            if (PikaGameSettingsVideo->PikaGetAntiAliasingSlider())
            {
                float PikaAntiAliasingQuality = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.AntiAliasingQuality;
                PikaGameSettingsVideo->PikaGetAntiAliasingSlider()->SetValue(PikaAntiAliasingQuality / PikaScalabilityQualityLevel);
            }

            //后期质量
            if (PikaGameSettingsVideo->PikaGetPostProcessingSlider())
            {
                float PikaCurrentQualityLevel = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.PostProcessQuality;
                PikaGameSettingsVideo->PikaGetPostProcessingSlider()->SetValue(PikaCurrentQualityLevel / PikaScalabilityQualityLevel);
            }

            //特效质量
            if (PikaGameSettingsVideo->PikaGetEffectsSlider())
            {
                float PikaCurrentQualityLevel = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.EffectsQuality;
                PikaGameSettingsVideo->PikaGetEffectsSlider()->SetValue(PikaCurrentQualityLevel / PikaScalabilityQualityLevel);
            }

            //Foliage质量
            if (PikaGameSettingsVideo->PikaGetFoliageSlider())
            {
                float PikaCurrentQualityLevel = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.FoliageQuality;
                PikaGameSettingsVideo->PikaGetFoliageSlider()->SetValue(PikaCurrentQualityLevel / PikaScalabilityQualityLevel);
            }

            //视距离质量
            if (PikaGameSettingsVideo->PikaGetViewDistanceSlider())
            {
                float PikaCurrentViewDistanceQuality = UPikaGameUserSettings::PikaGetGameUserSettings()->ScalabilityQuality.ViewDistanceQuality;
                PikaGameSettingsVideo->PikaGetViewDistanceSlider()->SetValue(PikaCurrentViewDistanceQuality / PikaScalabilityQualityLevel);
            }

            //摄像机FOV设置
            if (PikaGameSettingsVideo->PikaGetFOVSlider())
            {
                //能够滑块
                PikaGameSettingsVideo->PikaGetFOVSlider()->SetIsEnabled(true);

                //载入摄像机FOV
                if (GetWorld() && GetWorld()->GetWorldSettings())
                {
                    APikaWorldSettings* PikaWorldSettings = Cast<APikaWorldSettings>(GetWorld()->GetWorldSettings());

                    if (PikaWorldSettings && PikaWorldSettings->PikaCurrentLevel != INDEX_NONE)
                    {
                        if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGetTowreCamera())
                        {
                            PikaGetPlayerController()->PikaGetTowreCamera()->PikaSetCameraFOV(UPikaGameUserSettings::PikaGetGameUserSettings()->PikaCameraFOV);
                        }
                    }
                    else
                    {
                        PikaGameSettingsVideo->PikaGetFOVSlider()->SetIsEnabled(false);
                    }
                }

                //显示FOV
                //FOV 40 - 240 ,取中间数
                //FOVMAX_VALUE
                float PikaCurrentFOV = UPikaGameUserSettings::PikaGetGameUserSettings()->PikaCameraFOV - 40;
                if (PikaCurrentFOV >= 0 && PikaCurrentFOV <= 200)
                {
                    PikaGameSettingsVideo->PikaGetFOVSlider()->SetValue(PikaCurrentFOV / PIKA_FOVMAX_VALUE);
                }
            }

            //全局质量
            if (PikaGameSettingsVideo->PikaGetOverallScalabilityLevelSlider())
            {
                float PikaCurrentQualityLevel = UPikaGameUserSettings::PikaGetGameUserSettings()->GetOverallScalabilityLevel();
                if (PikaCurrentQualityLevel < 0)
                    PikaCurrentQualityLevel = 0;
                PikaGameSettingsVideo->PikaGetOverallScalabilityLevelSlider()->SetValue(PikaCurrentQualityLevel / PikaScalabilityQualityLevel);
            }

            //更新显示
            PikaGameSettingsVideo->PikaUpdateAttibe();
        }

        /*//游戏的设置
        if (GameSettingsGameSettings)
        {
            //读取鼠标设置
            if (GameSettingsGameSettings->GetMouseSpeedSlider())
                GameSettingsGameSettings->GetMouseSpeedSlider()->SetValue((UTowerDefenceGameUserSettings::GetTowerDefenceGameUserSettings()->MouseSpeed / 500));

            //读取模型角度设置
            if (GameSettingsGameSettings->GetAngleOfRotationSlider())
                GameSettingsGameSettings->GetAngleOfRotationSlider()->SetValue(UTowerDefenceGameUserSettings::GetTowerDefenceGameUserSettings()->AngleOfRotation / 180);

            GameSettingsGameSettings->UpdateAttibe();
        }

        //其他设置(该设置,不会在本地进行存储,只为了显示,只读不可修改)
        if (GameSettingsOtherSettings && GameUIData)
        {
            //物品栏
            if (GameSettingsOtherSettings->GetInventoryColumSlider())
                GameSettingsOtherSettings->GetInventoryColumSlider()->SetValue(GameUIData->InventoryColumn / 10);

            //物品栏
            if (GameSettingsOtherSettings->GetInventoryRowSlider())
                GameSettingsOtherSettings->GetInventoryRowSlider()->SetValue(GameUIData->InventoryRow / 10);

            //Log持续时间
            if (GameSettingsOtherSettings->GetLogContinueTimeSlider())
                GameSettingsOtherSettings->GetLogContinueTimeSlider()->SetValue(GameUIData->LogDisplayTime / 10);

            GameSettingsOtherSettings->UpdateAttibe();
        }*/
    }
}

PikaUIHallKey.h

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

#pragma once

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

class UPikaUIMainHall;

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUIHallKey : public UPikaUIWidgetBase
{
    GENERATED_BODY()
    
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaNewWarButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaHistoryButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaMySettingsButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaTutorialWebsiteButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaAuthorButtonName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaQuitGameButtonName;

    //当前摄像机序列
    uint8 PikaCurrentCameraType;
public:

    UPikaUIHallKey();

    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    virtual void PikaOnClickedWidget(UWidget* MyWidget) override;

    //播放音效
    virtual void PikaOnHoveredWidget(UWidget* CurrentButton) override;

    virtual void PikaOnUnHoveredWidget() override;

    UPikaUIMainHall* PikaGetMainHall()const { return PikaMainHall; }

    //关闭Button Hov
    //void PikaCloseButtonHov();

    void PikaSetMainHall(UPikaUIMainHall* NewWidget) { PikaMainHall = NewWidget; }

    //virtual void SetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<EButtonSureType::Type> MyButtonSureType = EButtonSureType::BST_None) override;
private:

    UButton* PikaNewWarButton;
    UButton* PikaHistoryButton;
    UButton* PikaMySettingsButton;
    UButton* PikaTutorialWebsiteButton;
    UButton* PikaAuthorButton;
    UButton* PikaQuitGameButton;

    UPikaUIMainHall* PikaMainHall;

    TArray<UButton*> PikaButtonArray;
    
};

PikaUIHallKey.cpp

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

#include "Public/PikaUIHallKey.h"
#include "WidgetSwitcher.h"
#include "Kismet/GameplayStatics.h"
#include "WidgetAnimation.h"




UPikaUIHallKey::UPikaUIHallKey()
{
    PikaCurrentCameraType = INDEX_NONE;
}

void UPikaUIHallKey::PikaDrawUIToScreen()
{
    PikaNewWarButton = PikaGetButtonFromBlueprint(PikaNewWarButtonName);
    PikaHistoryButton = PikaGetButtonFromBlueprint(PikaHistoryButtonName);
    PikaMySettingsButton = PikaGetButtonFromBlueprint(PikaMySettingsButtonName);
    PikaTutorialWebsiteButton = PikaGetButtonFromBlueprint(PikaTutorialWebsiteButtonName);
    PikaQuitGameButton = PikaGetButtonFromBlueprint(PikaQuitGameButtonName);
    PikaAuthorButton = PikaGetButtonFromBlueprint(PikaAuthorButtonName);

    //记录按键
    if (PikaNewWarButton)
        PikaButtonArray.Add(PikaNewWarButton);
    if (PikaHistoryButton)
        PikaButtonArray.Add(PikaHistoryButton);
    if (PikaMySettingsButton)
        PikaButtonArray.Add(PikaMySettingsButton);
    if (PikaTutorialWebsiteButton)
        PikaButtonArray.Add(PikaTutorialWebsiteButton);
    if (PikaQuitGameButton)
        PikaButtonArray.Add(PikaQuitGameButton);
    if (PikaAuthorButton)
        PikaButtonArray.Add(PikaAuthorButton);
}

void UPikaUIHallKey::PikaOnClickedWidget(UWidget* MyWidget)
{
    //新游戏
    if (MyWidget == PikaNewWarButton)
    {
        //打开新关卡
        if (GetWorld())
            UGameplayStatics::OpenLevel(GetWorld(), "PikaGodStone_Level_Main_01");
        /*if (GetMainHall())
        {
            //提示游戏开始关卡
            GetMainHall()->MainUMGFadeInAndFadeOut(false, EAnimationState::EA_GameStart);
        }

        //播放开始游戏动画
        if (GetTowerDefenceHallGameMode())
        {
            GetTowerDefenceHallGameMode()->PlayCurrentCameraAnimation(ECameraArray::Camera_IntoGame, EMatineeArray::Matinee_Max, TD_CAMARE_BLENDTIME + 1);
        }

        //设置不可敲击
        SetVisibility(ESlateVisibility::HitTestInvisible);

        //关闭其他按键
        CloseButtonHov();*/
    }

    //游戏存储
    if (MyWidget == PikaHistoryButton)
    {
        if (PikaGetMainHall() && PikaGetMainHall()->PikaGetBoxList())
        {
            if (PikaGetMainHall()->PikaGetBoxList()->GetVisibility() == ESlateVisibility::Visible)
            {
                if (PikaGetMainHall()->PikaGetBoxListDisplayOrder() != PikaBoxListType::BOXLIST_SaveList)
                {
                    //设置显示界面类型
                    PikaGetMainHall()->PikaSetBoxListDisplayOrder(PikaBoxListType::BOXLIST_SaveList);
                }
                
                //隐藏和显示界面
                PikaGetMainHall()->PikaClickedMenuFadeInAndFadeOut(false , PikaqiuEAnimationState::BST_QuitMeun);
            }
            else
            {
                PikaGetMainHall()->PikaGetBoxList()->SetVisibility(ESlateVisibility::Visible);
                //隐藏和显示界面
                PikaGetMainHall()->PikaClickedMenuFadeInAndFadeOut(true);
                //设置显示界面类型
                PikaGetMainHall()->PikaSetBoxListDisplayOrder(PikaBoxListType::BOXLIST_SaveList);
            }
        }
    }

    //游戏设置
    if (MyWidget == PikaMySettingsButton)
    {
        if (PikaGetMainHall() && PikaGetMainHall()->PikaGetBoxList())
        {
            //关闭网站
            //if (!GetMainHall()->IsWeb())
                //GetMainHall()->CloseWeb();

            if (PikaGetMainHall()->PikaGetBoxList()->GetVisibility() == ESlateVisibility::Visible)
            {

                if (PikaGetMainHall()->PikaGetBoxListDisplayOrder() == PikaBoxListType::BOXLIST_GameSettings)
                {
                    //设置显示界面类型
                    PikaGetMainHall()->PikaSetBoxListDisplayOrder(PikaBoxListType::BOXLIST_GameSettings);
                }
                
                //隐藏和显示界面
                PikaGetMainHall()->PikaClickedMenuFadeInAndFadeOut(false, PikaqiuEAnimationState::BST_QuitMeun);
            }
            else
            {
                PikaGetMainHall()->PikaGetBoxList()->SetVisibility(ESlateVisibility::Visible);
                //隐藏和显示界面
                PikaGetMainHall()->PikaClickedMenuFadeInAndFadeOut(true);
                //设置显示界面类型
                PikaGetMainHall()->PikaSetBoxListDisplayOrder(PikaBoxListType::BOXLIST_GameSettings);
            }
        }
    }

    //教程网站
    if (MyWidget == PikaTutorialWebsiteButton)
    {
        if (PikaGetMainHall() && PikaGetMainHall()->PikaGetBoxList())
        {
            PikaGetMainHall()->PikaGetBoxList()->SetVisibility(ESlateVisibility::Hidden);
            //隐藏和显示界面
            /*if (GetMainHall()->GetBoxList()->GetVisibility() == ESlateVisibility::Visible)
                GetMainHall()->ClickedMenuFadeInAndFadeOut(false);

            if (GetMainHall()->IsWeb())
            {
                //打开网站
                GetMainHall()->OpenWeb();
            }
            else
            {
                //关闭网站
                GetMainHall()->CloseWeb();
            }*/
        }
    }

    //游戏秘境
    if (MyWidget == PikaAuthorButton)
    {
        /*if (GetMainHall() && GetMainHall()->GetBoxList())
        {
            //关闭网站
            if (!GetMainHall()->IsWeb())
                GetMainHall()->CloseWeb();

            if (GetMainHall()->GetBoxList()->GetVisibility() == ESlateVisibility::Visible)
            {
                //隐藏和显示界面
                GetMainHall()->ClickedMenuFadeInAndFadeOut(false);
            }

            //提示游戏开始关卡
            GetMainHall()->MainUMGFadeInAndFadeOut(false, EAnimationState::EA_SteamGameStart);

            //播放开始游戏动画
            if (GetTowerDefenceHallGameMode())
            {
                GetTowerDefenceHallGameMode()->PlayCurrentCameraAnimation(ECameraArray::Camera_IntoGame, EMatineeArray::Matinee_Max, TD_CAMARE_BLENDTIME + 1);
            }

            //关闭其他按键
            CloseButtonHov();
        }*/
    }

    //游戏退出
    if (MyWidget == PikaQuitGameButton)
    {
        if (PikaGetPlayerController())
            PikaGetPlayerController()->ConsoleCommand(FString(TEXT("exit")));
        /*if (MainHall)
        {
            if (TD_WText.IsValidIndex(0))
            {
                SetWarningWindowsState(true, 0, TD_WText[0].ToString(), EButtonSureType::BST_QuitGame);
            }
        }*/
    }
}

void UPikaUIHallKey::PikaOnHoveredWidget(UWidget* CurrentButton)
{
    //播放声音
    
    if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGameResourcePtr)
    {
        PikaGetPlayerController()->PikaPlaySoundAtVetor(PikaGetPlayerController()->PikaGameResourcePtr->PikaHoverSound, FVector::ZeroVector, PikaETowerDefenceSoundType::SOUND_KEY_SOUND);
    }
}

void UPikaUIHallKey::PikaOnUnHoveredWidget()
{
    //播放声音
    if (PikaGetPlayerController() && PikaGetPlayerController()->PikaGameResourcePtr)
    {
        PikaGetPlayerController()->PikaPlaySoundAtVetor(PikaGetPlayerController()->PikaGameResourcePtr->PikaUnHoverSound, FVector::ZeroVector, PikaETowerDefenceSoundType::SOUND_KEY_SOUND);
    }
}

PikaUIHealthShow.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 "ProgressBar.h"
#include "PikaCharacterData.h"
#include "PikaUIHealthShow.generated.h"

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

    //血条栏
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaHealthValueBarName;
    //角色名称和等级
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FName PikaCharacterNameAndLVName;
    
    //角色的信息数据
    UPikaCharacterData* PikaCharacterDataPtr;
public:
    UPikaUIHealthShow();

    virtual void PikaDrawUIToScreen() override;

    //只要角色值变动机会更新
    void PikaUpdateCharacterInformation();

    //设置我们的数据
    void PikaSetGameCharacterData(UPikaCharacterData* NewCharacterData) { PikaCharacterDataPtr = NewCharacterData; }
    
private:
    UTextBlock* PikaCharacterNameAndLV;
    UProgressBar* PikaHealthValueBar;
};

PikaUIHealthShow.cpp

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

#include "Public/PikaUIHealthShow.h"

UPikaUIHealthShow::UPikaUIHealthShow()
{
}

void UPikaUIHealthShow::PikaDrawUIToScreen()
{
    if (!PikaCharacterNameAndLV)
        PikaCharacterNameAndLV = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterNameAndLVName));

    if (!PikaHealthValueBar)
        PikaHealthValueBar = Cast<UProgressBar>(PikaqiuGetBlueprintWidget(PikaHealthValueBarName));
}

void UPikaUIHealthShow::PikaUpdateCharacterInformation()
{
    if (PikaCharacterDataPtr)
    {
        if (PikaCharacterNameAndLV)
            PikaCharacterNameAndLV->SetText(FText::FromString(FString::Printf(TEXT("Lv - %i    %s"), PikaCharacterDataPtr->PikaCharBaseData.PikaCharacterLevel, *PikaCharacterDataPtr->PikaCharBaseData.PikaCharacterName.ToString())));

        if (PikaHealthValueBar)
            PikaHealthValueBar->SetPercent(PikaCharacterDataPtr->PikaCharBaseData.PikaGetHealthValuePercentage());
    }
}

PikaUILoading.h

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

#pragma once

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

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUILoading : public UPikaUIWidgetBase
{
    GENERATED_BODY()
};

PikaUILoading.cpp

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

#include "Public/PikaUILoading.h"

PikaUIMainHall.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaUIWebBrowser.h"
#include "PikaUIGameWarning.h"
#include "PikaUIHallGameSetting.h"
#include "PikaUIReadAndWrite.h"
#include "PikaUIHallKey.h"
#include "PikaGameErrorHint.h"
#include "WidgetSwitcher.h"
#include "WidgetAnimation.h"
#include "PikaUIMainHall.generated.h"


DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FEventPikaHallLog, FString, MyString);

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUIMainHall : public UPikaUIWidgetBase
{
    GENERATED_BODY()
    
    UPROPERTY(EditDefaultsOnly, Category = "PikaAnimation")
    FString PikaSaveSettingAnimationBeginName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaAnimation")
    FString PikaSaveSettingAnimationEndName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaAnimation")
    FString PikaMaskAnimationBeginName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaAnimation")
    FString PikaMaskAnimationEndName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaAnimation")
    FString PikaGradualChangeAnimationBeginName;

    UPROPERTY(EditDefaultsOnly, Category = "PikaAnimation")
    FString PikaGradualChangeAnimationEndName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaBoxKeyHallListName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaBoxWindowListName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaBoxWarningListName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaBoxListName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaBoxWebName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaMoveToLogName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    TSubclassOf<UPikaGameErrorHint> PikaGamePromptErrorClass;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    TSubclassOf<UPikaUIHallKey> PikaHallKeyClass;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    TSubclassOf<UPikaUIReadAndWrite> PikaReadAndWriteClass;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    TSubclassOf<UPikaUIHallGameSetting> PikaHallGameSettingsClass;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    TSubclassOf<UPikaUIGameWarning> PikaGameWarningClass;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    TSubclassOf<UPikaUIWebBrowser> PikaWebBrowserClass;
public:
    FGeometry PikaHallGeometry;

    UPikaUIMainHall();

    FEventPikaHallLog PikaHallLog;

    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen() override;

    //virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime)override;
    //负责打印GameLOG
    //virtual void PikaLogPrintf(FString MyString) override;

    //移除相应设置
    virtual void RemoveFromViewport()override;

    /*void SetMoveToLogPostion(FVector2D NewPostion);*/

    //提示窗口显示
    virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None) override;

    //存档与游戏设置切换
    void PikaSetBoxListDisplayOrder(PikaBoxListType::Type BoxListType);

    PikaBoxListType::Type PikaGetBoxListDisplayOrder() const;

    /*********************Web***********************/
    //void PikaSetWebListState(bool IsWebState);

    //打开网站
    //void PikaOpenWeb();

    //关闭网站
    //void PikaCloseWeb();

    //是不是网站
    //bool PikaIsWeb();

    FORCEINLINE UWidgetSwitcher* PikaGetBoxList() const { return PikaBoxList; }
    FORCEINLINE UPikaUIReadAndWrite* PikaGetReadAndWrite() const { return PikaReadAndWritePtr; }
    FORCEINLINE UPikaUIHallGameSetting* PikaGetHallGameSettings()const { return PikaHallGameSettingsPtr; }
    FORCEINLINE UPikaUIGameWarning* PikaGetGameWarning() const { return PikaGameWarningPtr; }
    FORCEINLINE UPikaUIWebBrowser* PikaGetCurrentWebBrowser() const { return PikaCurrentWebBrowserPtr; }
public:
    /***********************动画相关**************************/
    void PikaInitAnimation();
    //主UMG淡入淡出
    void PikaMainUMGFadeInAndFadeOut(bool IsOut, PikaqiuEAnimationState NewAnimationState = PikaqiuEAnimationState::EA_Empty);
    //敲击动画淡入淡出
    void PikaClickedMenuFadeInAndFadeOut(bool IsOut, PikaqiuEAnimationState NewAnimationState = PikaqiuEAnimationState::EA_Empty);
    //窗口状态
    /*void PikaSetWindowsState(bool IsState);
    //获取淡入淡出列表设置
    FORCEINLINE UWidgetAnimation* PikaGetBeginAndEndSaveAndSettingUMGFadeInAndFadeOut(bool IsBeign = true) const { return IsBeign ? GameSaveSettingsBeginAnimation : GameSaveSettingsEndAnimation; }*/
    //窗口淡入淡出
    void PikaWindowsFadeInAndFadeOut(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None);
private:
    //动画完成播放后执行该函数
    virtual void OnAnimationFinished_Implementation(const UWidgetAnimation* Animation) override;
    //动画开始播放后执行该函数
    virtual void OnAnimationStarted_Implementation(const UWidgetAnimation* Animation) override;
private:
    //控件相关
    USizeBox* PikaBoxKeyHallList;
    USizeBox* PikaBoxWebList;
    USizeBox* PikaBoxWarningList;
    USizeBox* PikaBoxWindowList;
    UWidgetSwitcher* PikaBoxList;

    //UMG类相关
    UPikaUIHallGameSetting* PikaHallGameSettingsPtr;
    UPikaUIReadAndWrite* PikaReadAndWritePtr;
    UPikaUIHallKey* PikaHallKeyPtr;
    UPikaGameErrorHint* PikaGamePromptErrorPtr;
    UPikaUIGameWarning* PikaGameWarningPtr;
    UPikaUIWebBrowser* PikaCurrentWebBrowserPtr;
    //UUI_MoveToLog* MoveToLogPtr;

    //动画相关
    UWidgetAnimation* PikaSaveSettingAnimationBegin;
    UWidgetAnimation* PikaSaveSettingAnimationEnd;
    UWidgetAnimation* PikaMaskAnimationBegin;
    UWidgetAnimation* PikaMaskAnimationEnd;
    UWidgetAnimation* PikaGradualChangeAnimationBegin;
    UWidgetAnimation* PikaGradualChangeAnimationEnd;
};

PikaUIMainHall.cpp

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

#include "Public/PikaUIMainHall.h"

UPikaUIMainHall::UPikaUIMainHall()
{

}

void UPikaUIMainHall::PikaDrawUIToScreen()
{
    //UE控件相关
    PikaBoxKeyHallList = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaBoxKeyHallListName));
    PikaBoxList = Cast<UWidgetSwitcher>(GetWidgetFromName(PikaBoxListName));
    PikaBoxWarningList = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaBoxWarningListName));
    PikaBoxWindowList = Cast<USizeBox>(GetWidgetFromName(PikaBoxWindowListName));
    PikaBoxWebList = Cast<USizeBox>(PikaqiuGetBlueprintWidget(PikaBoxWebName));
    //PikaMoveToLog = Cast<UUI_MoveToLog>(GetWidgetFromName(MoveToLogName));
    //动画相关
    PikaSaveSettingAnimationBegin = PikaqiuGetNameWidgetAnimation(PikaSaveSettingAnimationBeginName);
    PikaSaveSettingAnimationEnd = PikaqiuGetNameWidgetAnimation(PikaSaveSettingAnimationEndName);
    PikaMaskAnimationBegin = PikaqiuGetNameWidgetAnimation(PikaMaskAnimationBeginName);
    PikaMaskAnimationEnd = PikaqiuGetNameWidgetAnimation(PikaMaskAnimationEndName);
    PikaGradualChangeAnimationBegin = PikaqiuGetNameWidgetAnimation(PikaGradualChangeAnimationBeginName);
    PikaGradualChangeAnimationEnd = PikaqiuGetNameWidgetAnimation(PikaGradualChangeAnimationEndName);
    //////////////////////////////////////////////////////////////////////////
    if (GetWorld())
    {
        //添加我们的打印
        if (PikaGamePromptErrorClass && PikaBoxWindowList)
        {
            PikaGamePromptErrorPtr = CreateWidget<UPikaGameErrorHint>(GetWorld(), PikaGamePromptErrorClass);
            check(PikaGamePromptErrorPtr);
            PikaGamePromptErrorPtr->PikaUIDataPtr = PikaUIDataPtr;
            //将PikaGamePromptErrorPtr添加到size Box里
            PikaAddSizeBox(PikaBoxWindowList, PikaGamePromptErrorPtr);
            PikaGamePromptErrorPtr->PikaSetMainHall(this);
            PikaGamePromptErrorPtr->PikaDrawUIToScreen();
        }

        //添加我们的HallKey
        if (PikaHallKeyClass && PikaBoxKeyHallList)
        {
            PikaHallKeyPtr = CreateWidget<UPikaUIHallKey>(GetWorld(), PikaHallKeyClass);
            check(PikaHallKeyPtr);
            PikaHallKeyPtr->PikaUIDataPtr = PikaUIDataPtr;
            //将PikaHallKeyPtr添加到WidgetSwitcher类的实例PikaBoxList里
            PikaAddSizeBox(PikaBoxKeyHallList, PikaHallKeyPtr);
            PikaHallKeyPtr->PikaSetMainHall(this);
            PikaHallKeyPtr->PikaDrawUIToScreen();
        }

        //添加Box列表
        if (PikaBoxList)
        {
            //隐藏自己
            PikaBoxList->SetVisibility(ESlateVisibility::Hidden);
            //获取存储
            if (PikaReadAndWriteClass)
            {
                PikaReadAndWritePtr = CreateWidget<UPikaUIReadAndWrite>(GetWorld(), PikaReadAndWriteClass);
                check(PikaReadAndWritePtr);
                PikaReadAndWritePtr->PikaUIDataPtr = PikaUIDataPtr;
                //将PikaReadAndWritePtr添加到WidgetSwitcher类的实例PikaBoxList里
                PikaBoxList->AddChild(PikaReadAndWritePtr);
                PikaReadAndWritePtr->PikaSetMainHall(this);
                PikaReadAndWritePtr->PikaDrawUIToScreen();
                //ReadAndWrite->SetButtonText(TD_WText[0], TD_WText[1]);
                PikaReadAndWritePtr->PikaSetNewButtonType(PikaETDButtonType::LoadingGame);
            }

            //游戏设置
            if (PikaHallGameSettingsClass)
            {
                PikaHallGameSettingsPtr = CreateWidget<UPikaUIHallGameSetting>(GetWorld(), PikaHallGameSettingsClass);
                check(PikaHallGameSettingsPtr);
                PikaHallGameSettingsPtr->PikaUIDataPtr = PikaUIDataPtr;
                //将PikaHallGameSettingsPtr添加到WidgetSwitcher里
                PikaBoxList->AddChild(PikaHallGameSettingsPtr);
                PikaHallGameSettingsPtr->PikaSetMainHall(this);
                PikaHallGameSettingsPtr->PikaDrawUIToScreen();
            }
        }

        //游戏提示
        if (PikaGameWarningClass, PikaBoxWarningList)
        {
            //隐藏自己
            PikaBoxWarningList->SetVisibility(ESlateVisibility::Hidden);

            PikaGameWarningPtr = CreateWidget<UPikaUIGameWarning>(GetWorld(), PikaGameWarningClass);
            check(PikaGameWarningPtr);
            PikaGameWarningPtr->PikaUIDataPtr = PikaUIDataPtr;
            //将PikaGameWarningPtr添加到WidgetSwitcher类的实例PikaBoxList里
            PikaAddSizeBox(PikaBoxWarningList, PikaGameWarningPtr);
            PikaGameWarningPtr->PikaSetMainHall(this);
            PikaGameWarningPtr->PikaDrawUIToScreen();
        }

        /*//添加网络web浏览功能
        if (WebBrowserClass, BoxWebList)
        {
            CurrentWebBrowser = CreateWidget<UUI_WebBrowser>(GetWorld(), WebBrowserClass);
            check(CurrentWebBrowser);
            CurrentWebBrowser->GameUIData = GameUIData;
            //添加进去
            AddUSizeBox(BoxWebList, CurrentWebBrowser);
            CurrentWebBrowser->SetMainHall(this);
            CurrentWebBrowser->DrawUIToSrceen();
            BoxWebList->SetVisibility(ESlateVisibility::Hidden);
        }
        if (MoveToLog)
        {
            MoveToLog->SetMianHall(this);
        }*/
    }
    //初始化动画
    PikaInitAnimation();
}

void UPikaUIMainHall::RemoveFromViewport()
{
    //UMG类相关
    if (PikaHallGameSettingsPtr)
        PikaHallGameSettingsPtr->RemoveFromViewport();
    PikaHallGameSettingsPtr = NULL;

    if (PikaReadAndWritePtr)
        PikaReadAndWritePtr->RemoveFromViewport();
    PikaReadAndWritePtr = NULL;

    if (PikaHallKeyPtr)
        PikaHallKeyPtr->RemoveFromViewport();
    PikaHallKeyPtr = NULL;

    if (PikaGamePromptErrorPtr)
        PikaGamePromptErrorPtr->RemoveFromViewport();
    PikaGamePromptErrorPtr = NULL;

    if (PikaGameWarningPtr)
        PikaGameWarningPtr->RemoveFromViewport();
    PikaGameWarningPtr = NULL;

    if (PikaCurrentWebBrowserPtr)
        PikaCurrentWebBrowserPtr->RemoveFromViewport();
    PikaCurrentWebBrowserPtr = NULL;

    Super::RemoveFromViewport();
}

void UPikaUIMainHall::PikaSetWarningWindowsState(bool IsVisibles, float StateTime /*= 0*/, FString MyWidget /*= ""*/, TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType /*= PikaEButtonSureType::BST_None*/)
{
    if (IsVisibles) 
    {
        //显示警告框
        PikaGameWarningPtr->PikaSetText(MyWidget);
        PikaBoxWarningList->SetVisibility(ESlateVisibility::Visible);
    }
    else 
    {
        PikaBoxWarningList->SetVisibility(ESlateVisibility::Hidden);
    }
    

}

void UPikaUIMainHall::PikaSetBoxListDisplayOrder(PikaBoxListType::Type BoxListType)
{
    if (PikaBoxList)
        PikaBoxList->SetActiveWidgetIndex(BoxListType);
}

PikaBoxListType::Type UPikaUIMainHall::PikaGetBoxListDisplayOrder() const
{
    if (PikaBoxList)
        return (PikaBoxListType::Type)PikaBoxList->GetActiveWidgetIndex();
    return (PikaBoxListType::Type)INDEX_NONE;
}

void UPikaUIMainHall::PikaInitAnimation()
{
    PikaMainUMGFadeInAndFadeOut(true);

    if (PikaBoxWindowList)
        PikaBoxWindowList->SetVisibility(ESlateVisibility::Hidden);

    if (PikaBoxList)
        PikaBoxList->SetVisibility(ESlateVisibility::Hidden);
}

void UPikaUIMainHall::PikaMainUMGFadeInAndFadeOut(bool IsOut, PikaqiuEAnimationState NewAnimationState /*= PikaqiuEAnimationState::EA_Empty*/)
{
    if (IsOut)
    {
        if (PikaGradualChangeAnimationBegin)
        {
            PlayAnimation(PikaGradualChangeAnimationBegin);
            PikaGradualChangeAnimationBegin->PikaqiuSetAnimationState(NewAnimationState);
        }
    }
    else
    {
        if (PikaGradualChangeAnimationEnd)
        {
            PlayAnimation(PikaGradualChangeAnimationEnd);
            PikaGradualChangeAnimationEnd->PikaqiuSetAnimationState(NewAnimationState);
        }
    }
}

void UPikaUIMainHall::PikaClickedMenuFadeInAndFadeOut(bool IsOut, PikaqiuEAnimationState NewAnimationState /*= EAnimationState::EA_Empty*/)
{
    if (IsOut)
    {
        if (PikaSaveSettingAnimationBegin)
        {
            //设置动画状态
            if (NewAnimationState != PikaqiuEAnimationState::EA_Empty)
                PikaSaveSettingAnimationBegin->PikaqiuSetAnimationState(NewAnimationState);

            //开始动画开始
            PlayAnimation(PikaSaveSettingAnimationBegin);
        }
    }
    else
    {
        if (PikaSaveSettingAnimationEnd)
        {
            //设置动画状态
            if (NewAnimationState != PikaqiuEAnimationState::EA_Empty)
                PikaSaveSettingAnimationEnd->PikaqiuSetAnimationState(NewAnimationState);

            //开始动画结束
            PlayAnimation(PikaSaveSettingAnimationEnd);
        }
    }
}

void UPikaUIMainHall::PikaWindowsFadeInAndFadeOut(bool IsVisibles, float StateTime /*= 0*/, FString MyWidget /*= ""*/, TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType /*= EButtonSureType::BST_None*/)
{
    if (IsVisibles)
    {
        if (PikaMaskAnimationBegin)
            PlayAnimation(PikaMaskAnimationBegin);
    }
    else
    {
        if (PikaMaskAnimationEnd)
            PlayAnimation(PikaMaskAnimationEnd);
    }
}

void UPikaUIMainHall::OnAnimationFinished_Implementation(const UWidgetAnimation* Animation)
{
    if (PikaSaveSettingAnimationEnd == Animation) 
    {
        //PikaSaveSettingAnimationEnd动画播放完成
        if (Animation->PikaqiuGetAnimationState() == PikaqiuEAnimationState::BST_QuitMeun)
        {
            PikaGetBoxList()->SetVisibility(ESlateVisibility::Hidden);
        }
    }
}

void UPikaUIMainHall::OnAnimationStarted_Implementation(const UWidgetAnimation* Animation)
{

}

PikaUIReadAndWrite.h

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

#pragma once

#include "CoreMinimal.h"
#include "PikaUIWidgetBase.h"
#include "PikaUISaveLoadBar.h"
#include "ScrollBox.h"
#include "PikaUIGameMenu.h"
#include "TextBlock.h"
#include "PikaUIReadAndWrite.generated.h"

class UPikaUIMainHall;
class UPikaUIGameMenu;

namespace PikaEClickedSaveType
{
    enum Type
    {
        CST_UnClicked,
        CST_HasStruck,
        CST_Max
    };
}

enum class PikaETDButtonType : uint8
{
    LoadingGame,
    SaveGame,
    Max
};


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

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

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

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

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

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

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TSubclassOf<UPikaUISaveLoadBar> PikaSaveLoadBarClass;

    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FText PikaCoverText;

    //准备读取该进度
    uint8 PikaIsLoading : 1;

    //当前类型
    PikaETDButtonType PikaButtonType;

public:
    //存储界面上,按钮是显示存储还是载入
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    TArray<FText> Pika_WText;

    UPikaUIReadAndWrite();

    //checkBox存储类型,表示是否产生了checkBox的点击事件
    TEnumAsByte<PikaEClickedSaveType::Type>  PikaClickedSaveType;

    //当前游戏存储区域Bar号码 (SaveBarNumber)
    int32 PikaCurrentSaveGameBoxNumber;

    //存储游戏的编号
    int32 PikaCurrentGameArchiveNumber;

    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen()override;

    virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None) override;

    //设置显示字体
    void PikaSetButtonText(FText Text_A);


    void PikaReadArchive();

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString) override;

    virtual void PikaOnClickedWidget(UWidget* MyWidget) override;

    //存储游戏,参数为存储内容时是覆盖操作还是新建操作
    void PikaSaveArchiveSlot(bool IsCopy);

    FORCEINLINE UPikaUIMainHall* PikaGetMainHall()const { return PikaMainHall; }

    FORCEINLINE UPikaUIGameMenu* PikaGetGameMenu()const { return PikaUIGameMenu; }

    void PikaSetMainHall(UPikaUIMainHall* NewWidget) { PikaMainHall = NewWidget; }

    void PikaSetGameMenu(UPikaUIGameMenu* NewWidget) { PikaUIGameMenu = NewWidget; }

    //checkBox显示
    void PikaShowOneClickButton(int32 NumberBIndex);
    void PikaShowOneClickButton();

    //设置按键类型
    void PikaSetNewButtonType(PikaETDButtonType NewButtonType) { PikaButtonType = NewButtonType; }

    //清除第几号存储
    void PikaClearNumberSave(int32 CurrentDelectNumber = INDEX_NONE);

private:
    UPikaUIMainHall* PikaMainHall;

    UButton* PikaReturnListButton;
    UButton* PikaLoadingGameButton;
    UTextBlock* PikaReturnListText;
    UTextBlock* PikaLoadingListText;
    UScrollBox* PikaSaveListButton;

    TArray<UPikaUISaveLoadBar*> PikaSaveLoadBarArrayEnter;
    UPikaUIGameMenu* PikaUIGameMenu;
};

PikaUIReadAndWrite.cpp

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

#include "Public/PikaUIReadAndWrite.h"

UPikaUIReadAndWrite::UPikaUIReadAndWrite()
{
    PikaCurrentGameArchiveNumber = INDEX_NONE;
    PikaCurrentSaveGameBoxNumber = INDEX_NONE;
    PikaIsLoading = false;
    PikaButtonType = PikaETDButtonType::Max;
    PikaClickedSaveType = PikaEClickedSaveType::CST_UnClicked;
}

void UPikaUIReadAndWrite::PikaDrawUIToScreen()
{
    PikaReturnListButton = PikaGetButtonFromBlueprint(PikaReturnListButtonName);
    PikaLoadingGameButton = PikaGetButtonFromBlueprint(PikaLoadingGameButtonName);
    PikaSaveListButton = Cast<UScrollBox>(PikaqiuGetBlueprintWidget(PikaSaveListButtonName));
    PikaReturnListText = Cast<UTextBlock>(GetWidgetFromName(PikaReturnListTextName));
    PikaLoadingListText = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaLoadingListTextName));

    //读取存档
    PikaReadArchive();
}

void UPikaUIReadAndWrite::PikaSetWarningWindowsState(bool IsVisibles, float StateTime /*= 0*/, FString MyWidget /*= ""*/, TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType /*= PikaEButtonSureType::BST_None*/)
{
    if (PikaMainHall)
    {
        PikaMainHall->PikaSetWarningWindowsState(IsVisibles, StateTime, MyWidget, MyButtonSureType);
    }
    if (PikaUIGameMenu)
    {
        PikaUIGameMenu->PikaSetWarningWindowsState(IsVisibles, StateTime, MyWidget, MyButtonSureType);
    }
}

void UPikaUIReadAndWrite::PikaSetButtonText(FText Text_A)
{
    if (PikaLoadingListText)
        PikaLoadingListText->SetText(Text_A);
}

void UPikaUIReadAndWrite::PikaReadArchive()
{
    if (PikaGetGameInstance() && GetWorld())
    {
        //创建20可以存档的区域
        if (PikaSaveLoadBarClass)
        {
            //注册
            for (int32 i = 0; i < Pika_SAVE_SaveBarNumber; i++)
            {
                UPikaUISaveLoadBar* PikaSlotBar = CreateWidget<UPikaUISaveLoadBar>(GetWorld(), PikaSaveLoadBarClass);
                if (PikaSlotBar)
                {
                    //初始化绘制信息
                    PikaSlotBar->PikaDrawUIToScreen();
                    //显示默认的存储的信息
                    PikaSlotBar->PikaClearSaveGameSlot();
                    //传递自己
                    PikaSlotBar->PikaSetReadAndWrite(this);
                    //存储游戏序列
                    PikaSlotBar->PikaSaveGameBoxNumber = i;
                    //添加如容器
                    PikaSaveLoadBarArrayEnter.Add(PikaSlotBar);

                    //添加到列表
                    if (PikaSaveListButton)
                    {
                        PikaSaveListButton->AddChild(PikaSlotBar);
                    }
                    else
                    {
                        PIKA_LOG(FString::Printf(TEXT("SaveListButton == NULL ,Failed to create %i storage"), i));
                    }
                }
                else
                {
                    PIKA_LOG(FString::Printf(TEXT("Failed to create %i storage"), i));
                }
            }
        }

        //游戏开始读取我们的存档
        if (PIKA_ISSAVE_SLOT(PIKA_SAVE_SLOT_NAME, 0))
        {
            //读取游戏存档在本地,并且验证存档
            UPikaGameSaveData* PikaGameSaveData = PikaGetGameInstance()->PikaVerificationSaveData();

            //获取存档信息
            if (PikaGameSaveData)
            {
                int32 PikaCurrentIndex = 0;
                //用读取的本地存档还原bar里的数据
                for (int32 PikaCurrentSaveValue : PikaGameSaveData->PikaReadGameData.PikaSaveGameBoxNumberArray)
                {
                    if (PikaCurrentSaveValue != INDEX_NONE)
                    {
                        if (PikaSaveLoadBarArrayEnter.IsValidIndex(PikaCurrentIndex))
                        {
                            PikaSaveLoadBarArrayEnter[PikaCurrentIndex]->PikaSetSaveGameText(PikaCurrentSaveValue, FText::FromString(PikaGameSaveData->PikaReadGameData.PikaSaveDataText[PikaCurrentSaveValue]));
                        }
                        else
                        {
                            PIKA_LOG(FString::Printf(TEXT("Nothingness %i"), PikaCurrentIndex));
                        }
                    }
                    PikaCurrentIndex++;
                }
            }
            else
            {
                PIKA_LOG("GameSaveData read failed!!");
            }
        }
        else
        {
            PIKA_LOG("There is no stored data!!");
        }
    }
    else
    {
        PIKA_LOG("Not obtained TowerDefenceGameInstance!!");
    }
}

void UPikaUIReadAndWrite::PikaLogPrintf(FString MyString)
{

}

//通过PikaGetButtonFromBlueprint获取的Button,在本类的基类UPikaUIWidgetBase里绑定
void UPikaUIReadAndWrite::PikaOnClickedWidget(UWidget* MyWidget)
{
    if (MyWidget == PikaLoadingGameButton)
    {
        check(PikaLoadingListText);

        //必须是敲击状态
        if (PikaClickedSaveType == PikaEClickedSaveType::CST_HasStruck)
        {
            //是读取界面
            if (PikaButtonType == PikaETDButtonType::LoadingGame)
            {
                if (PikaGetGameInstance())
                {
                    //检查当选中的PikaUISaveLoadBar里,是否能读取成功
                    PikaIsLoading = PikaGetGameInstance()->PikaIsLoadingGameData(PikaCurrentGameArchiveNumber);

                    if (PikaIsLoading)
                    {
                        //存档读取成功
                        PIKA_LOG("Loading Successfully");

                        //激活关闭菜单功能
                        if (PikaGetMainHall() && PikaGetMainHall()->PikaGetBoxList())
                        {
                            //GetMainHall()->ClickedMenuFadeInAndFadeOut(false, EAnimationState::EA_ReadyLoading);

                            //设置界面不可敲击
                            PikaGetMainHall()->SetVisibility(ESlateVisibility::HitTestInvisible);
                        }

                        /*//播放开始游戏动画
                        if (GetTowerDefenceHallGameMode())
                        {
                            GetTowerDefenceHallGameMode()->PlayCurrentCameraAnimation(ECameraArray::Camera_IntoGame, EMatineeArray::Matinee_Max, TD_CAMARE_BLENDTIME + 1);
                        }*/

                        //存储读档序列
                        PikaGetGameInstance()->PikaTmpArchive = PikaCurrentGameArchiveNumber;
                        if (GetWorld())
                            UGameplayStatics::OpenLevel(GetWorld(), "PikaGodStone_Level_Main_01");
                    }
                    else
                    {
                        PIKA_LOG("Loading Failure");
                    }
                }
                else
                {
                    PIKA_LOG("GetTowerDefenceGameInstance() = NULL!");
                }
            }//是存储界面
            else if (PikaButtonType == PikaETDButtonType::SaveGame)
            {
                if (PikaGetPlayerController() && PikaGetGameInstance())
                {
                    //游戏存储
                    if (PikaCurrentGameArchiveNumber == INDEX_NONE)//存储到一个空的存储条里面
                    {
                        PikaSaveArchiveSlot(false);
                    }
                    else//需要覆盖一个存档,这个时候会跳出一个弹窗,确定是否要覆盖
                    {
                        PikaSaveArchiveSlot(true);
                        /*//打开在大厅
                        if (GetMainHall() && GetMainHall()->GetGameWarning())
                        {
                            //弹窗状态设置为显示
                            SetWarningWindowsState(true, 0, FString::Printf(TEXT("%s # %i?"), *CoverText.ToString(), CurrentGameArchiveNumber), EButtonSureType::BST_CoverSave);
                        }

                        //打开在游戏
                        if (GetGameMenu() && GetGameMenu()->GetMianScreen() && GetGameMenu()->GetMianScreen()->GetGameWarning())
                        {
                            SetWarningWindowsState(true, 0, FString::Printf(TEXT("%s # %i?"), *CoverText.ToString(), CurrentGameArchiveNumber), EButtonSureType::BST_CoverSave);
                        }*/
                    }
                }
                else
                {
                    PIKA_LOG("GetTowerDefencePlayerController() = NULL!");
                }
            }
        }

    }
    if (MyWidget == PikaReturnListButton)
    {
    
        //取消
        //隐藏自己
        if (PikaUIGameMenu)
            PikaUIGameMenu->PikaGetMainInterface()->PikaGetBoxSaveGameArray()->SetVisibility(ESlateVisibility::Hidden);;
        //隐藏自己
        if (PikaMainHall)
            PikaMainHall->PikaGetBoxList()->SetVisibility(ESlateVisibility::Hidden);;

        /*if (TDButtonType == ETDButtonType::LoadingGame)
        {
            if (GetMainHall() && GetMainHall()->GetBoxList())
            {
                GetMainHall()->ClickedMenuFadeInAndFadeOut(false);
            }
        }
        else if (TDButtonType == ETDButtonType::SaveGame)
        {
            if (GetGameMenu() && GetGameMenu()->GetMianScreen())
            {
                GetGameMenu()->GetMianScreen()->ClickedMenuFadeInAndFadeOut(false);
            }
        }*/
    }
}

//存储游戏,参数为存储内容时是覆盖操作还是新建操作
void UPikaUIReadAndWrite::PikaSaveArchiveSlot(bool IsCopy)
{
    //标记存档序列
    if (PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon)
        PikaGetPlayerController()->PikaGlobalConfigPrePlayerCon->PikaSaveCount++;

    if (IsCopy)
    {
        //覆盖,指定要覆盖的存档
        //PikaCurrentSaveGameBoxNumber的值为点击了某个存档bar类PikaUISaveLoadBar后,在PikaUISaveLoadBar类里设置的
        //------------------------------------
        //在已有的存档上进行覆盖存档时,由PikaUIReadAndWrite::PikaReadArchive读取的本地存档,
        //并将PikaCurrentGameArchiveNumber的值保存在PikaUISaveLoadBar里的PikaGameArchiveNumber属性里
        //点击某个PikaUISaveLoadBar类的按钮后,
        //通过读取PikaUISaveLoadBar类里的PikaGameArchiveNumber属性就能得到PikaCurrentGameArchiveNumber的值
        bool PikaIsSave = PikaGetPlayerController()->PikaSaveGameData(PikaFSaveSlotDataChannel(PikaCurrentSaveGameBoxNumber, PikaCurrentGameArchiveNumber));

        if (PikaIsSave)
        {
            //更新显示日期
            //上面的PikaSaveGameData成功时,新的日期已经存档,此处再次读取存档,用来更新界面
            FText PikaNewText = PikaGetPlayerController()->PikaGetSaveInformation(PikaCurrentGameArchiveNumber);
            if (PikaCurrentSaveGameBoxNumber >= 0 && PikaCurrentSaveGameBoxNumber < PikaSaveLoadBarArrayEnter.Num())
            {
                PikaSaveLoadBarArrayEnter[PikaCurrentSaveGameBoxNumber]->PikaSetSaveGameText(PikaCurrentGameArchiveNumber, PikaNewText);
                PikaSaveLoadBarArrayEnter[PikaCurrentSaveGameBoxNumber]->PikaUpdateClickState();
            }
            PIKA_LOG("Save Successfully");
        }
        else
        {
            PIKA_LOG("Save Failure");
        }
    }
    else//新建
    {
        //PikaGetCurrentSaveArchiveNumber:获取当前存档的编号
        int32 PikaCurrenMaxArchiveNumber = PikaGetGameInstance()->PikaGetCurrentSaveArchiveNumber();
        if (PikaCurrenMaxArchiveNumber == INDEX_NONE)
        {
            PIKA_LOG(" GetTowerDefenceGameInstance()->GetCurrentSaveArchiveNumber() = INDEX_NONE,Is error!!");
            return;
        }
        //将最新的存档数PikaCurrenMaxArchiveNumber作为下一个存档的一个序列号角标
        //PikaCurrentSaveGameBoxNumber的值为点击了某个存档bar类PikaUISaveLoadBar后,在PikaUISaveLoadBar类里设置的
        bool PikaIsSave = PikaGetPlayerController()->PikaSaveGameData(PikaFSaveSlotDataChannel(PikaCurrentSaveGameBoxNumber, PikaCurrenMaxArchiveNumber));

        if (PikaIsSave)
        {
            //更新显示日期
            FText PikaNewTDText = PikaGetPlayerController()->PikaGetSaveInformation(PikaCurrenMaxArchiveNumber);
            if (PikaCurrentSaveGameBoxNumber >= 0 && PikaCurrentSaveGameBoxNumber < PikaSaveLoadBarArrayEnter.Num())
            {
                PikaSaveLoadBarArrayEnter[PikaCurrentSaveGameBoxNumber]->PikaSetSaveGameText(PikaCurrenMaxArchiveNumber, PikaNewTDText);
                PikaSaveLoadBarArrayEnter[PikaCurrentSaveGameBoxNumber]->PikaUpdateClickState();
            }
            PIKA_LOG("Save Successfully");
        }
        else
        {
            PIKA_LOG("Save Failure");
        }
    }
}

//checkBox显示
void UPikaUIReadAndWrite::PikaShowOneClickButton(int32 NumberBIndex)
{
    for (int32 i = 0; i < PikaSaveLoadBarArrayEnter.Num(); i++)
    {
        if (PikaSaveLoadBarArrayEnter[i]->PikaSaveGameBoxNumber == NumberBIndex)
            PikaSaveLoadBarArrayEnter[i]->PikaShowOneCheckBox(true);
        else
            PikaSaveLoadBarArrayEnter[i]->PikaShowOneCheckBox(false);
    }
}
//checkBox显示
void UPikaUIReadAndWrite::PikaShowOneClickButton()
{
    for (int32 i = 0; i < PikaSaveLoadBarArrayEnter.Num(); i++)
    {
        if (PikaSaveLoadBarArrayEnter.IsValidIndex(i))
        {
            PikaSaveLoadBarArrayEnter[i]->PikaShowOneCheckBox(false);
        }
    }
}

//还原
void UPikaUIReadAndWrite::PikaClearNumberSave(int32 CurrentDelectNumber /*= INDEX_NONE*/)
{
    for (int32 i = 0; i < PikaSaveLoadBarArrayEnter.Num(); i++)
    {
        if (PikaSaveLoadBarArrayEnter.IsValidIndex(i))
        {
            if (PikaSaveLoadBarArrayEnter[i]->PikaGameArchiveNumber == CurrentDelectNumber)
            {
                //还原数据显示
                PikaSaveLoadBarArrayEnter[i]->PikaClearSaveGameSlot();
                break;
            }
        }
    }
    PikaCurrentSaveGameBoxNumber = INDEX_NONE;
    PikaCurrentGameArchiveNumber = INDEX_NONE;
    PikaClickedSaveType = PikaEClickedSaveType::CST_UnClicked;
    PikaShowOneClickButton();
}

PikaUISaveLoadBar.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 "CheckBox.h"
#include "Button.h"
#include "PikaUISaveLoadBar.generated.h"

class UPikaUIReadAndWrite;

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

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

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

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

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

    //删除存档时,警告框上显示的文件
    //确定删除该存档吗,皮卡~~~
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FText PikadeleteText;

    //无存档时的默认显示文本
    UPROPERTY(EditDefaultsOnly, Category = "PikaUI")
    FText PikaDefaultNoDataText;
public:
    //游戏bar序列,在PikaUIReadAndWrite::PikaReadArchive里循环生成本类时,将循环的index存入该值
    int32 PikaSaveGameBoxNumber;

    //游戏存储后,本存档存档编号,默认值为-1
    int32 PikaGameArchiveNumber;

    UPikaUISaveLoadBar();

    //调用去初始化我们的UI
    virtual void PikaDrawUIToScreen() override;

    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString) override;

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

    void PikaShowOneCheckBox(bool ClickedWidget);

    void PikaUpdateClickState();

    virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None) override;

    virtual void PikaOnClickedWidget(UWidget* MyWidget) override;

    //设置字体数据信息显示
    void PikaSetSaveGameText(int32 IndexSaveData, FText MySaveText);

    //清除游戏存储显示节点
    void PikaClearSaveGameSlot();

    void PikaSetReadAndWrite(UPikaUIReadAndWrite* NewReadAndWrite) { PikaReadAndWrite = NewReadAndWrite; }

private:
    UTextBlock* PikaGameTextBlock;
    UTextBlock* PikaSaveGameDataBlock;
    UButton* PikaDeleteMyDataButton;
    UCheckBox* PikaClickedBoxButton;
    UPikaUIReadAndWrite*  PikaReadAndWrite;
};

PikaUISaveLoadBar.cpp

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

#include "Public/PikaUISaveLoadBar.h"

UPikaUISaveLoadBar::UPikaUISaveLoadBar()
{
    PikaGameArchiveNumber = -1;
    PikaSaveGameBoxNumber = -1;
}

void UPikaUISaveLoadBar::PikaDrawUIToScreen()
{
    PikaGameTextBlock = Cast<UTextBlock>(GetWidgetFromName(PikaGameTextBlockName));
    PikaSaveGameDataBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaSaveGameDataBlockName));
    PikaDeleteMyDataButton = PikaGetButtonFromBlueprint(PikaDeleteMyDataButtonName);
    PikaClickedBoxButton = Cast<UCheckBox>(PikaqiuGetBlueprintWidget(PikaClickedBoxButtonName));

    //绑定按键
    if (PikaClickedBoxButton)
        PikaClickedBoxButton->PikaqiuOnCheckStateChanged.AddDynamic(this, &UPikaUISaveLoadBar::PikaClickedCheckBox);
}

void UPikaUISaveLoadBar::PikaLogPrintf(FString MyString)
{

}

void UPikaUISaveLoadBar::PikaClickedCheckBox(UWidget* MyWidget, bool ClickedWidget)
{
    if (MyWidget == PikaClickedBoxButton)
    {
        if (PikaReadAndWrite)
        {
            //显示当前游戏敲击显示
            //PikaSaveGameBoxNumber的值为PikaUIReadAndWrite类循环加载本类里时用循环的index设置的
            PikaReadAndWrite->PikaShowOneClickButton(PikaSaveGameBoxNumber);
            //设置当前存储游戏的载体数字
            PikaUpdateClickState();
            //打印选择第几个存储
            PIKA_LOG(FString::Printf(TEXT("Select archive # %i"), PikaGameArchiveNumber));
        }
    }
}

void UPikaUISaveLoadBar::PikaShowOneCheckBox(bool ClickedWidget)
{
    if (PikaClickedBoxButton)
    {
        if (ClickedWidget)
            PikaClickedBoxButton->SetCheckedState(ECheckBoxState::Checked);
        else
            PikaClickedBoxButton->SetCheckedState(ECheckBoxState::Unchecked);
    }
}

void UPikaUISaveLoadBar::PikaUpdateClickState()
{
    if (PikaReadAndWrite)
    {
        PikaReadAndWrite->PikaCurrentSaveGameBoxNumber = PikaSaveGameBoxNumber;
        //游戏存储后,本存档存档编号,默认值为-1
        //PikaGameArchiveNumber该值由PikaUIReadAndWrite类调用PikaDrawUIToScreen函数进行加载时,
        //由PikaUIReadAndWrite::PikaReadArchive函数调用本类里的PikaSetSaveGameText函数进行设置
        //PikaUIReadAndWrite::PikaSaveArchiveSlot的新建逻辑时,PikaGameArchiveNumber为-1
        PikaReadAndWrite->PikaCurrentGameArchiveNumber = PikaGameArchiveNumber;
        PikaReadAndWrite->PikaClickedSaveType = PikaEClickedSaveType::CST_HasStruck;
    }
}

void UPikaUISaveLoadBar::PikaSetWarningWindowsState(bool IsVisibles, float StateTime /*= 0*/, FString MyWidget /*= ""*/, TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType /*= PikaEButtonSureType::BST_None*/)
{
    if (PikaReadAndWrite)
    {
        PikaReadAndWrite->PikaSetWarningWindowsState(IsVisibles, StateTime, MyWidget, MyButtonSureType);
    }
}

void UPikaUISaveLoadBar::PikaOnClickedWidget(UWidget* MyWidget)
{
    if (MyWidget == PikaDeleteMyDataButton)
    {
        if (PikaGameArchiveNumber != INDEX_NONE)
        {
            //游戏大厅
            if (PikaReadAndWrite &&
                PikaReadAndWrite->PikaGetMainHall() &&
                PikaReadAndWrite->PikaGetMainHall()->PikaGetGameWarning())
            {
                //弹窗状态设置为显示
                PikaSetWarningWindowsState(true, 0, FString::Printf(TEXT("%s %i"), *PikadeleteText.ToString(), PikaGameArchiveNumber), PikaEButtonSureType::BST_DeleteSave);
                //设置是哪个控件
                PikaReadAndWrite->PikaGetMainHall()->PikaGetGameWarning()->PikaCurrentGameArchiveNumber = PikaGameArchiveNumber;
                PikaReadAndWrite->PikaGetMainHall()->PikaGetGameWarning()->PikaSetGameButtonSureType(PikaEButtonSureType::BST_DeleteSave);
                //播放背景模糊动画
                PikaReadAndWrite->PikaGetMainHall()->PikaWindowsFadeInAndFadeOut(true);
            }

            //游戏
            if (PikaReadAndWrite &&
                PikaReadAndWrite->PikaGetGameMenu() &&
                PikaReadAndWrite->PikaGetGameMenu()->PikaGetMainInterface() &&
                PikaReadAndWrite->PikaGetGameMenu()->PikaGetMainInterface()->PikaGetGameWarning())
            {
                //弹窗状态设置为显示
                PikaSetWarningWindowsState(true, 0, FString::Printf(TEXT("%s %i"), *PikadeleteText.ToString(), PikaGameArchiveNumber), PikaEButtonSureType::BST_DeleteSave);
                //设置是哪个控件
                PikaReadAndWrite->PikaGetGameMenu()->PikaGetMainInterface()->PikaGetGameWarning()->PikaCurrentGameArchiveNumber = PikaGameArchiveNumber;
                PikaReadAndWrite->PikaGetGameMenu()->PikaGetMainInterface()->PikaGetGameWarning()->PikaSetGameButtonSureType(PikaEButtonSureType::BST_DeleteSave);
            }
        }
        else
        {
            PIKA_LOG("No archive");
        }
    }
}

//设置字体数据信息显示
void UPikaUISaveLoadBar::PikaSetSaveGameText(int32 IndexSaveData, FText MySaveText)
{
    //赋值我们的存档序列
    PikaGameArchiveNumber = IndexSaveData;

    //第几个存档
    if (PikaGameTextBlock)
        PikaGameTextBlock->SetText(FText::FromString(FString::Printf(TEXT("Storage number %i"), IndexSaveData)));

    //存档日期
    if (PikaSaveGameDataBlock)
        PikaSaveGameDataBlock->SetText(MySaveText);
}

void UPikaUISaveLoadBar::PikaClearSaveGameSlot()
{
    if(!(PikaDefaultNoDataText.IsEmpty()))
        PikaSetSaveGameText(INDEX_NONE, PikaDefaultNoDataText);
}

PikaUITowerInfo.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 "MultiLineEditableTextBox.h"
#include "PikaUITowerInfo.generated.h"


/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUITowerInfo : public UPikaUIWidgetBase
{
    GENERATED_BODY()
    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaCharacterNameBlockName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaComsumeGlodBlockName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName  PikaCharacterHealthBlockName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaCharacterAttackBlockName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaCharacterArmorBlockName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaCharacterAttackSpeedBlockName;

    UPROPERTY(EditDefaultsOnly, Category = UI)
    FName PikaIntroductionName;
public:
    FPikaCharacterInformationWidget* PikaCharacterInformationWidget;

    //作为初始化实例
    virtual void PikaDrawUIToScreen() override;

    //只要角色值变动机会更新
    void PikaUpdateCharacterInformation();
private:
    UTextBlock* PikaCharacterNameBlock;
    UTextBlock* PikaComsumeGlodBlock;
    UTextBlock* PikaCharacterHealthBlock;
    UTextBlock* PikaCharacterAttackBlock;
    UTextBlock* PikaCharacterArmorBlock;
    UTextBlock* PikaCharacterAttackSpeedBlock;
    UMultiLineEditableTextBox* PikaIntroduction;
protected:
    //virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent)override;
};

PikaUITowerInfo.cpp

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

#include "Public/PikaUITowerInfo.h"

void UPikaUITowerInfo::PikaDrawUIToScreen()
{
    if (!PikaCharacterNameBlock)
        PikaCharacterNameBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterNameBlockName));

    if (!PikaComsumeGlodBlock)
        PikaComsumeGlodBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaComsumeGlodBlockName));

    if (!PikaCharacterHealthBlock)
        PikaCharacterHealthBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterHealthBlockName));

    if (!PikaCharacterAttackBlock)
        PikaCharacterAttackBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterAttackBlockName));

    if (!PikaCharacterArmorBlock)
        PikaCharacterArmorBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterArmorBlockName));

    if (!PikaCharacterAttackSpeedBlock)
        PikaCharacterAttackSpeedBlock = Cast<UTextBlock>(PikaqiuGetBlueprintWidget(PikaCharacterAttackSpeedBlockName));

    if (!PikaIntroduction)
        PikaIntroduction = Cast<UMultiLineEditableTextBox>(PikaqiuGetBlueprintWidget(PikaIntroductionName));
}

void UPikaUITowerInfo::PikaUpdateCharacterInformation()
{
    if (PikaCharacterInformationWidget)
    {
        if (PikaCharacterNameBlock)
            PikaCharacterNameBlock->SetText(FText::FromName(PikaCharacterInformationWidget->PikaCharacterName));

        if (PikaComsumeGlodBlock)
            PikaComsumeGlodBlock->SetText(FText::AsNumber(PikaCharacterInformationWidget->PikaComsumeGlod));

        if (PikaCharacterHealthBlock)
            PikaCharacterHealthBlock->SetText(FText::AsNumber(PikaCharacterInformationWidget->PikaCharacterHealth));

        if (PikaCharacterAttackBlock)
            PikaCharacterAttackBlock->SetText(FText::AsNumber(PikaCharacterInformationWidget->PikaCharacterAttack));

        if (PikaCharacterArmorBlock)
            PikaCharacterArmorBlock->SetText(FText::AsNumber(PikaCharacterInformationWidget->PikaCharacterArmor));

        if (PikaCharacterAttackSpeedBlock)
            PikaCharacterAttackSpeedBlock->SetText(FText::AsNumber(PikaCharacterInformationWidget->PikaCharacterAttackSpeed));

        if (PikaIntroduction)
            PikaIntroduction->SetText(PikaCharacterInformationWidget->PikaCharacterIntroduction);
    }
}

PikaUIWebBrowser.h

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

#pragma once

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

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

};

PikaUIWebBrowser.cpp

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

#include "Public/PikaUIWebBrowser.h"

PikaUIWidgetBase.h

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

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Button.h"
#include "PikaUIData.h"
#include "SizeBox.h"
#include "Public/PikaGodStonePlayerController.h"
#include "PikaGameSaveData.h"
#include "Slider.h"
#include "ComboBoxString.h"
#include "CheckBox.h"
#include "PikaUIWidgetBase.generated.h"

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

UENUM()
namespace PikaEButtonSureType
{
    enum Type
    {
        BST_None,

        BST_DeleteSave,        //删除游戏存储
        BST_CoverSave,        //覆盖游戏存储
        BST_QuitMeun,        //退到主菜单
        BST_QuitGame,
        BST_MAX
    };
}

//面板Box类型
namespace PikaBoxListType
{
    //枚举的定义顺序对应于UPikaUIMainHall::PikaDrawUIToScreen里添加到PikaBoxList的顺序
    enum Type
    {
        BOXLIST_SaveList,//对应PikaBoxList里第一个被添加进去的控件
        BOXLIST_GameSettings,//对应PikaBoxList里第二个被添加进去的控件
        BOXLIST_Max,
    };
}

//作为角色的基础信息现实
USTRUCT()
struct FPikaCharacterInformationWidget
{
    GENERATED_BODY()

    //角色名字
    FName PikaCharacterName;
    //金币消费
    int32 PikaComsumeGlod;
    //生命值
    float PikaCharacterHealth;
    //攻击力
    float PikaCharacterAttack;
    //防御力
    float PikaCharacterArmor;
    //攻击速度
    float PikaCharacterAttackSpeed;
    //角色简介
    FText PikaCharacterIntroduction;

    FPikaCharacterInformationWidget() :
        PikaComsumeGlod(0),
        PikaCharacterHealth(0),
        PikaCharacterAttack(0),
        PikaCharacterArmor(0),
        PikaCharacterAttackSpeed(0),
        PikaCharacterIntroduction(FText::FromString("NoData")),
        PikaCharacterName(FName(*FString("None")))
    {}

    //重载等号操作符
    FORCEINLINE FPikaCharacterInformationWidget& operator=(const FPikaCharacterInformationWidget& CharacterInformationWidget)
    {
        PikaComsumeGlod = CharacterInformationWidget.PikaComsumeGlod;
        PikaCharacterHealth = CharacterInformationWidget.PikaCharacterHealth;
        PikaCharacterAttack = CharacterInformationWidget.PikaCharacterAttack;
        PikaCharacterArmor = CharacterInformationWidget.PikaCharacterArmor;
        PikaCharacterAttackSpeed = CharacterInformationWidget.PikaCharacterAttackSpeed;
        PikaCharacterName = CharacterInformationWidget.PikaCharacterName;
        PikaCharacterIntroduction = CharacterInformationWidget.PikaCharacterIntroduction;
        return *this;
    }

    FORCEINLINE bool operator==(const FPikaCharacterInformationWidget& NewCharacterInformation)
    {
        return (PikaComsumeGlod == NewCharacterInformation.PikaComsumeGlod &&
            PikaCharacterHealth == NewCharacterInformation.PikaCharacterHealth &&
            PikaCharacterAttack == NewCharacterInformation.PikaCharacterAttack &&
            PikaCharacterArmor == NewCharacterInformation.PikaCharacterArmor &&
            PikaCharacterAttackSpeed == NewCharacterInformation.PikaCharacterAttackSpeed &&
            PikaCharacterName == NewCharacterInformation.PikaCharacterName);
    }
    //还原出厂设置
    void PikaClearCharacterInformationWidget()
    {
        PikaComsumeGlod = 0;
        PikaCharacterHealth = 0;
        PikaCharacterAttack = 0;
        PikaCharacterArmor = 0;
        PikaCharacterAttackSpeed = 0;
        PikaCharacterName = FName(*FString("None"));
        PikaCharacterIntroduction = FText::FromString("NoData");
    }
};

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API UPikaUIWidgetBase : public UUserWidget
{
    GENERATED_BODY()
    
public:
    //存储弹窗类型
    UPROPERTY()
    TEnumAsByte<PikaEButtonSureType::Type> PikaGameButtonSureType;
    //设置弹窗类型
    //ButtonSureType: 弹窗类型
    void PikaSetGameButtonSureType(TEnumAsByte<PikaEButtonSureType::Type> MewButtonSureType);

    //UIData的指针
    UPikaUIData * PikaUIDataPtr;
    //直接获取Button
    UButton * PikaGetButtonFromBlueprint(FName BlueprintWidgetName) const;
    UButton * PikaGetButtonFromBlueprint(FString BlueprintWidgetName) const;

    USlider* PikaGetSliderFormBlueprint(FString BlueprintWidgetName) const;
    USlider* PikaGetSliderFormBlueprint(FName BlueprintWidgetName) const;

    UFUNCTION()
    virtual void PikaChangedValue(float Val) {}

    virtual    void PikaUpdateAttibe() {}

    UComboBoxString* PikaGetComboBoxStringFormBlueprint(FName BlueprintWidgetName) const;
    UComboBoxString* PikaGetComboBoxStringFormBlueprint(FString BlueprintWidgetName) const;

    UCheckBox* PikaGetCheckBoxFormBlueprint(FName BlueprintWidgetName) const;
    UCheckBox* PikaGetCheckBoxFormBlueprint(FString BlueprintWidgetName) const;

    UFUNCTION()
    virtual void PikaSelectionChangedBySelf(UWidget* MyWidget, FString SelectedItem, ESelectInfo::Type SelectionType) {}
    

    //调用后去初始化自已的UI,需要手动调用
    //相当于BeginPlay,用于解决BeginPlay执行时,有些资源可能还没加载完成的问题
    //继承自本类的UI类里,会在主界面类里手动调用本函数
    virtual void PikaDrawUIToScreen() {}
    //负责打印GameLOG
    virtual void PikaLogPrintf(FString MyString) {}

    void PikaAddSizeBox(USizeBox* TargetWidget , UWidget* BaseWidget);

    //获取PlayerController
    APikaGodStonePlayerController* PikaGetPlayerController();
    //获取PlayerState
    APikaGodStonePlayerState* PikaGetPlayerState();
    //获取GameInstance
    UPikaGodStoneGameInstance* PikaGetGameInstance();
    //获取SaveGameData
    FPikaGodStoneSaveGameData* PikaGetSaveGameData();

    ///////////////////////////////Button///////////////////////////////////////////
    UFUNCTION()
    virtual void PikaOnClickedWidget(UWidget* MyWidget) {}

    UFUNCTION()
    virtual void PikaOnHoveredWidget(UWidget* CurrentButton) {}

    UFUNCTION()
    virtual void PikaOnUnHoveredWidget() {}

    UFUNCTION()
    virtual void PikaClickedCheckBox(UWidget* MyWidget, bool ClickedWidget) {}

    virtual void PikaSetWarningWindowsState(bool IsVisibles, float StateTime = 0, FString MyWidget = "", TEnumAsByte<PikaEButtonSureType::Type> MyButtonSureType = PikaEButtonSureType::BST_None) {}
};

PikaUIWidgetBase.cpp

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

#include "Public/PikaUIWidgetBase.h"

void UPikaUIWidgetBase::PikaSetGameButtonSureType(TEnumAsByte<PikaEButtonSureType::Type> MewButtonSureType)
{
    PikaGameButtonSureType = MewButtonSureType;
}

UButton * UPikaUIWidgetBase::PikaGetButtonFromBlueprint(FName BlueprintWidgetName) const
{
    /*
     * 修改了引擎后,除了Button,所有widget都可以用Cast进行强转
     */
    UButton * PikaMyButton = Cast<UButton>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));
    if (PikaMyButton) 
    {
        PikaMyButton->PikaqiuClickedWidget.AddDynamic(this , &UPikaUIWidgetBase::PikaOnClickedWidget);
        PikaMyButton->PikaqiuHoverWidget.AddDynamic(this, &UPikaUIWidgetBase::PikaOnHoveredWidget);
        PikaMyButton->OnUnhovered.AddDynamic(this, &UPikaUIWidgetBase::PikaOnUnHoveredWidget);
    }
    return PikaMyButton;
}

UButton * UPikaUIWidgetBase::PikaGetButtonFromBlueprint(FString BlueprintWidgetName) const
{
    UButton * PikaMyButton = Cast<UButton>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));
    if (PikaMyButton)
    {
        PikaMyButton->PikaqiuClickedWidget.AddDynamic(this, &UPikaUIWidgetBase::PikaOnClickedWidget);
        PikaMyButton->PikaqiuHoverWidget.AddDynamic(this, &UPikaUIWidgetBase::PikaOnHoveredWidget);
        PikaMyButton->OnUnhovered.AddDynamic(this, &UPikaUIWidgetBase::PikaOnUnHoveredWidget);
    }
    return PikaMyButton;
}


USlider* UPikaUIWidgetBase::PikaGetSliderFormBlueprint(FString BlueprintWidgetName) const
{
    USlider* TmpSlider = Cast<USlider>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));

    if (TmpSlider)
    {
        TmpSlider->OnValueChanged.AddDynamic(this, &UPikaUIWidgetBase::PikaChangedValue);
    }
    return TmpSlider;
}

USlider* UPikaUIWidgetBase::PikaGetSliderFormBlueprint(FName BlueprintWidgetName) const
{
    USlider* TmpSlider = Cast<USlider>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));

    if (TmpSlider)
    {
        TmpSlider->OnValueChanged.AddDynamic(this, &UPikaUIWidgetBase::PikaChangedValue);
    }
    return TmpSlider;
}

UComboBoxString* UPikaUIWidgetBase::PikaGetComboBoxStringFormBlueprint(FName BlueprintWidgetName) const
{
    UComboBoxString* PikaComboBoxString = Cast<UComboBoxString>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));
    if (PikaComboBoxString)
    {
        PikaComboBoxString->PikaqiuOnSelectionChangedBySelf.AddDynamic(this, &UPikaUIWidgetBase::PikaSelectionChangedBySelf);
    }
    return PikaComboBoxString;
}

UComboBoxString* UPikaUIWidgetBase::PikaGetComboBoxStringFormBlueprint(FString BlueprintWidgetName) const
{
    UComboBoxString* PikaComboBoxString = Cast<UComboBoxString>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));
    if (PikaComboBoxString)
    {
        PikaComboBoxString->PikaqiuOnSelectionChangedBySelf.AddDynamic(this, &UPikaUIWidgetBase::PikaSelectionChangedBySelf);
    }
    return PikaComboBoxString;
}

UCheckBox* UPikaUIWidgetBase::PikaGetCheckBoxFormBlueprint(FName BlueprintWidgetName) const
{
    UCheckBox* PikaCheckBoxWidget = Cast<UCheckBox>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));
    if (PikaCheckBoxWidget)
    {
        PikaCheckBoxWidget->PikaqiuOnCheckStateChanged.AddDynamic(this, &UPikaUIWidgetBase::PikaClickedCheckBox);
    }
    return PikaCheckBoxWidget;
}

UCheckBox* UPikaUIWidgetBase::PikaGetCheckBoxFormBlueprint(FString BlueprintWidgetName) const
{
    UCheckBox* PikaCheckBoxWidget = Cast<UCheckBox>(PikaqiuGetBlueprintWidget(BlueprintWidgetName));
    if (PikaCheckBoxWidget)
    {
        PikaCheckBoxWidget->PikaqiuOnCheckStateChanged.AddDynamic(this, &UPikaUIWidgetBase::PikaClickedCheckBox);
    }
    return PikaCheckBoxWidget;
}

void UPikaUIWidgetBase::PikaAddSizeBox(USizeBox* TargetWidget, UWidget* BaseWidget)
{
    if (TargetWidget && BaseWidget) 
    {
        TargetWidget->AddChild(BaseWidget);
    }
}

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

APikaGodStonePlayerState* UPikaUIWidgetBase::PikaGetPlayerState()
{
    return PikaGetPlayerController() ? PikaGetPlayerController()->PikaGetTowerPlayerState() : NULL ;
}

UPikaGodStoneGameInstance* UPikaUIWidgetBase::PikaGetGameInstance()
{
    return PikaGetPlayerState() ? PikaGetPlayerState()->PikaGetGameInstance() : NULL;
}

/*FPikaGodStoneSaveGameData* UPikaUIWidgetBase::PikaGetSaveGameData()
{
    return (PikaGetGameInstance() && PikaGetGameInstance()->PikaGetGameSaveData()) ? &(PikaGetGameInstance()->PikaGetGameSaveData()->PikaGodStongSaveData) : NULL;
}*/

FPikaGodStoneSaveGameData* UPikaUIWidgetBase::PikaGetSaveGameData()
{
    return PikaGetPlayerController() != nullptr ? (PikaGetPlayerController()->PikaGlobalConfigPre ? &PikaGetPlayerController()->PikaGlobalConfigPre->PikaGameData : nullptr) : nullptr;
}

PikaWorldSettings.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/WorldSettings.h"
#include "PikaWorldSettings.generated.h"

/**
 * 
 */
UCLASS()
class PIKAGODSTONE_API APikaWorldSettings : public AWorldSettings
{
    GENERATED_BODY()
public:
    APikaWorldSettings();
    //当前关卡的序列ID
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PikaBaseSettings", meta = (DisplayName = "Level Number"))
    int32 PikaCurrentLevel;
};

PikaWorldSettings.cpp

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

#include "Public/PikaWorldSettings.h"

//PikaGameUserSettings属于ini配置类,该构造函数只会在代码编译时以及启动游戏时分别调用一次,在运行中不会再次执行
APikaWorldSettings::APikaWorldSettings()
{
    PikaCurrentLevel = 1;
}

11

22