【转载】找到本地磁盘上的 Mesh 并随机切换

274 阅读3分钟

原文链接:【小功能】找到磁盘上的网格并随机切换 | Mantra

正文

只是看别人的代码,永远不知道自己的问题 —— Mantra

动画123.gif

最近遇到一个需求,在编辑器中拖动,随机在几个网格中切换。这个需求用蓝图做非常简单,写代码的话也不难,但是会遇到几个(主要几种在 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);
	}

终于,达到了我们想要的效果