Unreal Engine 大象无形学习笔记 (第一部分:虚幻C++编程)

Posted 番茄玛丽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unreal Engine 大象无形学习笔记 (第一部分:虚幻C++编程)相关的知识,希望对你有一定的参考价值。

Q1. 什么时候继承自UObject类,什么时候声明纯C++类?

UObject自带功能:

1. 垃圾收集:继承自UObject并被标记为UProperty的变量,会被引擎自动进行生命周期管理。

2. Reference updating 引用自动更新

3. 反射

4. 序列化:纯C++类也可以手动实现

5. Automatic updating of default property changes 自动检测默认变量的更改

6. Automatic property initialization 自动变量初始化

7. Automatic editor integration 编辑器自动交互:在Editor里可直接编辑

8. Type information available at runtime 运行时类型识别 :Cast<>

9. 网络复制:在引擎加载阶段创建Default Object,构造函数被调用时,UWorld不一定存在,GetWorld()返回值可能为空!

Q2. 什么时候继承自Actor类?

需要挂载组件时。

Q3. 什么时候继承Pawn类,什么时候继承Character类?

需要移动组件继承Character类,不需要移动逻辑,比如飞船,继承Pawn类。

Q4. 怎么创建纯C++类并被UE识别?

1. 创建你的.h和.cpp文件,如果你是第一种文件结构,.h文件放在public文件夹内,.cpp文件放置在private文件夹内。
2. 在.h中声明你的类:如果你的类继承自UObject,你的类名上方需要加入UCLASS()宏。同时,你需要在类体的第一行添加GENERATED_UCLASS_BODY()宏,或者GENERATED_BODY()宏。前者需要手动实现一个带有const FObject Initializer&参数的构造函数。后者需要手动实现一个无参数构造函数。注意笔者说的是“实现”而非声明。
3. 在你的.cpp文件中,包含当前模块的PCH文件。一般是模块名+private PCH.h。如果是游戏模块,有可能包含的是游戏工程名.h。
4. 编译。
Q5. UE命名规则有哪些,怎么创建、获取、销毁各种类型的对象?
F 纯C++类
前缀 类型 创建对象 获取对象 内存管理
F 纯C++类 new  

delete

普通指针转换智能指针:

TSharedPtr<YourClass> YourClassPtr = MakeShareable(new YourClass());

U 继承自UObject,但不继承自Actor NewObject<T>()   自动垃圾回收,通过AddToRoot不被回收
A 继承自Actor GetWorld()->SpawnActor<ActorClass>()

for (TActorIterator<AStaticMeshActor> ActorIter(GetWorld()); ActorIter; ++ActorIter)

UKismetSystemLibrary::PrintString(GetWorld(), FString::Printf(TEXT("%s"), *ActorIter->GetName()));

 

TArray<AActor*> OutActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AStaticMeshActor::StaticClass(), OutActors);
for (AActor* actor : OutActors)

UKismetSystemLibrary::PrintString(GetWorld(), actor->GetName());

 

static void GetAllActorsWithTag(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors);

 

static void GetAllActorsWithInterface(const UObject* WorldContextObject, TSubclassOf<UInterface> Interface, TArray<AActor*>& OutActors);

 

APlayerController* playerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);

 

APlayerController* playerController = GetWorld()->GetFirstPlayerController();

 

APawn* myPawn = Cast<ADrone>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));

 

APawn* myPawn = GetWorld()->GetFirstPlayerController()->GetPawn();

 

ACharacter* myPawn = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);

 

ACharacter* myPawn = GetWorld()->GetFirstPlayerController()->GetCharacter();

Destory()  只是从世界中销毁,内存还是系统自动回收

S Slate控件相关类 SNew    
H HitResult相关类      
Q6. 蓝图怎么调用C++?
注册变量到蓝图中:UPROPERTY(BlueprintReadWrite,VisibleAnyWhere,Category="Object")
注册函数到蓝图中:UFUNCTION(BlueprintCallable,Category="Test")   //BlueprintImplementEvent 表示由蓝图的子类实现   BlueprintNativeEvent C++默认实现可被蓝图重载,需要提供一个“函数名_Implement”函数实现于CPP中

Q7. 怎么获取游戏路径?

FPath::GameDir() //游戏根目录
FPath::FileExists() //判断文件是否存在
FPath::ConvertRelativePathToFull() //相对路径转换为绝对路径

Q8. XML与JASON怎么序列化与反序列化?

XML Parse工具:FastXML、FXmlFile

Q9.文件操作

获取路径:FPaths

读写:FPlatformFileManager

配置文件:写:GConfig->SetString();  读:GConfig->GetString();  刷新缓冲区,马上生效:GConfig->Flush();

Q10. 怎么输出UE日志?

