您的位置:首页 > 编程语言 > C语言/C++

分享C++寫blueprint block的方法

2016-01-04 11:26 721 查看
最後更新 2015/12/13

加入uproject產生sln方法。

請不要買頂新集團,包括德克士、布列德、康師傅等相關企業商品,

就算檢驗合格也無法確保頂新不會因為成本的關係私下偷偷用黑心原料,

已經確認在台灣使用餿水黑心油,可能造成癌症,其它爭議事件包括:

使用混充花生油、假橄欖油事件、鑫好飼料油事件、松青超市販賣過期食品

太多類似的食品安全事件了,

如果不想要增加一個對健康有害的因子,請拒買謝謝。

uproject要怎麼產生指定 visual studio版本的專案檔呢?

這問題來自我灌了vs2015後,他預設產生的專案版本就會到2015

可是我要2013時怎麼辨?

你需要這樣下指令 重點在後面的 -2013,要2015就是 -2015

UnrealBuildTool.exe -projectfiles -project="uproject路徑" -game -engine -progress -2013

實際範例會像這樣

M:/C++/UnrealEngine/Engine/Binaries/DotNET/UnrealBuildTool.exe -projectfiles -project="M:/Unreal Projects/talking-with-cat/Matthiola.uproject" -game -engine -progress -2013

复制代码

剛好有人問了,來做個個人使用經驗的總整理。

任何c++函數你要用在BP(blueprint),你一定要寫在class裡面。

這個class一定要有UCLASS()宣告

這個class一定要有GENERATED_BODY()巨集宣告

你要在BP裡面呼叫的函數一定要在函數宣告前宣告UFUNCTION

而這個函數一定要在Public:之下

像是這樣 UFUNCTION(BlueprintCallable, Category = "OpenCV|ImageProcess")

BlueprintCallable表示你可以控制他什麼時候執行

BlueprintPure你不能確定他在什麼時候執行,但他就是會執行,而且你不能用他來修改任何狀態

承上,如果你的函數是不需要 class instance 的函數你還是要宣告在class裡,然後定義成static

純函數可以用 BlueprintCallable 也可以用 BlueprintPure

在給BP呼叫的函數的參數部份,只要是class 只能用有UCLASS()宣告的class而且一定要用指標

參數的部份不能用int, short, char, double 你只能用 int8, int16, int32, uint 16, float來取代他們

所有的函數不能用一樣的名字,你可以使用

UFUNCTION(BlueprintCallable, meta = (DisplayName = "SmoothingEach5 (ueLine array)"), Category = "Line")

DisplayName 這個屬性來修正在ue4裡面顯示的名字

Category 可以用 | 來做子集

UFUNCTION(BlueprintCallable, Category = "OpenCV|CurveExtraction")

bool SetPicture(UueMat* umat);

复制代码



陣列的部份你只能用TArray<YourClass*> 來傳入,也不能用 typedef 來定義別名

你不能用std::vector

UFUNCTION(BlueprintCallable, meta = (FriendlyName = "SmoothingEach5 (ueLine array)"), Category = "Line")

static TArray<UueLine*> SmoothingEach5_Array(const TArray<UueLine*>& cvp, float centroidRadio = 1.0, int32 repeat = 1);

复制代码



當你想做出一個大家都能存取的變數時,請在變數宣告前面加上

"UPROPERTY(BlueprintReadWrite)" or "UPROPERTY(BlueprintReadOnly)"

class應該要使用指標,使用NewObject<Class>() 來new。

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mat")

UTexture2D* MyTexture2D;

复制代码



當你想要輸出多個變數時,在參數宣告使用非const 的 reference 變數來輸出。

UFUNCTION(BlueprintCallable, Category = "Test")

static bool TestOut2(TArray<UTexture2D*>& t1, TArray<UTexture2D*>& t2);

复制代码



當你想要輸出多個執行分支時,參考以下的方法



UENUM(BlueprintType)

enum class EMyEnum : uint8

{

BranchA,

BranchB

};

复制代码
在ue4.8版中更新後

TEnumAsByte<EMyEnum>& 不在需要,要改成 EMyEnum&

UFUNCTION(BlueprintCallable, Category = "Stuff", Meta = (ExpandEnumAsExecs = "Branches"))

void DoSomeBranch(int32 SomeInput, EMyEnum& Branches);

复制代码

void AMyActor::DoSomeBranch(int32 SomeInput, EMyEnum& Branches)

{

if (SomeInput == 1)

{

Branches = EMyEnum::BranchA;

}

else

{

Branches = EMyEnum::BranchB;

}

}

