IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> UE4 C++编程入门整理 -> 正文阅读

[游戏开发]UE4 C++编程入门整理

参考文献:

  1. 《大象无形》
  2. ue4中c++的网络同步、RPC_zhangxiaofan666的博客-CSDN博客_rpc ue4

虚幻C++编程

调试技巧

  • 打开控制台
  • 调整窗口大小:Execute Console Command r.setres 600x600

UObject

UObbject提供的功能

  1. Garbage collection垃圾收集
    • 继承自UObject类,自动地进行对象的生命周期管理。
    • 使用智能指针,只有非UObject类型,才能使用智能指针进行内存释放。
  2. Reference updating引用自动更新
  3. Reflectio反射
    • 运行时获取一个类、成员函数、变量名字
  4. Serialization序列化
    • 你也可以通过给自己的纯C++类手动实现序列化所需要的函数
  5. Automatic updating of default property changes自动检测默认变量的更
  6. Automatic property initialization自动变量初始化
  7. Automatic editor integration和虚幻引擎编辑器的自动交互
  8. Type information available at runtime运行时类型识别
    • 虚幻引擎打开了/GR-编译器参数。意味着你无法使用C++标准的RTTI机制:dynamic_cast,需要继承UObject,使用Cast<>函数进行
  9. Network replication网络复制

UObject构造函数

注意:UObject类会在引擎加载阶段,创建一个Default Object默认对象。

  1. 因此构造函数并不在游戏运行时调用,如果只有一个UObject对象存在于场景中,构造函数会被调用两次。
  2. 构造函数被调用时,UWorld不一定存在!

AActor

AActor提供的功能

  1. 它能够被挂载组件。
  2. 坐标和旋转量,是SceneComponent,如果Actor不需要一个固定位置,可以不挂载该组件。

Pawn、Character 、Controller

Pawn是棋子、Controller是棋手。

创建C++类

  • UCLASS()

  • GENERATED_UCLASS_BODY()

    1. 需要手动实现带有const FObject Initializer&的构造函数
  • GENERATED_BODY()

    1. 需要手动实现无参数的构造函数
  • 如果.cpp文件包含当前模块的PCH文件,一般模块+private.PCH.h

    1. PCH文件是包含了一些常用头文件的头文件。可以加快代码的编译速度。
  • 命名

    F 纯C++类
    U 继承自UObject,但不继承自Actor
    A 继承自Actor
    S Slate控件相关类
    H HitResult相关类

  • 创建对象

    1. 如果是纯C++类,可以使用new创建对象。
    2. 如果继承UObject类,需要使用NewObject函数创建对象。
    3. 如果继承AActor类,需要使用SpawnActor创建对象。
      • SpawnActor在UWorld类中,需要GetWorld获取该对象。
  • 类对象的获取

    1. 获取场景中的所有Actor实例

      for(TActorIterator <AActor> Iterator(GetWorld());Iterator;++Iterator)
      {
      ...//do something
      }
      
  • 类对象的销毁

    1. 如果纯C++类使用new创建的,需要delete删除。
    2. 如果使用TSharedPtr YourClassPtr=MakeShareable(new YourClass());,你不需要手动释放。
    3. 当继承自UObject类,如果该类有UPROPERTY宏定义,该类会总根节点Root开始检查,当UObject没有被别的UObject引用,就会被垃圾回收。你可以用AddToRoot让UObject类不会被回收。
    4. Actor可以调用Destory类删除,而内存回收依然由系统决定。

从C++到蓝图

UPROPERTY

  • BlueprintReadWrite
  • VisibleAnywhere

UFUNCTION

  • BlueprintCallable
  • BlueprintImplementEvent:由蓝图子类实现
  • BlueprintNativeEvent:提供了C++默认实现,你需要提供一个“函数名_Implement”为名字的函数实现

RPC

同步
  • 如果一个actor拥有复制属性且移动时也能同步,在构造函数中

    SetReplicates(true);
    SetReplicateMovement(true);
    
