Actor spawning meshless

This code does successfully spawn the actor, however, spawns it without a mesh. How do I make it spawn with a mesh?


AMess* mess = GetWorld()->SpawnActor<AMess>(AMess::StaticClass(), location, rotate, SpawnInfo);

Can you show the code for your AMess constructor here?

Here you go. I initially did not have the last two lines, however I added them after some research - to no effect.



// Sets default values
AMess::AMess(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
 	// 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;
	trigVolume = ObjectInitializer.CreateDefaultSubobject<UBoxComponent>(this, TEXT("trigVolume"));
	trigVolume->bGenerateOverlapEvents = true;
	trigVolume->SetRelativeScale3D(FVector(2, 2, 5));
	RootComponent = trigVolume;
	trigVolume->OnComponentBeginOverlap.AddDynamic(this, &AMess::TriggerEnter);
	trigVolume->OnComponentEndOverlap.AddDynamic(this, &AMess::TriggerExit);
	CubeMesh = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("CubeMesh"));
	static ConstructorHelpers::FObjectFinder <UStaticMesh>StaticMesh(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
	CubeMesh->SetStaticMesh(StaticMesh.Object);
}

Edit: I got it working elsewhere. Only problem is that I cannot seem to get it to spawn by the actor (spawns under the map)


const FVector location = GetPawn()->GetActorLocation();//FVector(AICon->GetControlledPawn()->GetActorLocation());
	const FRotator rotate = FRotator(0.f, 0.f, 0.f);
	FActorSpawnParameters SpawnInfo;
	UWorld* World = GetWorld();
	if (World)
	{
		World->SpawnActor<AMess>(WhatToSpawn, location, rotate, SpawnInfo);
	}
	GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "Spawned");

You need to attach it to the RootComponent

CubeMesh ->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);

I did that, now it spawns (thanks!), BUT now it is the same size as the trigger volume to the point that you cannot enter the trigger and cube is out of proportion.

Rather than being this size, it takes up the entire area now with that line.
2017-06-21_1252.png

What it is like now when spawned by code (rather than via editor). It is around twice the size.

Hi,

What if you try this for attaching?



CubeMesh->SetupAttachment(RootComponent);


I attempted that, however, there was no change.

Hmm, maybe the relative scale messes the things up? Is there any particular reason for using relative scale transform instead of setting the size of the volume by cm?

There was no particular reason for the relative size line so I commented it out and recompiled to discover that now the objects were smaller (fixable/expected) but that I still had the problem of the cube being identical in size to the trigger volume (therefore making trigger volume in-enterable). The ideal scale for this object is having the trigger volume (2,2,5) and the mesh itself (0.5,0.5,0.2) - or at least proportional so that the player can actually get within the trigger volume.

Okay, so why don’t you take the size of your CubeMesh in cm-s, multiply it with your scale values (2,2,5) then with 0,5 and set that as the BoxExtent of your trigger volume?

something like:



FVector TriggerSize;

TriggerSize.X = CubeMeshX * 2.0f * 0.5f; //oc this can be skipped
TriggerSize.Y = CubeMeshY * 2.0f * 0.5f; //this as well
TriggerSize.Z = CubeMeshZ * 5.0f * 0.5f;

TriggerVolume->SetBoxExtent(TriggerSize, true);


Oddly enough, no difference. This is a weird problem. Do you want the header and cpp?

Well, yes, that might be helpful, however this is indeed a weird issue.

Here is the header and CPP, I have trimmed out irrelevant code in an effort to make it smaller (did not copy over totally unrelated methods/variables). Spawning itself is taken care of in the AIController. Once this is working properly, I will clean out the old commented out code, but I merely leave it there as it may be useful in trying to solve this issue.

AMess header


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

#pragma once

#include "GameFramework/Actor.h"
#include "MopSimulatorCharacter.h"
#include "Mess.generated.h"

UCLASS()
class MOPSIMULATOR_API AMess : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AMess(const FObjectInitializer& ObjectInitializer);
//	UPROPERTY(Category = Meshes, VisibleAnywhere)
	UPROPERTY(Category = Meshes, EditAnywhere)
		UStaticMeshComponent* CubeMesh;
	UPROPERTY(Category = Mess, EditAnywhere)
		int16 numberOfCleanupActionsNeeded = 1;

	UPROPERTY(Category = Mess, EditAnywhere)
		UParticleSystem* ParticleEmitter;
	UPROPERTY(Category = Mess, EditAnywhere)
		USoundCue* CleaningSound;

	UFUNCTION()
		void TriggerEnter(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
	UFUNCTION()
		void TriggerExit(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);


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

private:
	UPROPERTY(EditDefaultsOnly)
	UBoxComponent* trigVolume;
	
};


