This is the Blueprint implementation of the C++ code below.


This is the C++ implementation of the code.

// Copyright 2019-2020 Polargryph Ltd. All Rights Reserved.
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CharacterInteraction.generated.h"

USTRUCT(BlueprintType)
struct FFoliageStruct {
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintreadWrite, Category = "Struct")
		FTransform Transform;

	/**The Blueprint that contains the physics mesh*/
	UPROPERTY(EditAnywhere, BlueprintreadWrite, Category = "Struct")
		AActor* FoliageBP;

	/**The original foliage actor used for removing the physics and readding the original mesh*/
	UPROPERTY(EditAnywhere, BlueprintreadWrite, Category = "Struct")
		AActor* FoliageActor;

	UPROPERTY(EditAnywhere, BlueprintreadWrite, Category = "Struct")
		UInstancedStaticMeshComponent* InstancedMesh;
};

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SORIA_API UCharacterInteraction : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UCharacterInteraction();
	
	/**Sphere collision for detecting when the player is close enough to foliage to make it physics automically*/
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Collision, meta = (AllowPrivateAccess = "true"))
		class USphereComponent* SphereCollision;

	/*Box shape for the collision trace*/
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Collision, meta = (AllowPrivateAccess = "true"))
		class UBoxComponent* BoxCollision;

	/*Blueprint Reference of PhysicsFoliage class
	This is set in the class that this component is placed in
	Though in this case its BP_Foliage*/
	//TODO: Update this to not have to be set in Editor
	UPROPERTY(EditDefaultsOnly, Category = "ActorSpawning")
		TSubclassOf<AActor> PhysicsFoliage;
	
protected:
	// Called when the game starts
	virtual void BeginPlay() override;

	/**Struct to contain the Transform, BP Reference, Actor Reference and Instance mesh reference.*/
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = TraceVariables)
		FFoliageStruct FoliageStruct;

private:

	/**An aray of strcts to maintain the relavent infor for replacing foliage with a physics actor*/
	TArray<FFoliageStruct> FoliageStructArray;

	/**Function to do a box trace over a distace dictated by the players character and fill the FoliageStruct*/
	UFUNCTION(BlueprintCallable, Category = FoliageInteraction)
	void Gust();

	/**Set the box and sphere component to the owners matching components (if they exist)*/
	UFUNCTION(BlueprintCallable, Category = FoliageInteraction)
	void Initiate();

	/**Set the values within the Foliagestruct*/
	UFUNCTION(BlueprintCallable, Category = FoliageInteraction)
	void SetStruct(UStaticMesh* TargetMesh, FTransform SpawnTransform, AActor* FoliageActor, UInstancedStaticMeshComponent* InstancedMesh);

	UFUNCTION(BlueprintCallable, Category = FoliageInteraction)
	void SortThroughHits(TArray<FHitResult> OutHits);

	//Instanced Static Mesh Components Array in current Level.
	TArray<UActorComponent*> InstancedStaticMeshCompArray;

	//Each Instanced Static Mesh's Transform. Key: Instanced Static Mesh Component Name, Value:All Transforms in current Instanced Static Mesh Component
	TMap<FString, TArray<FTransform>> TransformMap;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

		
};
// Copyright 2019-2020 Polargryph Ltd., All Rights Reserved.
#include "CharacterInteraction.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "Components/SphereComponent.h"
#include "Components/ActorComponent.h"
#include "Components/StaticMeshComponent.h"
#include "EngineUtils.h"
#include "Foliage/Public/InstancedFoliageActor.h"
#include "Engine/World.h"
#include "PhysicsFoliage.h"
#include "Math/Vector.h"

// Sets default values for this component's properties
UCharacterInteraction::UCharacterInteraction()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void UCharacterInteraction::BeginPlay()
{
	Super::BeginPlay();

	Initiate();
	// ...
	
}

/**Get the parent/owner of this component and set the Box and Sphere to the fist inctance of them in the parent.
This is to allow for the component to be used on any actor as long as they have a sphere and box component attached.
It's also to control where the two components will be in world space for this class.*/
void UCharacterInteraction::Initiate()
{
	TArray<USphereComponent*> SComp;
	AActor* Parent = GetOwner();
	Parent->GetComponents<USphereComponent>(SComp);
	if (SComp[0])
	{
		SphereCollision = SComp[0];
	}
	TArray<UBoxComponent*> BComp;
	Parent->GetComponents<UBoxComponent>(BComp);
	if (BComp[0])
	{
		BoxCollision = BComp[0];
	}
}

/**Actiave when the player gusts, this is triggered within the character class*/
void UCharacterInteraction::Gust()
{
	//Creat an empty array
	TArray<FHitResult> OutHits;

	//Set the transform and end position for the trace
	FVector Start = BoxCollision->GetComponentLocation();
	FVector End = BoxCollision->GetComponentLocation() - FVector(0.0f, 0.0f, 50.0f);

	FQuat Orientation = BoxCollision->GetComponentQuat();

	FCollisionShape BoxColl = BoxCollision->GetCollisionShape();

	//Set the box size, this was done through trail and error
	BoxColl.SetBox(FVector(50.0f, 110.0f, 100.0f));

	//Set what the trace is looking for, in this case its a custom collision type of StaticFoliage, which translates into ObjectQuery12 beacause Unreal.
	TEnumAsByte<EObjectTypeQuery> ObjectToTrace = EObjectTypeQuery::ObjectTypeQuery12;
	TArray<TEnumAsByte<EObjectTypeQuery> > ObjectsToTraceAsByte;
	ObjectsToTraceAsByte.Add(ObjectToTrace);

	//Perfrom the box trace and add it to the array OutHits
	bool IsHit = GetWorld()->SweepMultiByObjectType(OutHits, Start, End, Orientation, FCollisionObjectQueryParams(ObjectsToTraceAsByte), BoxColl);
		
	//Only trigger if the trace returned a succesful hit
	if (IsHit)
	{
		SortThroughHits(OutHits);
	}
}

