Unreal Engine 4 —— 常见Tips

时间:2021-08-11 18:03:03
Editor Only

如果想要代码只在Editor下编译,需要如下操作:

[代码]:

1 #if WITH_EDITOR
2  
3 //editor only code -- doesn't appe<a href="http://www.52vr.com/armr/" style="font-weight: bold;color: ;" target="_blank">AR</a> in final game
4  
5 #endif


Build Error

InputCoreTypes.h如果出现各种syntax error. 
在PCH中加入#include "Engine.h"即可。

Plugin

如果你要构建一个Function Lib Plugin,记得将LoadingPhase设定为PreDefault.

Console Command

创建Console Command的方法:在GameMode中创建对应的函数,UFUNCTION的Category需要为ExecFunctions

创建无参数的Console Command

   

[代码]:

1 UFUNCTION(Exec, Category = ExecFunctions)
2    void DoSomething();


创建带参数的Console Command

[代码]:

1 /*Function with one parameter*/
2 UFUNCTION(Exec, Category = ExecFunctions)
3 void DoSomethingElse(float param);


如此即可

Android

获得Env

[代码]:

1 auto env = FAndroidApplication::GetJavaEnv();


GNativeAndroidApp

调用extern struct android_app* GNativeAndroidApp; 
记得需要加入

[代码]:

1 #include <android_native_app_glue.h></android_native_app_glue.h>


JNI

记得在.cs文件中的PrivateIncludePaths加入: 
"Runtime/Launch/Private"

锁帧

直接修改 引擎设置的方法:
在config/ConsoleVariables.ini中找到[Startup]

在其后加入:

[代码]:

1 t.MaxFPS=30


针对项目的方法: 
在DefaultEngine.ini中查找[SystemSettings]的section,如果没有则新建一个,在其后加入:

[代码]:

1 t.MaxFPS=30


Log to screen

如果你想向屏幕上输出一些东西,可以使用如下代码:

[代码]:

1 GEngine->AddOnScreenDebugMessage(-1, -1, FColor::Red, TEXT("阿妹你看,上帝压狗! "));


Log Category

如果你想要定义并且使用自己的Log,那么你应该这么做:

[代码]:

01 // Decleare Log Category
02  
03 // General Log
04 DECLARE_LOG_CATEGORY_EXTERN(YourLog, Log, All);
05  
06 // Logging during game startup
07 DECLARE_LOG_CATEGORY_EXTERN(YourInit, Log, All);
08  
09 // Logging for your AI system
10 DECLARE_LOG_CATEGORY_EXTERN(YourAI, Log, All);
11  
12 // Logging for Critical Errors that must always be addressed
13 DECLARE_LOG_CATEGORY_EXTERN(YourCriticalErrors, Log, All);
14  
15 // Define Log Category
16 // General Log
17 DEFINE_LOG_CATEGORY(YourLog);
18  
19 // Logging during game startup
20 DEFINE_LOG_CATEGORY(YourInit);
21  
22 // Logging for your AI system
23 DEFINE_LOG_CATEGORY(YourAI);
24  
25 // Logging for Critical Errors that must always be addressed
26 DEFINE_LOG_CATEGORY(YourCriticalErrors);
27  
28 // Using UE_LOG
29 //"This is a message to yourself during runtime!"
30 UE_LOG(YourLog,Warning,TEXT("This is a message to yourself during runtime!"));


格式化的Log

Log Message

[代码]:

1 //"阿妹你看,上帝压狗!"
2 UE_LOG(YourLog,Warning,TEXT("阿妹你看,上帝压狗!"));


Log an FString

%s 字符串在Log中是使用TCHAR* 的, 所以我们要使用 *FString

[代码]:

1 //"阿妹你看,上帝压狗!"
2 UE_LOG(YourLog,Warning,TEXT("阿妹你看,上帝压%s!"), *TheDog->GetName() );


Log an Int

[代码]:

1 //"有了金坷垃,小麦亩产1800!"
2 UE_LOG(YourLog,Warning,TEXT("有了金坷垃,小麦亩产%d!"), 1800);


Log a Float

[代码]:

1 //"有了金坷垃,小麦亩产1800.0f!"
2 UE_LOG(YourLog,Warning,TEXT("有了金坷垃,小麦亩产%f!"), 1800.0f);


其余的关于Vector, Color, FName等都同理可以进行输出。

Current Camera

当前相机的获得可以通过两种方式:

可以使用GetOwningPlayerController函数:

[代码]:

1 auto pc = GetOwningPlayerController();
2 auto *vt = pc->GetViewTarget();
3 ACameraActor* camera = Cast(vt);
4 if (camera) {
5     //do stuff
6 }


使用GetPlayerCameraManager函数

[代码]:

1 auto camera = UGameplayStatics::GetPlayerCameraManager(WorldContext, 0);


