zoukankan      html  css  js  c++  java
  • Unreal Engine 4 —— 常见Tips

    Unreal Engine 4的常见Tips

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


    锁帧

    • 直接修改引擎设置的方法:

    在config/ConsoleVariables.ini中找到[Startup]

    在其后加入:

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

    Log to screen

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

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

    Log Category

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

    // 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

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

    Log an FString

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

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

    Log an Int

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

    Log a Float

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

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

    Current Camera

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

    • 可以使用GetOwningPlayerController函数:
    auto pc = GetOwningPlayerController();
    auto *vt = pc->GetViewTarget();
    ACameraActor* camera = Cast(vt);
    if (camera) {
        //do stuff
    }
    • 使用GetPlayerCameraManager函数
    auto camera = UGameplayStatics::GetPlayerCameraManager(WorldContext, 0);

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


    Enumeration in C++

    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:

    Use Async Scene

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

    • 破碎后产生的小Chunks是默认与WorldDynamic无碰撞的,如果需要其有碰撞,那么需要将Large Chunk Threshold设定为一个比较大的数字:
      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,比如说头发啊或者裙摆之类的东西。
    • 如果你在有布料的骨骼模型看到了一个奇怪的顶点,例如下面这个:
      Strange Vertice
      这种情况通常是你的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中的初始化:
    // Your .h file
    class USphereComponent* Sphere;
    
    // Your .cpp file
    Sphere = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("SphereComp"));

    Blueprint

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

    Interface

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

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

    .h

    #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);
    };

    .cpp

    #include "MyGame.h"
    #include "InterfaceXBoxEvent.h"
    
    UInterfaceXBoxEvent::UInterfaceXBoxEvent(const FObjectInitializer& ObjectInitializer)
        : Super(ObjectInitializer)
    {
    
    }

    C++

    • TMap的使用
    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"  }
    // ]

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

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

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

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

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

    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中的物件或类:
    static ConstructorHelpers:: FObjectFinder<UStaticMesh > CubeMesh (TEXT( "StaticMesh'Content/TopDownBP/CubeMesh'" ));
    
    if ( CubeMesh.Object )
    {
        Mesh ->SetStaticMesh (CubeMesh. Object );
    }
    
    • GetGlobalShaderMap如何使用?
    const auto FeatureLevel = GMaxRHIFeatureLevel;
    auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
    • 默认材质
    if(Material == NULL)
    {
        Material = UMaterial:: GetDefaultMaterial(MD_Surface );
    }
    
    • 在C++中调用Blueprint的函数

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

    
    // 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);
    }
    

    Particle

    • 如果想在BP/C++中动态调整Particle的参数,需要添加Dynamic模块。然后将Distribution设定为ParticleParameter。Param Name设定为Material Editor中的名字,Parameter Name设定为BP中的名字。
      Particle Dynamic Value
  • 相关阅读:
    Lesson 九、Eclipse中打jar包并使用jar包
    Lesson 八、eclipse开发中常用的快捷键
    Lesson 七、关键字final和多态,抽象类和接口
    Lesson 六、Java中的继承
    Lesson 五、Java中代码块和静态代码块的用法
    Lesson 四、Java工具类帮助文档的制作和帮助文档的使用
    Lesson 三、匿名对象的理解和使用
    Lesson 二:java.util.Scanner的使用
    Lesson 一:Windows 常见DOS命令的使用以及Java语言的环境配置
    插件新增
  • 原文地址:https://www.cnblogs.com/arrowinmyknee/p/5470370.html
Copyright © 2011-2022 走看看