UE4之C++寫blueprint block UE4.18

[复制链接]
查看5622 | 回复6 | 2018-1-21 20:58:50 | 显示全部楼层 |阅读模式
本帖最后由 damody 于 2018-1-22 08:38 编辑

大家好 教學之前先打一下廣告
https://www.patreon.com/nobu_game
小的我在做一套開源的信長重製
如果這篇教學有幫助到你請幫忙訂閱個1元謝謝。
有需要源代碼的可以去上面的網站有下載連結
2018年1月4日的進度影片


加入uproject產生sln方法。

uproject要怎麼產生指定 visual studio版本的專案檔呢?
這問題來自我灌了vs2017後,他預設產生的專案版本就會到2017
可是我要2015時怎麼辨?
你需要這樣下指令 重點在後面的 -2015,要2013就是 -2013
[mw_shl_code=bash,true]UnrealBuildTool.exe -projectfiles -project="uproject路徑" -game -engine -progress -2015[/mw_shl_code]
實際範例會像這樣
[mw_shl_code=bash,true]C:\Program Files\Epic Games\UE_4.18\Engine\Binaries\DotNET\UnrealBuildTool.exe  -projectfiles -project="C:/Unreal Projects/AON/AON.uproject" -game -engine -progress -2015[/mw_shl_code]

1. 任何c++函數你要用在BP(blueprint),你一定要寫在class裡面,不能寫成獨立的函數。
2. 這個class一定要有UCLASS()宣告
3. 這個class一定要有 GENERATED_BODY() 巨集宣告
   或是 GENERATED_UCLASS_BODY()
   這兩個宣告決定於你的初始化函數方式
   如果你有 U????Component 的初始化需求就會用 GENERATED_UCLASS_BODY()
   沒有的話就預設的 GENERATED_BODY() 就可以了
[mw_shl_code=cpp,true]// in .h file
UCLASS()
class MOBA_API AHeroCharacter : public ACharacter
{
        GENERATED_UCLASS_BODY()
public:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
        // Called every frame
        virtual void Tick(float DeltaTime) override;
     // 選人的地版光環
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Interaction")
        UDecalComponent * SelectionDecal;
};

// in .cpp file
AHeroCharacter::AHeroCharacter(const FObjectInitializer& ObjectInitializer)
        : Super(FObjectInitializer::Get())
{
    ObjectInitializer.CreateDefaultSubobject<UDecalComponent>(this, TEXT("SelectionDecal0"));

    CreateDefaultSubobject<UParticleSystemComponent>(TEXT("BulletParticle0"));
}[/mw_shl_code]

[mw_shl_code=cpp,true]// in .h file
UCLASS()
class MOBA_API ABasicCharacter : public ACharacter
{
        GENERATED_BODY()

public:
        // Sets default values for this character's properties
        ABasicCharacter();
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
        // Called every frame
        virtual void Tick(float DeltaTime) override;
};

// in .cpp file
ABasicCharacter::ABasicCharacter()
{
         // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
}[/mw_shl_code]

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

而這個函數一定要在c++ class public: 之下
像是這樣 UFUNCTION(BlueprintCallable, Category = "OpenCV|ImageProcess")
BlueprintCallable與BlueprintPure 都會把這個c++函數標記給藍圖呼叫
BlueprintCallable 表示你可以從藍圖控制他什麼時候執行
BlueprintPure 你不能確定他在什麼時候執行,但他就是會執行,而且你不能用他來修改任何狀態

5. 承上,如果你的函數是不需要 class instance 的函數你還是要宣告在class裡,然後定義成static
純函數可以用 BlueprintCallable 也可以用 BlueprintPure

6. 在給BP呼叫的函數的參數部份,只要是class 只能用有UCLASS()宣告的class而且一定要用指標不能直接傳整個物件
參數的部份不能用int, short, char, double 你只能用 int32, float來取代他們
目前可以用的內建型態為
[mw_shl_code=cpp,true]uint32:1 // bitfields same as bool
bool
int32
float
FString
FVector
FRotator[/mw_shl_code]

如果你用了其它型態 那就不能標為藍圖函數 編譯會失敗
請參考 https://docs.unrealengine.com/latest/INT/Engine/Blueprints/UserGuide/Variables/index.html
在C++14已經標準化的情況下
建議在class inline初始化
像下面這樣

