Unreal Engine 4的常见Tips

发表于2016-06-07
评论1 2.5k浏览
  算到现在使用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() );

Log an Int
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
  最基本代码如下:
.h
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);
};
.cpp
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""> FruitMap;
 
FruitMap.Add(5, TEXT("Banana"));
FruitMap.Add(2, TEXT("Grapefruit"));
FruitMap.Add(7, TEXT("Pineapple"));
// FruitMap == [
//  { Key: 5, Value: "Banana"     },
//  { Key: 2, Value: "Grapefruit" },
//  { Key: 7, Value: "Pineapple"  }
// ]
注意与TMultiMap的区别,TMap中的key都是唯一的,因此当插入一个重复键值时,原来的会被替换:
1
2
3
4
5
6
FruitMap.Add(2, TEXT("Pear"));
// FruitMap == [
//  { Key: 5, Value: "Banana"    },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" }
// ]
  也可以使用Emplace函数来进行元素的替换或增加,这种方法可以避免临时变量的创建:
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"    }
// ]
  可以使用FindOrAdd来进行查找键值的查找,如TMap中没有这个键值,那么则会创建一个默认的值:
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 );
}
  GetGlobalShaderMap如何使用?
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中的名字。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论