属性同步
  • actor中的变量要有复制属性,加上Replicated

    UPROPERTY(Replicated,BlueprintReadOnly,Category="Gameplay")
    bool bIsCarryingObjective;
    

    如果变量设置复制的时候,还要在.cpp中加上复制的规则,在DOREPLIFETIME的第一个参数是该actor,第二个参数是要复制的变量名,DOREPLIFETIME是一般属性复制,第二个 DOREPLIFETIME_CONDITION是条件属性复制

    void MyActor::GetLifetimeReplicatedProps(TArray<FLiftimeProperty>& OutLifetimeProps) const
    {
    	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    	DOREPLIFETIME(MyActor, mMyProp);
    }
    

    为了加强对属性复制的控制,您可以使用一个专门的宏来添加附加条件, COND_SimulatedOnly表示在复制属性前执行一次额外检查。这时,它只会复制到拥有此 actor 模拟复本的客户端

    
    void AActor::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
    {
        DOREPLIFETIME_CONDITION( AActor, ReplicatedMovement, COND_SimulatedOnly );
    }
    

    还有一个名叫 DOREPLIFETIME_ACTIVE_OVERRIDE 的宏,利用您想要的任何定制条件来决定何时复制/不复制某个属性

    void AActor::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )
    {
        DOREPLIFETIME_ACTIVE_OVERRIDE( AActor, ReplicatedMovement, bReplicateMovement );
    }
    

    现在 ReplicatedMovement 属性只会在 bReplicateMovement 为 true 时复制。

函数同步
  • 如果函数要在服务端运行,函数这样声明
//WithValidation表示这个函数在调用远端相应函数时会进行有效性检查,有一种防止作弊的味道
//而reliable表示这个函数在本地调用之后,无论网络传输是否丢包,一定会在远端得到调用,是一个可靠传输
UFUNCTION(Server,Reliable,WithValidation)
void ServerFunc();

而且注意,如果是在服务端运行的函数,函数实现的名字必须后面加上_Implementation,把在服务端运行的逻辑写到下面函数里

同时,如果加了WithValidation关键字,除了实现ServerFire_Implementation这个函数,还需要实现函数名_Validate,只不过返回值为bool,返回为true,它会在服务端进行完整性检查时用到,如果客户端return false这一反馈时,服务端会取消连接,因为出现了问题,比如某处使用外挂,或出现了严重问题,它会强硬的断开

bool AFPSCharacter::ServerFunc_Validate()
{
	//增加判断
    return true;
}

WithValidation 关键字是RPC 增加验证函数的功能,比如如下,如果加的血大于一定血量,就强行断开

最后在该调用的时候调用下面的函数即可

ServerFunc();
多播函数
  • 创建一个多播函数

    UFUNCTION(NetMulticast,Reliable)
    void sdfsf();
    

    函数实现后面加上_Implementation

游戏性框架概述

行为树

  1. 行为树:行为树包含流程控制、装饰器、行为结点
  2. 流程控制分为Selector和Sequence

网络架构

  1. UE3时期,服务器不断发送状态给客户端,因为延迟原因,往往状态已经过时。
  2. UE4,客户端不再同步服务器状态,而是模仿服务端,既根据服务端的之前的状态和时间差“猜测”现在的状态。
  3. 一般网络延迟不太大时,游戏运行会比较流畅,但如果出现网络频繁波动太大的,就会出现在地上来回滑动的现象。