[mw_shl_code=cpp,true]UCLASS()
class MOBA_API AHeroCharacter : public ACharacter
{
        GENERATED_UCLASS_BODY()
public:
    // ...
    // 基礎魔法受傷倍率
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MOBA")
        float BaseMagicInjuredRatio = 1;
        // 基礎物理受傷倍率
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MOBA")
        float BasePhysicsInjuredRatio = 1;
        // 基礎裝甲
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MOBA")
        float BaseArmor = 2;
        // 基礎攻擊力
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MOBA")
        float BaseAttack = 80;
        // 基礎移動速度
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MOBA")
        float BaseMoveSpeed = 300;
}[/mw_shl_code]

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

[mw_shl_code=cpp,true]// TArray 可以使用所有基本型態 繼承UObject的物件一定要用物件指標
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "MOBA")
TArray<float> LevelProperty_Attack;
// TMap 可以包上面的基本型態 但不能包TArray 繼承UObject的物件一定要用物件指標
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Current", Replicated)
TMap<EHeroBuffState, bool> BuffStateMap;[/mw_shl_code]



8. 所有的函數不能用一樣的名字,如果是C++重載同名函數時,你可以使用 meta DisplayName 來更改名字

[mw_shl_code=cpp,true]
UFUNCTION(BlueprintCallable, meta = (DisplayName = "SmoothingEach5 (Vector2D array)"), Category = "Line")
static TArray<FVector2D>   SmoothingEach5(const TArray<FVector2D>& data, float centroidRadio = 1.0f, int32 repeat = 1);

UFUNCTION(BlueprintCallable, meta = (DisplayName = "SmoothingEach5 (LineV2)"), Category = "Line")
static ULineV2 * SmoothingEach5(const ULineV2* ul, float centroidRadio = 1.0f, int32 repeat = 1);
[/mw_shl_code]

DisplayName 這個屬性來修正在ue4裡面顯示的名字
Category 可以用 | 來做子集
[mw_shl_code=cpp,true]UFUNCTION(NetMulticast, WithValidation, Reliable, BlueprintCallable, Category = "MOBA|Server")
void ServerPlayAttackStartSFX();[/mw_shl_code]


9. 當你想要輸出多個變數時,在參數宣告使用非const 的 reference 變數來輸出。
```clike=
UFUNCTION(BlueprintCallable, Category = "Test")
static bool TestOut2(TArray<UTexture2D*>& t1, TArray<UTexture2D*>& t2);
```

10. 當你想要輸出多個執行分支時,參考以下的方法
[mw_shl_code=cpp,true]// .h
UFUNCTION(BlueprintCallable, Category = "Stuff", Meta = (ExpandEnumAsExecs = "Branches"))
void DoSomeBranch(int32 SomeInput, EMyEnum& Branches);

// .cpp
void ASkillSplineActor:oSomeBranch(int32 SomeInput, EMyEnum& Branches)
{
        if (SomeInput == 1)
        {
                Branches = EMyEnum::BranchA;
        }
        else
        {
                Branches = EMyEnum::BranchB;
        }
}[/mw_shl_code]


11. BlueprintNativeEvent 新增內建的事件,但實作由C++來實作 一般用在Actor裡

[mw_shl_code=cpp,true]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)
    {

    }
}[/mw_shl_code]

12. BlueprintImplementableEvent 在c++中只寫宣告,給blueprint實作的事件
一般用在Actor裡,你的Function 會多一個可以Overide
[mw_shl_code=cpp,true]UFUNCTION(BlueprintImplementableEvent, Category = "MOBA")
void OnHit(AHeroCharacter* caster, AHeroCharacter* target);[/mw_shl_code]




13. 變數開關的設定
使用 meta = (EditCondition = "bYourVariable") 可以控制
我這邊用 SkillType 去改變 UseDirectionSkill 來開關 IsFixdLength, SkillLength, MinimalLength 這三個設定
[mw_shl_code=cpp,true]UPROPERTY(Category = "SkillHint", EditAnywhere, BlueprintReadOnly)
ESkillHintEnum SkillType;

UPROPERTY(Category = "SkillHint", EditAnywhere, BlueprintReadOnly)
FVector SkillPos;

UPROPERTY(Category = "SkillHint", VisibleAnywhere, BlueprintReadOnly)
uint32 UseDirectionSkill: 1;