其中的WorldContext是世界上下文参数。

Enumeration in C++

[代码]:

01 UENUM ()
02 namespace EBattleState
03 {
04         enum Type
05         {
06               CameraWander = 0 ,                       // The camera is wandering around.
07               ChooseCharacter ,                        // Choose one character, and is going to choose location.
08               CharacterMoving ,                        // The character is moving, player input is not allowed.
09               Count ,
10         };
11 }
12  
13 TEnumAsByte BattleStateEnum;


Apex Destruction

Destruction Mesh在还未破碎的情况下,是没有碰撞的,如果要启用,需要在Level中选中该Actor,将Use Async Scene设为False:

Unreal Engine 4 —— 常见Tips


如果这个值是灰色不可改变,那么需要在Edit->Project Settings->Physics->Simulation->Enable Async Scene设定为True

破碎后产生的小Chunks是默认与WorldDynamic无碰撞的,如果需要其有碰撞,那么需要将Large Chunk Threshold设定为一个比较大的数字: 

Unreal Engine 4 —— 常见Tips


千万不要进行缩放你的Destructible Mesh,会导致Chunks的碰撞计算出错。

ue4中只支持随机切片,如果要进行自定义的Destructible,你需要apex physx lab,非常酷的东西。
Animation&Rigging Tool

如果你不幸在ART中遇到了“Parent of end effector must be a joint”的错误,那么需要检查一下在你的骨骼 模型中是否有double system,比如说头发啊或者裙摆之类的东西。
如果你在有布料的骨骼模型看到了一个奇怪的顶点,例如下面这个: 

Unreal Engine 4 —— 常见Tips


这种情况通常是你的Skinning出了问题,着重检查那些不该有蒙皮信息的骨骼。
若是出现了类似于No object matches name: ik_upperarm_fk_matcher_l的问题,则有可能是Rigging的版本太老。使用最新的ART代码重新Build一次整个Rigging系统。

Class名称的前缀

Template classes are prefixed with the letter T.
Classes inheriting from UObject are prefixed with the letter U.
Classes inheriting from AActor are prefixed with the letter A.
Classes inheriting from SWidget are prefixed with the letter S.
Abstract interface classes are prefixed with the letter I.
Most other classes are typically prefixed with the letter F.

关于Unreal Engine 4的工作流程

当你在给你的场景进行光照布局的时候,记得一定要把眼球自适应关掉!
UE4与Perforce简直是天生一对,我他娘的太喜欢这一对了。
当你每导入一个人形骨骼模型的时候,记得一定要在 Retarget Manager 中进行骨骼的设定。这样可以确保动画的Retargeting正常工作,而且可以节省很多工作量。

Components

Components一个很方便的作用是可以任意挂载,我用它来设计技能模块非常方便。
Components在CPP中的初始化:

[代码]:

1 // Your .h file
2 class USphereComponent* Sphere;
3  
4 // Your .cpp file
5 Sphere = PCIP.CreateDefaultSubobject<uspherecomponent>(this, TEXT("SphereComp"));</uspherecomponent>


Blueprint

Bind一个event之后,要记得在event上点右键,选择RefreshNode。
只有在Event Graph中才能设定Timeline。
你每在Level中更改了一个Actor的信息,都会重新调用一次Construction Script。
想要摄像机Lag吗?在SpringArm中进行设定吧!

Interface

在UE4的编程中,Interface非常重要。类之间只能进行单一继承,而针对于Interface则可以进行多继承。个人的经验中,它对于物品交互等的构建都非常方便。

在C++中创建Interface 
最基本代码如下:

.h

[代码]:

01 #pragma once
02  
03 #include "Interface.h"
04 #include "InterfaceXBoxEvent.generated.h"
05  
06 /** Class needed to support InterfaceCast<itostringinterface>(Object) */
07 UINTERFACE()
08 class UInterfaceXBoxEvent : public UInterface
09 {
10     GENERATED_UINTERFACE_BODY()
11 };
12  
13 class IInterfaceXBoxEvent
14 {
15     GENERATED_IINTERFACE_BODY()
16  
17 public:
18     UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Activate")
19     void XboxEvent_KillAI(EAIType::Type type);
20 };
21  
22 .cpp
23  
24 #include "MyGame.h"
25 #include "InterfaceXBoxEvent.h"
26  
27 UInterfaceXBoxEvent::UInterfaceXBoxEvent(const FObjectInitializer& ObjectInitializer)
28     : Super(ObjectInitializer)
29 {
30  
31 }
32 </itostringinterface>

C++

避免Garbage Collection的做法:
使用UObjectBaseUtility::AddToRoot()可以达到该目的。

TMap的使用

[代码]:

