[UE4]使用C++重寫藍圖,SpawnObject根據類型動態創建UObject


先大量使用藍圖制作項目,后續再用C++把復雜的藍圖重寫一遍,用C++代碼按照藍圖依葫蘆畫瓢就可以了,很簡單,但需要遵守一些原則:

 

第一種方法:使用繼承

一、創建一個C++類作為藍圖的父類(C++類繼承藍圖一樣的父類),在UE4中修改藍圖的父類。

二、C++類中的方法、成員變量與藍圖一一對應,並且方法和成員變量名稱不能與藍圖的重復。

三、A藍圖不能直接使用B藍圖的變量,A藍圖把要公開的變量封裝在函數內返回,並且只返回UE4自帶的基礎變量類型,不能返回自定義類型,以方便C++重寫時返回C++中的成員變量。

四、用C++中實現好的方法逐個替換藍圖中方法,每次替換一個方法就必須要運行游戲進行詳細測試,防止修改太多萬一出錯無法定位問題所在。如下圖所示:保留原藍圖的實現,方便C++代碼查錯。

五、任意一個用C++方法替換藍圖相應的方法 ,都能保證游戲能正常運行,盡量避免出現要同時替換2個以上藍圖方法才能正常運行游戲。這一點非常重要。同樣也是防止修改太多萬一出錯無法定位問題所在。

六、藍圖方法給變量賦值,也可以直接調用C++對應方法賦值,以保證C++其它函數方法能正常運行。

  

 

第二種使用C++重寫藍圖的方法:使用組合

一、創建一個繼承自UObject的C++類,一般加后綴Helper,並且加上BlueprintType標簽,共藍圖作為變量類型使用。

  頭文件:  

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

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Components/CanvasPanel.h"
#include "Blueprint/UserWidget.h"
#include "MiniMap/MiniMapFlagData.h"
#include "Components/CanvasPanelSlot.h"

#include "StaticMiniMapHelper.generated.h"

/**
 * 
 */
UCLASS(BlueprintType)
class PROJ10_0121_API UStaticMiniMapHelper : public UObject
{
    GENERATED_BODY()

private:
    UUserWidget* self;    //自身引用

public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myMethods, meta = (ToolTip = "小圖標容器面板"))
        UCanvasPanel * FlagPanel;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myMethods, meta = (ToolTip = "存放小圖標數據機構數組"))
        TArray<FUMiniMapFlagDataC> FlagArray;
    //TArray<TSubclassOf<class UMiniMapFlagData>> FlagArray;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myVariables, meta = (ToolTip = "小地圖比例尺"))
        float MapRatio;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myVariables, meta = (ToolTip = "小地圖縮放比例"))
        float UIScale;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = myVariables, meta = (ToolTip = "中心對位點"))
        FVector CenterPosition;

public:
    UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "初始化"))
        void Ini(UUserWidget* me,UCanvasPanel * Panelflag, FVector PositionCenter, float ScaleUI, float RatioMap);

    UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "添加圖標到小地圖"))
        void AddFlag(UUserWidget* flag, AActor* actor);

    UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "更新所有圖標在小地圖上的位置"))
        virtual void UpdateFlags();

    UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "更新某個圖標在小地圖上的位置"))
        virtual void UpdateFlag(FUMiniMapFlagDataC data);

    UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "設置小地圖縮放比例"))
        void SetUIScale(float scale);

    UFUNCTION(BlueprintCallable, Category = myMethods, meta = (ToolTip = "圖標的像素坐標轉換成Pivot坐標,小地圖是以Pivot為中心點旋轉的"))
        void SetFlagAsPivot(float x, float y);
};
View Code

   CPP文件:  

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

#include "StaticMiniMapHelper.h"

/**
 *  功能描述:初始化迷你地圖類參數
 *  @self 自身引用
 *  @FlagPanel 小圖標父級容器對象
 *  @CenterPosition 中心對位點
 *  @UIScale 地圖縮放比例
 *  @MapRatio 地圖比例尺
 *
 *  @return
 */
void UStaticMiniMapHelper::Ini(UUserWidget * me, UCanvasPanel * Panelflag, FVector PositionCenter, float ScaleUI, float RatioMap)
{
    this->self = me;
    this->FlagPanel = Panelflag;
    this->CenterPosition = PositionCenter;
    this->UIScale = ScaleUI;
    this->MapRatio = RatioMap;
}