复制代码

BlueprintNativeEvent 新增內建的事件,但實作由C++來實作

一般用在Actor裡

UFUNCTION(BlueprintNativeEvent, Category = "Switch Functions")

void OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

复制代码
值的注意的是,實作的函數名是 XXXX_Implementation 在執行時會自動呼叫

void AMyActor::OnOverlapBegin_Implementation(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)

{

if (OtherActor && (OtherActor != this) && OtherComp)

{

ToggleLight();

}

}

复制代码



BlueprintImplementableEvent 在c++中只寫宣告,給blueprint實作的事件

一般用在Actor裡,你的Function 會多一個可以Overide

UFUNCTION(BlueprintImplementableEvent, Category = Tracking)

void IsActorInJunction(AActor* Actor, bool& Result);

复制代码







比如我在Tick 中呼叫了這個函數

// Called every frame

void AMyActor::Tick( float DeltaTime )

{

Super::Tick( DeltaTime );

bool res;

IsActorInJunction(this, res);

}

复制代码
看的出來就一般的C++函數呼叫,但是我在code中沒有寫他的實作

在UE4裡面我也只拉了一個print string hello當實作



實行時就出來了



變數開關的設定

UPROPERTY( Category=Location, EditAnywhere )

uint32 bUseOffset : 1;

UPROPERTY(Category = Location, EditAnywhere, meta = (EditCondition = "bUseOffset"))

FVector Offset;

复制代码



meta=(MakeEditWidget)



當你想要在editor改變數時不可編輯的變數也更新就實作下面的函數

實作PostEditChangeProperty函數

virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;

這個函數將在每次變數在editor內被修改時呼叫

函數最後要加

Super:: PostEditChangeProperty(PropertyChangedEvent);

可以用下面的code來判斷是哪個成員被改動

FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;

if((PropertyName == GET_MEMBER_NAME_CHECKED(AHeroCharacter, Skill_LevelCDs)))

{

}

复制代码

TArray改動就實作PostEditChangeChainProperty函數

virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override;

可以用下面的code來判斷是哪個成員被改動

const FName TailPropName = PropertyChangedEvent.PropertyChain.GetTail()->GetValue()->GetFName();

static FName Mobility_NAME(TEXT("CDs"));

if (TailPropName == Mobility_NAME)

{

}

复制代码
也可以用下面方法得到改動的 index

int32 index = PropertyChangedEvent.GetArrayIndex(TEXT("Meshes"));

复制代码

實作PostInitProperties函數

virtual void PostInitProperties() override;

這個函數將在每次變數在初始化時呼叫

函數最前面要加

Super:: PostInitProperties();

參考:

https://answers.unrealengine.com ... pdates-in-code.html

//for regular properties:

void ACustomClass::PostEditChangeProperty(struct FPropertyChangedEvent& e)

{

FName PropertyName = (e.Property != NULL) ? e.Property->GetFName() : NAME_None;

if (PropertyName == GET_MEMBER_NAME_CHECKED(UCustomClass, PropertyName))

{

//various uproperty tricks, see link

}

Super::PostEditChangeProperty(e);

}

//for TArrays:

void ACustomClass::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& e)

{

int32 index = e.GetArrayIndex(TEXT("Meshes")); //checks skipped

UStaticMesh *mesh = Meshes[index]; //changed mesh

Super::PostEditChangeChainProperty(e);

}

复制代码

有人反應說怎麼動態呼叫BP的函數

而且是沒在C++宣告的

查了一下終於查了到

就是使用 CallFunctionByNameWithArguments 這個函數

我只知道傳基本型態 int32 float FString 之類的方法

其它參數怎麼傳請看 Engine/Source/Runtime/CoreUObject/Private/UObject/ScriptCore.cpp

我是從這裡學來的 https://answers.unrealengine.com ... -c.html?sort=oldest

主要就是 你的函數要格式化成 functionname parameter1 parameter2 ...

第一個是函數名 第二個是參數一表示成字串的方式 以此類推

這邊我有做了一個小測試 證明是可行的

void XXXXX::TestCallFunctionByName(FString str)

{

FOutputDeviceNull ar;

this->CallFunctionByNameWithArguments(*str, ar, NULL, true);

}

复制代码







Component可以加事件

ex.

要在begin play加,在建構式加會沒反應

Component->OnClicked.AddDynamic(this, &AYourActor::OnMouseClicked);

傳入參數請參考標頭檔



建構式可以加入各種Component在該actor底下

首先使用 GENERATED_UCLASS_BODY()

然後在cpp實作建構式

