??大家好,我是阿趙。
??之前在介紹HelloWorld的時候,我們很創建了一個MyGameModeBase的c++類,然后就可以在BeginPlay函數里面寫打印的HelloWorld。這一篇主要是說一下,GameMode究竟是一個什么東西,然后UE里面的生命周期是怎樣的。
一、 GameMode和關卡藍圖
1、C++程序的Main函數
??如果是純粹的C++項目,按道理都是有一個Main函數作為整個項目的啟動入口。UE的C++項目實際上也是有的。不過那個是引擎本身啟動的時候調用的,具體的位置在Engine\Source\Runtime\Launch\Private\Launch.cpp里面的GuardedMain函數
??在這個主入口的函數里面,引擎會做很多事情,比如初始化引擎功能之類。
??所以,寫UE的C++不能以Main函數作為我們邏輯的入口。那應該怎么辦呢?
2、關卡藍圖
??用過Unity引擎的朋友,可以對比一下Unity引擎的啟動方式。在Unity引擎里面的做法,一般是指定一個場景,然后在上面建一個空節點,然后掛一個繼承了MonoBehaviour的腳本,通過MonoBehaviour腳本的生命周期自動運行的函數,來進入游戲實際的邏輯。
??和Unity引擎一樣,UE引擎在剛開始運行的時候,也是需要指定一個默認的關卡(Level)的。當這個Level被載入并運行的時候,就可以運行這個關卡對應的代碼。如果從藍圖的角度看,UE有一個叫做“關卡藍圖”的東西:
??打開了之后,會看見里面默認有Event BeginPlay和EventTick兩個節點,然后我們就可以在里面編寫邏輯。
??UE的每一個Level,都會有一個對應的關卡藍圖。
3、 GameMode
??GameMode的概念和關卡藍圖有點類似,它也是在游戲啟動的時候會自動運行的。不過有個區別是,GameMode可以設置一個默認的,如果關卡本身不指定單獨的GameMode,那么每個關卡在載入的時候,都會運行默認的同一個GameMode代碼,然后每一個Level也可以單獨指定屬于自己的一個特殊的GameMode。
??這樣的操作,就有點類似于在Unity引擎的關卡里面掛一個空物體再掛一個MonoBehaviour的情況,反正這個關卡載入成功后,就會自動調用這個指定的GameMode里面的代碼,并且運行生命周期。
??所以,關卡藍圖和GameMode是可以同時存在的,但兩者做的事情有點類似。
二、 Actor和GameModeBase
1、 Actor
??如果我們創建一個藍圖類,會看到可以讓我們選擇父級:
??然后最基礎的父類,就是Actor,這個東西,對比Unity引擎來說,其實就是類似于GameObject,它是一個可以放置在場景里面的對象,包含著一些基礎的Transform屬性,還有生命周期。
??雙擊打開Actor的藍圖,會看到里面有EVent BeginPlay、Event ActorBeginOverlap和Event Tick三個默認的節點。這和之前的關卡藍圖有點類似,其實都是代表著這個Actor的生命周期,比如BeginPlay就是在Actor被載入的時候會運行一次。
??實際上創建其他藍圖類型,他們的基類都是Actor。
2、 GameModeBase
??再來創建一個游戲模式基礎的藍圖看看:
??這里我把這個游戲模式的藍圖命名為”GameModeBaseBP”:
??可以發現,在之前指定游戲模式的地方,會同時出現之前用C++寫的MyGameModeBase,和用藍圖創建的GameModeBaseBP。
??所以,這個GameModeBase,既可以用C++實現,又可以用藍圖實現。
??雙擊打開這個GameModeBaseBP藍圖,會看到和Actor是差不多的,也是有BeginPlay和Tick的生命周期。
??當然,Actor的生命周期不止這兩個,還有其他,比如可以添加一個EndPlay的生命周期。
??又來對比一下Unity引擎,Unity引擎里面的生命周期,都是由MonoBehaviour而來的,所以繼承MonoBehaviour的所有類都可以使用Awake、Start、Update、OnDestroy之類的生命周期。
那么UE這邊,因為都是基礎Actor的,所以Actor的生命周期,比如BeginPlay、Tick和EndPlay之類的,其他繼承Actor的類都能使用,包括GameModeBase。
三、 生命周期說明
??如果用C++來編寫GameModeBase,那么需要這樣聲明生命周期:
MyGameModeBase.h
#pragma once#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameModeBase.generated.h"/*** */
UCLASS()
class UECPPTEST_API AMyGameModeBase : public AGameModeBase
{GENERATED_BODY()
public:AMyGameModeBase();
public:virtual void BeginPlay();virtual void Tick(float DeltaTime);virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
};
MyGameModeBase.cpp
#include "MyGameModeBase.h"
AMyGameModeBase::AMyGameModeBase()
{PrimaryActorTick.bCanEverTick = true;
}void AMyGameModeBase::BeginPlay()
{Super::BeginPlay();UE_LOG(LogTemp, Display, TEXT("On level Start"));
}void AMyGameModeBase::Tick(float DeltaTime)
{Super::Tick(DeltaTime);UE_LOG(LogTemp, Display,TEXT("OnUpdate:%f"),DeltaTime);
}void AMyGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{ Super::EndPlay(EndPlayReason);FString str = StaticEnum<EEndPlayReason::Type>()->GetNameStringByValue((int64)EndPlayReason);UE_LOG(LogTemp, Display, TEXT("End Play:%s"), *str);
}
說明:
BeginPlay
相當于Unity的Start,在Actor對象剛開始播放時調用
Tick
相當于Unity的Update,但默認是不會調用的,需要在構造函數的時候開啟:
PrimaryActorTick.bCanEverTick = true;
EndPlay
相當于Unity的OnDestroy,在Actor對象被銷毀的時候調用,其中EndPlayReason是銷毀的原因。
對應的枚舉EEndPlayReason::Type
enum Type : int
{/** When the Actor or Component is explicitly destroyed. */Destroyed,/** When the world is being unloaded for a level transition. */LevelTransition,/** When the world is being unloaded because PIE is ending. */EndPlayInEditor,/** When the level it is a member of is streamed out. */RemovedFromWorld,/** When the application is being exited. */Quit,
};
如果要打印這個枚舉,可以:
FString str = StaticEnum<EEndPlayReason::Type>()->GetNameStringByValue((int64)EndPlayReason);UE_LOG(LogTemp, Display, TEXT("End Play:%s"), *str);