自定义模块
在开发游戏项目中我们有时候需要自定义多个模块,把代码分为几个模块,比如仅仅是客户端执行的,仅仅是服务器端执行的,又或者服务器和客户端共同执行的。(可能你也会这样分,战斗系统的为一个模块,休闲系统为一个模块等等)
下面我们在MyProject2项目自定义一个Client模块
在Source下增加一个Client文件夹,里面存在 Client.h, Client.cpp, Client.Build.cs (其实是模拟我们项目创建最初的模块)
Client.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
Client.cpp
#include "Client.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, Client, "Client" );
Client.Build.cs
using UnrealBuildTool;
public class Client : ModuleRules
{
public Client(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
}
}
项目的uproject: MyProject2.uproject
{
"FileVersion": 3,
"EngineAssociation": "{03A88847-4BCD-C0B2-8DF4-C38C8A466AAB}",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "MyProject2",
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine"
]
},
{
"Name": "Client",
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine"
]
}
]
}
然后在两个.Target.cs加上模块名字
MyProject2.Target.cs
using UnrealBuildTool;
using System.Collections.Generic;
public class MyProject2Target : TargetRules
{
public MyProject2Target(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
ExtraModuleNames.Add("MyProject2");
ExtraModuleNames.Add("Client");
}
}
至于MyProject2Editor.Target.cs也是一样的,这里就不写了。
类的宏导出
自定义n个模块,自然生产n个项目dll,在
而在我们的每个模块的类前面有个"模块名_API"的宏,起到把代码导出到相应dll的作用,经常在复制粘贴代码的时候得注意把类这个宏改的对应上相应的模块。比如我们Client模块下有个AMyActor类,AMyActor类的代码自然导出到“UE4Editor-MyProject2.dll”中,如下所示:
模块包含(依赖Dependency)(用到其他模块的类,API等等)
在我们的项目模块里的模块配置文件XXX.build.cs经常看到各种依赖模块的包含。
如下我们的MyProject2模块所示:
MyProject2.Build.cs
using UnrealBuildTool;
public class MyProject2 : ModuleRules
{
public MyProject2(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
}
}
这里
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
Core,CoreUObject,Engine,InputCore等等是啥,在哪?
这些其实是UE4内部源码的模块,
UE4的源码分为”Developer“,"Editor", "Programs","RunTime", "ThirdParty",五大部分。如下所示:
而这五大部分的每个部分都分为n个小部分。拿我们的游戏来说,经常用到的是Runtime这部分,可以看下Runtime由哪些小部分组成,如下所示:
可以看到我们上面提到的Core,CoreUObject,Engine,InputCore等等都是Runtime下的,是我们生成一个项目时模块配置文件就包含了的。
当我们需要用到其中一个模块的时候,就需要包含这个模块,也就是上面的 ”PublicDependencyModuleNames.AddRange“
比如我们需要给我们的Actor添加一个 UPawnSensingComponent 组件,而UPawnSensingComponent在Runtime的AIModule模块下:
MyActor.h
UPROPERTY(VisibleAnywhere)
class UPawnSensingComponent* PawnSensingComponent;
MyActor.cpp
#include "MyActor.h"
#include "Perception/PawnSensingComponent.h"
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
PawnSensingComponent = CreateDefaultSubobject<UPawnSensingComponent>(TEXT("PawnSensingComponent"));
}
如果我们没有在.Build.cs的PublicDependencyModuleNames.AddRange 添加 AIModule, 运行直接爆下面的错误
error LNK2019: 无法解析的外部符号 "__declspec(dllimport).........................
所以应该配置上相应模块AIModule,才能编译成功
PublicDependencyModuleNames.AddRange(new string[] { "Core",
"CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "AIModule" });
除了“Runtime”其他的大模块下,如”Developer“,"Editor", "Programs"的子模块也是也是同样的包含(之前我一直以为只是Runtime之下的子模块才是这样包含),像“Editor”模块下的“UnrealEd”等等也是一样包含的。
用FModuleManager直接绕过配置文件获取相应模块
上前说调用一个模块的类或者API, 得在.build.cs文件中依赖中包含相应模块。其实也不一定需要这样做,有时候可以用FModuleManager来直接获取各个模块,这样就不用在.build.cs文件配置相应模块了。比如我们经常用到“AssetRegistry”模块的FAssetRegistryModule来删除资源,获取各种资源的路径等等。
FAssetRegistryModule& assetRegisterModule = FModuleManager::Get().GetModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> arrayAssetData;
assetRegisterModule.Get().GetAllAssets(arrayAssetData);
参考资料
[1]UE4 游戏模块