01 TMap<int32, fstring=""> FruitMap;
02  
03 FruitMap.Add(5, TEXT("Banana"));
04 FruitMap.Add(2, TEXT("Grapefruit"));
05 FruitMap.Add(7, TEXT("Pineapple"));
06 // FruitMap == [
07 //  { Key: 5, Value: "Banana"     },
08 //  { Key: 2, Value: "Grapefruit" },
09 //  { Key: 7, Value: "Pineapple"  }
10 // ]</int32,>


注意与TMultiMap的区别,TMap中的key都是唯一的,因此当插入一个重复键值时,原来的会被替换:

[代码]:

1 FruitMap.Add(2, TEXT("Pear"));
2 // FruitMap == [
3 //  { Key: 5, Value: "Banana"    },
4 //  { Key: 2, Value: "Pear"      },
5 //  { Key: 7, Value: "Pineapple" }
6 // ]


也可以使用Emplace函数来进行元素的替换或增加,这种方法可以避免临时变量的创建:

[代码]:

1 FruitMap.Emplace(3, TEXT("Orange"));
2 // FruitMap == [
3 //  { Key: 5, Value: "Banana"    },
4 //  { Key: 2, Value: "Pear"      },
5 //  { Key: 7, Value: "Pineapple" },
6 //  { Key: 3, Value: "Orange"    }
7 // ]


可以使用FindOrAdd来进行查找键值的查找,如TMap中没有这个键值,那么则会创建一个默认的值:

[代码]:

01 FString& Ref7 = FruitMap.FindOrAdd(7);
02 // Ref7     == "Pineapple"
03 // FruitMap == [
04 //  { Key: 5, Value: "Mango"     },
05 //  { Key: 2, Value: "Pear"      },
06 //  { Key: 7, Value: "Pineapple" },
07 //  { Key: 3, Value: "Orange"    }
08 // ]
09 FString& Ref8 = FruitMap.FindOrAdd(8);
10 // Ref8     == ""
11 // FruitMap == [
12 //  { Key: 5, Value: "Mango"     },
13 //  { Key: 2, Value: "Pear"      },
14 //  { Key: 7, Value: "Pineapple" },
15 //  { Key: 3, Value: "Orange"    },
16 //  { Key: 8, Value: ""          }
17 // ]


可以使用Remove函数,RemoveAndCopyValue函数或者FindAndRemoveChecked函数来进行元素的删除。

我去……关于TMap都可以单独出一个博客了……

在C++中寻找BP中的物件或类:

[代码]:

1 static ConstructorHelpers:: FObjectFinder<ustaticmesh> CubeMesh (TEXT( "StaticMesh'Content/TopDownBP/CubeMesh'" ));
2  
3 if ( CubeMesh.Object )
4 {
5     Mesh ->SetStaticMesh (CubeMesh. Object );
6 }</ustaticmesh>


GetGlobalShaderMap如何使用?

[代码]:

1 const auto FeatureLevel = GMaxRHIFeatureLevel;
2 auto ShaderMap = GetGlobalShaderMap(FeatureLevel);


默认材质

[代码]:

1 if(Material == NULL)
2 {
3     Material = UMaterial:: GetDefaultMaterial(MD_Surface );
4 }


在C++中调用Blueprint的函数
先吐槽,这个时候其实建议使用Interface来进行调用会清晰的多,以下方式只是Trick……

[代码]:

01 // MainPlayerCharacter.cpp
02 // By: Noah Zuo
03 // Disc: Call functions in a blueprint from C++
04  
05 #include "MainPlayerCharacter.h"
06  
07 AMainPlayerCharacter::AMainPlayerCharacter (const class FObjectInitializer& PCIP)
08 : Super( PCIP)
09 {
10     // The BP is located at /Game/Blueprints/TestTest folder.
11     static ConstructorHelpers ::FObjectFinder<ublueprint> assetObject(TEXT( "Blueprint'/Game/Blueprints/TestTest'" ));
12  
13     if (assetObject.Succeeded())
14     {
15         TestBlueprint = ( UClass*)assetObject .Object-> GeneratedClass;
16     }
17 }
18  
19 void AMainPlayerCharacter::BeginPlay()
20 {
21     // Spawn a Actor in the world.
22     TestObjectActor = GWorld->SpawnActor<aactor>(TestBlueprint);
23 }
24  
25  
26 void AMainPlayerCharacter::Tick(float DeltaSeconds)
27 {
28     Super::Tick (DeltaSeconds);
29  
30     UFunction *tmp = TestObjectActor->FindFunction(TEXT ("TestPrint"));
31  
32     if (tmp != NULL)
33         TestObjectActor ->ProcessEvent(tmp, nullptr);
34 }</aactor></ublueprint>


Particle

如果想在BP/C++中动态调整Particle的参数,需要添加Dynamic模块。然后将Distribution设定为ParticleParameter。Param Name设定为Material Editor中的名字,Parameter Name设定为BP中的名字。

Unreal Engine 4 —— 常见Tips