(UE4 4.20)UE4 繼承AnimNotify建立自定義動畫通知事件(結合PoseableMeshComponent實現技能殘影效果)
AnimNotify
在UE4的動畫系統中,動畫通知事件(AnimNotify),就是在一段動畫的某幀觸發的事件。
動畫通知事件分為:Native和Custom(除了一個用藍圖實現,一個用C++實現,沒感覺有多大區別)
Custom動畫通知事件:
通過編輯器直接新增的 "AnimNotify" 事件
Native動畫通知事件
其中我們系統已經存在的 “播放粒子特效PlayParticleEffect” , "播放聲音PalySound", “重置布料模擬Reset Clothing Simulation”都是Native動畫通知事件,如下所示
如果我們碰上一個需求,(用C++)得實現一種新的AnimNotify事件,怎麼做?很簡單,整合UAnumNotify原始碼就行了,看看下面
"PlayParticleEffect" 和“PalySound”的原始碼,都是繼承UAnumNotify
UCLASS(const, hidecategories=Object, collapsecategories, meta=(DisplayName="Play Particle Effect"))
class ENGINE_API UAnimNotify_PlayParticleEffect : public UAnimNotify
{
GENERATED_BODY()
UCLASS(const, hidecategories=Object, collapsecategories, meta=(DisplayName="Play Sound"))
class ENGINE_API UAnimNotify_PlaySound : public UAnimNotify
{
GENERATED_BODY()
像我們增加一個“TestAnimNotify”的動畫通知事件,像下面這樣
UCLASS()
class MYPROJECT3_API UTestAnimNotify : public UAnimNotify
{
GENERATED_BODY()
}
實現動畫通知事件,就是改寫類的Notify函式
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
下面是PlaySound的
void UAnimNotify_PlaySound::Notify(class USkeletalMeshComponent* MeshComp, class UAnimSequenceBase* Animation)
{
// Don't call super to avoid call back in to blueprints
if (Sound)
{
if (Sound->IsLooping())
{
UE_LOG(LogAudio, Warning, TEXT("PlaySound notify: Anim %s tried to spawn infinitely looping sound asset %s. Spawning suppressed."), *GetNameSafe(Animation), *GetNameSafe(Sound));
return;
}
if (bFollow)
{
UGameplayStatics::SpawnSoundAttached(Sound, MeshComp, AttachName, FVector(ForceInit), EAttachLocation::KeepRelativeOffset, false, VolumeMultiplier, PitchMultiplier);
}
else
{
UGameplayStatics::PlaySoundAtLocation(MeshComp->GetWorld(), Sound, MeshComp->GetComponentLocation(), VolumeMultiplier, PitchMultiplier);
}
}
}
其中 USkeletalMeshComponent* MeshComp 就是我們角色的SkeletalMesh
UPoseableMeshComponent和AnimNotify實現殘影
我們遊戲中某些移動性技能,在播放動畫(AnimNotify)的時候,會在某些“動畫幀”凍結姿勢,產生一個半溶解的Mesh, 也就是殘影效果。利用 UPoseableMeshComponent(複製骨骼網格姿勢的Mesh元件) 和 AnimNotify 實現殘影。
(1) AGhostTrail ---- UPoseableMeshComponent的介面 CopyPoseFromSkeletalComponent負責複製骨骼網格姿勢
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GhostTrail.generated.h"
class UPoseableMeshComponent;
class USkeletalMeshComponent;
UCLASS(Blueprintable)
class MYPROJECT3_API AGhostTrail : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AGhostTrail();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere)
UPoseableMeshComponent* poseableMeshComponent;
UPROPERTY(EditAnywhere, meta = (AllowedClasses = "Material,MaterialInstance"))
FSoftObjectPath GhostMaterialPath;
UPROPERTY(EditAnywhere, Category = "Time", meta = (UIMin = "0.0", UImax = "1.0"))
float DestroyTime;
FTimerHandle destroyTimerHandle;
private:
void ReplaceMaterial();
void DestroyActor();
public:
void MakeGhost(USkeletalMeshComponent* skeletalMeshComponent);
};
#include "GhostTrail.h"
#include "Components/PoseableMeshComponent.h"
#include "Materials/MaterialInterface.h"
#include "Engine/StreamableManager.h"
#include "TimerManager.h"
#include "Engine/SkeletalMesh.h"
// Sets default values
AGhostTrail::AGhostTrail()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
poseableMeshComponent = CreateDefaultSubobject<UPoseableMeshComponent>(TEXT("poseableMeshComponent"));
poseableMeshComponent->SetupAttachment(RootComponent);
}
// Called when the game starts or when spawned
void AGhostTrail::BeginPlay()
{
Super::BeginPlay();
}
void AGhostTrail::MakeGhost(USkeletalMeshComponent* skeletalMeshComponent)
{
poseableMeshComponent->SetSkeletalMesh(skeletalMeshComponent->SkeletalMesh);
poseableMeshComponent->CopyPoseFromSkeletalComponent(skeletalMeshComponent);
ReplaceMaterial();
}
void AGhostTrail::ReplaceMaterial()
{
FStreamableManager streamableManager;
UMaterialInterface* ghostMaterialInterface = streamableManager.LoadSynchronous<UMaterialInterface>(GhostMaterialPath);
if (nullptr == ghostMaterialInterface)
return;
for (int nMaterialIndex = 0; nMaterialIndex < poseableMeshComponent->GetNumMaterials(); ++nMaterialIndex)
{
poseableMeshComponent->SetMaterial(nMaterialIndex, ghostMaterialInterface);
}
GetWorldTimerManager().SetTimer(destroyTimerHandle, this, &ThisClass::DestroyActor, DestroyTime, false);
}
void AGhostTrail::DestroyActor()
{
GetWorldTimerManager().ClearTimer(destroyTimerHandle);
}
建立AGhostTrail藍圖:
(2)UTestAnimNotify -- 動畫通知事件,產生AGhostTrailActor
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "TestAnimNotify.generated.h"
UCLASS()
class MYPROJECT3_API UTestAnimNotify : public UAnimNotify
{
GENERATED_BODY()
public:
UTestAnimNotify();
protected:
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation);
};
#include "TestAnimNotify.h"
#include "GhostTrail.h"
#include "Engine//World.h"
#include "Components/SkeletalMeshComponent.h"
UTestAnimNotify::UTestAnimNotify()
{
}
void UTestAnimNotify::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
UWorld* pWorld = MeshComp->GetWorld();
const FString ghostTrailActorBpPath = "/Game/Geometry/GhostTrailActor.GhostTrailActor_C";
UClass* ghostTrailActorClass = LoadClass<AGhostTrail>(MeshComp->GetOuter(), *ghostTrailActorBpPath);
if (nullptr == ghostTrailActorClass)
return;
AGhostTrail* ghostTrailActor = pWorld->SpawnActor<AGhostTrail>(ghostTrailActorClass);
if (nullptr == ghostTrailActor)
return;
AActor* characterActor = MeshComp->GetOwner();
if (nullptr == characterActor)
return;
ghostTrailActor->SetActorLocation(characterActor->GetActorLocation());
ghostTrailActor->MakeGhost(MeshComp);
}
這裡比較注意的一個大坑是,雖然AnimNotify繼承於UObject, 但在在Notify函式中,直接通過 GetWorld() 獲取的UWorld為空物件,得通過MeshComp->GetWorld()來獲取。因為AnimNotify的不是執行時物件,而是類似資源的一種東西。執行時物件,才有所在“世界UWorld”這個說法。
在UE4編輯器裡編輯TestAnimNotify動畫通知事件,這裡是給 “Run”動作
相關文章
- (UE4 4.20)UE4 TimeLine(UTimelineComponent)
- ue4繫結動畫、重定向動畫動畫
- (UE4 4.20)UE4 UCLASS,UENUM, USTRUCT, UPROPERTY 的 常用配置Struct
- (UE4 4.20 )UE4的GC(垃圾回收)程式設計規範GC程式設計
- 如何在UE4中實現植物風場效果?
- (UE4 4.20)UE4 如何判斷一個點是否在導航網格(Navigation)內Navigation
- UE4 Dash功能實現
- 如何在 UE4 移動端中實現 HZB?
- Java 給PPT新增動畫效果(預設動畫/自定義動畫)Java動畫
- Vue結合原生js實現自定義元件自動生成VueJS元件
- 自定義實現MIUI的拖動視差效果(阻尼效果)UI
- Flutter 建立自定義路由過渡動畫Flutter路由動畫
- 帶你自定義實現Spring事件驅動模型Spring事件模型
- 【Android初級】如何實現一個有動畫效果的自定義下拉選單Android動畫
- 自定義TabBar動畫效果 - 頁面轉場(Swift)tabBar動畫Swift
- Android SeekBar 自定義thumb,thumb旋轉動畫效果Android動畫
- UE4的移動碰撞
- 繫結自定義事件事件
- 自定義事件實現子傳父事件
- UE4 Shader 編譯以及變種實現編譯
- js 建立和觸發事件 和 自定義事件JS事件
- prototype實現繼承繼承
- 實現JavaScript繼承JavaScript繼承
- UE4 地形編輯-建立地形Landscape/terrainAI
- Flutter視訊編輯軌道 | 自定義View實現UI互動效果 | 觸控事件處理FlutterViewUI事件
- Javascript繼承2:建立即繼承—-建構函式繼承JavaScript繼承函式
- 【android】自定義佈局控制控制元件的位置可以通過繼承FrameLayout實現Android控制元件繼承
- flink 透過繼承RichSinkFunction實現自定義sink,將資料錄入資料庫繼承Function資料庫
- UE4 ProjectileMovement Component 延遲啟動Project
- UE4純C++實現遊戲中快捷欄C++遊戲
- UE4委託
- 如何實現swipe、tap、longTap等自定義事件事件
- 繼承的實現方式繼承
- Javascript如何實現繼承JavaScript繼承
- 080 元件自定義事件-繫結元件事件
- Pytorch技法:繼承Subset類完成自定義資料拆分PyTorch繼承
- Flutter動畫實現粒子漂浮效果Flutter動畫
- 直播平臺搭建,自定義View實現loading動畫載入View動畫