今天就開始進入實作的部分,我希望能講得慢點讓整個實作的思路能夠清晰,那就廢話不多說開始今天的主題。
當安裝完UE後先新增一份第三人稱模板的專案,設定C/C++並將Starter Content打勾,自行設定專案路徑與名稱後按下Create
進到專案後先進行簡單的設定,首先在場景上如果已經有基本的腳色模型先將他刪除,若沒有可以省略此步驟,然後要新增Player Start物件到場景裡,可以從左邊的列表中找到,直接將他拖進場景中即可。
這時在場景裡應該要有兩個Player Start物件,他是UE的原生物件,目的是將玩家在開始遊戲時的基本設定都設定好,讀者只要按下左上角的綠色三角形就可以執行遊戲,你會發現遊戲會直接生成一個腳色並且能夠自由的移動。
接下來是打開專案的程式碼,在最上面的Tool欄中點開就可以看到,因為筆者是使用VSCode所以直接點開Open Visual Studio Code即可,使用Visual Studio也是同理。
打開編譯器後所有我們自行寫的程式碼文件都會放在Source/”專案名稱”/Public與Private裡面,Publlic內會放標頭檔而Private內會放.cpp檔,到這邊就完成了專案設置的基本步驟,接下來要講解今天會用到的變數與函式。
// NetworkDemoCharacter.h
//在 public裡面添加
public:
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
// 在 protected裡面添加
protected:
UPROPERTY(EditDefaultsOnly, Category = "Health")
float MaxHealth;
UPROPERTY(ReplicatedUsing=OnRep_CurrentHealth)
float CurrentHealth;
UFUNCTION()
void OnRep_CurrentHealth();
void OnHealthUpdate();
這兩個變數只有CurrentHealth需要Replicated因此在他的UPROPERTY中有添加ReplicatedUsing=OnRep_CurrentHealth的指令,CurrentHealth也是上一張提到的Variable replication。
這是一個類似於事件的函示,是被用於註冊在CurrentHealth這個變數上,當CurrentHealth更新時就會去呼叫此函示,執行是在本地端。
在UE中會以On…_XXXX()來代表註冊事件的函示,而此函示又可以稱為RepNotify。
用於管理當CurrentHealth更新時該執行的邏輯。
這是最重要的一個函示,必須要在腳色內實作不然腳色的資訊是沒有辦法與伺服端溝通,這個函示就是客戶與伺服器之間的溝通橋樑。
// NetworkDemoCharacter.cpp
#include "Net/UnrealNetwork.h"
Constructor(){
...
MaxHealth = 100.0f;
CurrentHealth = MaxHealth;
}
void ANetworkDemoCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ANetworkDemoCharacter, CurrentHealth);
}
void ANetworkDemoCharacter::OnRep_CurrentHealth()
{
OnHealthUpdate();
}
void ANetworkDemoCharacter::OnHealthUpdate()
{
if(IsLocallyControlled())
{
FString healthMessage = FString::Printf(TEXT("Current health : %f"), CurrentHealth);
UE_LOG(LogTemp, Warning, TEXT("%s"), *healthMessage);
if(CurrentHealth <= 0)
{
FString deathMessage = FString::Printf(TEXT("Dead."));
UE_LOG(LogTemp, Warning, TEXT("%s"), *deathMessage);
}
}
if(GetLocalRole() == ROLE_Authority)
{
FString healthMessage = FString::Printf(TEXT("Player : %s, Current Health : %f"), *GetFName().ToString(), CurrentHealth);
UE_LOG(LogTemp, Warning, TEXT("%s"), *healthMessage);
}
}
cpp裡的部分先將Max與CurrentHealth兩個變數初始化,並實作其他三個函示。
在GetLifetimeReplication()中的DOREPLIFETIME巨集就是Property replication的主要指令,其中Super這個指令一定要寫,不然即使父類別有執行replicate,子類別也不會有效果。
其他兩個HealthUpdate的函示一個是註冊在CurrentHealth上面,當此變數更新時就會呼叫OnRep_CurrentHealth(),而OnHealthUpdate()則是作為方便管理要執行的邏輯,這裡會先檢查是否為本地的玩家後再判斷血量使否歸零並用UE_LOG把當前的血量資訊輸出在終端機裡面,GetLocalRole()簡而言之是要確認本地端是否有權利決定該腳色的最終狀態,而相等也等於腳色存在於伺服端上。
當這些都加完後回到專案裡面點選右下角的Recompile的選項,當編譯完成就會顯示綠色勾勾並且因能夠在專案裡面看到我們設定的MaxHealth值出現在"Health”的欄位裡面,若編譯錯誤就要看看有哪個部分遺漏了。(UE4的使用者要在上面的工具列點選Recompile)
整體的關係圖如下: