Download

Accessing 2D sprites as layers in code

Hi!

INTRODUCTION
I’ve been trying to create a 2D game in UE4 and I want to implement parallax there. For parallax, it is neccessary to access sprite layers and move those layers in the opposite direction to player’s movement. Layers that are further away, move slower, layers that are closer, move faster. I’ve already created some sprites I could use to create some kind of background (for testing purposes).
However, it seems very hard for me to implement parallax in my game, because all the sprites I’ve created are placed in UE4 editor and thus it’s hard to access those sprites in code.

THE WAYS I WOULD TRY TO SOLVE THIS PROBLEM
I’ve been thinking I should iterate over instances of sprite by using object iterator in code to find those objects and group them (or add them into array based on depth of the sprite), but where should I call a function, which does that? I’ve also wondered whether I could access object groups (that I’ve created earlier in UE4 editor) in code. If this would be possible, alot work could be spared. Maybe there are better ways to solve this problem, so feel free to make suggestions :).

Thank you in advance!

I’ve already done this when I was playing around.

  1. Create a brush that you can draw over your scene.
  2. Create a component you can attach to every actor that you want to be parallax
  3. Store the spawn location of your actor (mine is in StartLocation of my ACharacterClass)

With those 3 things done, add this to the brush code



// In your brush.cpp

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

	AdatumCharacter* player = Cast<AdatumCharacter>(GetWorld()->GetFirstPlayerController()->GetPawn());

	// Dont do this if we cant get a pawn to work against
	if (player == nullptr )
		return;

	FVector PlayerVelocity = player->GetVelocity();
	FVector PlayerOffset = player->StartLocation - player->GetActorLocation();
	

		for (UDTMParallaxComponent* pComp : ParallaxActors)
		{
			FVector relLoc = pComp->GetOwner()->GetActorLocation();

			relLoc.X = pComp->StartLocation.X + (PlayerOffset.X * pComp->AttachedLayer.Speed.X);
			relLoc.Z = pComp->StartLocation.Z - (PlayerOffset.Z * pComp->AttachedLayer.Speed.Y);

			pComp->GetOwner()->SetActorLocation(relLoc);
		}


	

}

void ADTMDimensionVolume::OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor && (OtherActor != this) && OtherComp)
	{		
		UDTMParallaxComponent* dtmComp = Cast<UDTMParallaxComponent>(OtherComp);

		if (dtmComp != NULL)
		{
			// Add me to the parallax list
			AddParallaxActor(dtmComp);
			return;
		}

	}
}

void ADTMDimensionVolume::AddParallaxActor(UDTMParallaxComponent* ParallaxComponent)
{
	// Add the actor to the overall list
	ParallaxActors.AddUnique(ParallaxComponent);
	ParallaxActors.Sort(ADTMDimensionVolume::ParallaxSorter);

	// Store the actor in the correct layer
	if (ParallaxLayers.IsValidIndex(ParallaxComponent->ParallaxLayer))
	{
		ParallaxComponent->AttachedLayer = ParallaxLayers[ParallaxComponent->ParallaxLayer];
	}
}

bool ADTMDimensionVolume::ParallaxSorter(const UDTMParallaxComponent& ip1, const UDTMParallaxComponent& ip2)
{
	return (ip1.ParallaxLayer > ip2.ParallaxLayer);
}





// relevant info for the brush.h file

USTRUCT()
struct FParallaxLayer {

	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere)
	FVector2D Speed;

};

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Parallax)
	TArray<FParallaxLayer> ParallaxLayers;





// component.cpp
// Called when the game starts
void UDTMParallaxComponent::BeginPlay()
{
	Super::BeginPlay();

	StartLocation = GetOwner()->GetActorLocation();

	TArray<AActor*> ItemList;
	GetOwner()->GetOverlappingActors(ItemList, ADTMDimensionVolume::StaticClass());

	for (auto item : ItemList)
	{
		ADTMDimensionVolume* vol = Cast<ADTMDimensionVolume>(item);
		vol->AddParallaxActor(this);
	}

//	ADTMWorldSettings* WorldSettings = Cast<ADTMWorldSettings>(GetWorld()->GetWorldSettings());
//	WorldSettings->GetParallaxManager()->AddParallaxActor(this);
}




// relevant bits for the component.h

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Parallax)
	int32 ParallaxLayer;

	UPROPERTY(VisibleAnywhere)
	FParallaxLayer AttachedLayer;

	virtual void DestroyComponent(bool bPromoteChildren) override;

	FVector StartLocation;


Now when your game starts anything with a component inside the brush will register with the brush, and the brush will shift them all about based on which layer you put them in and what speed you gave the layer.

Obviously I’ve not given you all the code here, but it should be enough for you to figure out how to get it running. Your idea was already on the right track, I’ve just shown you how to efficiently iterate over only the actors you are interested in making parallax.

Feel free to ask more, or even access to the source. I’m not precious about it as I’ve already discarded it :slight_smile:

Hey!

I’m sorry. My answer to your reply is extremely late, but I had a rough schoolyear and had basically no time to focus on learning UE4. I would like to know few things looking at your code.
The brush you created, inherits some Volume class, right?
If I get the idea right - there will be some volume in the scene that has several parallaxComponents in it (component holds information about the layer - how fast it should move).
Apparently I don’t feel myself home in UE4 yet. What exactly did you do to attach the component to actors you wanted to use in parallax? I can’ t find a way how to delete the default inherited PaperSpriteComponent and replace it with the parallaxComponent.

I think I got the idea, but it’s still a bit too fuzzy, if I don’ t bother you too much, would you please try to explain the idea behind this parallax effect once more?

Thanks for helping me and also sorry for replying that late…

Yep. It directly inherits from AVolume.

No, but to understand that part of the code you’d need to see more of how the volume works. The volume has an array of FParallaxLayer exposed to the editor so that the user can create as many parallax layers as they want within the volume



USTRUCT()
struct FParallaxLayer {

	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere)
	FVector2D Speed;

};




	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Parallax)
	TArray<FParallaxLayer> ParallaxLayers;


As you can see its a simple array that just consists of the layers speed.

The component is to be added to an actor, so it doesn’t replace paper2D. You can add the component to any actor - 3d model, particle effect, sound object, etc. All it does is flag that the actor needs to be processed, and which layer its on.

So this

is more

Hope that makes sense. If you have a bitbucket account I can give you the entire code to figure it out if you want. It is slightly more confused by the fact I learned how to write a save game system and have multiple parallax levels in the same world, so you’ll see code about landmarks and things in there too, but its pretty clean and easy to read.