How to create a new component?

Hello,

Here’s my problem: I have a Paper2D game with a pawn that has variable width (in chunks of 16 pixels wide). I have a 16x16 sprite that art-wise just tiles to whatever specific size I need.

While designing the pawn, I was just using a UPaperSpriteComponent inherited and initialized with CreateDefaultSubobject in the constructor; then scaling it to 48x16 from 16x16 to see how the gameplay logic worked. That much is working well!

But since Scale stretches the sprite out rather than tiling three of them over, I need to create more UPaperSpriteComponents. I can achieve this fairly easily in the editor but I’d like to code it so that whenever I drag and drop it “generates” these pawns with all of the correct preset settings.

I setup in the header

Pawn.h
UPROPERTY()
TArray<UPaperSpriteComponent*> SpriteArray;

and in the class

Pawn.cpp
SpriteArray.Add(CreateDefaultSubobject<UPaperSpriteComponent>(TEXT("Sprite")));
SpriteArray[0]->RegisterComponent();
SpriteArray[0]->AttachTo(RootComponent);

And besides losing the ability to edit the sprite component itself in the editor it worked fine. However when I go to add a second CreateDefaultSubobject call the editor crashes. (I had the intent of setting it to the next position at [1]) I looked around and saw people suggesting to use NewObject but I can’t really figure out how to use it even with the documentation and searching around here.

tl;dr - The editor lets me add as many PaperSpriteComponents as I want to an Instance and I can edit them with the editor, how the heck do I achieve that in C++!?

Thanks

Do you have a set amount of components you want to add, or at least a maximum? Then you can declare all those and add them into the array/pawn later with a function that calculates your width. If you try to define something in the constructor you didn’t declare you will get a crash.

You can create them also in a function with NewObject, but that doesn’t let you edit them in the editor.

I’m at a point where I still don’t know the maximum so I opted for the NewObject option. Not quite there yet but I’m on the right track.

.h
int32 pawnWidth; // Counter for how many sprites to spawn
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Sprites, meta = (AllowPrivateAccess = "true"))
	class UPaperSpriteComponent* firstSprite; // Set up the "anchor" sprite the old fashioned way to allow editor access mostly
UPROPERTY(EditAnywhere)
		TArray<class UPaperSpriteComponent*> SpriteComponent;

.cpp
pawnWidth = 4; // just a test value

	if (pawnWidth > 1) // Ignore this if we only want 1
	{
		for (int i = 0; i < (pawnWidth - 1); i++) // Skip the first sprite and start at #2
		{
			SpriteComponent.Add(NewObject<UPaperSpriteComponent>(this, TEXT("SpriteComponent" + i))); // Make sure to add the + i, if each one doesn't have a unique name they get merged
			// General sprite setup goes here
			SpriteComponent[i]->WeldTo(firstSprite); // Needed this to keep NewObject from spawning the sprite at the inverse value (opposite +/- sign of the parent on each axis)
			SpriteComponent[i]->SetRelativeLocation(FVector((16.0f * (i + 1)), 0.0f, 0.0f)); // Stack em up to the right
		}
	}

The big thing to note seems to be that NewObject doesn’t necessarily have to take any parameters, but it helps to set Outer (a reference to the parent Actor) and give it a unique name. Also WeldTo was a hiccup I wasn’t expecting.

Once I get this moved from the constructor and setup to work with instances instead of on a class level I’ll be done, so I’m going to mark this as solved. Thanks for your help! And hope this short write-up helps somebody having problems in the future