├── .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 |
--------------------------------------------------------------------------------