├── .gitignore ├── Resources └── Icon128.png ├── Source └── PIFS │ ├── Public │ ├── PIFS.h │ ├── Types │ │ └── PIFS_Types.h │ ├── Component │ │ └── FoliageInteractionComponent.h │ └── Actors │ │ └── PhysicalInteractiveFoliageActor.h │ ├── Private │ ├── Types │ │ └── PIFS_Types.cpp │ ├── PIFS.cpp │ ├── Component │ │ └── FoliageInteractionComponent.cpp │ └── Actors │ │ └── PhysicalInteractiveFoliageActor.cpp │ └── PIFS.Build.cs ├── Config └── FilterPlugin.ini ├── README.md └── PIFS.uplugin /.gitignore: -------------------------------------------------------------------------------- 1 | Binaries/ 2 | Intermediate/ 3 | build/ -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiarIbrahim/Physical-Interactive-Foliage-System/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/PIFS/Public/PIFS.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FPIFS : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Physical Interactive Foliage System UE5 & C++ 2 | A UE5 plugin for making intractive foliages in very simple steps. 3 | 4 | Watch Toturial on Youtube 5 | 6 | **for Blurprint Projects** : find release section in the right hand side of this page (Release version of this plugin) and download PIFS.zip and enjoy 7 | 8 | **Contents** 9 | 10 | **1- APhysicalInteractiveFoliageActor** 11 | 12 | **2- UFoliageInteractionComponent** 13 | -------------------------------------------------------------------------------- /Source/PIFS/Private/Types/PIFS_Types.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "Types/PIFS_Types.h" 5 | 6 | #include "components/InstancedStaticMeshComponent.h" 7 | 8 | FTransform FIntractFoliageRecord::GetFoliageTransform() const 9 | { 10 | if (!FoliageComponent) return FTransform(); 11 | 12 | FTransform transform; 13 | FoliageComponent->GetInstanceTransform(FoliageIndex, transform, true); 14 | return transform; 15 | 16 | } -------------------------------------------------------------------------------- /PIFS.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Physical Interactive Foliage System", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "Diar Ibrahim", 9 | "CreatedByURL": "https://twitter.com/diar__Ibrahim", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "PIFS", 20 | "Type": "Runtime", 21 | "LoadingPhase": "PreDefault" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /Source/PIFS/Private/PIFS.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "PIFS.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FPIFS" 6 | 7 | void FPIFS::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FPIFS::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FPIFS, PhysicalInteractiveFoliageSystem) -------------------------------------------------------------------------------- /Source/PIFS/PIFS.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class PIFS : ModuleRules 6 | { 7 | public PIFS(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "CoreUObject", 38 | "Engine", 39 | "Slate", 40 | "SlateCore", 41 | // ... add private dependencies that you statically link with here ... 42 | } 43 | ); 44 | 45 | 46 | DynamicallyLoadedModuleNames.AddRange( 47 | new string[] 48 | { 49 | // ... add any modules that your module loads dynamically here ... 50 | } 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Source/PIFS/Public/Types/PIFS_Types.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/NoExportTypes.h" 7 | #include "PIFS_Types.generated.h" 8 | 9 | 10 | class APhysicalInteractiveFoliageActor; 11 | /* 12 | used to store foliage index and its component during the intraction detection 13 | */ 14 | USTRUCT(BlueprintType) 15 | struct FIntractFoliageRecord { 16 | GENERATED_BODY() 17 | 18 | FIntractFoliageRecord() {} 19 | 20 | FIntractFoliageRecord(class UInstancedStaticMeshComponent* _FoliageComponent, int32 _FoliageIndex) { 21 | 22 | if (_FoliageComponent == nullptr) { 23 | UE_LOG(LogTemp, Error, TEXT(" FoliageComponent is nullptr ! FIntractFoliageRecord::FIntractFoliageRecord()")); 24 | } 25 | 26 | FoliageComponent = _FoliageComponent; 27 | FoliageIndex = _FoliageIndex; 28 | } 29 | 30 | /* 31 | the component that its foliage overlapped (intracted) 32 | */ 33 | class UInstancedStaticMeshComponent* FoliageComponent; 34 | /* 35 | Index of the foliage in the instaced component 36 | */ 37 | int32 FoliageIndex; 38 | 39 | /* 40 | returns the transform of the foliage 41 | */ 42 | FTransform GetFoliageTransform()const; 43 | 44 | }; 45 | 46 | USTRUCT(BlueprintType) 47 | struct FIntractFoliageSet { 48 | GENERATED_BODY() 49 | 50 | FIntractFoliageSet() {} 51 | 52 | /* 53 | the BP class of the interactive foliage actor 54 | */ 55 | UPROPERTY(BlueprintType, EditAnywhere , Category = "Foliage Set") 56 | TSubclassOf foliageClass; 57 | 58 | /* 59 | the static mesh of the foliage that spawned in to the world 60 | */ 61 | UPROPERTY(BlueprintType, EditAnywhere, Category = "Foliage Set") 62 | UStaticMesh* foliageStaticMesh; 63 | 64 | }; 65 | 66 | 67 | UCLASS() 68 | class PIFS_API UPIFS_Types : public UObject 69 | { 70 | GENERATED_BODY() 71 | 72 | }; 73 | -------------------------------------------------------------------------------- /Source/PIFS/Public/Component/FoliageInteractionComponent.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Components/ActorComponent.h" 7 | #include "FoliageInteractionComponent.generated.h" 8 | 9 | 10 | UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) 11 | class PIFS_API UFoliageInteractionComponent : public UActorComponent 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | // Sets default values for this component's properties 17 | UFoliageInteractionComponent(); 18 | 19 | 20 | 21 | /* 22 | whether this component will Automatically activates surrounding foliages or not 23 | if set to false, using ActivateFoliagesInRange() is a must to make foliages Interactive 24 | */ 25 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Settings") 26 | bool bAutoActivateFoliages = true; 27 | 28 | /* 29 | if @bAutoActivateFoliages is true, how offen ActivateFoliagesInRange() should be called to activate surrounding foliages 30 | the lower the acurate it is but it is also more performance heavy . 31 | ( bigger @AutoActivateCheckRadius required less @AutoActivateRate and vice versa) 32 | 33 | clamped to (min: 0.2 , max: 2.0) 34 | */ 35 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Settings") 36 | float AutoActivateRate = 0.5; 37 | 38 | /* 39 | if @bAutoActivateFoliages is true, what should be the trace range (how far the foliage must be in order to activate it ? ) 40 | ( bigger @AutoActivateRate required less @AutoActivateCheckRadius and vice versa) 41 | */ 42 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Settings") 43 | float AutoActivateCheckRadius = 150.0; 44 | 45 | /* 46 | list of all interactive foliages 47 | */ 48 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Interactive Foliage Actors") 49 | TArray FoliageActorsSet; 50 | 51 | 52 | 53 | protected: 54 | // Called when the game starts 55 | virtual void BeginPlay() override; 56 | 57 | public: 58 | // Called every frame 59 | virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; 60 | 61 | /* 62 | check for any foliage static mesh around the actor 63 | and replaces them with proper Interactive foliage actor if provided 64 | */ 65 | UFUNCTION(BlueprintCallable, Category = "Interactive Foliage") 66 | void ActivateFoliagesInRange(float ActivationRadius = 30, bool bDebug = false); 67 | 68 | /* 69 | check if any blueprint provided for @record in @UFoliageInteractionComponent::FoliageActorsSet 70 | */ 71 | void ReplaceFoliageStaticMeshWithInteractiveFoliageActor(struct FIntractFoliageRecord record); 72 | }; 73 | -------------------------------------------------------------------------------- /Source/PIFS/Private/Component/FoliageInteractionComponent.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "Component/FoliageInteractionComponent.h" 5 | #include "Components/InstancedStaticMeshComponent.h" 6 | #include "Actors/PhysicalInteractiveFoliageActor.h" 7 | #include "Types/PIFS_Types.h" 8 | #include "Engine/World.h" 9 | #include "Kismet/kismetSystemLibrary.h" 10 | 11 | 12 | // Sets default values for this component's properties 13 | UFoliageInteractionComponent::UFoliageInteractionComponent() 14 | { 15 | // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features 16 | // off to improve performance if you don't need them. 17 | PrimaryComponentTick.bCanEverTick = true; 18 | 19 | // ... 20 | } 21 | 22 | 23 | float AutoActivateCounter = 0; 24 | // Called when the game starts 25 | void UFoliageInteractionComponent::BeginPlay() 26 | { 27 | Super::BeginPlay(); 28 | AutoActivateRate = FMath::Clamp(AutoActivateRate , 0.2 , 2.0); 29 | 30 | } 31 | 32 | 33 | // Called every frame 34 | void UFoliageInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) 35 | { 36 | Super::TickComponent(DeltaTime, TickType, ThisTickFunction); 37 | 38 | 39 | if (bAutoActivateFoliages && AutoActivateCounter <= 0) { 40 | ActivateFoliagesInRange(AutoActivateCheckRadius,0); 41 | AutoActivateCounter = AutoActivateRate; 42 | } 43 | else if (bAutoActivateFoliages) { 44 | AutoActivateCounter -= DeltaTime; 45 | } 46 | 47 | } 48 | 49 | void UFoliageInteractionComponent::ActivateFoliagesInRange(float ActivationRadius, bool bDebug) 50 | { 51 | if (bDebug) { 52 | DrawDebugSphere(GetWorld(), GetOwner()->GetActorLocation(), ActivationRadius, 20, FColor::Red, 0, 2); 53 | } 54 | 55 | 56 | int32 counter = 0; 57 | TArray hits; 58 | 59 | TArray indexes; 60 | 61 | TArray IntractRecords; 62 | 63 | if (UKismetSystemLibrary::SphereTraceMulti(this, GetOwner()->GetActorLocation(), GetOwner()->GetActorLocation(), ActivationRadius, ETraceTypeQuery::TraceTypeQuery2, 0, {}, EDrawDebugTrace::None, hits, 1)) { 64 | 65 | for (const FHitResult h : hits) { 66 | if (UInstancedStaticMeshComponent* instComp = Cast(h.GetComponent())) { 67 | 68 | // track current peek of the array 69 | int32 PeekIndex = indexes.Num() - 1; 70 | // add unique index and track the index 71 | int32 uniqueIndex = indexes.AddUnique(h.Item); 72 | // check if it was a unique index ! 73 | if (uniqueIndex > PeekIndex) { 74 | // store the index of the foliage with InstancedStaticMeshComponent in a record 75 | FIntractFoliageRecord record = FIntractFoliageRecord(instComp, h.Item); 76 | IntractRecords.Add(record); 77 | } 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | for (FIntractFoliageRecord& r : IntractRecords) { 85 | if (!r.FoliageComponent) continue; 86 | 87 | // debug 88 | if (bDebug) { 89 | DrawDebugPoint(GetWorld(), r.GetFoliageTransform().GetLocation() + FVector(0, 0, 120), 12, FColor::Red, 0, 5); 90 | } 91 | 92 | // replace 93 | ReplaceFoliageStaticMeshWithInteractiveFoliageActor(r); 94 | 95 | 96 | } 97 | 98 | // debug 99 | if (bDebug) { 100 | UE_LOG(LogTemp, Warning, TEXT(" number of Unique foliages Intraction records %d"), IntractRecords.Num()); 101 | } 102 | 103 | 104 | } 105 | 106 | void UFoliageInteractionComponent::ReplaceFoliageStaticMeshWithInteractiveFoliageActor(FIntractFoliageRecord record) 107 | { 108 | if (FoliageActorsSet.IsEmpty()) return; 109 | for (FIntractFoliageSet foliage_actor : FoliageActorsSet) { 110 | if (foliage_actor.foliageStaticMesh == record.FoliageComponent->GetStaticMesh()) { 111 | 112 | if (APhysicalInteractiveFoliageActor* Spawned = GetWorld()->SpawnActor(foliage_actor.foliageClass, record.GetFoliageTransform())) { 113 | Spawned->InitSpawnedFoliage(record); 114 | 115 | // remove instance that we spawned this actor for 116 | if (record.FoliageComponent && record.FoliageComponent->IsValidInstance(record.FoliageIndex)) { 117 | record.FoliageComponent->RemoveInstance(record.FoliageIndex); 118 | } 119 | 120 | } 121 | else UE_LOG(LogTemp, Warning, TEXT("Failed to spawn foliage actor !")); 122 | 123 | 124 | return; 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /Source/PIFS/Public/Actors/PhysicalInteractiveFoliageActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "Components/SphereComponent.h" 8 | #include "Types/PIFS_Types.h" 9 | #include "PhysicalInteractiveFoliageActor.generated.h" 10 | 11 | UCLASS() 12 | class PIFS_API APhysicalInteractiveFoliageActor : public AActor 13 | { 14 | GENERATED_BODY() 15 | 16 | protected: 17 | // Sets default values for this actor's properties 18 | APhysicalInteractiveFoliageActor(); 19 | 20 | 21 | // Called when the game starts or when spawned 22 | virtual void BeginPlay() override; 23 | 24 | // Components 25 | 26 | 27 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Components") 28 | class USceneComponent* Root; 29 | 30 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Components") 31 | class USkeletalMeshComponent* Mesh; 32 | 33 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage | Components") 34 | class USphereComponent* Collision; 35 | 36 | 37 | //// Settings 38 | 39 | /* 40 | from this bone and below all bones will be set to simulate physics during the activation and vice versa for the Deactivation 41 | if left blank, the root bone will be selected ! 42 | */ 43 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage settings") 44 | FString CustomeRootBoneName; 45 | /* 46 | the time needed to blend out the physics simulation and deactivate the foliage. 47 | in seconds 48 | */ 49 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage settings") 50 | float DeactiveBlendOutTime = 4.0; 51 | /* 52 | the time to destroy the Interactive Foliage and replace it by instanced static mesh 53 | in seconds 54 | NOTE: if less than @DeactiveBlendOutTime , then DeactiveBlendOutTime will be used. 55 | */ 56 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Interactive Foliage settings") 57 | float ReplacementTime = 5.0; 58 | 59 | 60 | 61 | 62 | // variables 63 | 64 | /* 65 | is a time in seconds used to check how long this actor is spawned and never been intracted by an actor 66 | after this time OnSpawnTimeEnded() will be called 67 | */ 68 | float SpawnedTimeCounter = -1; 69 | // whethere @SpawnedTimeCounter is ended or not 70 | bool bSpawnTimeEnded = false; 71 | 72 | /* 73 | is a time that the this foliage actor been activated (not for spawn since actors may be spawned but never colide with player so we dont activate it right after spawn) 74 | after this time ended the foliage actor will be de activated 75 | 76 | */ 77 | float ActiveTimeConter = 0; 78 | /* 79 | whether the foliage actor is activated or deactivated 80 | */ 81 | bool bIsActivated = false; 82 | /* 83 | number of actors currently coliding with this foliage actor 84 | */ 85 | TArray actorsOverlapped; 86 | /* 87 | the data provided for this foliage actor 88 | contains : the instanced static mesh that this actor spawned to replace 89 | */ 90 | struct FIntractFoliageRecord FoliageRecord; 91 | 92 | /* 93 | manuly activates this actor and it will be physics interactiveS 94 | */ 95 | UFUNCTION(BlueprintCallable , Category = "Interactive Foliage") 96 | void ActivateFoliage(); 97 | /* 98 | manulay deactivates this foliage 99 | */ 100 | UFUNCTION(BlueprintCallable, Category = "Interactive Foliage") 101 | void DeactivateFoliage(); 102 | /* 103 | will be called when spawn time ends and player is not near this foliage 104 | so the actor will be destroy() and a new instance will be added as replace ment 105 | */ 106 | void OnSpawnTimeEnded(); 107 | 108 | // called if @collision overlaps 109 | UFUNCTION() 110 | void OnOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); 111 | 112 | // called if @collision overlap ended 113 | 114 | UFUNCTION() 115 | void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex); 116 | 117 | 118 | /* 119 | this function is called when this foliage activated 120 | NOTE: for foliages with wide radius collision will be bad choise to used this function for things like sound (not accurate) 121 | */ 122 | UFUNCTION(BlueprintImplementableEvent, Category = "Interactive Foliage") 123 | void OnFoliageActivated(); 124 | 125 | /* 126 | this function is called when this foliage deactivated 127 | */ 128 | UFUNCTION(BlueprintImplementableEvent, Category = "Interactive Foliage") 129 | void OnFoliageDeactivated(); 130 | 131 | 132 | public: 133 | 134 | /* 135 | called after this actor spawned 136 | used to init some data like @FoliageRecord , @SpawnedTimeCounter 137 | */ 138 | void InitSpawnedFoliage(struct FIntractFoliageRecord record); 139 | 140 | 141 | 142 | // Called every frame 143 | 144 | virtual void Tick(float DeltaTime) override; 145 | 146 | }; 147 | -------------------------------------------------------------------------------- /Source/PIFS/Private/Actors/PhysicalInteractiveFoliageActor.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "Actors/PhysicalInteractiveFoliageActor.h" 4 | #include "Components/InstancedStaticMeshComponent.h" 5 | #include "Component/FoliageInteractionComponent.h" 6 | #include "Components/SkeletalMeshComponent.h" 7 | 8 | 9 | 10 | // Sets default values 11 | APhysicalInteractiveFoliageActor::APhysicalInteractiveFoliageActor() 12 | { 13 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 14 | PrimaryActorTick.bCanEverTick = true; 15 | 16 | 17 | if (!Root) { 18 | Root = CreateDefaultSubobject(FName("Root component")); 19 | SetRootComponent(Root); 20 | 21 | } 22 | 23 | if (!Mesh) { 24 | Mesh = CreateDefaultSubobject(FName("Mesh component")); 25 | Mesh->SetSimulatePhysics(false); 26 | Mesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly); 27 | Mesh->SetCollisionResponseToAllChannels(ECR_Block); 28 | Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore); 29 | Mesh->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); 30 | Mesh->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Ignore); 31 | Mesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore); 32 | Mesh->SetupAttachment(Root); 33 | } 34 | 35 | if (!Collision) { 36 | Collision = CreateDefaultSubobject(FName("Sphere Component")); 37 | Collision->SetCollisionEnabled(ECollisionEnabled::QueryOnly); 38 | Collision->SetRelativeLocation(FVector(0, 0, 50)); 39 | Collision->SetSphereRadius(100); 40 | Collision->SetupAttachment(Root); 41 | } 42 | 43 | } 44 | 45 | // Called when the game starts or when spawned 46 | void APhysicalInteractiveFoliageActor::BeginPlay() 47 | { 48 | Super::BeginPlay(); 49 | 50 | 51 | // set the bone name to root bone name if CustomeRootBoneName left blank 52 | if (CustomeRootBoneName.IsEmpty() && Mesh) { 53 | CustomeRootBoneName = Mesh->GetBoneName(0).ToString(); 54 | } 55 | 56 | if (ReplacementTime < DeactiveBlendOutTime) { 57 | ReplacementTime = DeactiveBlendOutTime; 58 | } 59 | 60 | if (Collision) { 61 | Collision->OnComponentBeginOverlap.AddDynamic(this, &APhysicalInteractiveFoliageActor::OnOverlap); 62 | Collision->OnComponentEndOverlap.AddDynamic(this, &APhysicalInteractiveFoliageActor::OnOverlapEnd); 63 | } 64 | } 65 | 66 | void APhysicalInteractiveFoliageActor::OnOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) 67 | { 68 | if (OtherActor->GetComponentByClass()) { 69 | actorsOverlapped.AddUnique(OtherActor); 70 | ActivateFoliage(); 71 | 72 | } 73 | } 74 | 75 | void APhysicalInteractiveFoliageActor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) 76 | { 77 | actorsOverlapped.Remove(OtherActor); 78 | } 79 | 80 | void APhysicalInteractiveFoliageActor::InitSpawnedFoliage(FIntractFoliageRecord record) 81 | { 82 | FoliageRecord = record; 83 | SpawnedTimeCounter = ReplacementTime; 84 | } 85 | 86 | 87 | void APhysicalInteractiveFoliageActor::ActivateFoliage() 88 | { 89 | Mesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly); 90 | Mesh->SetCollisionProfileName("Custom"); 91 | Mesh->SetCollisionObjectType(ECC_PhysicsBody); 92 | Mesh->SetCollisionResponseToAllChannels(ECR_Block); 93 | Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore); 94 | Mesh->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); 95 | Mesh->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Ignore); 96 | Mesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore); 97 | Mesh->SetAllBodiesBelowSimulatePhysics(FName(CustomeRootBoneName), true, 0); 98 | ActiveTimeConter = DeactiveBlendOutTime; 99 | bIsActivated = true; 100 | 101 | // call bp function 102 | OnFoliageActivated(); 103 | } 104 | 105 | void APhysicalInteractiveFoliageActor::DeactivateFoliage() 106 | { 107 | Mesh->SetAllBodiesBelowSimulatePhysics(FName(CustomeRootBoneName), false, 0); 108 | Mesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); 109 | bIsActivated = false; 110 | 111 | // call on bp 112 | OnFoliageDeactivated(); 113 | 114 | } 115 | 116 | 117 | void APhysicalInteractiveFoliageActor::OnSpawnTimeEnded() 118 | { 119 | 120 | if (FoliageRecord.FoliageComponent) { 121 | 122 | // add instance to the component ! 123 | FoliageRecord.FoliageComponent->AddInstance(GetActorTransform(), 1); 124 | 125 | 126 | // destroy this 127 | Destroy(); 128 | 129 | } 130 | 131 | 132 | 133 | } 134 | 135 | 136 | 137 | // Called every frame 138 | void APhysicalInteractiveFoliageActor::Tick(float DeltaTime) 139 | { 140 | Super::Tick(DeltaTime); 141 | 142 | if (ActiveTimeConter <= 0 && bIsActivated && actorsOverlapped.IsEmpty()) { 143 | // deactivate 144 | DeactivateFoliage(); 145 | } 146 | else if (bIsActivated && actorsOverlapped.IsEmpty()) { 147 | ActiveTimeConter -= DeltaTime; 148 | Mesh->SetAllBodiesBelowPhysicsBlendWeight(FName(CustomeRootBoneName), ActiveTimeConter / DeactiveBlendOutTime, 0, 0); 149 | } 150 | 151 | 152 | if (!bIsActivated && actorsOverlapped.IsEmpty() && SpawnedTimeCounter == 0.0) { 153 | if (!bSpawnTimeEnded) { 154 | bSpawnTimeEnded = true; 155 | OnSpawnTimeEnded(); 156 | } 157 | } 158 | else if (!bIsActivated && actorsOverlapped.IsEmpty()) { 159 | SpawnedTimeCounter -= DeltaTime; 160 | SpawnedTimeCounter = FMath::Clamp(SpawnedTimeCounter, 0.0, 15.0); 161 | } 162 | } 163 | 164 | --------------------------------------------------------------------------------