void UStaticMiniMapHelper::AddFlag(UUserWidget* flag, AActor* actor)
{
    UCanvasPanelSlot* slot = FlagPanel->AddChildToCanvas(flag);
    FAnchors InAnchors(0.5f, 0.5f);
    slot->SetAnchors(InAnchors);                //設置錨點:中心對齊
    slot->SetAlignment(FVector2D(0.5f, 0.5f));    //設置對齊:中心對齊
    slot->SetPosition(FVector2D(0, 0));            //設置原點不偏移
    slot->SetAutoSize(true);                    //設置自動尺寸:為圖片原始尺寸

    //FUMiniMapFlagDataC data;
    //data.flag = flag;
    //data.Actor = actor;
    //data.Slot = slot;
    //FUMiniMapFlagDataC(flag,actor,slot)
    FUMiniMapFlagDataC data(flag, actor, slot);
    FlagArray.Add(data);

}

void UStaticMiniMapHelper::UpdateFlags()
{
    for (int i = 0; i < FlagArray.Num(); i++)
    {
        UpdateFlag(FlagArray[i]);
    }
}

void UStaticMiniMapHelper::UpdateFlag(FUMiniMapFlagDataC data)
{
    FVector ActorLocation = data.Actor->GetActorLocation();
    FVector temp = (ActorLocation - CenterPosition)*(MapRatio*UIScale);
    data.Slot->SetPosition(FVector2D(temp.Y, temp.X*-1));        //設置小圖標位置

    data.flag->SetRenderAngle(data.Actor->GetActorRotation().Yaw);    //旋轉小圖標
}

void UStaticMiniMapHelper::SetUIScale(float scale)
{
    UIScale = scale;
}

//更新所有圖標在小地圖上的位置
void UStaticMiniMapHelper::SetFlagAsPivot(float x, float y)
{
    FVector2D 地圖實際尺寸 = self->GetCachedGeometry().GetLocalSize();        //獲得地圖實際尺寸
    float 地圖長度 = 地圖實際尺寸.X;
    float 地圖寬度 = 地圖實際尺寸.Y;

    float 圖標X坐標 = x;
    float 圖標Y坐標 = y;

    //小地圖旋轉X坐標 = (圖標X坐標 + 地圖長度/2)/地圖長度
    float 小地圖旋轉X坐標 = (圖標X坐標 + 地圖長度 / 2) / 地圖長度;
    float 小地圖旋轉Y坐標 = (圖標Y坐標 + 地圖長度 / 2) / 地圖長度;

    //設置小地圖旋轉原點
    self->SetRenderTransformPivot(FVector2D(小地圖旋轉X坐標, 小地圖旋轉Y坐標));
}
View Code

 二、在藍圖中添加一個名為MyHelper的變量,類型是第一步C++創建的類型。

  

三、在使用helper對象之前,必須先實例化。“Spawn Object”是自己用C++寫的一個藍圖庫中的一個方法。

  

四、接着就是要初始化helper的成員變量值。其中當前藍圖對象的引用(也就是self)要傳遞給me參數,這是關鍵,用helper的成員對象保存起來。

  

五、最終用C++相同的方法替換原來用藍圖寫的功能。

  

 

使用繼承和組合都可以實現C++重寫藍圖,但是組合比繼承要更好,耦合度更低! 

遷移遇到的問題:

1、如果藍圖繼承一個C++類,則遷移的時候會出現問題,這個藍圖在新項目里面打不開的。

2、使用C++組合時可以遷移到其他項目,可以打開藍圖,但是helper所有的相關方法調用變成無效,需要手動重新照着做一次。

 

附注用C++ 實現藍圖函數庫,根據類型動態創建UObject,並返回引用給藍圖使用:

  頭文件

#include "Kismet/BlueprintFunctionLibrary.h"
#include "TimyLibrary.generated.h"

/**
 * 
 */
UCLASS()
class PROJ10_0121_API UTimyLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = "TimyLibrary|Object", meta = (ToolTip = "創建UObject實例"))
    static UObject* SpawnObject(UObject* owner, UClass* ObjClass);
};
View Code

  CPP文件

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

#include "TimyLibrary.h"
#include "Runtime/Engine/Classes/Engine/Engine.h"

//創建根據類型創建UObject
UObject* UTimyLibrary::SpawnObject(UObject* owner, UClass* ObjClass)
{
    UWorld* World = GEngine->GetWorldFromContextObject(owner);
    UObject* tempObject = NewObject<UObject>(World, ObjClass);
    //UObject* tempObject = NewObject<UObject>(ObjClass);        //創建對象會失敗
    return tempObject;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM