UE4(一)- 源码分析起点

06/21/2020

UE4文件结构

现在UE4游戏引擎现在都很简单,可以从Epic Games Launcher启动器中直接下载不同版本的UE4。我下载了版本是UE_4.24版本,双击打开文件夹,里面的结构是见图
UE_4,24文件结构
FeaturePacks,Samples和Templates是由UE4制作的一些模板和样本,比如有第三人称设计游戏等等,我们主要关注Engine文件夹里面内容
打开Engine文件夹
UE_4.24Engine文件夹
UE4 我们都知道是开源的,源码放在Source文件夹中,游戏引擎还提供了做好的游戏资源给我们,比如材质,纹理和模型,放在了Content里面。打开Source文件夹
UE.4_24Engine下Source文件夹内容
这里面包含了UE4游戏引擎的源码,主要分为5大内容,有Developer,Editor,Programs,Runtime,ThirdParty,游戏引擎核心代码放在Runtime中,相当于main的启动过程,和初始窗口和显卡部分,因为UE4是跨平台的,各个平台之间肯定有不同的地方。
打开UE4,创建一个C++项目。

UE4 Editor

UE4C++创建
当项目创建启动,它会默认打开默认的IDE,我设置的是VS2019,进而打开了UE4游戏引擎编辑器(Editor)窗口
U
UE4Editor

UE4 C++ 项目文件夹结构

UE4.24_C++MyProject文件夹
我使用VS2019打开MyProjct.sln文件,也可以开启编译并显示处UE4Editor可视化窗口

UE4 源码分析的起点

UE4Engine
再一次看到UE4Engine文件夹,我们就可以从这里开始阅读UE4源码

项目中的main函数

从上述项目中MyProject2里面Source文件夹中会先运行MyProject2.cpp 里面的函数再运行MyProject2GameModeBase.cpp

//MyProject2.h
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, MyProject2, "MyProject2" );

//MyProject2GameModeBase.h
UCLASS() class MYPROJECT2_API AMyProject2GameModeBase : public AGameModeBase
{
	GENERATED_BODY()
	
};

编译游戏主要模块,然后初始化Editor并加载游戏模块。

跨平台(Main函数)

最近使用Direct3D在windows下完成了一款简易游戏,但现在我需要把这个游戏跨平台到PS4上,所以我需要了解一些跨平台的知识。在Engine/Source/Runtime/Lanuch/Lanuch.cpp中有关如何启动不同平台的main函数 - GuardedMain
UE4_Lanuch文件夹
查看Lanuch.cpp 找到GuardMain函数
UE4_Launch文件
这里有关于如何区分Windows和其他平台的方法,用的是宏定义,Windows就是和其他平台不一样,单独需要有一个main函数呢

跨平台方法之一(宏定义if-else)

//Launch.h   ------ Main 函数
#if PLATFORM_WINDOWS
int32 GuardedMain(const TCHAR* CmdLine,/*....HINSTANCE hInInstance ...*/) //绕不开的HINSTANCE
#else
int32 GuardedMain(const TCHAR* CmdLine,/*....*/)
#endif
{
/*
Debug
FileManager - log
*/
//-----------
EnginePreInit(CmdLine); 
#if WITH_EDITOR
		if (GIsEditor)
		{
			ErrorLevel = EditorInit(GEngineLoop);
		}
		else
#endif
		{
			ErrorLevel = EngineInit();
		}
	}
// 时间开始 ----游戏开始
while( !IsEngineExitRequested() )
	{
		EngineTick();  //FEngineLoop GEngineLoop.Tick();
	}
}

IEngineLoop 和FEngineLoop

开始循环UE4游戏引擎和启动UE4Editor界面

扫描二维码关注公众号,回复: 11342086 查看本文章
//UnrealEngin.h   -----------EngineLoop 游戏死循环
class IEngineLoop
{
public:
	virtual int32 Init() = 0;				//初始化
	virtual void Tick() = 0;    			//游戏时间
	virtual void ClearPendingCleanupObjects() = 0;
}//LaunchEngineLoop.h   ----------------全局类
class FEngineLoop:public IEngineLoop
{
public:
		/**
	 * Pre-Initialize the main loop, and generates the commandline from standard ArgC/ArgV from main().
	 *
	 * @param ArgC The number of strings in ArgV.
	 * @param ArgV The command line parameters (ArgV[0] is expected to be the executable name).
	 * @param AdditionalCommandLine Optional string to append to the command line (after ArgV is put together).
	 * @return Returns the error level, 0 if successful and > 0 if there were errors.
	 */ 
	int32 PreInit(int32 ArgC, TCHAR* ArgV[], const TCHAR* AdditionalCommandline = nullptr);  //main函数的形参

	/**
	 * Pre-Initialize the main loop - parse command line, sets up GIsEditor, etc.
	 *
	 * @param CmdLine The command line.
	 * @return The error level; 0 if successful, > 0 if there were errors.
	 */ 
	int32 PreInit(const TCHAR* CmdLine);
	
	/** First part of PreInit. */
	int32 PreInitPreStartupScreen(const TCHAR* CmdLine);

	/** Second part of PreInit. */
	int32 PreInitPostStartupScreen(const TCHAR* CmdLine);

	/** Load all modules needed before Init. */ 
	void LoadPreInitModules();

	/** Load core modules. */
	bool LoadCoreModules();

#if WITH_ENGINE
	
	/** Load all core modules needed at startup time. */
	bool LoadStartupCoreModules();
	
	/** Load all modules needed at startup time. */
	bool LoadStartupModules();

	/**
	 * Initialize the main loop (the rest of the initialization).
	 *
	 * @return The error level; 0 if successful, > 0 if there were errors.
	 */ 
	virtual int32 Init() override;

	/** Initialize the timing options from the command line. */ 
	void InitTime();

	/** Performs shut down. */
	void Exit();

	/** Whether the engine should operate in an idle mode that uses no CPU or GPU time. */
	bool ShouldUseIdleMode() const;

	/** Advances the main loop. */
	virtual void Tick() override;

	/** Removes references to any objects pending cleanup by deleting them. */
	virtual void ClearPendingCleanupObjects() override;

#endif // WITH_ENGINE

	/** RHI post-init initialization */
	static void PostInitRHI();

	/** Pre-init HMD device (if necessary). */
	static void PreInitHMDDevice();

public:

	/** Initializes the application. */
	static bool AppInit();

	/**
	 * Prepares the application for shutdown.
	 *
	 * This function is called from within guarded exit code, only during non-error exits.
	 */
	static void AppPreExit();

	/**
	 * Shuts down the application.
	 *
	 * This function called outside guarded exit code, during all exits (including error exits).
	 */
	static void AppExit();
	
};

- FEngineLoop 类基本是初始化各个资源,比如窗口,显卡,时间和应用程序
- 基本执行顺序FEngineLoop ---> Editor初始化 ----->时间计算 --- > Tick函数(还不是游戏时间的时间)

FEngineLoop 的核心函数-Tick函数

//LanuchEngineLoop.cpp 是一个超级长的函数
void FEngineLoop::Tick()
{
	//.....
	//set FApp::CurrentTime, FApp::DeltaTime .... 应用程序的时间,游戏世界的时间
	GEngine->UpdateTimeAndHandleMaxTickRate();
	//.....
	//Beginning of RHI frame
	ENQUEUE_RENDER_COMMAND(BeginFrame)([CurrentFrameCounter](FRHICommandListImmediate& RHICmdList)
		{
			BeginFrameRenderThread(RHICmdList, CurrentFrameCounter);
		});
	for (const FWorldContext& Context : GEngine->GetWorldContexts())
		{
			UWorld* CurrentWorld = Context.World();
			if (CurrentWorld)
			{
				FSceneInterface* Scene = CurrentWorld->Scene;

				ENQUEUE_RENDER_COMMAND(SceneStartFrame)([Scene](FRHICommandListImmediate& RHICmdList)
				{
					Scene->StartFrame();
				});
			}
		}
	//
	//.....
	CalculateFPSTiming(); //FPS/MS
	GEidtor->PlayWorld; // Editor 有一个Play的按键
	//main game engine tick (world,game objects ....)
	GEngine->Tick(FApp::GetDeltaTime,bIdleMode);
	//...
	UGameEngine* GameEngine = Cast<UGameEngine>(GEngine); //游戏引擎的开始
	//....
}

UEngine类

  • UEngine是一个全局的实例
  • 继承了UObject和FExec
//Engine.h
/**
 * Abstract base class of all Engine classes, responsible for management of systems critical to editor or game systems.
 * Also defines default classes for certain engine systems.
 */
class ENGINE_API UEngine
	: public UObject
	, public FExec
{
	//....
	/** A UObject spawned at initialization time to handle game-specific data */
	UPROPERTY()
	UObject *GameSingleton;
	
	//Texture Font Asset Material ...
	//..

	/** Initialize the game engine. */
	virtual void Init(IEngineLoop* InEngineLoop);

	/** Start the game, separate from the initialize call to allow for post initialize configuration before the game starts. */
	virtual void Start();
	//....
}
/** Global engine pointer. Can be 0 so don't use without checking. */
extern ENGINE_API class UEngine*			GEngine;

UGameEngine 游戏的核心

UGameEngine是UEngine的一个子类,游戏的框架从这里开始

/**
 * Engine that manages core systems that enable a game.
 */
class ENGINE_API UGameEngine
	: public UEngine
{
// UEngine interface

	virtual void Init(class IEngineLoop* InEngineLoop) override;
	virtual void Start() override;
	virtual void PreExit() override;
	virtual void Tick( float DeltaSeconds, bool bIdleMode ) override;
	virtual float GetMaxTickRate( float DeltaTime, bool bAllowFrameRateSmoothing = true ) const override;
	virtual void ProcessToggleFreezeCommand( UWorld* InWorld ) override;
	virtual void ProcessToggleFreezeStreamingCommand( UWorld* InWorld ) override;
	virtual bool NetworkRemapPath(UNetDriver* Driver, FString& Str, bool bReading = true) override;
	virtual bool ShouldDoAsyncEndOfFrameTasks() const override;
}

总结

UE4自己首先需要加载模块,并且初始化自己UE4引擎循环和编译器界面,当你点击Play按钮之后,会触发UGameEngine来开始游戏的循环。

  • 针对问题去了解游戏引擎,比如这篇针对的是关于UE4如何跨平台和循环游戏的
  • To be continued

UE4文件结构介绍
UE4启动从Main函数开始

猜你喜欢

转载自blog.csdn.net/weixin_44200074/article/details/106875563