正文
只是看别人的代码,永远不知道自己的问题 —— Mantra
最近遇到一个需求,在编辑器中拖动,随机在几个网格中切换。这个需求用蓝图做非常简单,写代码的话也不难,但是会遇到几个坑(主要几种在 FObjectFinder 中),在这里和大家分享一下
注意:其实是原作者用了
static的关系才给自己埋了坑 !
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder(*Path);
所有后面的 “换一个思路” 也是没有必要的。
虽然是小问题,但是还是贴出来让自己和大家都以后注意一下。
首先说一下基本的思路:首先定义一个数组,把所有的 Mesh 存储到数组中,然后在 OnConstruction 中随机切换网格组件中的网格即可
MyTestActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyTestActor.generated.h"
UCLASS()
class MYPROJECT_API AMyTestActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyTestActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 用于存储所有资源的路径
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Mantra")
TArray<FString> MeshPaths;
// 用于显示找到的 Mesh
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Mantra")
UStaticMeshComponent* MantraMesh;
// 存储路径下找到的所有 Mesh(此处只是为了测试,所以才定义成了UPROPERTY)
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Mantra")
TArray<UStaticMesh*> AllPossibleMeshes;
virtual void OnConstruction(const FTransform& Transform) override;
};
MyTestActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyTestActor.h"
#include "ConstructorHelpers.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
// Sets default values
AMyTestActor::AMyTestActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 指定网格的路径
MeshPaths.Add(TEXT("StaticMesh'/Game/StarterContent/Props/SM_Chair.SM_Chair'"));
MeshPaths.Add(TEXT("StaticMesh'/Game/StarterContent/Props/SM_MatPreviewMesh_02.SM_MatPreviewMesh_02'"));
MeshPaths.Add(TEXT("StaticMesh'/Game/StarterContent/Props/SM_Lamp_Ceiling.SM_Lamp_Ceiling'"));
MeshPaths.Add(TEXT("StaticMesh'/Game/StarterContent/Props/SM_Couch.SM_Couch'"));
// 通过循环找到指定的网格并设置(但是并不起作用,必须分开写,原因不明)
for (int32 i = 0; i < MeshPaths.Num(); i++)
{
/// @note 其实是原作者这里用了 static 的关系才给自己埋了坑 !
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder(*(MeshPaths[i]));
if (MeshFinder.Succeeded())
{
AllPossibleMeshes.Add(MeshFinder.Object);
}
}
// 初始化组件
MantraMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MantraMesh"));
MantraMesh->AttachTo(RootComponent);
}
// Called when the game starts or when spawned
void AMyTestActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyTestActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyTestActor::OnConstruction(const FTransform & Transform)
{
int32 Index = FMath::RandRange(0, AllPossibleMeshes.Num() - 1);
UStaticMesh* TargetMesh = AllPossibleMeshes[Index];
if (TargetMesh != nullptr)
{
MantraMesh->SetStaticMesh(TargetMesh);
}
Super::OnConstruction(Transform);
}
但是上面代码中 FObjectFinder 相关的核心代码不是按照我们想象的去执行,最终会把第一个网格物体赋予给数组中的所有元素。
既然循环中不行,那么接下来我们换一个思路,我们接下来把赋值网格的方法单独定义成一个函数,然后分别调用这个函数,核心代码修改如下
StaticMeshFinder(MeshPaths[0]);
StaticMeshFinder(MeshPaths[1]);
StaticMeshFinder(MeshPaths[2]);
StaticMeshFinder(MeshPaths[3]);
void AMyTestActor::StaticMeshFinder(FString Path)
{
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder(*Path);
if (MeshFinder.Succeeded())
{
AllPossibleMeshes.Add(MeshFinder.Object);
}
}
但是发现结果还是一样的,所以笔者初步推测这是 FObjectFinder 的特殊之处,只要是同一个变量,那么找到的就是同一个物体。所以我们使用最笨的方式,为每一个物体查找定义一个变量,于是便有了下面的代码(最原始,也是最笨的方式)
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder0(*(MeshPaths[0]));
if (MeshFinder0.Succeeded())
{
AllPossibleMeshes.Add(MeshFinder0.Object);
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder1(*(MeshPaths[1]));
if (MeshFinder1.Succeeded())
{
AllPossibleMeshes.Add(MeshFinder1.Object);
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder2(*(MeshPaths[2]));
if (MeshFinder1.Succeeded())
{
AllPossibleMeshes.Add(MeshFinder2.Object);
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder3(*(MeshPaths[3]));
if (MeshFinder1.Succeeded())
{
AllPossibleMeshes.Add(MeshFinder3.Object);
}
终于,达到了我们想要的效果