【转】Unreal Engine 4的常见Tips

[复制链接]
查看6961 | 回复9 | 2016-7-13 23:19:00 | 显示全部楼层 |阅读模式
本帖最后由 czbming 于 2016-7-13 23:19 编辑

算到现在使用UE4大概有两年了吧,从它每月还收费19美金的时候用到现在4.11都出来了。这是一款很强大的引擎,因此我也总结了方方面面的一些经验,这篇博客会时时更新。

锁帧
  • 直接修改引擎设置的方法:
在config/ConsoleVariables.ini中找到[Startup]
在其后加入:
t.MaxFPS=30
  • 针对项目的方法: 在DefaultEngine.ini中查找[SystemSettings]的section,如果没有则新建一个,在其后加入:
t.MaxFPS=30


Log to screen
如果你想向屏幕上输出一些东西,可以使用如下代码:
[mw_shl_code=cpp,true]GEngine->AddOnScreenDebugMessage(-1, -1, FColor::Red, TEXT("阿妹你看,上帝压狗! "));[/mw_shl_code]
Log Category
如果你想要定义并且使用自己的Log,那么你应该这么做:
[mw_shl_code=cpp,true]// 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!"));
[/mw_shl_code]
格式化的Log
Log Message

[mw_shl_code=cpp,true]//"阿妹你看,上帝压狗!"
UE_LOG(YourLog,Warning,TEXT("阿妹你看,上帝压狗!"));
[/mw_shl_code]
Log an FString
%s 字符串在Log中是使用TCHAR* 的, 所以我们要使用 *FString
[mw_shl_code=cpp,true]//"阿妹你看,上帝压狗!"
UE_LOG(YourLog,Warning,TEXT("阿妹你看,上帝压%s!"), *TheDog->GetName() );
[/mw_shl_code]
Log an Int
[mw_shl_code=cpp,true]//"有了金坷垃,小麦亩产1800!"
UE_LOG(YourLog,Warning,TEXT("有了金坷垃,小麦亩产%d!"), 1800);
[/mw_shl_code]
Log a Float
[mw_shl_code=cpp,true]//"有了金坷垃,小麦亩产1800.0f!"
UE_LOG(YourLog,Warning,TEXT("有了金坷垃,小麦亩产%f!"), 1800.0f);
[/mw_shl_code]
Current Camera
当前相机的获得可以通过两种方式:
  • 可以使用GetOwningPlayerController函数:
[mw_shl_code=cpp,true]auto pc = GetOwningPlayerController();
auto *vt = pc->GetViewTarget();
ACameraActor* camera = Cast(vt);
if (camera) {
    //do stuff
}
[/mw_shl_code]
  • 使用GetPlayerCameraManager函数
[mw_shl_code=cpp,true]auto camera = UGameplayStatics::GetPlayerCameraManager(WorldContext, 0);
[/mw_shl_code]
其中的WorldContext是世界上下文参数。


Enumeration in C++
[mw_shl_code=cpp,true]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;[/mw_shl_code]
Apex Destruction
  • Destruction Mesh在还未破碎的情况下,是没有碰撞的,如果要启用,需要在Level中选中该Actor,将Use Async Scene设为False:
20160411230137943.png
如果这个值是灰色不可改变,那么需要在Edit->Project Settings->Physics->Simulation->Enable Async Scene设定为True
  • 破碎后产生的小Chunks是默认与WorldDynamic无碰撞的,如果需要其有碰撞,那么需要将Large Chunk Threshold设定为一个比较大的数字:
    20160411230746329.png
  • 千万不要进行缩放你的Destructible Mesh,会导致Chunks的碰撞计算出错。
  • UE4中只支持随机切片,如果要进行自定义的Destructible,你需要apex physx lab,非常酷的东西。

Animation&Rigging Tool
  • 如果你不幸在ART中遇到了“Parent of end effector must be a joint”的错误,那么需要检查一下在你的骨骼模型中是否有double system,比如说头发啊或者裙摆之类的东西。
  • 如果你在有布料的骨骼模型看到了一个奇怪的顶点,例如下面这个:
    20160411231722670.png
    这种情况通常是你的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中的初始化:
[mw_shl_code=cpp,true]// Your .h file
class USphereComponent* Sphere;

// Your .cpp file
Sphere = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("SphereComp"));

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


Interface
  • 在UE4的编程中,Interface非常重要。类之间只能进行单一继承,而针对于Interface则可以进行多继承。个人的经验中,它对于物品交互等的构建都非常方便。
  • 在C++中创建Interface
    最基本代码如下:
.h
[mw_shl_code=cpp,true]#pragma once

#include "Interface.h"
#include "InterfaceXBoxEvent.generated.h"