UPROPERTY(Category = "SkillHint", VisibleAnywhere, BlueprintReadOnly)
uint32 UseRangeSkill: 1;

// 是否固定長度
UPROPERTY(Category = "FlySkill", EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "UseDirectionSkill"))
uint32 IsFixdLength: 1;

// 技能最遠距離
UPROPERTY(Category = "FlySkill", EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "UseDirectionSkill"))
float SkillLength;

// 技能最短距離
UPROPERTY(Category = "FlySkill", EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "UseDirectionSkill"))
float MinimalLength;

// 法術直徑
UPROPERTY(Category = "FlySkill", EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "UseRangeSkill"))
float SkillDiameter;

// 施法距離
UPROPERTY(Category = "FlySkill", EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "UseRangeSkill"))
float SkillCastRadius;

// 技能最小施法距離
UPROPERTY(Category = "FlySkill", EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "UseRangeSkill"))
float MinimalCastRadius;[/mw_shl_code]




14. 各種UPROPERTY meta 介紹
    在變數加上 meta = (MakeEditWidget = true)
    如果這個變數適合在3d場景中編輯,那他就會可以編輯
    不是每種型態都可以
[mw_shl_code=cpp,true]UPROPERTY(Category = "MOBA", EditAnywhere, BlueprintReadWrite, meta = (MakeEditWidget = true))
FVector Direction;[/mw_shl_code]


ExposeOnSpawn 在Spawn Actor時可以強制得到輸入
[mw_shl_code=cpp,true]UPROPERTY(BlueprintReadWrite, Category = "Variable", Meta = (ExposeOnSpawn = true))
FVector Location1;

UPROPERTY(BlueprintReadWrite, Category = "Variable", Meta = (ExposeOnSpawn = true))
FVector Location2;[/mw_shl_code]

一般的 actor

特製的


各種Edit Visible 選項
[mw_shl_code=cpp,true]// Edit
UPROPERTY(EditAnywhere, Category = "Edit")
int32 EditAnywhere;
UPROPERTY(EditDefaultsOnly, Category = "Edit")
int32 EditDefaultsOnly;
UPROPERTY(EditInstanceOnly, Category = "Edit")
int32 EditInstanceOnly;
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Edit")
int32 AdvancedDisplay1;

// Visible
UPROPERTY(VisibleAnywhere, Category = "Visible")
int32 VisibleAnywhere;
UPROPERTY(VisibleDefaultsOnly, Category = "Visible")
int32 VisibleDefaultsOnly;
UPROPERTY(VisibleInstanceOnly, Category = "Visible")
int32 VisibleInstanceOnly;
UPROPERTY(VisibleAnywhere, AdvancedDisplay, Category = "Visible")
int32 AdvancedDisplay2;

UPROPERTY(EditAnywhere, Category = "Variable", Meta = (MultiLine = true))
FString StringData;
UPROPERTY(EditAnywhere, Category = "Variable", Meta = (UIMin = 5, UIMax = 100))
int32 minmax;

UPROPERTY(EditAnywhere, EditFixedSize, Category = "EditFixedSize")
TArray<FName> EditFixedSize;
UPROPERTY(EditAnywhere, Category = "EditFixedSize")
TArray<FName> NoEditFixedSize;[/mw_shl_code]

這邊是在Actor Editor裡

這邊是在Level Editor裡

EditFixedSize則可以限制TArray不能增減,但可以修改裡面元素的內容

15. 當你想要在editor改變後變數自動更新可以這樣做
    實作PostEditChangeProperty函數
    virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
    這個函數將在每次變數在editor內被修改時呼叫
    函數最後要加
    Super:: PostEditChangeProperty(PropertyChangedEvent);
   
    TArray改動就實作PostEditChangeChainProperty函數
    virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override;
    可以用下面的code來判斷是哪個成員被改動
   
    由於只能在editor裡使用所以要用#if WITH_EDITOR包起來
    不然打包會失敗
[mw_shl_code=cpp,true]// .h
#if WITH_EDITOR
        virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;

        virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR

// .cpp
// .h
#if WITH_EDITOR
    virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;

    virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR

// .cpp
#if WITH_EDITOR

void AHeroCharacter:ostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
    //Get the name of the property that was changed
    FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
   
    if((PropertyName == GET_MEMBER_NAME_CHECKED(AHeroCharacter, Skill_LevelCDs)))
    {
        Skill_BaseCD.SetNum(Skill_LevelCDs.Num());
    }
   
    Super:ostEditChangeProperty(PropertyChangedEvent);
}