AMess Cpp


#include "MopSimulator.h"
#include "Engine.h"
//#include "MopSimulatorCharacter.h"
#include "Mess.h"


// Sets default values
AMess::AMess(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
 	// 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;
	trigVolume = ObjectInitializer.CreateDefaultSubobject<UBoxComponent>(this, TEXT("trigVolume"));
	trigVolume->bGenerateOverlapEvents = true;
	trigVolume->SetRelativeScale3D(FVector(2, 2, 5));
	//trigVolume->set
	RootComponent = trigVolume;
	trigVolume->OnComponentBeginOverlap.AddDynamic(this, &AMess::TriggerEnter);
	trigVolume->OnComponentEndOverlap.AddDynamic(this, &AMess::TriggerExit);
//	CubeMesh = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("CubeMesh"));
	CubeMesh = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("CubeMesh"));
	FVector CubeMeshSize = CubeMesh->GetComponentLocation();
//	CubeMesh->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale);	// works but makes it too large
	FVector TriggerSize;
	TriggerSize.X = CubeMeshSize.X * 2.0f * 0.5f; //oc this can be skipped
	TriggerSize.Y = CubeMeshSize.Y * 2.0f * 0.5f; //this as well
	TriggerSize.Z = CubeMeshSize.Z * 5.0f * 0.5f;

//	trigVolume->SetBoxExtent(TriggerSize, true);
	CubeMesh->SetupAttachment(RootComponent);
	//CubeMesh->SetRelativeScale3D(FVector(1, 1, 1));
	static ConstructorHelpers::FObjectFinder <UStaticMesh>StaticMesh(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
	CubeMesh->SetStaticMesh(StaticMesh.Object);
	//CubeMesh->SetWorldScale3D(FVector(0.5, 0.5, 0.2));
	//CubeMesh->SetRelativeScale3D(FVector(0.5, 0.5, 0.2));
}

// Called when the game starts or when spawned
void AMess::BeginPlay()
{
	Super::BeginPlay();
	
}

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

}


void AMess::TriggerEnter(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	if (OtherActor == UGameplayStatics::GetPlayerCharacter(GetWorld(), 0)) {
		GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "TriggerEnter");
		//((OtherActor)UGameplayStatics::GetPlayerCharacter(GetWorld(), 0))->SetClickable(false);
		//AMopSimulatorCharacter* Player = Cast<AMopSimulatorCharacter>(OtherActor);
		//AMopSimulatorCharacter* Player = Cast(OtherActor);
		//AMopSimulatorCharacter* mainChar = Cast(GWorld->GetFirstPlayerCharacter());
		AMopSimulatorCharacter* Player = Cast<AMopSimulatorCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
		check(Player);
		//Player->SetMopable(true);	// collision has occured so tell player character know
		//Player->SetMoppingObject(this);
		Player->SetMopable(true, this);
	}
}

void AMess::TriggerExit(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OtherActor == UGameplayStatics::GetPlayerCharacter(GetWorld(), 0)) {
		GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "TriggerExit");
		AMopSimulatorCharacter* Player = Cast<AMopSimulatorCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
		check(Player);
		Player->SetMopable(false);	// trigger exited so let player character know
	}
}
}

Related AIController header section (is public)


UPROPERTY(EditDefaultsOnly, Category = "MyBPForCPP")
		class AMess* Mess;

AIController spawning method (Cpp). Nothing in the constructor pertains to spawning of the “mess”.


void ANPCAIController::SpawnMess()
{
	FVector location = GetPawn()->GetActorLocation();//FVector(AICon->GetControlledPawn()->GetActorLocation());
	//const FRotator rotate = GetPawn()->GetActorRotation();//FRotator(*insert rotation here*);
	const FRotator rotate = FRotator(0.f, 0.f, 0.f);
	FActorSpawnParameters SpawnInfo;
	//SpawnInfo.Instigator = GetPawn();
	SpawnInfo.Owner = GetPawn();
	//AMess* mess = GetWorld()->SpawnActor<AMess>(AMess::StaticClass(), location, rotate, SpawnInfo);	// This does actually spawn, only it does not contain mesh
	UWorld* World = GetWorld();
	if (World)
	{
		float randNumFloat = FMath::RandRange(1, 10);
		int16 randNum = (int16)randNumFloat;
		AMess* mess = World->SpawnActor<AMess>(WhatToSpawn, location, rotate, SpawnInfo);
		mess->SetNeededNumberOfCleanupActions(randNum);
		//location.Z = GetPawn()->GetActorLocation().Z;
		//mess->SetActorLocation(location);
	//	mess->setrelative
	}
	GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "Spawned");
	GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, location.ToString());