怎么查看LOG?打包后需要在启动参数加-log,编辑器运行需要打开Window->DeveloperTools->OutputLog

语法:UE_LOG(LogMy,Warning,TEXT("show a string:%s"),*FString("test"));

日志类型 输出颜色
Log 灰色
Warning 黄色
Error

红色

Q11. 怎么自定义Category?

将以下定义放在被多数源文件引用的文件里

DEFINE_LOG_CATEGORY_STATIC(LogMyCategory,Warning,All);

Q12. 虚幻引擎有哪些字符串类?

类型 特点
FName 数据结构是哈希表,所以是唯一的,大小写不敏感,不可修改
FText 数据结构是查找表,内置本地化支持,用作被显示的字符串,不可修改
FString 可修改

Q13. 虚幻引擎怎么跨平台?

平台相关工具函数类:FPlatformMisc 

eg. typedef FLinuxPlatformMisc FPlatformMisc; 

Q14. 怎么转换图片类型?

图片压缩后的数据:CompressedData

图片RGBA数据无压缩:RawData

ImageWrapper = CompressedData + RawData

怎么读取硬盘里的贴图?

 UTexture2D * FSystemToolsPublic::LoadTexture2DFromBytesAndExtension(const FString& ImagePath , uint8* InCompressedData ,int32 InCompressedSize ,int32 & OutWidth,int32 & OutHeight)
 
     UTexture2D * Texture = nullptr;
 4     IImageWrapperPtr ImageWrapper = GetImageWrapperByExtention(ImagePath);
 5     if (ImageWrapper.IsValid() && ImageWrapper ->SetCompressed(InCompressedData , InCompressedSize))//读取压缩后的图片数据
 6     
 7         const TArray <uint8>* UncompressedRGBA = nullptr;
         if (ImageWrapper ->GetRaw(ERGBFormat::RGBA, 8,UncompressedRGBA))//获取原始图片数据
 9         
10             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;
36     
37     //读取文件资源
     TArray <uint8 > CompressedData;
     if (!FFileHelper::LoadFileToArray(CompressedData , *ImagePath))
     
         return nullptr;
     
     return LoadTexture2DFromBytesAndExtension(ImagePath,CompressedData.GetData(), CompressedData.Num(), OutWidth,OutHeight);
                

 

Unreal Engine 4 笔记

1、UE4的调试输出

//*1 调试输出*//
/*case a、快速使用 不设置log类别 默认为LogTemp*/
UE_LOG(LogTemp,Log,TEXT("Your message"));
UE_Log(LogTemp,Warning,TEXT("You Number type of float value is %f"),YourFloatTypeValue);
UE_Log(LogTemp,Error,TEXT("Your Number type of FString value is %s"),YourFStringTypeValue);
//Log:输出日志字体颜色为灰色 Warning:输出字体颜色为黄色 Error:输出字体颜色为红色

/*case b、设置自定义Log类别*/
//在YourCode.h文件中声明自定义Log类别@parm YourLog
DECLARE_LOG_CATEGORY_EXTERN(YourLog, Log, All);
//在YourCode.cpp文件中定义
DEFINE_LOG_CATEGORY(YourLog);
UE_LOG(YourLog, Log, TEXT("This is a message to yourself during runtime!"));

/*case c、触发严重中断 程序执行到此时会触发程序中断*/
UE_LOG(YourLog, Fatal, TEXT("This fringe case was reached! Debug this!"));
//log输出在output log面板中显示

/*case d、 向屏幕打印消息 显示在屏幕上*/
if(GEngine)
{
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("This is an on screen message!"));
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Some variable values: x: %f, y: %f"), x, y));
}
//我们可以在.cpp文件下进行如下宏定义以快速使用该方法
#define print(text) if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 1.5, FColor::White,text)

2、在场景中查找对象

