• UE4笔记-底层基础和底层渲染相关记录备查


    记录一些UE4文档以外编程相关的底层基础的概念和使用方法,如GC和Gameplay,各种宏/配置项等说明和使用

    备查

     文档:http://api.unrealengine.com/INT/Programming/index.html

    Q. UE4  配置Game的默认UGameEngine类:

    UGameEngine算是整个Game的入口类了(Editor模式时是UEditorEngine),

    最初的Viewport的Layout都是通过GameEngine里进行生成的.

    可不使用源码版本的情况下,可通过继承UGameEngine类,修改扩展初始化Game的初始化布局等功能:

    step:

    往DefaultEngine.ini的[/Script/Engine.Engine]下添加,如:

    +GameEngine=/Script/BJShowEx.QGameEngine

    Q. UE4 第三人称(SpringArm控制下)设置初始视口位置 :

     Q. UE4的委托和事件(Delegate):

      记一下,开发方便Copy.

      吐槽:UE4 的Delegate得定义宏又臭又长又多,C#的委托和事件不知道比UE4高到哪里去了!(UE5请务必砍掉从新实现!)

      快速索引:委托文档:https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/Delegates/index.html

      概念:

        从设计模式上来说 多播Delegate就是 观察者模式得实现,只不过C#集成到了语言特性上,刚好C#(QT single-slot)得方式 被UE4借鉴到了引擎里

        Q0:C++是怎么去实现委托的?

          链接:C++实现的类C# Delegate 参考: https://stackoverflow.com/questions/23973914/c-like-delegates-in-c

          实现委托的代码:      

    /* 代码来着上述stackoverflow */
    #include <algorithm>
    #include <iostream>
    #include <memory>
    #include <utility>
    #include <vector>
    
    template <typename Signature>
    struct delegate;
    
    template <typename... Args>
    struct delegate<void(Args...)>
    {
        struct base
        {
            virtual ~base() {}
            virtual bool do_cmp(base* other) = 0;
            virtual void do_call(Args... args) = 0;
        };
        template <typename T>
        struct call : base
        {
            T d_callback;
            template <typename S>
            call(S&& callback) : d_callback(std::forward<S>(callback))
            {
    
            }
    
            bool do_cmp(base* other)
            {
                call<T>* tmp = dynamic_cast<call<T>*>(other);
                
                return tmp && this->d_callback == tmp->d_callback;
            }
            void do_call(Args... args)
            {
                return this->d_callback(std::forward<Args>(args)...);
            }
        };
        std::vector<std::unique_ptr<base>> d_callbacks;
    
        delegate(delegate const&) = delete;
        void operator=(delegate const&) = delete;
    public:
        delegate()
        {
    
        }
        template <typename T>
        delegate& operator+= (T&& callback)
        {
            this->d_callbacks.emplace_back(new call<T>(std::forward<T>(callback)));
            return *this;
        }
    
        template <typename T>
        delegate& operator-= (T&& callback)
        {
            call<T> tmp(std::forward<T>(callback));
            auto it = std::remove_if(this->d_callbacks.begin(),
                this->d_callbacks.end(),
                [&](std::unique_ptr<base>& other) {
                    return tmp.do_cmp(other.get());
                });
            this->d_callbacks.erase(it, this->d_callbacks.end());
            return *this;
        }
    
        void operator()(Args... args) 
        {
            for (auto& callback : this->d_callbacks) 
            {
                callback->do_call(args...);
            }
        }
    };
    
    // ----------------------------------------------------------------------------
    
    template <typename RC, typename Class, typename... Args>
    class member_call 
    {
        Class* d_object;
        RC(Class::* d_member)(Args...);
    public:
        member_call(Class* object, RC(Class::* member)(Args...))
            : d_object(object)
            , d_member(member) 
        {
        }
    
        RC operator()(Args... args) 
        {
            return (d_object->*d_member)(std::forward<Args>(args)...);
        }
    
        bool operator== (member_call const& other) const 
        {
            return (this->d_object == other.d_object) && (this->d_member == other.d_member);
        }
    
        bool operator!= (member_call const& other) const 
        {
            return !(*this == other);
        }
    };
    
    /**
    * @prarms RC 返回类型, Class
    **/
    template <typename RC, typename Class, typename... Args>
    member_call<RC, Class, Args...> mem_call(Class& object,
        RC(Class::* member)(Args...)) 
    {
        return member_call<RC, Class, Args...>(&object, member);
    }
    
    // ----------------------------------------------------------------------------
    
    void f(char const* str) { std::cout << "f(" << str << ")
    "; }
    void g(char const* str) { std::cout << "g(" << str << ")
    "; }
    void h(char const* str) { std::cout << "h(" << str << ")
    "; }
    
    // ----------------------------------------------------------------------------
    
    struct foo
    {
        int d_id;
        explicit foo(int id) : d_id(id) 
        {
    
        }
    
        void bar(char const* str) 
        {
            std::cout << "foo(" << this->d_id << ")::bar(" << str << ")
    ";
        }
    
        void cbs(char const* str) 
        {
            std::cout << "foo(" << this->d_id << ")::cbs(" << str << ")
    ";
        }
    
    };
    
    // ----------------------------------------------------------------------------
    
    int main()
    {
        delegate<void(char const*)> d0;
    
        foo f0(0);
        foo f1(1);
    
        d0 += f;
        d0 += g;
        d0 += g;
        d0 += h;
    
        d0 += mem_call(f0, &foo::bar);
        d0 += mem_call(f0, &foo::cbs);
        d0 += mem_call(f1, &foo::bar);
        d0 += mem_call(f1, &foo::cbs);
        d0("first call");
        d0 -= g;
        d0 -= mem_call(f0, &foo::cbs);
        d0 -= mem_call(f1, &foo::bar);
        d0("second call");
    }
    View Code

          

          C++触发时调用的假设:

    #include <iostream>
    
    class DemonstrateClass
    {
    public:
        int a = 10;
        //绑定函数
        void Func( std::string a) 
        {
            std::cout << "hi : [ "<< a << " ] " << std::endl;    
        }
    };
    
    /**
     *
     * Qt或UE4实现绑定成员函数核心调用代码Demo:
     *
     **/
    int main()
    {
        // 假设 pa 为绑定对象
        DemonstrateClass* pa = new DemonstrateClass();
    
        // &DemonstrateClass::Func为绑定函数
        void(DemonstrateClass::* func_ref)(std::string) = &DemonstrateClass::Func;
    
        //委托触发时,实际调用以下代码
        (pa->*(func_ref))(" linqing demonstrate ");
    
        std::cout << "exam  complated....  " << std::endl;
    }

      

        Q1.什么是单播委托/什么是多播委托/什么是事件:

          wait

        Q1.什么是动态委托:

          wait

     Q. UE4的Input全局钩子,全局监听鼠标键盘等外设输入事件:

      方式一:

      可继承GameViewportClient类实现

      

      例子(全局监听鼠标左键并打印Log):

      .h:

    UCLASS()
    class BJ_YourProject_API UGameViewportClient_Demonstrate : public UGameViewportClient
    {
        GENERATED_BODY()
    
    public:
        UGameViewportClient_Demonstrate();
    public:
        virtual EMouseCaptureMode CaptureMouseOnClick() override;
    
        /*
            输入设备的全局钩子函数
        */
        virtual bool InputKey
        (
            const FInputKeyEventArgs & EventArgs
        ) override;
    };

    .cpp

    UGameViewportClient_Demonstrate::UGameViewportClient_Demonstrate()
    {
    }
    
    EMouseCaptureMode UGameViewportClient_Demonstrate::CaptureMouseOnClick()
    {
        UE_LOG(LogTemp, Log, TEXT("[GVC:] CaptureMouseOnClick"));
        return Super::CaptureMouseOnClick();
    }
    
    bool UGameViewportClient_Demonstrate::InputKey(const FInputKeyEventArgs & EventArgs)
    {
        if (EKeys::LeftMouseButton == EventArgs.Key)
        {
            UE_LOG(LogTemp, Log, TEXT("[GVC:] InputKey is left mouse button"));
        }
    
        return Super::InputKey(EventArgs);
    }

      最后在Editor中的Project Setttings里修改默认的GameViewportClient即可

      

     Q. UE4 关于 C++中使用或设置组件的自定义 Trace Response/Object Type的问题:

    UE4关于这块配置是用的枚举映射.

    自定义Collision:

    打开Config/DefaultEngine.ini

    在[/Script/Engine.CollisionProfile]下可以找到:

    类似如:

    的设置项.其中ECC_GameTraceChannel1就是映射的ECollisionChannel枚举项:

    C++例子:

            //因为QingResponse映射到ECC_GameTraceChannel1,所以这里修改的是 QingResponse的Collision值
            Dynamic_Com->SetCollisionResponseToChannel(ECollisionChannel::ECC_EngineTraceChannel1, ECollisionResponse::ECR_Block);

     Q. UE4 Skeletal Mesh模型 个别视角莫名消失隐藏的问题背后原因:

      问题:UE4的场景剔除优化渲染算法(可参考DX里的视锥体优化算法)引起的问题./UE4SketalMesh无法正常显示可见

      剔除和遮挡的相关文档:https://docs.unrealengine.com/en-US/Engine/Rendering/VisibilityCulling/VisibilityCullingReference/index.html

      解决:剔除算法在Skeletal Mesh上,用的是Physics Asset 的碰撞资源来做的运算的,把Physics Asset刷正确就OK。(也可以使用UE4 两个 Precomputed Volume处理或关闭遮挡剔除)

     Q.UE4 Blueprint(蓝图) Template Function实现(蓝图中的模板函数实现):

      Meta文档:https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/Reference/Metadata/index.html

      其实就是加meta标签,只是官方的meta的说明文档标签不够完善o(╥﹏╥)o

      具体的可以翻源码里的ObjectMacros.h 头文件查看标签意义.

      这里顺便记录一下比较常用的Meta标签:

      UFUNCTION:

    1. DefaultToSelf            指定参数默认值为调用本函数类实例的self(类似于javascript的this,谁调用指向谁)
    2. HidePin                指定BP里要隐藏成员参数,通常配合DefaultToSelf标签一起食用

      解决:

      根据蓝图中传入的类返回类的实例.

      可以参考SpawnActor的代码实现.

      栗子:

        note:这是个 BPLibraryFunction类,所有声明的static

        UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "actorClass"))
              static class AActor* TestActor(class TSubclassOf<class AActor> actorClass);

     Q.UE4中使用dynamic_cast,编译报错问题.

      UE4 默认是关闭RTTI的,如果要使用dynamic_cast,需要在build.cs中启用rtti;

      

    using UnrealBuildTool;
    
    public class MyAPP : ModuleRules
    {
        public MyAPP(ReadOnlyTargetRules Target) : base(Target)
        {
            PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
            bUseRTTI = true;
            bEnableExceptions = true;
    
            PublicDependencyModuleNames.AddRange(
                new string[] 
                {
                   ""
                });
    
            PrivateDependencyModuleNames.AddRange(new string[] 
            {
                   ""
            });
        }
    }
        

    Q.BP 蓝图宏 (Macros)和函数(functions)的区别:

      记录一下 具体描述

      BP下的function不支持delay,AI Move to的等时间Latent型节点的,但是Macro支持.

      总体来说编译上function是静态编译,而Macro就是c++的动态宏,是动态编译的.

    Q.跨平台相关宏:

      如果需要编写平台相关的代码段的话,需要使用宏来约定。

      跨平台相关的宏定义都在Platform.h里。

      例如:

    #if PLATFORM_WINDOWS
        UE_LOG(LogTemp, Error, TEXT("hi ,windows"));
    #elif PLATFORM_MAC
        UE_LOG(LogTemp, Error, TEXT("hi ,MAC"));
    #endif

    Q.关于C++使用LoadClass加载资源的路径问题:

      一般平常加载Texture或mesh时,都是采用例如TEXT("/Game/Blueprints/MyMesh.MyMesh")的路径形式,

      但是如果是用LoadClass加载BP类或UMG类的话需要在路径后面加上_C的后缀:例如

    LoadClass<UUserWidget>(NULL,TEXT("/Game/Blueprints/BaseWgt.BaseWgt_C"));

       文档里没有详细介绍,但是这问题几乎会困扰所有刚使用LoadClass这个函数小伙伴。

    Q.保存游戏/应用信息到本地:

      USaveGame的应用:

      发现UE4已经写了相关文档,之前没有找到:

      http://api.unrealengine.com/INT/Gameplay/SaveGame/index.html

     

    Q.Struct 和Emum 类型的定义方式 :

    UE4 基础,但是不经常用总是忘记,做个笔记马一下:

    Note:虽然USTRUCT可以定义函数,但是不能加UFUNCTION 标签喔

    结构体:

    USTRUCT(BlueprintType)
    struct FData_PageInfo
    {
        GENERATED_USTRUCT_BODY()
    
        FData_PageInfo();
        FData_PageInfo(UChildActorComponent *parent_Com);
    
        void reInit();
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "struct_Variants")
            class UChildActorComponent *Parent_Com;
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "struct_Variants")
            class ACpp_PagerActor *PageActor;
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "struct_Variants")
            class UChildActorComponent *FrontCanvas_Com;
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "struct_Variants")
            class UChildActorComponent *BackCanvas_Com;
    };

    枚举:

    UENUM(BlueprintType)
    enum class GroupActorState : uint8
    {
        EM_Expand    UMETA(DisplayName = "Expand"),
        EM_Merge    UMETA(DisplayName = "Merge")
    };

     Q.C++ DatatableRow的定义方式:(经常忘,留底Copy用):

    #include "Runtime/Engine/Classes/Engine/DataTable.h"
    #include "DTOData_AssetItem.generated.h"
    USTRUCT(BlueprintType)
    struct FDTOData_AssetItem : public FTableRowBase
    {
        GENERATED_USTRUCT_BODY()
    public:
        FDTOData_AssetItem()
        {
        }
    public:
    
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FDTO_AssetItem")
            int Id = 0;
    };

    Q.C++ Interface 的定义方式 :(经常忘,留底Copy用)

    UE4的interface 分蓝图可继承和不可集成两种:

    文档:http://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Reference/Interfaces/

    可继承:

    #include "CoreMinimal.h"
    #include "Demonstrate.generated.h"
    /**
     * 
     */
    
    UINTERFACE(BlueprintType)
    class BJ_3DDESIGNAPP_API UDemonstrate : public UInterface
    {
        GENERATED_BODY()
    
    };
    class BJ_3DDESIGNAPP_API IDemonstrate
    {
        GENERATED_BODY()
    public:
        UFUNCTION(BlueprintImplementableEvent, Category = "Trigger Reaction")
            void Hi();
    };

     不可继承:

    UINTERFACE(meta = (CannotImplementInterfaceInBlueprint) )
    class BJ_3DDESIGNAPP_API UDemonstrateGameMode : public UInterface
    {
        GENERATED_BODY()
    
    };
    
    class BJ_3DDESIGNAPP_API IDemonstrateGameMode
    {
        GENERATED_BODY()
    public :
        UFUNCTION(BlueprintCallable, Category = "IDemonstrateGameMode")
            virtual class ACppCharacter_Demonstrate * GetCurrentDemonstrateActor() const { return nullptr; }
    };
  • 相关阅读:
    将自己的工作环境全面移植到C++最后一道工序:能用MFC制作简单的图形界面
    Finally, the working environment has been moved to C++
    统计方面的书籍【zz】
    zz sql 通配符以及转义字符用法
    转载学习并实现DES加密解密算法(三)
    【资源分享】2009版大陆汉语常用字.txt下载
    自己实现的C++Trim()
    nmake命令(windows下的makefile)
    c++对象内存模型【内存布局】
    UML类图关系(VPUML工具绘图)
  • 原文地址:https://www.cnblogs.com/linqing/p/5995621.html
Copyright © 2020-2023  润新知