篇写的是关于UE4的C++方面的小技巧:

1.在构造函数里

//构建组件
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
//把组件放到其它组件下
VRCamera->SetupAttachment(VROrigin);
//下面这条不能用于构造函数中,否则编辑器崩溃或报错
//VRCamera->AttachToComponent(VROrigin, FAttachmentTransformRules::SnapToTargetIncludingScale);

2.加载资源

具体细节教程(非本人制作):https://ke.qq.com/course/308721

//同步加载,一般用于少量物体加载
//从内存中读取文件,但由于还没从硬盘中读取,所以内存中没有(耗时相对较短),因此读取失败
UHapticFeedbackEffect_Base* ShakeEffect = FindObject<UHapticFeedbackEffect_Base>(NULL,TEXT("HapticFeedbackEffect_Curve'/Game/VirtualRealityBP/Blueprints/MotionControllerHaptics.MotionControllerHaptics'")); //从硬盘中读取文件,放到内存中(耗时相对较长)
UHapticFeedbackEffect_Base* ShakeEffect = LoadObject<UHapticFeedbackEffect_Base>(NULL,TEXT("HapticFeedbackEffect_Curve'/Game/VirtualRealityBP/Blueprints/MotionControllerHaptics.MotionControllerHaptics'")); //读取文件,只能用于构造函数

  static ConstructorHelpers::FObjectFinder<UStaticMesh> Object(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
  Sphere->SetStaticMesh(Object.Object);


.h:
FStreamableManager* WealthLoader;
TSharedPtr<FStreamableHandle> WealthHandle;

  //各种Class的地址
  UPROPERTY(EditAnywhere)
  TArray<TSoftClassPtr<UObject>> ClassWealthPaths;


.cpp:
//异步加载,一般用于大量物体加载
void AWelathActor::StreamableManagerOperate()
{
//创建加载管理器
WealthLoader = new FStreamableManager();
//执行异步加载,添加资源链接数组和加载完成回调函数,其中TexturePath为加载内容的地址
WealthHandle = WealthLoader->RequestAsyncLoad(TexturePath, FStreamableDelegate::CreateUObject(this,
&AWelathActor::StreamableManagerLoadComplete));
  //如果加载的内容是UClass,则需要先在蓝图那赋值要加载的UClass,然后再进行加载

  //获取所有资源路径
  TArray<FSoftObjectPath> ObjectWealthPaths;
  for (int i = 0; i < ClassWealthPaths.Num(); ++i)
  {
  ObjectWealthPaths.Push(ClassWealthPaths[i].ToSoftObjectPath());
  }
  //进行异步加载
  WealthHandle = WealthLoader.RequestAsyncLoad(ObjectWealthPaths,
  FStreamableDelegate::CreateUObject(this, &AAsynClassActor::LoadWealthCompleted));

}

void AWelathActor::StreamableManagerLoadComplete()
{
//加载完成后动态修改图片
TArray<UObject* >OutObjects;
WealthHandle->GetLoadedAssets(OutObjects);
for (int32 i = ; i < OutObjects.Num(); ++i)
{
UTexture2D* WorkTexture = Cast<UTexture2D>(OutObjects[i]);
if (WorkTexture)
{
TextureGroup.Add(WorkTexture);
}
}
} //加载UClass

  void AAsynClassActor::LoadWealthCompleted()
  {
  //获取所有Class
  TArray<UObject*> WealthObjects;
  WealthHandle->GetLoadedAssets(WealthObjects);
  for (int i = 0; i < WealthObjects.Num(); ++i)
  {
  //把Object转为UClass
  UClass* WeathClass = Cast<UClass>(WealthObjects[i]);
  //生成AActor,由于这里的地址指向的都是AActor,故这里生成AActor.
  AActor* Wealthactor = GetWorld()->SpawnActor<AActor>(WeathClass, FVector(0.f, 0.f, 1000.f)
  , FQuat::Identity.Rotator());
  //填充到数组
  WealthActors.Push(Wealthactor);
  }
  }

 

3.通过UObjectLibrary获取批量内容的地址

.h:
class UObjectLibrary* ObjectLibrary; .cpp:
void AWelathActor::ObjectLibraryOperate()
{
if (!ObjectLibrary)
{
ObjectLibrary = UObjectLibrary::CreateLibrary(UObject::StaticClass(), false, false);
//添加到根那,防止被UE4的垃圾回收机制干掉
ObjectLibrary->AddToRoot();
} //搜索所有Texture的路径
ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/Resource/UI/Texture/MenuTex")); TArray<FAssetData> TextureData;
ObjectLibrary->GetAssetDataList(TextureData); for (int32 i=; i<TextureData.Num(); ++i)
{
TexturePath.AddUnique(TextureData[i].ToSoftObjectPath());
}
}

4.计时器

.h:
FTimerHandle CountdownTimerHandle;
//如果委托事件有参数
void ShiningObject(AStaticMeshActor* Object); .cpp:
//事件委托
FTimerDelegate UpdateTextureDele = FTimerDelegate::CreateUObject(this, &AWelathActor::UpdateTexture);
//如果事件有参数
FTimerDelegate UpdateTextureDele = FTimerDelegate::CreateUObject(this, &ABIMVRPawn::ShiningObject, Object);
//每0.5秒执行一次事件委托
GetWorld()->GetTimerManager().SetTimer(CountdownTimerHandle, UpdateTextureDele, 0.5f, true);
//停止运行定时器
GetWorldTimerManager().ClearTimer(CountdownTimerHandle);

5.随机数

//产生随机整数(范围1~5)
int AWelathActor::Rand5()
{
FRandomStream Stream;
Stream.GenerateNewSeed();
   //返回值优化
return Stream.RandRange(, );
}

6.UE4的智能指针

  可参考:https://www.cnblogs.com/timy/p/8685953.html

  官方文档:https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/SmartPointerLibrary

7.C++与蓝图交互

  参考视频:https://ke.qq.com/course/308721

.h:
   //蓝图调用,C++实现
UFUNCTION(BlueprintCallable, Category = "FrameWork")
void CAFuncOne(int32 Input, bool& Output);
//只能在蓝图实现
UFUNCTION(BlueprintImplementableEvent, Category = "FrameWork")
void CAFuncTwo(int32 Input, bool& Output);
//蓝图和C++都可实现,C++实现需要加后缀_Implementation
UFUNCTION(BlueprintNativeEvent, Category = "FrameWork")
void CAFuncThree(int32 Input, bool& Output);
  //变量在蓝图中可读写
  
  UPROPERTY(BlueprintReadWrite, Category = "FrameWork")

   int A;


.cpp:

void AFWCharacter::CAFuncOne(int32 Input, bool& Output)
{ } void AFWCharacter::CAFuncThree_Implementation(int32 Input, bool& Output)
{ }

8. C++中的代码注释有中文时的特殊处理

  如果不处理,可能会出现在蓝图中调用C++函数时,注释乱码的情况或者用C++写了一个UE4模板,调用此模板时,c++的注释乱码的情况

  处理方法之一:将文件保存为utf8格式。方法:https://blog.csdn.net/jiegemena/article/details/79369650

9.UE4的内存管理

  UObject有一个垃圾回收系统来管理它们。而非UObject派生的(例如UStruct、UUserWidget等)则需要用智能指针来管理它们的生命周期。

  垃圾回收系统详解:https://wiki.unrealengine.com/Garbage_Collection_%26_Dynamic_Memory_Allocation

10.调用&修改参数

  尽量用get()、set()来调用修改,直接改参数可能会失败。(尽管参数是public的)

//修改参数成功
Cast<UStaticMeshComponent>(HighLightThis->GetRootComponent())->SetRenderCustomDepth(WantHighLight);
//修改参数失败
Cast<UStaticMeshComponent>(HighLightThis->GetRootComponent())->bRenderCustomDepth = WantHighLight;

11.打包可能遇到的问题&解决方法

解决:

12. Delay函数调用

.h:
//一定要BlueprintCallable
UFUNCTION(BlueprintCallable, Category = "RoomVR")
void FinishMission(); .cpp: FLatentActionInfo Action;
Action.CallbackTarget = this;
Action.ExecutionFunction = "FinishMission";
Action.UUID = ;
Action.Linkage = ;
UKismetSystemLibrary::Delay(GetWorld(), 2.0f, Action);

13. 创建动态材质

//获取材质
UMaterialInterface* HintMaterial = LoadObject<UMaterialInterface>(NULL, TEXT("Material'/Game/Material/SpecialBrick.SpecialBrick'")); //创建动态材质
UMaterialInstanceDynamic* HintMaterialDynamic = UMaterialInstanceDynamic::Create(HintMaterial, nullptr);
//修改材质参数
HintMaterialDynamic->SetVectorParameterValue("Color", FLinearColor::Green);

14. 关于打印(Printstring)

//float转Fstring
UKismetSystemLibrary::PrintString(this, "Value: " + FString::SanitizeFloat(1.23));
//bool转FString
UKismetStringLibrary::Conv_BoolToString(true);

15. Timeline

.h:

    UPROPERTY()
class UTimelineComponent* MyTimeLine;
UPROPERTY()
class UCurveFloat* FloatCurve;
UFUNCTION()
void TimelineCallback(float val); UFUNCTION()
void TimelineFinishedCallback(); void PlayTimeline(); UPROPERTY()
TEnumAsByte<ETimelineDirection::Type> TimelineDirection; .cpp: AYourClass::AYourClass()
{
static ConstructorHelpers::FObjectFinder<UCurveFloat> Curve(TEXT("/Game/Curves/C_MyCurve"));
check(Curve.Succeeded()); FloatCurve = Curve.Object;
} void AYourClass::BeginPlay()
{
FOnTimelineFloat onTimelineCallback;
FOnTimelineEventStatic onTimelineFinishedCallback; Super::BeginPlay(); if (FloatCurve != NULL)
{
MyTimeLine = NewObject<UTimelineComponent>(this, FName("TimelineAnimation"));
//Timeline是来自蓝图的
MyTimeLine->CreationMethod = EComponentCreationMethod::UserConstructionScript;
//把它加到组件数组中,从而让它得以保存
this->BlueprintCreatedComponents.Add(MyTimeLine);
//可以作为引用(联网用)
MyTimeLine->SetNetAddressable();
//Set which object the timeline should drive properties on
MyTimeLine->SetPropertySetObject(this);
MyTimeLine->SetDirectionPropertyName(FName("TimelineDirection"));
//Timeline不循环,长度为1秒
MyTimeLine->SetLooping(false);
MyTimeLine->SetTimelineLength(1.0f);
MyTimeLine->SetTimelineLengthMode(ETimelineLengthMode::TL_LastKeyFrame);
MyTimeLine->SetPlaybackPosition(0.0f, false,false);
//绑定Timeline运行时,执行的曲线和事件;绑定Timeline结束时的事件。
onTimelineCallback.BindUFunction(this, FName{ TEXT("TimelineCallback") });
onTimelineFinishedCallback.BindUFunction(this, FName{ TEXT("TimelineFinishedCallback") });
MyTimeLine->AddInterpFloat(FloatCurve, onTimelineCallback);
MyTimeLine->SetTimelineFinishedFunc(onTimelineFinishedCallback);
MyTimeLine->RegisterComponent();
MyTimeLine->Play(); } void AYourClass::Tick(float deltaTime)
{
Super::Tick(deltaTime); if (MyTimeline != NULL)
{
MyTimeline->TickComponent(deltaTime, ELevelTick::LEVELTICK_TimeOnly, NULL);
}
} void AYourClass::TimelineCallback(float interpolatedVal)
{
// This function is called for every tick in the timeline.
} void AYourClass::TimelineFinishedCallback()
{
// This function is called when the timeline finishes playing.
} void AYourClass::PlayTimeline()
{
if (MyTimeline != NULL)
{
MyTimeline->PlayFromStart();
}
}

16. VS里不能启动UE4时

  出现问题如下图:

  

  解决办法:

  

  然后就可以了。

17. 关于LoadObject<UBlueprint>

  打包后,应用读取不了这个蓝图(在编辑器可以),故会出现问题。

  解决办法:

  1. 将此蓝图本地化(可能可以,但由于本地化会触发其它问题,导致打包失败,故未测试)

  2. 如果读此蓝图是为了获取它的class,从而动态地生成此蓝图的话,可以用TSubclassOf,然后在项目中赋值。

  3. 使用FClassFinder:

.h:
//控制器的类
TSubclassOf<ABIMVRController> BIMVRControllerBP; .cpp:
static ConstructorHelpers::FClassFinder<ABIMVRController>
BIMVRControllerBPFinder(TEXT("Blueprint'/Game/VRPlayer/BIMVRController.BIMVRController_C'"));
BIMVRControllerBP = BIMVRControllerBPFinder.Class;

  尽量不要用LoadObject<UBlueprint>。

18. UE4文件必须文件夹

  

  当然,没有代码的话,Source文件夹不需要。Media文件夹只是做模板的时候需要,其它时候可以不需要。

19. 生成物体

  

//生成物体
AStaticMeshActor* CopyActor = GetWorld()->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass());

20. 修改Plugins

  直接在UE4引擎上改是不行的。

  正确做法是把plugins从原Plugin中复制一份,且放进工程里编译。

  

  

21. 制作模板

  如果需要制作自己的模板(UE4中直接调用),可参照https://blog.csdn.net/u014532636/article/details/72832926

  注意,如果模板里有自己写的C++文件,注意这些C++文件名不要与模板名字雷同,如:

  

  如果模板的名字也叫BimVR,那么调用模板时会触发UE4埋下的陷井:UE4会修改同名的C++文件名字和内容,导致BUG出现。

  总之小心命名就好。

22. 项目改名

  把项目与VS关掉后,重命名此项目即可。改名后,把VS、Intermediate文件夹、Saved文件夹删掉,然后右键项目重新生成VS文件。

  C++里面不需要进行修改,经测试,改名后,未发现什么问题。

23. 在本地文件中写数据

  这段代码可以生成word文档,如果文档绝对位置不存在目标文档(FileName),则会创造一个该命名的word文档。

  如果有,则会覆盖原文档。

bool ABIMVRPawn::WriteFile(FString TestString, FString FileName)
{
if (FileName.IsEmpty())
{
FileName = "MyWord";
UKismetSystemLibrary::PrintString(this, "FileName.IsEmpty");
} //文档相对位置
FString Path = FString("Res/");
//文档绝对位置
FString AbsoPath = FPaths::GameContentDir() + Path + FileName + ".doc";
//输出pdf文件,文件会损坏,估计要按照特定的格式输入数据
//FString AbsoPath = FPaths::GameContentDir() + Path + FileName + ".pdf";
if (!TestString.IsEmpty())
{
if (FFileHelper::SaveStringToFile(TestString, *AbsoPath)) return true;
else UKismetSystemLibrary::PrintString(this, "WriteFail: " + AbsoPath);
}
return false;
}

24. 关于指针指向的东西是否有效

  众所周知,空指针会导致应用崩溃。所以,安全起见,使用指针前,先检查它是否有效。如:

if(!MyGCProtectedObj) return;

  但是,在UE4里,仅仅这样是不够的,某些情况下,还是会崩。

  因为指针可能不是空的,但它指向的是未完全析构的UObject,此时使用此指针也会崩溃,故还要检查指针指向的物体是否有效:

if(!MyGCProtectedObj) return;
if(!MyGCProtectedObj->IsValidLowLevel()) return;

25. UE4的垃圾回收系统

  参考官方的解释:http://api.unrealengine.com/CHN/Programming/Introduction/index.html

  首先,UE4的垃圾回收系统是基于反射系统来实现的。

  然后,这个系统有个叫根集的概念,该根集基本上是一个对象列表这些对象是回收程序知道将不会被垃圾回收的对象

  如何添加UObject到根集中?使用UPROPERTY,或者AddToRoot();

UPROPERTY()
UObject* MyObject; void CreateObject()
{
   MyObject = NewObject<UObject>();
 //手动添加到根集中
UObject* Object;
Object = NewObject<UObject>();
Object->AddToRoot();
}

  上述代码中,我们新建了个UObject,且实例化了。

  当我们要删除它时,我们可以把它变成空指针,然后它原本指向的东西会自动被垃圾回收系统检测到,且删除

MyObject = nullptr;

  或者手动删除它:

MyObject->ConditionalBeginDestroy();

  Actor通常不会被垃圾回收。因为Actor会自动成为根集的一部分。

  故,我们要手动删除它:

    AActor* TestGCActor;
TestGCActor->Destroy();

  注意,上述代码只是展示了删除方法,具体应用时,TestGCActor应该先生成出来!

  调用Destroy()后,它们将不会被立即删除,而是在下次垃圾回收时进行清理。

26. 生成物体

AStaticMeshActor* IntroducingActor = GetWorld()->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass());