void AHeroCharacter:ostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
{
    const FName TailPropName = PropertyChangedEvent.PropertyChain.GetTail()->GetValue()->GetFName();
   
    static FName Mobility_NAME(TEXT("CDs"));
    if(TailPropName == Mobility_NAME)
    {
        for(int32 i = 0; i < Skill_LevelCDs.Num(); ++i)
        {
            if(Skill_LevelCDs.CDs.Num() > 0)
            {
                Skill_BaseCD = Skill_LevelCDs[0];
                Skill_MaxCD = Skill_BaseCD;
            }
        }
    }
    Super:ostEditChangeChainProperty(PropertyChangedEvent);
}
#endif // WITH_EDITOR
   
// 例子2
#if WITH_EDITOR
void ACustomClass:ostEditChangeProperty(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:ostEditChangeProperty(e);
}

//for TArrays:
void ACustomClass:ostEditChangeChainProperty(struct FPropertyChangedChainEvent& e)
{
    // 也可以用下面方法得到改動的TArray index
    int32 index = e.GetArrayIndex(TEXT("Meshes")); //checks skipped
    UStaticMesh *mesh = Meshes[index]; //changed mesh
    Super:ostEditChangeChainProperty(e);
}
#endif // WITH_EDITOR
[/mw_shl_code]

也可以實作 PostInitProperties 函數
virtual void PostInitProperties() override;
這個函數將在每次變數在初始化時呼叫
函數最前面要加
Super:: PostInitProperties();
[mw_shl_code=cpp,true]// .h
#if WITH_EDITOR
    virtual void PostInitProperties() override;
#endif
// .cpp
#if WITH_EDITOR
void ASkillHintActor:ostInitProperties()
{
    Super:ostInitProperties();
    UpdateLength();
}
#endif // WITH_EDITOR[/mw_shl_code]
可以參考
https://answers.unrealengine.com ... pdates-in-code.html

16. 怎麼在C++呼叫沒在C++宣告的BP的函數

使用 CallFunctionByNameWithArguments 這個函數
我只知道傳基本型態 int32 float FString 之類的方法
其它參數怎麼傳請看 Engine/Source/Runtime/CoreUObject/Private/UObject/ScriptCore.cpp
參考 https://answers.unrealengine.com ... -c.html?sort=oldest
主要就是 你的函數要格式化成 functionname parameter1 parameter2 ...
第一個是函數名 第二個是參數一表示成字串的方式 以此類推
當你有一個函數叫做 Hello 接受一個 int32 的參數
你可以這樣呼叫 "Hello 42"
比較重要的是你必需對正確的Actor使用 CallFunctionByNameWithArguments
[mw_shl_code=cpp,true]void XXXXX::TestCallFunctionByName(FString str)
{
    FOutputDeviceNull ar;
    this->CallFunctionByNameWithArguments(*str, ar, NULL, true);
}[/mw_shl_code]

17. Component的事件觸發
所有事件的綁定都要在BeginPlay()加,在建構式加會沒反應
Generate Overlap Events 要打勾才會有 Overlap 事件


[mw_shl_code=cpp,true]// 建構式中
// ECR_Block 碰撞 觸發碰撞事件
// ECR_Overlap 重疊但不碰撞 觸發重疊事件
// ECR_Ignore 不碰撞且不觸發任何事件
CapsuleComponent->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore);
CapsuleComponent->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
CapsuleComponent->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Ignore);
CapsuleComponent->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Ignore);
CapsuleComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
CapsuleComponent->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Ignore);
CapsuleComponent->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Ignore);
CapsuleComponent->SetCollisionResponseToChannel(ECC_Destructible, ECR_Ignore);

// in BeginPlay()
Component->OnClicked.AddDynamic(this, &AYourActor::OnMouseClicked);
CapsuleComponent->OnComponentBeginOverlap.AddDynamic(this, &AYourActor::OnBeginOverlap);

// in AYourActor.h
UFUNCTION()
void OnMouseClicked(UPrimitiveComponent* ClickedComp, FKey ButtonPressed);

UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
```[/mw_shl_code]
函數宣告請參考標頭檔
[mw_shl_code=cpp,true]/**
* Delegate for notification of blocking collision against a specific component.  
* NormalImpulse will be filled in for physics-simulating bodies, but will be zero for swept-component blocking collisions.
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams( FComponentHitSignature, UPrimitiveComponent*, HitComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, FVector, NormalImpulse, const FHitResult&, Hit );
/** Delegate for notification of start of overlap with a specific component */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams( FComponentBeginOverlapSignature, UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, int32, OtherBodyIndex, bool, bFromSweep, const FHitResult &, SweepResult);
/** Delegate for notification of end of overlap with a specific component */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams( FComponentEndOverlapSignature, UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, int32, OtherBodyIndex);
/** Delegate for notification when a wake event is fired by physics*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentWakeSignature, UPrimitiveComponent*, WakingComponent, FName, BoneName);
/** Delegate for notification when a sleep event is fired by physics*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentSleepSignature, UPrimitiveComponent*, SleepingComponent, FName, BoneName);
/** Delegate for notification when collision settings change. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FComponentCollisionSettingsChangedSignature, UPrimitiveComponent*, ChangedComponent);

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FComponentBeginCursorOverSignature, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FComponentEndCursorOverSignature, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnClickedSignature, UPrimitiveComponent*, TouchedComponent , FKey, ButtonPressed);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnReleasedSignature, UPrimitiveComponent*, TouchedComponent, FKey, ButtonReleased);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnInputTouchBeginSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnInputTouchEndSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentBeginTouchOverSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentEndTouchOverSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );[/mw_shl_code]

18. 如何在任何地方拿到HUD跟Controller
[mw_shl_code=cpp,true]AMyHUD * hud = Cast<AMyHUD>(UGameplayStatics::GetPlayerController(this, 0)->GetHUD());

GetWorld()->GetFirstPlayerController()[/mw_shl_code]

19. 旋轉常要你填 FRotator
    可以用 FQuat 去初始化
    三個變數等於editor裡 Y Z X方向的旋轉
    FVector可以用Rotation()得到方向
    GetActorRotation() 可以得到Actor的旋轉
[mw_shl_code=cpp,true]SelectionDecal->SetWorldRotation(FQuat(FRotator(90, 0, 0)));

FVector dir = MousePos - PlayerPos;
dir.Z = 0;
SetActorRotation(dir.Rotation());[/mw_shl_code]

20. 網路連線同步
如果一個Actor要能被同步
那他Replicates要打勾

寫在c++裡就是 bReplicates 在建構式為 true
[mw_shl_code=cpp,true]AHeroSkill::AHeroSkill()
{
            bReplicates = true;
}[/mw_shl_code]
下面講的功能
cpp檔要先include
[mw_shl_code=cpp,true]#include "UnrealNetwork.h"[/mw_shl_code]

  • 變數同步
要同步的UPROPERTY要加 Replicated
[mw_shl_code=applescript,true]// .h
// .h
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hero", Replicated)
FVector ThrowDestination;

// .cpp
void AYourActor::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
     DOREPLIFETIME(AHeroCharacter, ThrowDestination);
}
[/mw_shl_code]
  • 變數自定義同步函數
Transient 是不能序列化的意思。
ReplicatedUsing 是同步請用這個函數
[mw_shl_code=cpp,true]// .h
UPROPERTY(Transient, ReplicatedUsing = OnRep_PosChange)
FVector CurrentPosition;

UFUNCTION()
void OnRep_PosChange();

// .cpp

void  AYourActor::OnRep_PosChange()
{
        SetActorLocation(CurrentPosition);
}[/mw_shl_code]

  • RPC
請先參考下面文檔
https://docs.unrealengine.com/la ... orking/Actors/RPCs/
https://docs.unrealengine.com/la ... ors/RPCs/index.html

> 基本上我們可以得知
> 想要在Client呼叫一個函數同步到所有的機器上
> 這個函數的的Actor的owner必需是local controller

最簡單的方法就是直接寫在local controller了
因為多人遊戲中的actor除非你的所有權不轉移 不然就不能分享控制單位
如果有這層考慮的話底層邏輯會變的很複雜
而所有的同步函數一定要加 WithValidation
以官方的條件同步範例來看

SomeRPCFunction_Validate 會在Server端被呼叫
如果回傳 true 就會被RPC到所有機器
如果你是在 SomeRPCFunction_Implementation 做判斷的話
那就會是RPC先呼叫,到了目標機器上再做處理
會浪費網路資源

一般的RPC是不保證一定會執行的
加上 Reliable 保證一定要執行
有些有時效性的函數就是時間過了再執行也來不及的函數
就建議不要加了

[mw_shl_code=cpp,true]
// .h
UFUNCTION( Server, Reliable, WithValidation )
void SomeRPCFunction( int32 AddHealth );
// .cpp
bool SomeRPCFunction_Validate( int32 AddHealth )
{
    if ( AddHealth > MAX_ADD_HEALTH )
    {
        return false;                       // This will disconnect the caller
    }
    return true;                              // This will allow the RPC to be called
}

void SomeRPCFunction_Implementation( int32 AddHealth )
{
    Health += AddHealth;
}

```
在Actor裡可以用
```clike
if (Role == ROLE_Authority)
{
    // 在Server端
}

if (GEngine->GetNetMode(GetWorld()) == ENetMode::NM_ListenServer
    ||GEngine->GetNetMode(GetWorld()) == ENetMode::NM_DedicatedServer))
{
    // 在Server端
}
[/mw_shl_code]

21. 當你需要動態產生 Actor 但想要從藍圖指定Class時
請用 TSubclassOf<AYourClass>
像下面這樣就可以在編輯器裡面選擇你要的類或衍生類

[mw_shl_code=cpp,true]// 技能提示
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "MOBA")
TSubclassOf<ASkillHintActor> HintActor;
   
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MOBA")
TArray<TSubclassOf<AHeroSkill>>        Skill_Classes;

// 在需要用的時候
ASkillHintActor* CurrentSkillHint = GetWorld()->SpawnActor<ASkillHintActor>(HintActor);[/mw_shl_code]
TSubclassOf<ASkillHintActor> HintActor;

TArray<TSubclassOf<AHeroSkill>>        Skill_Classes;


22.  一些內建基本功能介紹
> 各種字串轉換
https://wiki.unrealengine.com/St ... s:_FString_to_FName,_FString_to_Int32,_Float_to_FString
> TArray
https://docs.unrealengine.com/la ... chitecture/TArrays/
https://docs.unrealengine.com/la ... /TArrays/index.html
> TMap
https://docs.unrealengine.com/la ... ure/TMap/index.html
https://docs.unrealengine.com/la ... ure/TMap/index.html
> Timers
https://docs.unrealengine.com/la ... rchitecture/Timers/
https://docs.unrealengine.com/la ... e/Timers/index.html

23. debug 印字串
[mw_shl_code=cpp,true]#include "Engine.h"
   
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Cyan,
                            FString::Printf(TEXT("Server OnRMousePressed1 %f %f"), pos.X, pos.Y));[/mw_shl_code]

24.  給UE4 C++初學者的話
下面這三個官方基礎教學請一定要看過 可以更快上手
我只是把官方的功能大概整理一下而已。
https://docs.unrealengine.com/la ... ickStart/index.html
https://docs.unrealengine.com/la ... utorials/index.html
https://docs.unrealengine.com/la ... itecture/index.html


















评分

参与人数 1能量币 +30 收起 理由
shadowwp + 30 写的蛮好的,支持下

查看全部评分

rrfetch | 2018-1-22 08:51:18 | 显示全部楼层
不错,看完功力暴增 60 倍,可惜以前基础为 0,结果还是 0
回复 支持 反对

使用道具 举报

chinabeater | 2018-1-22 09:11:45 | 显示全部楼层
满满的干货,看完以后很有收获,谢谢
回复 支持 反对

使用道具 举报

kknsy | 2018-1-22 09:43:37 | 显示全部楼层
在大陆,请用简体
回复 支持 反对

使用道具 举报

123123123 | 2018-1-22 10:33:39 | 显示全部楼层
很有用的东西啊
回复 支持 反对

使用道具 举报

Xi_mo | 2018-1-22 10:50:48 | 显示全部楼层
牛逼,牛逼赞一个!
回复 支持 反对

使用道具 举报

扬天战神 | 2018-1-31 10:22:40 | 显示全部楼层
赞赞赞,台胞大佬,牛逼666,在群里也看到过你,奈何基础薄弱,不然肯定支持一波,很好很常用的一些教程
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1

主题

1

回帖

39

积分

初阶编码师

积分
39