系统引擎相关类

  1. 容器

    • UE4常用的容器有TArray、TMap
    • 不能依赖于容器内部对象的地址,因为容器在扩容的时候,会重新分配内存,并将旧的对象移动到新的内存中,过去的内存就会失效。
  2. 正则表达式

    • FRegexPattern
    • FRegexMatcher
  3. FPath

    • FPaths::GameDir():游戏根目录
    • FPaths::FileExists():文件是否存在
    • FPaths::ConvertRelativePathToFull():相对路径转绝对路径
  4. XML和JSON

    • FXmlFile
    • FXmlNode
    • TJsonReaderFactory
  5. 文件读写访问

    • FPlatformFileManager::Get()->GetPlatformFile();
    • CopyDirectoryTree
    • CopyFile
    • OpenRead、OpenWrite
    • LoadFileToArray、LoadFileToString
  6. GConfi

    • GConfig
  7. UE_LOG

    • DEFINE_LOG_CATEGORY_STATIC(LogMyCategory,Warning,All);
  8. 字符串处理

    • FName:大小写不敏感,无法修改
    • FText:本地化支持,无法修改
    • FString:字符串,支持修改
  9. 平台支持

    • FPlatformMisc:包含了平台实现
  10. Image

  • ImagerWrapper,压缩数据是CompressedData,原始数据是RawData

  • 例如:转换PNG图片到JPG

    1. 从文件中读取为TArray的二进制数据;
    2. 用SetCompressData填充为压缩数据;
    3. 使用GetRawData即可获取RGB数据;
    4. 将RGB数据填充到JPG类型的ImageWrapper中;
    5. 使用GetCompressData,即可获得压缩后的JPG数据;
    6. 使用FFileHelper写入到文件中。
  • UTexture2D贴图数据

    PNG转JPG

    bool FSystemToolsPublic::CovertPNG2JPG(const FString &SourceName, const FString &TargetName)
    {
        check(SourceName.EndsWith(TEXT(".png")) && TargetName.EndsWith(TEXT(".jpg")));
        IImageWrapperModule &ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
        IImageWrapperPtr SourceImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
        IImageWrapperPtr TargetImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
        TArray<uint8> SourceImageData;
        TArray<uint8> TargetImageData;
        int32 Width, Height;
        const TArray<uint8> *UncompressedRGBA = nullptr;
        if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*SourceName))
            ;
        {
            //文件不存在
            return false;
        }
        if (!FFileHelper::LoadFileToArray(SourceImageData, *SourceName))
        {
            //文件读取失败
            return false;
        }
        if (SourceImageWrapper.IsValid() && SourceImageWrapper->SetCompressed(SourceImageData.GetData(), SourceImageData.Num()))
        {
            if (SourceImageWrapper->GetRaw(ERGBFormat::RGBA, 8, UncompressedRGBA))
            {
                Height = SourceImageWrapper->GetHeight();
                Width = SourceImageWrapper->GetWidth();
                if (TargetImageWrapper->SetRaw(UncompressedRGBA->GetData(), UncompressedRGBA->Num(), Width, Height, ERGBFormat::RGBA, 8))
                {
                    TargetImageData = TargetImageWrapper->GetCompressed();
                    if (!FFileManagerGeneric::Get().DirectoryExists(*TargetName))
                    {
                        FFileManagerGeneric::Get().MakeDirectory(*FPaths::GetPath(TargetName), true);
                    }
                    return FFileHelper::SaveArrayToFile(TargetImageData, *TargetName);
                }
            }
        }
        return false;
    }
    

    从文件加载贴图

    UTexture2D* FSystemToolsPublic::LoadTexture2DFromBytesAndExtension(
        const FString& ImagePath,
        uint8* InCompressedData,
        int32 InCompressedSize,
        int32& OutWidth,
        int32& OutHeight
    )
    {
        UTexture2D* Texture = nullptr;
        IImageWrapperPtr ImageWrapper = GetImageWrapperByExtention(ImagePath);
        if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(InCompressedData, InCompressedSize))//读取压缩后的图片数据
        {
            const TArray <uint8>* UncompressedRGBA = nullptr;
            if (ImageWrapper->GetRaw(ERGBFormat::RGBA, 8,
                UncompressedRGBA))//获取原始图片数据
            {
                Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_R8G8B8A8);
                if (Texture != nullptr)
                {
                    //通过内存复制,填充原始RGB数据到贴图的数据中
                    OutWidth = ImageWrapper->GetWidth();
                    OutHeight = ImageWrapper->GetHeight()
                        ;
                    void* TextureData = Texture->
                        PlatformData->Mips[0].BulkData.
                        Lock(LOCK_READ_WRITE);
                    FMemory::Memcpy(TextureData,
                        UncompressedRGBA->GetData(),
                        UncompressedRGBA->Num());
                    Texture->PlatformData->Mips[0].
                        BulkData.Unlock();
                    Texture->UpdateResource();
                }
            }
        }
        return Texture;
    }
    UTexture2D* FSystemToolsPublic::LoadTexture2DFromFilePath(
        FString& ImagePath,
        int32& OutWidth,
        int32& OutHeight
    )
    {
        //文件是否存在
        if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*ImagePath))
        {
            return nullptr;
        }
        //读取文件资源
        TArray <uint8 > CompressedData;
        if (!FFileHelper::LoadFileToArray(CompressedData, *ImagePath))
        {
            return nullptr;
        }
        return LoadTexture2DFromBytesAndExtension(ImagePath, CompressedData.GetData(), CompressedData.Num(), OutWidth, OutHeight);
    }
    

虚幻引擎解析

模块机制

  • Runtime、Development、Editor、Plugin

  • Public、Private、.build.cs

  • /模块文件夹

    1. 模块名.Build.cs

    2. /Public

      • 模块名.h

      /Private

      • 模块名.cpp
      • 模块名 PrivatePCH.h
  • cs文件

    using UnrealBuildTool;
    //类的名称与模块名称一致
    public class pluginDev : ModuleRules
    {
    public pluginDev(TargetInfo Target)
    {
    	PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject"} );		PrivateDependencyModuleNames.AddRange(new string[] {
    });
    }
    }
    
  • 头文件

    #pragma once
    //#include "Engine.h"
    #include "ModuleManager.h"
    class FPluginDevModule : public IModuleInterface
    {
    public:
    /** IModuleInterface implementation */
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;
    };
    
  • 源文件

    //#include "pluginDev.h"//包含你刚刚创建的头文件
    //IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl , pluginDev, "pluginDev")\
    #include "PluginDevPrivatePCH.h"
    //包含接下来要定义的模块预编译头文件名
    void FPluginDevModule::StartupModule()
    {
    //...这里书写模块启动时需要执行的内容
    }
    void FPluginDevModule::ShutdownModule()
    {
    //...这里书写模块卸载时需要执行的内容
    }
    IMPLEMENT_MODULE(FPluginDevModule, pluginDev)
    
  • 引入当前模块

    工程名.Target.cs文件

    public override void SetupBinaries(TargetInfo Target, ref List<UEBuildBinaryConfiguration > OutBuildBinaryConfigurations , ref List<string> OutExtraModuleNames
    )
    {
    	OutExtraModuleNames.AddRange( new string[] { "pluginDev" } );//
    }
    