/**Set the struct to store the relevant information needed to replace the physics actor once no longer needed */
void UCharacterInteraction::SetStruct(UStaticMesh* TargetMesh, FTransform SpawnTransform, AActor* FoliageActor, UInstancedStaticMeshComponent* InstancedMesh)
{
	//Check is the PhysicsFoliage Actor is set in the parent BP.
	if (PhysicsFoliage)
	{
		//Spawn he phyics actor
		FActorSpawnParameters SpawnParams;
		AActor* SpawnedActorRef = GetWorld()->SpawnActor<AActor>(PhysicsFoliage, SpawnTransform);

		//Create and actor variable to temporarliy store
		APhysicsFoliage* ActorRef = Cast<APhysicsFoliage>(SpawnedActorRef);

		//Set all the values in the struct
		FoliageStruct.Transform = SpawnTransform;
		FoliageStruct.FoliageBP = ActorRef;
		FoliageStruct.FoliageActor = FoliageActor;
		FoliageStruct.InstancedMesh = InstancedMesh;

		FoliageStructArray.Add(FoliageStruct);

		//Set the mesh of the physics foliage
		ActorRef->SetAssetData(TargetMesh);
	}

	//SetAssetData(PhysicsFoliage.Get());
}

void UCharacterInteraction::SortThroughHits(TArray<FHitResult> OutHits)
{		
	//Run through the trace hits
	for (int Hit = 0; Hit < OutHits.Num(); Hit++)
	{
		//Cast to the foliage instance
		if (UInstancedStaticMeshComponent* InstancedComp = Cast<UInstancedStaticMeshComponent>(OutHits[Hit].Component))
		{
			//Get the various Variables and call SetStruct() for each Trace Hit
			UStaticMesh* Mesh = InstancedComp->GetStaticMesh();

			TArray<UMaterialInterface*> Materials = InstancedComp->GetMaterials();

			FTransform SpawnTransform;

			InstancedComp->GetInstanceTransform(OutHits[Hit].Item, SpawnTransform, true);

			SetStruct(Mesh, SpawnTransform, OutHits[Hit].GetActor(), InstancedComp);

			/**TODO: test to see if toggling visibility will improve performance as would save on collection*/
			InstancedComp->RemoveInstance(OutHits[Hit].Item);
			//InstancedComp->SelectInstance(false,Hit.Item,1)->SetVisibility(false, false);
		}
	}
}

// Called every frame
void UCharacterInteraction::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	/**TODO: could improve this to only trigger when character is moving, could also move this from a trace to an overlap*/
	TArray<FHitResult> OutHits;

	//Set the transform and end position for the trace
	FVector Start = SphereCollision->GetComponentLocation();
	FVector End = SphereCollision->GetComponentLocation() - FVector(0.0f, 0.0f, 50.0f);

	FQuat Orientation = SphereCollision->GetComponentQuat();

	FCollisionShape SphColl = SphereCollision->GetCollisionShape();

	//Set the box size, this was done through trail and error and also set through the size of the sphere on the player character
	SphColl.SetSphere(SphColl.GetSphereRadius());

	TEnumAsByte<EObjectTypeQuery> ObjectToTrace = EObjectTypeQuery::ObjectTypeQuery12;
	TArray<TEnumAsByte<EObjectTypeQuery> > ObjectsToTraceAsByte;
	ObjectsToTraceAsByte.Add(ObjectToTrace);

	//Perfrom the box trace and add it to the array OutHits
	bool IsHit = GetWorld()->SweepMultiByObjectType(OutHits, Start, End, Orientation, FCollisionObjectQueryParams(ObjectsToTraceAsByte), SphColl);

	//Only trigger if the trace returned a succesful hit
	if (IsHit)
	{
		SortThroughHits(OutHits);
	}

	//Trigger only if a struct exists
	if (FoliageStructArray.Num() > 0)
	{
		for (int i = 0 ; i < FoliageStructArray.Num(); i++)
		{
			//Find the distance from the player to the foliage
			FVector Length = FoliageStructArray[i].Transform.GetLocation() - SphereCollision->GetComponentLocation();

			//If the foliage is too far then remove the physics foliage and replace with original foliage 
			if (Length.Size() > 250.0f)
			{
				//Match the correct BP_Foliage actor with the correct InstancedFoliageInstance to correctly respawn the original foliage.
				TArray<UInstancedStaticMeshComponent*>FoliageComponents;
				FoliageStructArray[i].FoliageActor->GetComponents<UInstancedStaticMeshComponent>(FoliageComponents);

				int32 FoundItem = FoliageComponents.Find(FoliageStructArray[i].InstancedMesh);

				UInstancedStaticMeshComponent* InstanceToAdd = FoliageComponents[FoundItem];

				InstanceToAdd->AddInstanceWorldSpace(FoliageStructArray[i].Transform);
				//FoliageStructArray[i].InstancedMesh->SetVisibility(true, true);

				FoliageStructArray[i].FoliageBP->Destroy();

				FoliageStructArray.RemoveAt(i);
			}
		}
	}
}