這是將預設的subobject移除的建構式

AFightCharacter::AFightCharacter(const FObjectInitializer& ObjectInitializer)

: Super(ObjectInitializer.DoNotCreateDefaultSubobject(ACharacter::MeshComponentName))

{

}

复制代码
這是將預設的建構式

AHeroCharacter::AHeroCharacter(const FObjectInitializer& ObjectInitializer)

: Super(FObjectInitializer::Get())

{

}

复制代码
之後可以用

以Decal 為例,宣告在class

UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly)

UDecalComponent* SelectionDecal;

复制代码
建構式可以設定像這樣

AHeroCharacter::AHeroCharacter(const FObjectInitializer& ObjectInitializer)

: Super(FObjectInitializer::Get())

{

PrimaryActorTick.bCanEverTick = true;

SelectionDecal = ObjectInitializer.CreateDefaultSubobject<UDecalComponent>(this, TEXT("SelectionDecal0"));

SelectionDecal->SetWorldLocation(FVector(0, 0, -90));

// FRotator = rotation Y Z X

SelectionDecal->SetWorldRotation(FQuat(FRotator(90, 0, 0)));

SelectionDecal->SetWorldScale3D(FVector(10, 50, 50));

SelectionDecal->AttachParent = GetCapsuleComponent();

}

复制代码
初始化後設定位置、旋轉、縮放,並Attach 不然會在世界座標 (0, 0, 0) 的位置

如何在任何地方拿到HUD跟Controller

AMyHUD * hud = Cast<AMyHUD>(UGameplayStatics::GetPlayerController(this, 0)->GetHUD());

复制代码

GetWorld()->GetFirstPlayerController()

复制代码

旋轉常要你填FQuat

可以用FRotator去初始化

三個變數等於editor裡 Y Z X方向的旋轉

SelectionDecal->SetWorldRotation(FQuat(FRotator(90, 0, 0)));

复制代码

網路連線

首先是函數成員

所有要同步的UPROPERTY要加 Replicated

像這樣

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hero", Replicated)

FVector ThrowDestination;

复制代码
並加入同步代碼

void AYourActor::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const

{

Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME(AHeroCharacter, ThrowDestination);

}

复制代码
範例二

UPROPERTY(Transient, ReplicatedUsing = OnRep_PosChange)

FVector CurrentPosition;

复制代码
Transient 是不能序列化的意思。

ReplicatedUsing 是同步請用這個函數

UFUNCTION()

void OnRep_PosChange();

复制代码
類似這樣

void AYourActor::OnRep_PosChange()

{

SetActorLocation(CurrentPosition);

}

复制代码
再來是函數

有分Server在Call 的,這個函數會同步到所有Client

UFUNCTION(Server, Reliable, WithValidation)

void ServerSetLocation(FVector location);

复制代码
只有Client在Call 的,這個函數在Server上不會執行

UFUNCTION(Client, Reliable, WithValidation)

void ClientSetLocation(FVector location);

复制代码
但你的函數就會分成兩個實作

一個加 _Validate 回傳 bool

一個加 _Implementation 不回傳

bool AYourActor::ServerSetLocation_Validate(FVector location)

{

return true;

}

void AYourActor::ServerSetLocation_Implementation(FVector location)

{

CurrentPosition = location;

SetActorLocation(location);

}

复制代码
在這些 Actor裡面可以用

if (Role == ROLE_Authority)

复制代码


if (GEngine->GetNetMode(GetWorld()) == ENetMode::NM_Client)

复制代码
來判斷是不是在Server還是Client

最重要的要在Controller裡面將大家的Owener設成自己

這個動作在Server會做 Client不會做,這樣就可以同步了。

for(TActorIterator<AActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)

{

if(*ActorItr != this)

{

ActorItr->SetOwner(this);

}

}

复制代码

繼承UObject的TArray可以宣告

TArray<UYourClass>

繼承Actor的TArray可以宣告

TArray<TSubclassOf<AYourClass>> Skill_HintActor;

在需要用的時候

AYourClass* CurrentSkillHint = GetWorld()->SpawnActor<ASkillHintActor>(Skill_HintActor[index]);

像這樣Spawn出來

剩下各種規則細節詳見官網

可編輯 唯讀

EditAnywhere, VisibleAnywhere 在editor

BlueprintReadWrite, BlueprintReadOnly 在blueprint連連看

BlueprintAssignable 看不太懂要使用的場合,好像也是要給Actor用的

就給有用過的人分享經驗吧

2015-04-04
20 40 43.png (24.37 KB, 下载次数: 4)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: