What is the easiest way to assign a Mesh to an object from a discreet set?

First of all, I don’t understand why strings are not an obvious default datatype in C++. If I were programming this in Game Maker, I could select a sprite from a set of pre-loaded sprites based on an integer value, and I don’t have to worry about declaring datatypes beforehand.

Here, I have 8 different meshes that I could potentially assign a Fence obstacle object. My idea is that I declare an integer HeightMarker in the constructor in the *.h file. I can assign this integer value externally through another object upon creation, but when the BeginPlay() method is called, I want to assign the mesh.

Here is my code at present. Ideally, I would like to just have a variable that stores the reference path inside of the TEXT field, and then call the constructor helper function thereafter. My compiler is complaining right now based on the structure of my code. Any help is appreciated.

FenceObstacle.h:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "FenceObstacle.generated.h"

UCLASS()
class SCREECHTEST_API AFenceObstacle : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AFenceObstacle();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	//Set a component to check for collisions
	UPROPERTY(VisibleAnywhere)
		UBoxComponent* BoxComponent;

	//Create a visible mesh so we can see the fence
	UPROPERTY(VisibleAnywhere)
		UStaticMeshComponent* VisibleMesh;

	//Set the height of the fence. Strider has noted that rather than stretch the Z scale of the fence from one mesh, we
	// have a discreet number of possible heights to choose from. This will be denoted by a single integer.
	int HeightMarker;
	
};

FenceObstacle.cpp:

// Fill out your copyright notice in the Description page of Project Settings.


#include "FenceObstacle.h"

// Sets default values
AFenceObstacle::AFenceObstacle()
{
	// 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;

	//Create the box component for collisions
	BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionBox"));
	check(BoxComponent != nullptr);

	
}

// Called when the game starts or when spawned
void AFenceObstacle::BeginPlay()
{
	Super::BeginPlay();
	
	//Create a visible mesh for the fence and set it to the appropriate reference
	if (!VisibleMesh) {
		VisibleMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisibleMesh"));
		try {
			
			switch (HeightMarker) {
				case 0:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_1'"));
					break;
				case 1:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_2'"));
					break;
				case 2:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_3'"));
					break;
				case 3:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_4'"));
					break;
				case 4:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_5'"));
					break;
				case 5:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_6'"));
					break;
				case 6:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_7'"));
					break;
				case 7:
					static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_8'"));
					break;
				default:
					throw(HeightMarker);
					break;
			}
			if (Mesh.Succeeded()) {
				VisibleMesh->SetStaticMesh(Mesh.Object);
			}
		}
		catch (int height) {
			check(GEngine != nullptr);
			//Display a debug message for five seconds
			//The -1 "Key" vale argument prevents the message from being updated or refreshed.
			GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, TEXT("Invalid fence height value: " << height));
		}
		
	}
}

// Called every frame
void AFenceObstacle::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

Hey there,

Your compiler error is probably going to be centered around CreateDefaultSubobject, which should never be called outside of constructors.

As far as the object finders, I think you could accomplish this same behaviour far more easily by creating a TArray<UStaticMesh> UPROPERTY and setting the static meshes per index in that.

Instead of switch you can use FString::Printf to simplify code

something like:

void AFenceObstacle::BeginPlay()
{
  auto const Path = FString::Printf("/Game/Meshes/sm_obstacle_fence_size_1.sm_obstacle_fence_size_%i", HeightMarker);
  auto Mesh = LoadObject<UStaticMesh>(nullptr, TEXT("FenceMesh"), *Path);
  if (Mesh) {
    VisibleMesh->SetMesh(Mesh);
  }
}

Of course, you need to check beforehand that the HeightMarker has the correct value, but using exceptions in this case is not the best solution.

Thank you for your help. The compiler seems to be hung up on that FString line, though.

Is there an #include statement that I need to add in order to be able to access that function?

What do you mean? What compiler say?

Here is the printout. The main one that catches my attention is “FString::Printf”: no matching overloaded function found.

image

For reference, here is my up-to-date *.cpp code:

// Fill out your copyright notice in the Description page of Project Settings.


#include "FenceObstacle.h"

// Sets default values
AFenceObstacle::AFenceObstacle()
{
	// 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;

	//Create the box component for collisions
	BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionBox"));
	check(BoxComponent != nullptr);

	//Create a visible mesh for the fence. CreateDefaultSubobject cannot be called outside of constructors
	if (!VisibleMesh) {
		VisibleMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisibleMesh"));
	}
}

// Called when the game starts or when spawned
void AFenceObstacle::BeginPlay()
{
	Super::BeginPlay();
	
	if (HeightMarker > 0 && HeightMarker < 9) {
		//Assign the appropriate mesh to the fence based on the assigned HeightMarker
		auto const Path = FString::Printf("/Game/Meshes/sm_obstacle_fence_size_%i.sm_obstacle_fence_size_%i", HeightMarker, HeightMarker);
		auto Mesh = LoadObject<UStaticMesh>(nullptr, TEXT("FenceMesh"), *Path);
		if (Mesh) {
			VisibleMesh->SetMesh(Mesh)
		}
	} else {
		//If an erroneous HeightMarker value was assigned, show that in a debug message
		check(GEngine != nullptr);
		//Display a debug message for five seconds
		//The -1 "Key" vale argument prevents the message from being updated or refreshed.
		auto const err_msg = FString::Printf("Invalid fence height value: %i", HeightMarker)
		GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, err_msg);
	}
}

// Called every frame
void AFenceObstacle::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

Ohh, probably missing TEXT() macro.
Try

auto const Path = FString::Printf(TEXT("/Game/Meshes/sm_obstacle_fence_size_%i.sm_obstacle_fence_size_%i"), HeightMarker, HeightMarker);

That TEXT macro is going to be a real thorn in my side, I’ll bet.

Thank you. That did take care of most of my errors. Now the only one left is saying that ‘SetMesh’ is not a member of ‘UStaticMeshComponent’. Do I just not need this block?

if (Mesh) {
	VisibleMesh->SetMesh(Mesh);
}

Possibly try:

#include "Components/StaticMeshComponent.h"

VisibleMesh->SetStaticMesh(Mesh);

You may need Mesh.Object though.

Well, using just Mesh in there allowed the code to compile, at least. I’ll check back with this if I get a runtime error later.

Anyway, thanks to all of you for your help!