UBT

  • UBT项目会输出可执行文件,在win64平台上,输出.exe文件
  • UBT输出Editor.exe

UHT

  • UHT提供反射机制,在win64平台上,输出.exe文件
  • UHT编译出是.generated.cpp和.generated.h
  • 在.generated.cpp文件中,声明了的UClassCompiledInDefer<当前类名>静态全局变量实例,并在Main函数之前调用了该类的GetPrivateStaticClass函数,将该类的注册的UPROPERTY和UFUNCTION函数地址和变量地址收集出来,并提供给UE4作为反射的依据。

内存分配

  • TBB内存分配相比于标准内存分配,解决以下两个问题:
    1. 标准内存分配要求同一时间只能有一个线程分配内存,TBB不需要
    2. CPU存在高速缓存,当出现内存不对齐的时候,会让缓存假命中,TBB提供了内存对齐的方案。
    3. 虚幻采用了TBB方案分配内存。

初始化

  • UE4包含预初始化PreInit和初始化Init
  • 预初始化初始化了一些引擎必须要设置的内容、加载模块,之后才会进入正式初始化

主循环

  • 主循环是独立while循环,渲染线程并不是主循环。
  • 主循环主要更新遍历状态、请求渲染线程刷新、更新GEngine->Tick、收集下一帧需要清理的UObject对象。
  • 并不是所有的任务都在一次Tick中进行,例如加载地图不会要求一次加载完成而出现卡死的情况。

线程

  1. FRunnable

    • UE4提供了FRunnable类,包含Init、Run、Exit方法
    • 通过FRunnableThread::Create创建
  2. TaskGraph

    • TGraphTask采用模板编程,不需要继承特定的类,但是需要提供对应的函数。
  3. std::function:可以采用C++11的方法创建使用

  4. UE4提供了一些类似C++11的线程同步方案

    • FCriticalSection 临界区

    • TFuture、TPromise:C++11也提供了std::future和std::promise

    • UE4 提供了Async模板

      auto Result = Async()EAsyncExecution::Thread, {
      	return 123;
      }
      

UObject对象

  1. 构造:虚幻引擎采用了两步构造的机制,先分配内存,做出一些额外处理后再模塑对象。
  2. 销毁:一般从Root开始基于图算法遍历,然后标记出那些变量是不可达的,之后再进行删除。
  3. 序列化:UE4会默认保存类的默认变量,没有被标记成UPROPERTY不会被序列化,没有在默认值做修改的变量也不会被序列化。

SWidget

例如有如下的内容

ChildSlot
		[
			SNew(SOverlay)
			+ SOverlay::Slot()
			.HAlign(HAlign_Center)
			.VAlign(VAlign_Top)
			[
				SNew(STextBlock)
				.ColorAndOpacity(FLinearColor::White)
				.ShadowColorAndOpacity(FLinearColor::Black)
				.ShadowOffset(FIntPoint(-1, 1))
				.Font(FSlateFontInfo("Arial", 26))
				.Text(FText::FromString("Main Menu"))
			]
			+ SOverlay::Slot()
			.HAlign(HAlign_Right)
			.VAlign(VAlign_Bottom)
			[
				SNew(SVerticalBox)
				+ SVerticalBox::Slot()
				[
					SNew(SButton)
					.Text(FText::FromString("Play Game!"))
					.OnClicked(this, &SMainMenuUI::PlayGameClicked)
				]
				+ SVerticalBox::Slot()
				[
					SNew(SButton)
					.Text(FText::FromString("Quit Game"))
					.OnClicked(this, &SMainMenuUI::QuitGameClicked)
				]
			]
		];
  • 创建控件 SNew(WidgetClass)
  • 增加子控件 + WidgetClass::Slot() ,使用[]为该插槽指定项,注意 SLeafWidget 不能包含子控件
  • 组合控件 SVerticalBox 是垂直框
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-01-28 12:15:50  更:2022-01-28 12:18:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 18:22:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码