查看: 839|回复: 7

分享C++寫blueprint block的方法 UE4.9

[复制链接]

1

主题

342

帖子

7万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
75866
发表于 2015-9-30 21:30:49 | 显示全部楼层 |阅读模式




任何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::vectorUFUNCTION(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 會多一個可以OverideUFUNCTION(BlueprintImplementableEvent, Category = Tracking)  void IsActorInJunction(AActor* Actor, bool Result);复制代码
比如我在Tick 中呼叫了這個函數// Called every framevoid 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;复制代码
實作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)  {}复制代码也可以用下面方法得到改動的 indexint32 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.htmlsort=oldest
主要就是 你的函數要格式化成 functionname parameter1 parameter2 ...
第一個是函數名 第二個是參數一表示成字串的方式 以此類推
這邊我有做了一個小測試 證明是可行的void XXXXX::TestCallFunctionByName(FString str){  FOutputDeviceNull ar;  this- CallFunctionByNameWithArguments(*str, ar, NULL, true);}复制代码
要在begin play加,在建構式加會沒反應
Component- OnClicked.AddDynamic(this, AYourActor::OnMouseClicked);
傳入參數請參考標頭檔
然後在cpp實作建構式
這是將預設的subobject移除的建構式AFightCharacter::AFightCharacter(const FObjectInitializer ObjectInitializer)  : Super(ObjectInitializer.DoNotCreateDefaultSubobject(ACharacter::MeshComponentName)){}复制代码這是將預設的建構式AHeroCharacter::AHeroCharacter(const FObjectInitializer ObjectInitializer)  : Super(FObjectInitializer::Get()){}复制代码之後可以用
以Decal 為例,宣告在classUPROPERTY(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跟ControllerAMyHUD * 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();复制代码類似這樣voidAYourActor::OnRep_PosChange(){  SetActorLocation(CurrentPosition);}复制代码再來是函數
有分Server在Call的,這個函數會同步到所有ClientUFUNCTION(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); }  }复制代码
剩下各種規則細節詳見官網
可編輯   唯讀
EditAnywhere, VisibleAnywhere在editor
BlueprintReadWrite, BlueprintReadOnly在blueprint連連看
BlueprintAssignable 看不太懂要使用的場合,好像也是要給Actor用的
就給有用過的人分享經驗吧
楼主讲解的不错,赞一个!
能补充讲解下事件函数(BlueprintImplementableEvent / BlueprintNativeEvent)和 事件调度器(BlueprintAssignable)的蓝图暴露方法就更好了。

回复

使用道具 举报

0

主题

867

帖子

2874

积分

vip会员

Rank: 1

积分
2874
发表于 2015-10-12 23:45:16 | 显示全部楼层
感觉好像在哪里看过了,汗   
回复 支持 反对

使用道具 举报

0

主题

852

帖子

2835

积分

vip会员

Rank: 1

积分
2835
发表于 2015-10-13 01:22:10 | 显示全部楼层
不错,很简洁      
回复 支持 反对

使用道具 举报

0

主题

824

帖子

2721

积分

vip会员

Rank: 1

积分
2721
发表于 2015-10-13 02:10:11 | 显示全部楼层
今天没事来逛逛  
回复 支持 反对

使用道具 举报

0

主题

819

帖子

2675

积分

vip会员

Rank: 1

积分
2675
发表于 2015-10-13 02:34:19 | 显示全部楼层
是转的把 ,说得不错 再看一次  
回复 支持 反对

使用道具 举报

0

主题

859

帖子

2822

积分

vip会员

Rank: 1

积分
2822
发表于 2015-10-13 05:28:55 | 显示全部楼层
做一个 做好了 请看  
回复 支持 反对

使用道具 举报

0

主题

844

帖子

2763

积分

vip会员

Rank: 1

积分
2763
发表于 2015-10-13 08:27:39 | 显示全部楼层
楼主,支持   
回复 支持 反对

使用道具 举报

0

主题

881

帖子

2877

积分

vip会员

Rank: 1

积分
2877
发表于 2015-10-13 08:54:45 | 显示全部楼层
一骂人就激动  
回复 支持 反对

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | enginedx注册

本版积分规则

 
 



邮件留言:


 
返回顶部