//	ANonPlayerCharacter* AICharacter = Cast<ANonPlayerCharacter>();
	//check(AICharacter);
//	AICharacter->SpawnMess();
}

This game is a test project of mine to help improve my skills in C++ and Unreal Engine (mainly a Java developer by training).

I think you are overdoing a lot of things, I can even see some duplicated code.
I went ahead and created a test project to show you how to do it. I will post the code here, but if you need it, I can upload the project itself as well. I have only created the absolute minimum, so no sound, particles etc.

AMess.h



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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Mess.generated.h"

//@abstract: Optional, you will be not allowed to instanciate this class, only the Blueprint version.
//@Blueprintable: Tells the Engine that this class can be a base for a Blueprint class.
UCLASS(abstract, Blueprintable)
class DEMOPROJECT_API AMess : public AActor
{
	GENERATED_BODY()
	
	//This Constructor works as well, you don't have to use ObjectInitializer anymore (in most cases).
	public: AMess();

public:

	//The 'VisibleAnywhere' specifier allows the component to be visible in the Blueprinted version of the class, but it cannot be edited. 'BlueprintReadOnly' only allows the BP graphs to read the value.
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Meshes")
	//Forward declaring the UBoxComponent, we will include it in the .cpp
	class UBoxComponent* TriggerVolume;
	
	//The 'EditDefaultsOnly' specifier allows the CubeMesh to be set in the Blueprinted version of the class, but only in the 'Defaults' tab.
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Meshes")
	UStaticMeshComponent* CubeMesh;
};


AMess.cpp



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

#include "Mess.h"

//Only including the required header, not the whole Engine.h
#include "Components/BoxComponent.h"

//Constructor
AMess::AMess()
{
 	// 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;

	//Creating the TriggerVolume, note that there is no need to use the ObjectInitializer.
	TriggerVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerVolume"));
	
	//Setting the TriggerVolume as the RootComponent of the Actor.
	RootComponent = TriggerVolume;
	
	//Creating the CubeMesh.
	CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));

	//Attaching the CubeMesh to the RootComponent.
	CubeMesh->SetupAttachment(RootComponent);

	//The size of the CubeMesh in cm-s. Note that I'm just coming up with these values, you need to write the actual size of your mesh here.
	const FVector CubeMeshSize = FVector(100.0f,100.0f,100.0f);

	//Setting up the TriggerVolume
	{
		//Holds the size (cm) of the TriggerVolume.
		FVector TriggerVolumeSize = CubeMeshSize;

		//Scaling the 'TriggerVolumeSize' to the desired scale.
		TriggerVolumeSize.X = TriggerVolumeSize.X * 2.0f;
		TriggerVolumeSize.Y = TriggerVolumeSize.Y * 2.0f;
		TriggerVolumeSize.Z = TriggerVolumeSize.Z * 5.0f;

		//Setting the actual TriggerVoulme size with the calculated value.
		TriggerVolume->SetBoxExtent(TriggerVolumeSize);
	}
}


In the Editor:

Create a BP version of the AMess class:
90371d2c852542194dc2309e56923ba42d1a818b.jpeg

Set the StaticMeshComponent with your CubeMesh sekected from the editor:
8a5beae09efc3accc723d24cc62bb71e672ab88b.jpeg

I hope this will help you!

I attempted to create a new blueprint based off of the C++ class, however, whenever I attempt to compile it with a static mesh, it crashes the UE4 editor. This also happens whenever I attempt to compile.

Which Engine version are you using? I have compiled and tested it in 4.16.1 binary.

4.15.2 I believe. Hopefully 1.16.1 won’t break it - will update tomorrow

I do not see a reason why it shouldn’t compile in 4.15

Anyway, I have uploaded the project for you, so you can test it:
https://ufile.io/449em

I ran my version on the 4.16.1 and it crashed when I attempted to compile the blueprint after assigning a static mesh :frowning:

Okay, two questions then:

-Did you manage to make it work with the project I uploaded?

-Can you please post your relevant code again with the updates?