티스토리 뷰

언리얼엔진4를 이용한 1인 RPG게임 개발중 대쉬 스킬을 표현하기 위해 구현한 고스트 트레일 입니다.


트레일에 상용될 메터리얼과 그 메터리얼에 값 변경 파라미터 이름, 투명도 커브 데이터를 넣어 주면 초기 설정은 끝이 됩니다.


실제 사용은 애니메이션 노티파이로 이벤트를 받아서 고스트 트레일 액터를 생성하는 방식으로 처리 하였습니다.


투명도 커브의 시간이 끝이되면 자동으로 Destroy 되는데요 Actor의 생성과 파괴가 부담스럽다면 Object pool 을 만들어 관리하면 될 듯 합니다.






<GhostTrail.h>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#pragma once
 
 
 
#include "CoreMinimal.h"
 
#include "GameFramework/Actor.h"
 
#include "GameFramework/Character.h"
 
#include "Components/TimelineComponent.h"
 
#include "GhostTrail.generated.h"
 
 
 
class UPoseableMeshComponent;
 
class UMaterialInterface;
 
class UMaterialInstanceDynamic;
 
 
 
UCLASS()
 
class PROJECTA_API AGhostTrail : public AActor
 
{
 
    GENERATED_BODY()
 
    
 
public :
 
    /** Ghost trail material  */
 
    UPROPERTY(EditDefaultsOnly, Category = "Material")
 
    UMaterialInterface* GhostTrailMaterial;
 
 
 
    /** Ghost trail fade opacity parameter name  */
 
    UPROPERTY(EditDefaultsOnly, Category = "Opacity")
 
    FName ScalarParameterName = FName(TEXT("Opacity"));
 
 
 
    /** Ghost trail mesh opacity curve  */
 
    UPROPERTY(EditDefaultsOnly, Category = "Opacity")
 
    UCurveFloat* OpacityCurve;
 
 
 
    /** Ghost trail pose character  */
 
    UPROPERTY(BlueprintReadWrite, Category = "Character", meta = (ExposeOnSpawn = "true"))
 
    ACharacter* CharacterRef;
 
 
 
    /** BeginPlay starts automatically  */
 
    UPROPERTY(EditDefaultsOnly, Category = "Trail Activation")
 
    bool bAutoActive;
 
 
 
protected:
 
    /** Ghost trail mesh  */
 
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Mesh")
 
    UPoseableMeshComponent* PoseableMeshComp;
 
 
 
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Default")
 
    USceneComponent* SceneRootComp;
 
 
 
    /** Ghost trail mesh opacity timeline  */
 
    UPROPERTY()
 
    FTimeline OpacityTimeline;
 
 
 
    /** Ghost trail dynamic material  */
 
    UPROPERTY()
 
    UMaterialInstanceDynamic* DynamicMatInstance;
 
 
 
public:    
 
    AGhostTrail();
 
 
 
protected:
 
    virtual void BeginPlay() override;
 
 
 
public:
 
    virtual void Tick(float DeltaTime) override;
 
 
 
    UFUNCTION(BlueprintCallable, Category = "GhostTrail")
 
    void BeginGhostTrailEffect();
 
 
 
protected:
 
    
 
    UFUNCTION()
 
    void HandleTimelineUpdate(float Value);
 
    UFUNCTION()
 
    void HandleTimelineFinished();
 
};
 
 
cs



<GhostTrail.cpp>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include "GhostTrail.h"
 
#include "Components/PoseableMeshComponent.h"
 
#include "Components/SkeletalMeshComponent.h"
 
#include "Materials/MaterialInstanceDynamic.h"
 
 
 
AGhostTrail::AGhostTrail()
 
{
 
    PrimaryActorTick.bCanEverTick = true;
 
    bAutoActive = true;
 
 
 
    SceneRootComp = CreateDefaultSubobject<USceneComponent>(TEXT("RootComp"));
 
    RootComponent = SceneRootComp;
 
 
 
    PoseableMeshComp = CreateDefaultSubobject<UPoseableMeshComponent>(TEXT("PoseableMeshComp"));
 
    PoseableMeshComp->SetupAttachment(RootComponent);
 
}
 
 
 
void AGhostTrail::BeginPlay()
 
{
 
    Super::BeginPlay();
 
 
 
    if(bAutoActive)
 
        BeginGhostTrailEffect();
 
}
 
 
 
void AGhostTrail::Tick(float DeltaTime)
 
{
 
    Super::Tick(DeltaTime);
 
    OpacityTimeline.TickTimeline(DeltaTime);
 
}
 
 
 
void AGhostTrail::BeginGhostTrailEffect()
 
{
 
    if (OpacityCurve && CharacterRef && GhostTrailMaterial)
 
    {
 
        PoseableMeshComp->SetSkeletalMesh(CharacterRef->GetMesh()->SkeletalMesh);
 
        
 
        PoseableMeshComp->CopyPoseFromSkeletalComponent(CharacterRef->GetMesh());
 
 
 
        DynamicMatInstance = UMaterialInstanceDynamic::Create(GhostTrailMaterial, this);
 
 
 
        TArray <FSkeletalMaterial> materials = CharacterRef->GetMesh()->SkeletalMesh->Materials;
 
 
 
        for (int32 Index = 0; Index != materials.Num(); ++Index)
 
        {
 
            PoseableMeshComp->SetMaterial(Index, DynamicMatInstance);
 
        }
 
 
 
        FOnTimelineFloat TimelineUpdateCallback;
 
        FOnTimelineEventStatic TimelineFinishedCallback;
 
 
 
        OpacityTimeline.SetLooping(false);
 
 
 
        TimelineUpdateCallback.BindUFunction(this, FName{ TEXT("HandleTimelineUpdate" }));
 
        TimelineFinishedCallback.BindUFunction(this, FName{ TEXT("HandleTimelineFinished") } );
 
 
 
        OpacityTimeline.AddInterpFloat(OpacityCurve, TimelineUpdateCallback);
 
        OpacityTimeline.SetTimelineFinishedFunc(TimelineFinishedCallback);
 
 
 
        OpacityTimeline.PlayFromStart();
 
    }
 
}
 
 
 
void AGhostTrail::HandleTimelineUpdate(float Value)
 
{
 
    DynamicMatInstance->SetScalarParameterValue(ScalarParameterName, Value);
 
}
 
 
 
void AGhostTrail::HandleTimelineFinished()
 
{
 
    Destroy();
 
}
cs


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함