巨坑无比,东西比较杂,凑合看吧
这是我的目录结构,分为俩模块,一个伴随打包,一个只在编辑器阶段存在
"Modules": [
{
"Name": "AnimCsv",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
},
{
"Name": "AnimCsvEditor",
"Type": "UncookedOnly",
"LoadingPhase": "PreDefault"
}
]
注意看我模块的Type,不写UncookedOnly而是Editor会报错的
AnimCsvEditor模块需要自己建
1、新建文件夹AnimCsvEditor,以及里面的Private和Public子文件夹
2、从别的地方复制Build.cs改个名字叫XXXEditor.Build.cs
3、看代码
// Copyright 2021 - Michal Smoleň
using UnrealBuildTool;
public class AnimCsvEditor : ModuleRules
{
public AnimCsvEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
// ... add other public dependencies that you statically link with here ...
"AnimCsv"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Slate",
"SlateCore",
"Engine",
"InputCore",
"UnrealEd",
"AnimGraph",
"BlueprintGraph",
"AnimGraphRuntime",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
该有的模块一定要有,尤其是AnimGraph、BlueprintGraph、AnimGraphRuntime这些
记得改类名,因为你是复制的文件嘛,改成你的模块名
(xxx未找到符号什么的就是因为缺少模块)
4、新建这俩文件,命名也是跟模块名一样
里面的内容可以参考别的模块,类名字一定要改对
尤其是上图最后一行白色的,忘记改的话非常蛋疼!!!
总值名字千万不能错
5、 在引擎里给这编辑器模块加个类,继承自UAnimGraphNode_Base
(这是节点基类,不要继承他的子类,引擎不让你继承)
(这个类只是显示用的,实际的逻辑不在这里)
注意看class旁边的ANIMCSVEDITOR_API,有这种什么什么API的类才让你继承,没有的说明是引擎内部类,你在别的模块无法继承
6、看代码,这是头文件,你的应该会自己生成
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "AnimGraphNode_Base.h"
//#include "AnimGraphNode_ModifyCurve.h"
#include "EdGraph/EdGraphNode.h"
//#include "AnimNodes/AnimNode_ModifyCurve.h"
#include "AnimNode_Demo.h"
#include "AnimGraphNode_Demo.generated.h"
/**
*
*/
UCLASS()
class ANIMCSVEDITOR_API UAnimGraphNode_Demo : public UAnimGraphNode_Base
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FMotionMatchingStruct Node;
public:
UAnimGraphNode_Demo();
// UEdGraphNode interface
virtual FText GetTooltipText() const override;
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
// End of UEdGraphNode interface
// UAnimGraphNode_Base interface
virtual FString GetNodeCategory() const override;
};
FMotionMatchingStruct这个属性是我自己建的,先不用管
就记住这玩意才是真正运行逻辑的地方,以上这部分代码只是用来显示用的
7、看代码,这个是cpp,上面的头文件实现部分
// Fill out your copyright notice in the Description page of Project Settings.
#include "AnimGraphNode_Demo.h"
#define LOCTEXT_NAMESPACE "AnimGraphNode_Demo"
UAnimGraphNode_Demo::UAnimGraphNode_Demo()
{
}
FString UAnimGraphNode_Demo::GetNodeCategory() const
{
return TEXT("Skeletal Control Nodes");
}
FText UAnimGraphNode_Demo::GetTooltipText() const
{
return GetNodeTitle(ENodeTitleType::ListView);
}
FText UAnimGraphNode_Demo::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("AnimGraphNode_ModifyCurve_Title", "Modify Curve");
}
#undef LOCTEXT_NAMESPACE
这里没啥好主意的,名字别搞错就行了。这样我们就创建好了XXXEditor模块。
下面才是节点的逻辑部分,也就是我们的AnimCsv模块,而不是AnimCsvEditor模块
Build.cs和之前的差不多
然后这里说一下重点!!!!
你的FMotionMatchingStruct也就是你的节点逻辑类,无法从引擎创建,所以你得自己添加头文件和cpp文件,并自己继承
这个时候你可以选择FAnimNode_ModifyCurve(曲线节点)或其他类或基类都可以
先给大家看一下节点逻辑代码
#pragma once
#include "CoreMinimal.h"
#include "LoadCsvFile.h"
#include "AnimNodes/AnimNode_ModifyCurve.h"
#include "Math/UnrealMathUtility.h"
#include "AnimNode_Demo.generated.h"
USTRUCT(BlueprintType)
struct ANIMCSV_API FMotionMatchingStruct : public FAnimNode_ModifyCurve
{
GENERATED_BODY()
public:
FMotionMatchingStruct();
//面捕数据
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ModifyCurve, meta = (PinShownByDefault))
FMyStructs FaceData;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ModifyCurve, meta = (PinShownByDefault))
FMyDeformerNameStructs Names;
//播放动画的进度
int PlayValue = 0;
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
};
可以看到,你需要继承实现几个虚函数,Initialize_AnyThread或Update_AnyThread,当然你不需要也可以不继承,这里我是需要的,初始化和tick更新
然后就是实现部分,看代码。这里我省略了我读取CSV文件的逻辑,因为这不是本章的重点。
你肯定也要自己写自己的逻辑来弄动画节点吧
#include "AnimNode_Demo.h"
FMotionMatchingStruct::FMotionMatchingStruct()
{
}
void FMotionMatchingStruct::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
Super::Initialize_AnyThread(Context);
PlayValue = 0;
for (FName val : Names.name)
{
CurveValues.Add(0.0);
CurveNames.Add(val);
UE_LOG(LogTemp, Warning, TEXT("FMotionMatchingStruct CurveNames:%s"), *val.ToString());
}
UE_LOG(LogTemp, Warning, TEXT("FMotionMatchingStruct CurveNames length:%d"), Names.name.Num());
}
void FMotionMatchingStruct::Update_AnyThread(const FAnimationUpdateContext& Context)
{
Super::Update_AnyThread(Context);
//取出播放一帧数据
if(FaceData.test.Num() > 0)
{
const int FaceDataNum = FaceData.test.Num();
if(FaceDataNum > PlayValue)
{
//UE_LOG(LogTemp, Warning, TEXT("FMotionMatchingStruct FaceDataNum length:%d , PlayValue :%d"), FaceDataNum,PlayValue);
TArray<FString> facedata_strings = FaceData.test[PlayValue].test;//获取一帧的数据
for(int i=0; i<CurveNames.Num(); i++)
{
const int num = Names.data_num[i];//找出这个形态键对应着数据的第几个索引
if(facedata_strings.Num() > num)
{
UE_LOG(LogTemp, Warning, TEXT("FMotionMatchingStruct facedata_strings:%s"), *facedata_strings[num]);
CurveValues[i] = FMath::Clamp(FCString::Atof(*facedata_strings[num]),0.0f,1.0f);
}
}
}
else
{
PlayValue = 0;
}
PlayValue++;
}
}
有条件的顺着引擎看看源代码,可以学一下人家是咋写的
完整的代码我在码云开源了
Face UE5: ue5用的人脸面部捕捉,不需要使用苹果手机 支持VRoid 的vrm模型 (gitee.com)
视频我已经删了,因为找到了更好的面捕,就不需要这个之前写的了
不过可以学习一下怎么创建动画蓝图节点