Hi, every devs.
I’m writing a program that generates a mesh in Unreal based on vertex information.
When I press Play button in the editor, the mesh will be created normally.
During the execution process, actors are created in game mode.
The created actors each create a procedural mesh.
The problem is that the created meshes do not move at all even if the Transform value is changed. Since it is a Static Mesh, the situation is the same even if you change it to Movable.
If you look at the editor image, the objects checked with a red circle are the created procedural mesh. And you can tell that it is Static because the color of the Mesh_ label next to it is gold.
I’m a beginner so I don’t know why this happened.
Please let me know.
Thank you for your interest and reading.
I’ve posted the full working code below.
======= MyMeshActor.h ========
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "MyMeshActor.generated.h"
UCLASS()
class SQLITETEST_API AMyMeshActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyMeshActor();
// 프리미티브 큐브 생성
UPROPERTY()
UStaticMeshComponent* CubeComponent;
// 프리미티브 실린더 생성
UPROPERTY()
UStaticMeshComponent* CylinderComponent;
// 필요한 메시 생성 및 설정 함수
void CreateCylinder(float Width, float Height, float Length, FVector Position, float Rotx, float Roty, float Rotz);
void CreateCylinder(UStaticMeshComponent* InCubeComponent, float Width, float Height, float Length, FVector Position);
void CreateCube(float Width, float Height, float Length, FVector Position, float Rotx, float Roty, float Rotz);
void CreateCube(UStaticMeshComponent* InCubeComponent, float Width, float Height, float Length, FVector Position);
void CreateMeshByVertex(TArray<FVector> Vertices, TArray<int32> Triangles);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
UStaticMesh* CubeMesh;
UStaticMesh* CylinderMesh;
UStaticMeshComponent* MeshComponent;
UPROPERTY(VisibleAnywhere, Category = "Mesh")
UProceduralMeshComponent* ProceduralMesh;
};
========== MyMeshActor.cpp ==========
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyMeshActor.h"
#include "Components/StaticMeshComponent.h"
#include "KismetProceduralMeshLibrary.h"
#include "UObject/ConstructorHelpers.h"
// Sets default values
AMyMeshActor::AMyMeshActor()
{
// 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;
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
RootComponent = MeshComponent;
// 메시 로드 및 저장
static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMeshFinder(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));
if (CubeMeshFinder.Succeeded())
{
CubeMesh = CubeMeshFinder.Object;
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderMeshFinder(TEXT("/Game/StarterContent/Shapes/Shape_Cylinder.Shape_Cylinder"));
if (CylinderMeshFinder.Succeeded())
{
CylinderMesh = CylinderMeshFinder.Object;
}
}
void AMyMeshActor::CreateCube(float Width, float Height, float Length, FVector Position, float RotX, float RotY, float RotZ)
{
MeshComponent->SetStaticMesh(CubeMesh);
MeshComponent->SetWorldScale3D(FVector(Width, Height, Length));
MeshComponent->SetWorldLocation(Position);
// Y축 회전
FRotator RotationHor(0.0f + RotX, 90.0f + RotZ, -90.0f + RotY); // Pitch(X축), Yaw(Z축), Roll(Y축)
MeshComponent->SetRelativeRotation(RotationHor);
}
void AMyMeshActor::CreateCylinder(float Width, float Height, float Length, FVector Position, float RotX, float RotY, float RotZ)
{
MeshComponent->SetStaticMesh(CylinderMesh);
MeshComponent->SetWorldScale3D(FVector(Width, Height, Length));
MeshComponent->SetWorldLocation(Position);
// Y축 회전
FRotator RotationHor(0.0f + RotX, 90.0f + RotZ, -90.0f + RotY); // Pitch(X축), Yaw(Z축), Roll(Y축)
MeshComponent->SetRelativeRotation(RotationHor);
}
void AMyMeshActor::CreateMeshByVertex(TArray<FVector> Vertices, TArray<int32> Triangles)
{
TArray<FVector> Normals;
TArray<FVector2D> UV0;
TArray<FColor> VertexColors; // FColor 대신 FLinearColor 사용
TArray<FProcMeshTangent> Tangents;
// 메쉬 생성
ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, VertexColors, Tangents, true);
// 이동,회전 허용
ProceduralMesh->SetMobility(EComponentMobility::Movable);
// 법선 및 탄젠트 계산
UKismetProceduralMeshLibrary::CalculateTangentsForMesh(Vertices, Triangles, UV0, Normals, Tangents);
}
// Called when the game starts or when spawned
void AMyMeshActor::BeginPlay()
{
Super::BeginPlay();
// 현재 액터의 위치를 가져옴
FVector CurrentLocation = GetActorLocation();
// 로그로 현재 위치를 출력
UE_LOG(LogTemp, Warning, TEXT("Actor's Initial Location: X: %f, Y: %f, Z: %f"),
CurrentLocation.X, CurrentLocation.Y, CurrentLocation.Z);
}
// Called every frame
void AMyMeshActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//// 이동 간격(초) 및 이동할 거리를 정의
//const float MoveInterval = 2.0f; // 2초마다
//const float MoveDistance = 10.0f; // X축으로 10씩 이동
//// 현재까지 누적된 시간을 저장할 static 변수
//static float AccumulatedTime = 0.0f;
//// 누적 시간 업데이트
//AccumulatedTime += DeltaTime;
//// 누적 시간이 이동 간격을 초과했는지 확인
//if (AccumulatedTime >= MoveInterval)
//{
// // 현재 위치 가져오기
// FVector CurrentLocation = GetActorLocation();
// // X축으로 MoveDistance만큼 이동
// FVector NewLocation = CurrentLocation + FVector(MoveDistance, 0.0f, 0.0f);
// // 액터의 위치를 새 위치로 설정
// SetActorLocation(NewLocation, false, nullptr, ETeleportType::TeleportPhysics);
// // 누적 시간을 0으로 리셋 (또는 MoveInterval만큼 감소시키기)
// AccumulatedTime -= MoveInterval;
// // 로그로 새 위치를 출력
// UE_LOG(LogTemp, Warning, TEXT("Actor's New Location: X: %f, Y: %f, Z: %f"),
// NewLocation.X, NewLocation.Y, NewLocation.Z);
//}
}
//void AMyMeshActor::CreateCube(float XSize, float YSize, float ZSize, FVector Position)
//{
// // Load the cube mesh. Replace 'StaticMesh'/.../Cube.Cube' with the correct path to the cube mesh asset in your project
// static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMesh(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));
// if (CubeMesh.Succeeded())
// {
// CubeComponent->SetStaticMesh(CubeMesh.Object);
// CubeComponent->SetWorldScale3D(FVector(XSize, YSize, ZSize));
// CubeComponent->SetWorldLocation(Position);
// }
//}
//
//void AMyMeshActor::CreateCube(UStaticMeshComponent* InCubeComponent, float XSize, float YSize, float ZSize, FVector Position)
//{
// // 여기서 CubeMesh는 프로젝트 내에 있는 특정 Cube 메시를 찾도록 설정되어 있음
// static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMesh(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));
//
// if (CubeMesh.Succeeded())
// {
// // 새 컴포넌트에 메시 설정
// InCubeComponent->SetStaticMesh(CubeMesh.Object);
//
// // 새 컴포넌트의 크기 설정
// InCubeComponent->SetWorldScale3D(FVector(XSize, YSize, ZSize));
//
// // 위치
// InCubeComponent->SetWorldLocation(Position);
// }
//}
//
//void AMyMeshActor::CreateCylinder(float XSize, float YSize, float ZSize, FVector Position)
//{
// // 여기서 CubeMesh는 프로젝트 내에 있는 특정 Cube 메시를 찾도록 설정되어 있음
// static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderMesh(TEXT("/Game/StarterContent/Shapes/Shape_Cylinder.Shape_Cylinder"));
//
// if (CylinderMesh.Succeeded())
// {
// // 새 컴포넌트에 메시 설정
// CylinderComponent->SetStaticMesh(CylinderMesh.Object);
//
// // 새 컴포넌트의 크기 설정
// CylinderComponent->SetWorldScale3D(FVector(XSize, YSize, ZSize));
//
// // Y축 회전
// FRotator Rotation(0.0f, 0.0f, 90.0f); // Pitch(X축), Yaw(Z축), Roll(Y축)
// CylinderComponent->SetRelativeRotation(Rotation);
//
// // 위치
// CylinderComponent->SetWorldLocation(Position);
// }
//}
//void AMyMeshActor::CreateCylinder(UStaticMeshComponent* InCylinderComponent, float XSize, float YSize, float ZSize, FVector Position)
//{
// // 여기서 CubeMesh는 프로젝트 내에 있는 특정 Cube 메시를 찾도록 설정되어 있음
// static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderMesh(TEXT("/Game/StarterContent/Shapes/Shape_Cylinder.Shape_Cylinder"));
//
// if (CylinderMesh.Succeeded())
// {
// // 새 컴포넌트에 메시 설정
// InCylinderComponent->SetStaticMesh(CylinderMesh.Object);
//
// // 새 컴포넌트의 크기 설정
// InCylinderComponent->SetWorldScale3D(FVector(XSize, YSize, ZSize));
//
// // Y축 회전
// FRotator Rotation(0.0f, 0.0f, 90.0f); // Pitch(X축), Yaw(Z축), Roll(Y축)
// InCylinderComponent->SetRelativeRotation(Rotation);
//
// // 위치
// InCylinderComponent->SetWorldLocation(Position);
// }
//}
========== MyGameMode.h ==========
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyPlayerController.h"
#include "StackMeshVertexInfo.h"
#include "MyGameMode.generated.h"
/**
*
*/
UCLASS()
class SQLITETEST_API AMyGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AMyGameMode();
virtual void StartPlay() override;
private:
TArray<StackMeshVertexInfo> ParseMEPMeshes(const FString& FilePath);
};
========== MyGameMode.cpp ==========
#include "MyGameMode.h"
#include "Engine/World.h"
#include "MyActor.h"
#include "MyMeshActor.h"
AMyGameMode::AMyGameMode()
{
PlayerControllerClass = AMyPlayerController::StaticClass();
}
void AMyGameMode::StartPlay()
{
Super::StartPlay();
FString FilePath = TEXT("C:\\sqlite\\rvtobjs.txt"); // 적절한 파일 경로
TArray<StackMeshVertexInfo> LoadedData = ParseMEPMeshes(FilePath);
if (GetWorld())
{
int idx = 0;
for (const StackMeshVertexInfo& MeshInfo : LoadedData)
{
// 위치 및 회전값 설정
FVector Location(0.0f, 0.0f, 0.0f); // 예제에서는 임시값 사용, 실제 사용시 MeshInfo에서 적절한 값을 가져와야 함
FRotator Rotation(0.0f, 0.0f, 0.0f);
// 스폰 파라미터 설정
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
// AMyMeshActor 스폰
AMyMeshActor* SpawnedMeshActor = GetWorld()->SpawnActor<AMyMeshActor>(AMyMeshActor::StaticClass(), Location, Rotation, SpawnParams);
if (SpawnedMeshActor != nullptr)
{
FString LabelName = FString::Printf(TEXT("MESH_%d"), idx++);
SpawnedMeshActor->SetActorLabel(LabelName);
// 스폰된 메쉬 액터에 대한 추가 설정 (예: 메쉬 데이터 설정)
SpawnedMeshActor->CreateMeshByVertex(MeshInfo.Vertex, MeshInfo.Indices);
//// 정적으로 생성 된 메쉬를 동적 메쉬로 변환
//UStaticMeshComponent* MeshComponent = SpawnedMeshActor->FindComponentByClass<UStaticMeshComponent>();
//if (MeshComponent)
//{
// // 메쉬 컴포넌트를 동적으로 설정
// MeshComponent->SetMobility(EComponentMobility::Movable);
// UE_LOG(LogTemp, Warning, TEXT("Mobility set to Movable for: %s"), *LabelName);
//}
// 새로운 위치로 이동
FVector NewLocation = FVector(100.0f, 100.0f, 100.0f);
bool bSuccess = SpawnedMeshActor->SetActorLocation(Location);
UE_LOG(LogTemp, Warning, TEXT("Actor moved: %s"), bSuccess ? TEXT("True") : TEXT("False"));
idx++;
}
}
}
}