Unreal Engine 4的常见Tips
发表于2016-06-07
算到现在使用UE4大概有两年了吧,从它每月还收费19美金的时候用到现在4.11都出来了。这是一款很强大的引擎,因此我也总结了方方面面的一些经验,这篇博客会时时更新。
锁帧
直接修改引擎设置的方法:
在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,那么你应该这么做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // Decleare Log Category // General Log DECLARE_LOG_CATEGORY_EXTERN(YourLog, Log, All); // Logging during game startup DECLARE_LOG_CATEGORY_EXTERN(YourInit, Log, All); // Logging for your AI system DECLARE_LOG_CATEGORY_EXTERN(YourAI, Log, All); // Logging for Critical Errors that must always be addressed DECLARE_LOG_CATEGORY_EXTERN(YourCriticalErrors, Log, All); // Define Log Category // General Log DEFINE_LOG_CATEGORY(YourLog); // Logging during game startup DEFINE_LOG_CATEGORY(YourInit); // Logging for your AI system DEFINE_LOG_CATEGORY(YourAI); // Logging for Critical Errors that must always be addressed DEFINE_LOG_CATEGORY(YourCriticalErrors); // Using UE_LOG //"This is a message to yourself during runtime!" 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() ); |
1 2 | //"有了金坷垃,小麦亩产1800!" UE_LOG(YourLog,Warning,TEXT( "有了金坷垃,小麦亩产%d!" ), 1800); |
Log a Float
1 2 | //"有了金坷垃,小麦亩产1800.0f!" UE_LOG(YourLog,Warning,TEXT( "有了金坷垃,小麦亩产%f!" ), 1800.0f); |
其余的关于Vector, Color, FName等都同理可以进行输出。
Current Camera
当前相机的获得可以通过两种方式:
可以使用GetOwningPlayerController函数:
1 2 3 4 5 6 | auto pc = GetOwningPlayerController(); auto *vt = pc->GetViewTarget(); ACameraActor* camera = Cast(vt); if (camera) { //do stuff } 使用GetPlayerCameraManager函数 |
1 | auto camera = UGameplayStatics::GetPlayerCameraManager(WorldContext, 0); 其中的WorldContext是世界上下文参数。 |
Enumeration in C++
1 2 3 4 5 6 7 8 9 10 11 12 13 | UENUM () namespace EBattleState { enum Type { CameraWander = 0 , // The camera is wandering around. ChooseCharacter , // Choose one character, and is going to choose location. CharacterMoving , // The character is moving, player input is not allowed. Count , }; } TEnumAsByte BattleStateEnum; |
Apex Destruction
Destruction Mesh在还未破碎的情况下,是没有碰撞的,如果要启用,需要在Level中选中该Actor,将Use Async Scene设为False:

如果这个值是灰色不可改变,那么需要在Edit->Project Settings->Physics->Simulation->Enable Async Scene设定为True
破碎后产生的小Chunks是默认与WorldDynamic无碰撞的,如果需要其有碰撞,那么需要将Large Chunk Threshold设定为一个比较大的数字:

千万不要进行缩放你的Destructible Mesh,会导致Chunks的碰撞计算出错。
UE4中只支持随机切片,如果要进行自定义的Destructible,你需要apex physx lab,非常酷的东西。
Animation&Rigging Tool
如果你不幸在ART中遇到了“Parent of end effector must be a joint”的错误,那么需要检查一下在你的骨骼模型中是否有double system,比如说头发啊或者裙摆之类的东西。
如果你在有布料的骨骼模型看到了一个奇怪的顶点,例如下面这个:

这种情况通常是你的Skinning出了问题,着重检查那些不该有蒙皮信息的骨骼。
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 2 3 4 5 6 | // Your .h file class USphereComponent* Sphere; // Your .cpp file Sphere = PCIP.CreateDefaultSubobject( this , TEXT( "SphereComp" ));
|
Blueprint
Bind一个event之后,要记得在event上点右键,选择RefreshNode。
只有在Event Graph中才能设定Timeline。
你每在Level中更改了一个Actor的信息,都会重新调用一次Construction Script。
想要摄像机Lag吗?在SpringArm中进行设定吧!
Interface
在UE4的编程中,Interface非常重要。类之间只能进行单一继承,而针对于Interface则可以进行多继承。个人的经验中,它对于物品交互等的构建都非常方便。
在C++中创建Interface
最基本代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #pragma once #include "Interface.h" #include "InterfaceXBoxEvent.generated.h" /** Class needed to support InterfaceCast(Object) */ UINTERFACE() class UInterfaceXBoxEvent : public UInterface { GENERATED_UINTERFACE_BODY() }; class IInterfaceXBoxEvent { GENERATED_IINTERFACE_BODY() public : UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Activate" ) void XboxEvent_KillAI(EAIType::Type type); }; |
1 2 3 4 5 6 7 8 | #include "MyGame.h" #include "InterfaceXBoxEvent.h" UInterfaceXBoxEvent::UInterfaceXBoxEvent( const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } |
C++
TMap的使用
1 2 3 4 5 6 7 8 9 10 | TMap
|
1 2 3 4 5 6 | FruitMap.Add(2, TEXT( "Pear" )); // FruitMap == [ // { Key: 5, Value: "Banana" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" } // ] |
1 2 3 4 5 6 7 | FruitMap.Emplace(3, TEXT( "Orange" )); // FruitMap == [ // { Key: 5, Value: "Banana" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" }, // { Key: 3, Value: "Orange" } // ] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | FString& Ref7 = FruitMap.FindOrAdd(7); // Ref7 == "Pineapple" // FruitMap == [ // { Key: 5, Value: "Mango" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" }, // { Key: 3, Value: "Orange" } // ] FString& Ref8 = FruitMap.FindOrAdd(8); // Ref8 == "" // FruitMap == [ // { Key: 5, Value: "Mango" }, // { Key: 2, Value: "Pear" }, // { Key: 7, Value: "Pineapple" }, // { Key: 3, Value: "Orange" }, // { Key: 8, Value: "" } // ] |
可以使用Remove函数,RemoveAndCopyValue函数或者FindAndRemoveChecked函数来进行元素的删除。
我去……关于TMap都可以单独出一个博客了……
在C++中寻找BP中的物件或类:
1 2 3 4 5 6 | static ConstructorHelpers:: FObjectFinder CubeMesh (TEXT( "StaticMesh'Content/TopDownBP/CubeMesh'" )); if ( CubeMesh.Object ) { Mesh ->SetStaticMesh (CubeMesh. Object ); } |
1 2 | const auto FeatureLevel = GMaxRHIFeatureLevel; auto ShaderMap = GetGlobalShaderMap(FeatureLevel); |
1 2 3 4 | if (Material == NULL) { Material = UMaterial:: GetDefaultMaterial(MD_Surface ); } |
在C++中调用Blueprint的函数
先吐槽,这个时候其实建议使用Interface来进行调用会清晰的多,以下方式只是Trick……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // MainPlayerCharacter.cpp // By: Noah Zuo // Disc: Call functions in a blueprint from C++ #include "MainPlayerCharacter.h" AMainPlayerCharacter::AMainPlayerCharacter ( const class FObjectInitializer& PCIP) : Super( PCIP) { // The BP is located at /Game/Blueprints/TestTest folder. static ConstructorHelpers ::FObjectFinder assetObject(TEXT( "Blueprint'/Game/Blueprints/TestTest'" )); if (assetObject.Succeeded()) { TestBlueprint = ( UClass*)assetObject .Object-> GeneratedClass; } } void AMainPlayerCharacter::BeginPlay() { // Spawn a Actor in the world. TestObjectActor = GWorld->SpawnActor(TestBlueprint); } void AMainPlayerCharacter::Tick( float DeltaSeconds) { Super::Tick (DeltaSeconds); UFunction *tmp = TestObjectActor->FindFunction(TEXT ( "TestPrint" )); if (tmp != NULL) TestObjectActor ->ProcessEvent(tmp, nullptr); }
|
Particle
如果想在BP/C++中动态调整Particle的参数,需要添加Dynamic模块。然后将Distribution设定为 ParticleParameter。Param Name设定为Material Editor中的名字,Parameter Name设定为BP中的名字。