/** Class needed to support InterfaceCast<IToStringInterface>(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);
};
[/mw_shl_code]
.cpp
[mw_shl_code=cpp,true]
#include "MyGame.h"
#include "InterfaceXBoxEvent.h"

UInterfaceXBoxEvent::UInterfaceXBoxEvent(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{

}
[/mw_shl_code]


C++
  • TMap的使用
[mw_shl_code=cpp,true]TMap<int32, FString> 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"  }
// ]

[/mw_shl_code]
注意与TMultiMap的区别,TMap中的key都是唯一的,因此当插入一个重复键值时,原来的会被替换:
[mw_shl_code=cpp,true]FruitMap.Add(2, TEXT("Pear"));
// FruitMap == [
//  { Key: 5, Value: "Banana"    },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" }
// ][/mw_shl_code]
也可以使用Emplace函数来进行元素的替换或增加,这种方法可以避免临时变量的创建:
[mw_shl_code=cpp,true]FruitMap.Emplace(3, TEXT("Orange"));
// FruitMap == [
//  { Key: 5, Value: "Banana"    },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 3, Value: "Orange"    }
// ]

[/mw_shl_code]
可以使用FindOrAdd来进行查找键值的查找,如TMap中没有这个键值,那么则会创建一个默认的值:
[mw_shl_code=cpp,true]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: ""          }
// ]

[/mw_shl_code]
可以使用Remove函数,RemoveAndCopyValue函数或者FindAndRemoveChecked函数来进行元素的删除。
我去……关于TMap都可以单独出一个博客了……
  • 在C++中寻找BP中的物件或类:
[mw_shl_code=cpp,true]static ConstructorHelpers:: FObjectFinder<UStaticMesh > CubeMesh (TEXT( "StaticMesh'Content/TopDownBP/CubeMesh'" ));

if ( CubeMesh.Object )
{
    Mesh ->SetStaticMesh (CubeMesh. Object );
}

[/mw_shl_code]
  • GetGlobalShaderMap如何使用?
[mw_shl_code=cpp,true]const auto FeatureLevel = GMaxRHIFeatureLevel;
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);

[/mw_shl_code]
  • 默认材质
[mw_shl_code=cpp,true]if(Material == NULL)
{
    Material = UMaterial:: GetDefaultMaterial(MD_Surface );
}

[/mw_shl_code]
  • 在C++中调用Blueprint的函数
先吐槽,这个时候其实建议使用Interface来进行调用会清晰的多,以下方式只是Trick……
[mw_shl_code=cpp,true]// 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<UBlueprint> 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<AActor >(TestBlueprint);
}


void AMainPlayerCharacter::Tick(float DeltaSeconds)
{
    Super::Tick (DeltaSeconds);

    UFunction *tmp = TestObjectActor->FindFunction(TEXT ("TestPrint"));

    if (tmp != NULL)
        TestObjectActor ->ProcessEvent(tmp, nullptr);
}[/mw_shl_code]
Particle
  • 如果想在BP/C++中动态调整Particle的参数,需要添加Dynamic模块。然后将Distribution设定为ParticleParameter。Param Name设定为Material Editor中的名字,Parameter Name设定为BP中的名字。
    20160415161103874.png

原文转载自:http://www.cnblogs.com/arrowinmyknee/p/5470370.html
nameless123456 | 2016-7-14 10:29:42 | 显示全部楼层
谢谢分享,非常有用呢~~~
回复 支持 反对

使用道具 举报

i.m-x | 2016-7-15 15:05:53 | 显示全部楼层
谢谢楼主分享,有个问题。
怎么动态创建一个BP,除了用 static ConstructorHelpers ::FObjectFinder
我知道 BP的类型 继承Actor  还有BP的路径
回复 支持 反对

使用道具 举报

jdshx | 2016-7-16 14:29:14 | 显示全部楼层
谢谢分享,非常有用呢~~~
原文地址:http://www.unrealchina.com/thread-383-1-1.html
内容来源:虚幻中国 - http://www.unrealchina.com/
回复 支持 反对

使用道具 举报

alexpeng3d | 2016-8-3 18:18:06 | 显示全部楼层
感谢楼主分享
回复 支持 反对

使用道具 举报

method | 2016-8-9 09:55:07 | 显示全部楼层
挺好的。。谢谢
回复 支持 反对

使用道具 举报

madrid10 | 2016-8-20 09:09:20 | 显示全部楼层
谢谢楼主分享
回复 支持 反对

使用道具 举报

rello | 2016-9-14 16:17:05 | 显示全部楼层
支持一下
回复

使用道具 举报

ajhonson4 | 2016-12-30 21:44:25 | 显示全部楼层
路过,看一看。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1

主题

5

回帖

181

积分

游客

积分
181