27. 类命名前缀

  • 派生自 Actor 的类带有 A 前缀,如AController。

  • 派生自 Object 的类带有 U 前缀,如UComponent。

  • Enums 的前缀是 E,如EFortificationType。

  • Interface 的前缀通常是 I,如IAbilitySystemInterface。

  • Template 的前缀是 T,如TArray。

  • 派生自 SWidget 的类(Slate UI)带有前缀 S,如SButton。

  • 其他类的前缀为字母F ,如FVector。

28. Widget模块

  如果想新建的类里包含Widget模块,如:

  

  或者新建一个继承自UUserWidget的c++类时,

  则需要在Build.cs文件中添加UMG模块:

  

29. VS的代码块

  VS编辑器的一个命令:#pragma region 。。。 #pragma endregion。

  它可以把一堆代码视为一个代码块,可以收缩或展开这段代码。

  如:

#pragma region Test

void .....

void .....

void .....

#pragma endregion

  这段代码块名字为Test。

30. BindKey

  如果想用BindKey来把某个按键与事件绑定起来,如:

PlayerInputComponent->BindKey(EKeys::J, IE_Pressed, this, &ARPCCourseCharacter::KeyJEvent);

  则需要在Build.cs文件中添加Slate模块:

  

31. GetAllActorsOfClass

  想获取当前世界的某个类的所有物体时,可以用GetAllActorsOfClass:

TArray<AActor*> UIArray;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AIntroduceUI::StaticClass(), UIArray);

32. 对TMap进行For-Each

// TMap——迭代器返回键-值对
TMap<FName, AActor*> NameToActorMap = GetMapFromSomewhere();
for (auto& KVP :NameToActorMap)
{
FName Name = KVP.Key;
AActor* Actor = KVP.Value; // ...
}

  请记住,auto 关键字不会自动指定指针/引用,您需要自行添加。

33. 调用OnBeginOverLap

在蓝图里是这样的:

  

在C++里:

.h:
UFUNCTION()
    virtual void CollisonEvent(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
const FHitResult & SweepResult); .cpp:
//碰撞Delegate
FScriptDelegate CollisionDelegate;
CollisionDelegate.BindUFunction(this, "CollisonEvent");
//绑定开始触碰事件
TouchArea->OnComponentBeginOverlap.Add(CollisionDelegate);
  
void ALiftingActor::CollisonEvent(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{ if (OtherActor)
{
UKismetSystemLibrary::PrintString(this, "Result: " + OtherActor->GetName());
} }

  

  OnEndOverlap同理。

UE4 C++ Tips的更多相关文章

  1. UE4 Pro Tips(keeps updating)

    Consolidate 功能 :在工程范围内用一种资源替换另外一种或多种资源具体操作:同时在编辑器中选中两个或多个资源,右键>Asset Actions>Replace Reference ...

  2. UE4笔记:利用Widget设计一个切换材质功能

    UE4引擎中的Widget蓝图是一个重要的工具,可用于场景中的页面叠加,镜头绑定,场景切换等多处地方,在这里笔者介绍一种利用控件蓝图和场景中物体进行信息交互的方法,直观的体现就是进行物体的材质切换. ...

  3. 【UE4 C++】 UnrealPak 与 Pak 的制作、挂载、加载

    简介 通过 UnrealPak,可以将资源打包成 Pak 文件 Pak文件是UE4游戏生成的数据包文件. Pak 之前一般先有 Cooked 步骤,将资源烘焙为对应平台支持的资源 一般打包后的项目使用 ...

  4. UE4新手引导之下载和安装虚幻4游戏引擎

    1) 进入虚幻4的官方主页(https://www.unrealengine.com/) 这里你可以获得关于虚幻4的最新资讯,包括版本更新.博客更新.新闻和商城等.自2015年起,该引擎已经提供免费下 ...

  5. UE4新手引导入门教程

    请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf

  6. Mac上MySQL忘记root密码且没有权限的处理办法&workbench的一些tips (转)

    忘记Root密码肿么办 Mac上安装MySQL就不多说了,去mysql的官网上下载最新的mysql包以及workbench,先安装哪个影响都不大.如果你是第一次安装,在mysql安装完成之后,会弹出来 ...

  7. 【Tips】史上最全H1B问题合辑——保持H1B身份终级篇

    [Tips]史上最全H1B问题合辑——保持H1B身份终级篇 2015-04-10留学小助手留学小助手 留学小助手 微信号 liuxue_xiaozhushou 功能介绍 提供最真实全面的留学干货,帮您 ...

  8. layer.js中layer.tips

    <script src="~/Content/js/layer/layer.js"></script> layer.tips('名称不能为空', '#pro ...

  9. ue4 c++学习推荐

    我由易到难推荐,不过在此之前还是先看看官方对于VS设置的推荐: https://docs.unrealengine.com/latest/INT/Programming/Development/Vis ...

随机推荐

  1. matlab中高维数组怎么做PCA?

    PCA需要先求数据的散布矩阵x*x',再求其特征向量,那么随便一个400*450的图像,就是180000维,矩阵就是180000*180000,matlab无法容纳,那么通常的PCA对图像的降维,比如 ...

  2. ASCII编码、Unicode编码、UTF-8

    一.区别 ASCII.Unicode 是“字符集” UTF-8 .UTF-16.UTF-32  是“编码规则” 其中: 字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code ...

  3. linux系统管理 计划任务

    一次性计划任务 命令: at 语法: at [-f 文件名] 时间 绝对计时方法 HH:MM yyyy-MM-dd 相对计时方法 now + n minutes now+n hours now + n ...

  4. Win10系列:JavaScript访问文件和文件夹

    在实际开发中经常会遇到访问文件的情况,因此学习与文件有关的操作对程序开发很有帮助,关于文件操作的一些基本技术,在前面章节中有专门基于C#语言的详细讲解,本节主要介绍如何使用HTML5和JavaScri ...

  5. Python Django 之 直接执行自定义SQL语句(一)

    一.执行自定义SQL方法 1.Executing custom SQL directly      直接执行自定义SQL,这种方式可以完全避免数据模型,而是直接执行原始的SQL语句. 2.Manage ...

  6. 阿里云服务器上安装mysql的详细步骤

    阿里云安装mysql (1)下载mysql安装包,去mysql官网下载对应的包 mysql数据库官方下载网址:   https://downloads.mysql.com/archives/commu ...

  7. jquery 操作table样式拖动参考

    参考: http://blog.csdn.net/kdiller/article/details/6059727 http://www.jb51.net/article/59795.htm

  8. Java 几种showMessageDialog的表示

    最近在做swing程序中遇到使用消息提示框的,JOptionPane类其中封装了很多的方法. 很方便的,于是就简单的整理了一下. 1.1 showMessageDialog 显示一个带有OK 按钮的模 ...

  9. Oracle与MySQL的SQL语句区别

    2 表 2.1 创建表(同) create table tableName( columnName1 int, columnName2 int ) 2.2 删除表(异) MySQL: drop tab ...

  10. [Leetcode 104]求二叉树的深度Depth of BinaryTree

    [题目] Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the ...