#include "EngineUtils.h"
/*case a、Actor 迭代*/
for (TActorIterator<AStaticMeshActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
{
    AStaticMeshActor *Mesh = *ActorItr;
}
/*case b、Object迭代*/
for (TObjectIterator<AActor> Itr; Itr; ++Itr)
{
    AActor *Component = *Itr;
}
//Object 迭代可以迭代的内容包括Actor可迭代的内容

3、射线的使用

//GetHitResultAtScreenPosition函数为引擎框架下APlayerController的方法  
//顾名思义是以屏幕为起点向鼠标所在坐标发出射线,进行碰撞检测
//可查看APlayerController下此函数的定义  
FVector2D cursorPosition(0,0);  
this->GetMousePosition(cursorPosition.X, cursorPosition.Y);  
FHitResult hitInfo(ForceInit);  
FCollisionQueryParams collisionParms(false);  
this->GetHitResultAtScreenPosition(cursorPosition,ECC_PhysicsBody,collisionParms,hitInfo);
//在此处理hitInfo;
/*GetWorld()->LineTraceSingleByObjectType等一系列函数用于处理更多的射线类功能,但APlayerController下已经封装好很多常用的方法。*/

4、场景捕获组件的使用 

将SceneCapture2D组件拖入场景 

选中SceneCapture2D找到Texture Target属性 并为其赋值 

/*此处实现将SceneCapture2D看到的视角复制到Texture中 */ 
/*方式一、 */
/*sceneCapture为SceneCapture2D组件的引用 */ 
/*renderTexture为上图所示textureTarget的引用*/ 
UTexture2D *Texture = UTexture2D::CreateTransient(TextureRenderTarget->SizeX,TextureRenderTarget->SizeY, PF_B8G8R8A8);  
sceneCapture->GetCaptureComponent2D()->UpdateContent();  
TArray<FColor> SurfData;
FRenderTarget *RenderTarget = renderTexture->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixels(SurfData);
void* TextureData = Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
const int32 TextureDataSize = SurfData.Num() * 4;
FMemory::Memcpy(TextureData, SurfData.GetData(), TextureDataSize);
Texture->PlatformData->Mips[0].BulkData.Unlock();
Texture->UpdateResource();
/*方式二、*/
/*使用ConstructTexture2D函数,该函数每次返回的是同一块内存地址*/
sceneCapture->GetCaptureComponent2D()->UpdateContent();  
Texture = renderTexture->ConstructTexture2D(this,"AlphaTex",EObjectFlags::RF_NoFlags,CTF_DeferCompression);  
Texture->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;  
Texture->UpdateResource();

5、XML文件的使用 

initgameValue.xml文件内容如下 
需要在build.cs文件中添加模块”XmlParser”,在YourCode.h中包含XmlParser.h

<?xml version="1.0" encoding="UTF-8"?>  
<Value>  
    <ChildrenSex>Girl</ChildrenSex>  
    <SceneIndex>0</SceneIndex>  
</Value>

解析代码如下

#include "XmlParser.h" 
/*写数据*/
    FXmlFile InitVariable(L"X:\\\\initgameValue.xml");  
    FXmlNode* root = InitVariable.GetRootNode();  
    TArray<FXmlNode*> VarArray = root->GetChildrenNodes();  
    VarArray[0]->SetContent("Your content");  
    VarArray[1]->SetContent("Your content");  
    InitVariable.Save(L"X:\\\\initgameValue.xml");  
    InitVariable.Clear();  
/*读数据*/
    FXmlFile InitVariable(L"X:\\\\initgameValue.xml");  
    FXmlNode* root = InitVariable.GetRootNode();  
    TArray<FXmlNode*> VarArray = root->GetChildrenNodes();  
    FString tempContent=VarArray[0]->GetContent();
//一般情况下 文件加载路径不应该是绝对路径 我们可以使用FPaths::GameUserDir()得到项目路径根路径,从而便可以使用相对路径来进行配置文件的加载

6、UE4字符类型到基本数据类型的转换 

UnrealString.h下的内联函数如下

/** Covert a string buffer to intrinsic types */
    inline void FromString(int8& OutValue,      const TCHAR* Buffer)    {   OutValue = FCString::Atoi(Buffer);      }
    inline void FromString(int16& OutValue,     const TCHAR* Buffer)    {   OutValue = FCString::Atoi(Buffer);      }
    inline void FromString(int32& OutValue,     const TCHAR* Buffer)    {   OutValue = FCString::Atoi(Buffer);      }
    inline void FromString(int64& OutValue,     const TCHAR* Buffer)    {   OutValue = FCString::Atoi64(Buffer);    }
    inline void FromString(uint8& OutValue,     const TCHAR* Buffer)    {   OutValue = FCString::Atoi(Buffer);      }
    inline void FromString(uint16& OutValue,    const TCHAR* Buffer)    {   OutValue = FCString::Atoi(Buffer);      }
    inline void FromString(uint32& OutValue,    const TCHAR* Buffer)    {   OutValue = FCString::Atoi64(Buffer);    }   //64 because this unsigned and so Atoi might overflow
    inline void FromString(uint64& OutValue,    const TCHAR* Buffer)    {   OutValue = FCString::Strtoui64(Buffer, nullptr, 0); }
    inline void FromString(float& OutValue,     const TCHAR* Buffer)    {   OutValue = FCString::Atof(Buffer);      }
    inline void FromString(double& OutValue,    const TCHAR* Buffer)    {   OutValue = FCString::Atod(Buffer);      }
    inline void FromString(bool& OutValue,      const TCHAR* Buffer)    {   OutValue = FCString::ToBool(Buffer);    }

例:

#include "UnrealString.h"
using namespace LexicalConversion;

FString temp = "3.1415926":
float outFloat; FromString(outFloat, *temp);

7、UMG拖拽图标的实现

a、重载On Mouse Button Down函数 
新建UserWidget组件,在Graph事件图表中重载该函数实现检测是否触发拖拽事件 

b、重载OnDrag Detected函数 
重载OnDrag Detected函数,处理拖拽逻辑 
@parm payload是用于传递的参数 会在On Drop函数中用到 
@parm defaultdragvisual是用于拖拽时跟随的图标 

c、重载On Drop函数 
此时Operation中的Payload参数便是CreateDragDropOperation中的传递过来的Payload参数 

8、UE4官方文档&Answer Hub 
1、官方文档 :https://docs.unrealengine.com/latest/INT/ 
2、Answer Hub :https://answers.unrealengine.com/index.html

9、直接使用静态class生成Actor

// 直接使用静态class生成Actor
UClass* bbbWeaponClass = AMyWeapon::StaticClass();
if (bbbWeaponClass)
{
    AMyWeapon* bbbWeapon = World->SpawnActor<AMyWeapon>(bbbWeaponClass, this->GetActorLocation()+FVector(100,0,0), this->GetActorRotation(), SpawnInfo);
    if (bbbWeapon)
    {
    bbbWeapon->SpawnEffect(this->GetActorLocation());
    }
}

10.设置组件的Translation

// 设置组件的Translation
MeshComponent->RelativeLocation.Z = -100;

11、播放动画

FName animName = "/Game/Animations/Run.Run";
UAnimationAsset* runAnim = Cast<UAnimationAsset>(StaticLoadObject(UAnimationAsset::StaticClass(), NULL, *animName.ToString()));
MyWeaponActor->MeshComponent->PlayAnimation(runAnim, true);

12、生成粒子

// 生成粒子,并绑到模型指定骨骼上。(需要#include "EngineKismetLibraryClasses.h")
FName EffectName = "/Game/Particles/P_Sparks.P_Sparks";
UParticleSystem* MyEffect = Cast<UParticleSystem>(StaticLoadObject(UParticleSystem::StaticClass(), NULL, *EffectName.ToString()));
UParticleSystemComponent* PSC = UGameplayStatics::SpawnEmitterAtLocation(this, MyEffect, this->GetActorLocation());
FName tmpBone = "pelvis";
PSC->AttachTo(this->MeshComponent, tmpBone);

13、屏幕上打印文字

// 屏幕上打印文字(只有一行,多了会覆盖):
GEngine->AddOnScreenDebugMessage(0, 5.f, FColor::Yellow, FString("Hello World!!!") );

14、Spawn sound

// Spawn sound
UGameplayStatics::PlaySoundAtLocation( this, FractureEffect.Sound, Position );

15、Tick组的运行顺序:

//Tick组的运行顺序:
TG_PrePhysics
TG_StartPhysics
TG_DuringPhysics
TG_EndPhysics
TG_PostPhysics

16、// Actor出生流程

// Actor出生流程
SpawnActor-->PostSpawnInitialize-->
PostActorCreated
OnConstruction
PostActorConstruction-->

if (bWorldBegunPlay)
{
    PreInitializeComponents()
}
UpdateOverlaps();
if (bWorldBegunPlay)
{
    InitializeComponents();
    PostInitializeComponents();
    if (GetWorld()->bMatchStarted &&         !deferBeginPlayAndUpdateOverlaps)
    {
        BeginPlay();
    }
}

17、动画蓝图载入

//C++载入动画蓝图的方法
FName MM_0_Anim= "/Game/Character/MM_0/Anim/MM_AnimBP.MM_AnimBP";
//你动画蓝图的路径
UAnimBlueprint* AminClass;
AminClass = Cast<UAnimBlueprint>(StaticLoadObject(UAnimBlueprint::StaticClass(),NULL, *MM_0_Anim.ToString()));
Mesh3P->SetAnimInstanceClass(AminClass->GetAnimBlueprintGeneratedClass());

18、计时器

FTimerHandle B;
GetWorldTimerManager().SetTimer(B,this, &ADemoCharacter_Weapon::EndRoad, RoadTime, false);

 

以上是关于Unreal Engine 大象无形学习笔记 (第一部分:虚幻C++编程)的主要内容,如果未能解决你的问题,请参考以下文章

Unreal Engine 4 笔记 2

大象无形_虚幻引擎程序设计浅析pdf

Unreal Engine02:打包安卓apk和部署

虚幻引擎 Unreal Engine 详细笔记 根据谌嘉诚视频无遗漏总结 快速上手

Unreal Engine 4---学习文档

Rider for Unreal Engine虚幻4如何进行项目管理