这个连招系统是在YTB上搬运过来的,也是自我接触UE以来,认为最好用,最高级的一个连招使用方式,
非常完美的利用了UE中强大的动画编辑器来实现连招,对于程序员来说轻松、省事;对于策划来说,
我可以随便编辑连招了,控制玩家游玩的连招难度,功能再进一步封装就可以商用了,Produce Ready




展示的3个连招全部都带RootMotion,并且开启了RootMotion支持,使用的资源索引
Tutorial: Advanced combo animation system - Unreal Engine 4 + Unreal Engine 5
Frank_Slash_Pack动画包


场景资源是UE自带的,再VREditor目录下面,需要打开Engine Content


#1. 搭建框架蓝图

需要搭建3个框架类,分别是:
PlayerController:用于控制玩家的基础运动
GameMode:连招使用的相关游戏设置
Character:执行连招的角色,我们控制的角色

#1-1. ComboController类蓝图编程

Project Settings中绑定移动相关的按键,绑定的轴可以有正负,间接说明可以使用一个输入事件来对应2个变化

Get Controlled Pawn:获取控制器控制的玩家,也就是控制器作用到的角色
Get Control Rotation:获取控制角色的旋转方向
Get Forward Vector:获取控制器的前进方向,如果是负数轴,则是后退
Normalize:将获取到的前进方向进行规格化,因为我们只需要方向而已
Add Movement Input:添加移动输入,控制器的方法,作用到Pawn

Target:添加移动输入的目标,约定为Pawn类型,可以被操控的类型,Character的父类
World Direction:控制器操作的方向
Sacle Value:移动轴缩放,[-1, 1]之间取值,对应我们的2个方向
Force:是否强制进行移动,强制移动会忽略不能移动的输入计算

#1-2. GameMode

在视图中找好一个角度,使用杂项中的场景相机创建这个角度对应的相机

GameMode中,开始运行的时候将控制器的视角交给我们定义好的相机,因为我们是上帝视角预览,
并不是角色自带相机进行预览,SetViewTargetwithBlend可以将游戏的相机混合转换

Target:约束为PlayerController,哪个控制需要转移相机视角
New View Target:新的视角目标,约束为Actor类型
Blend Time:相机从控制器到新视角使用的时间
Blend Func:混合使用的曲线方式

Blend Exp:混合的倍速
Lock Outgoing:是否锁定出去的最后一帧作为混合


设置GameMode相关的参数,将默认框架类引用到我们创建的框架类

#1-3. ComboHero角色类

角色类需要有动画蓝图支持,我们在角色骨架网格体中使用了动画蓝图



调整网格体的转向和箭头的一直,调整网格体的高度和自带的Capsule一致


添加我们需要的按键绑定,Action是按键,Axis是轴

当我们按键的时候执行蒙太奇动画,使用切开片段的方式来执行动画,因为连招都是一帧切换的,使用切开片段可以省事快捷
Play Montage是播放蒙太奇动画的蓝图节点
#. Input 输入端口
In Skeletal Mesh Component:输入的骨架网格体组件,Character中叫Mesh的变量
Montage to Play:需要播放的蒙太奇动画
Play Rate:蒙太奇动画中所有动画的播放倍速
Starting Position:蒙太奇播放的起始位置
Starting Section:蒙太奇开始播放的切开片段
#. Output 输出端口
On Completed:播放蒙太奇完成的回调执行
On Blend Out:当播放蒙太奇开始出混合的时候且没有被中断时回调执行
On Interrupted:当播放蒙太奇被中断或取消时回调执行
On Notify Begin:当播放蒙太奇时候有蒙太奇通知且开始通知回调执行
On Notify End:当播放蒙太奇时候有蒙太奇通知且结束通知回调执行
Notify Name:当前蒙太奇播放过程中的有通知,当前通知的名称


#2. 动画蓝图及连招接口
切开片段动画连招的制作思路:使用AnimNotifyState来监听需要有效执行的连招,在Received_NotifyBeginReceived_NotifyEnd回调
阶段分别执行不同的逻辑达到切开片段动画连招的效果;在Received_NotifyBegin的时候,表示我们进入了有效的连招攻击收尾,
可以执行下一个切开片段动画,在Received_NotifyEnd的时候,我们需要恢复连招到默认的状态

#2-1. 连招接口定义

在蓝图类中选择创建蓝图接口,接口是任意类都可以调用的,实现类回调

在接口中添加2个方法,第一个用于回调下一个动画切开片段,第二个方法用于重置回默认的切开片段

在第一个接口方法中添加输入参数,用于指定回调的切开片段名称,第二方接口方法默认即可

#2-2. 动画蓝图连招接口

在动画蓝图的动画图表中,使用我们之前创建的混合空间,将SpeedDirection提升为变量,同时使用DefalutSlot插槽,
插槽用于蒙太奇动画,蒙太奇动画需要插槽才能播放,使用默认的插槽就可以了

在事件图表中,更新我们的Speed变量,播放混合空间的动画
Try Get Pawn Owner:尝试获取当前动画蓝图归属的Pawn,也就是我们控制的角色


在类设置中实现我们连招的切开片段接口

在动画蓝图类中实现接口中的方法

当接收到SetNextSection的时候,将传递过来的Name赋值到Pawn中,用于连招的下一次播放
当接收到SetDefaultSection的时候,重置Pawn中的Section到默认名称


#3. 蒙太奇动画和动画状态通知
使用动画的状态通知来告知我们播放的动画进度和一个有效的连招时间点,在时间点的范围表示可以切换到下一个切开的动画片段,
在动画状态通知类中暴露下一个切开片段名称,在蒙太奇动画中指定下一个切开片段动画名称

#3-1. 连招动画状态通知

新建蓝图类继承自AnimNotifyState

实现类中的Received Notify BeginReceived Notify End


Received_NotifyBegin的时候,我们需要告知需要播放的下一个切开动画片段,同时将NextSection暴露给编辑器
Mesh Comp是这个动画状态通知所拥有的骨架网格体,通过Get Anim Instance可以获取这个类的动画蓝图,因为这个方法
返回就是继承自UAnimInstance的子类(比较特殊哈,本来需要Cast到我们创建的动画蓝图)

在动画状态通知结束的时候,把动画恢复到默认的切开片段名称

#3-2. 蒙太奇动画


将需要连招的所有动画片段拖入到动画编辑器中,递增顺序的方式进行排列

给动画片段分别添加切开片段名称,分别命名为Section1Section2Section3,并把切开的片段名称依次放入到对应插槽位置处

点击Clear将连接的切开动画片段分离开,我们不要一次播放到底,我们需要手动控制播放哪个切开片段

在动画编辑器中的Notify栏中添加我们创建的动画状态通知,并且设置这个动画的下个连招名字


#. C++版本


注意第一个动画蓝图是不能自定义的,使用纯蓝图的动画蓝图即可,默认蓝图端也不能自定义的


C++的自定义类也不能派生出蓝图类

/*** An Anim Blueprint is essentially a specialized Blueprint whose graphs control the animation of a Skeletal Mesh.* It can perform blending of animations, directly control the bones of the skeleton, and output a final pose* for a Skeletal Mesh each frame.*/
UCLASS(BlueprintType)
class ENGINE_API UAnimBlueprint : public UBlueprint, public IInterface_PreviewMeshProvider

在源码上有注解:

动画蓝图本质上是一个特殊的蓝图,拥有图表可以控制骨骼网格体的动画
它可以执行混合动画,直接控制骨架的骨骼,并且可以最终为骨骼网格体每帧输出一个动画姿态

特殊的动画蓝图,不允许用户自定义,也不能在C++端重写的


#1. GameMode

在上帝视角的相机Actor上添加Tag

ACombo_GameMode::ACombo_GameMode()
{DefaultPawnClass = ACombo_Character::StaticClass();
}void ACombo_GameMode::BeginPlay()
{Super::BeginPlay();TArray<AActor*> AllActors;UGameplayStatics::GetAllActorsWithTag(GetWorld(), FName("ViewActor"), AllActors);APlayerController* FirstPlayerController = GetWorld()->GetFirstPlayerController();if (FirstPlayerController){FirstPlayerController->SetViewTarget(AllActors[0]);}
}

在代码中设置GameMode默认的Pawn,将控制器的视角转移到CameraActor

在C++设置的GameMode默认只能在C++层修改


#2. Pawn(Character)类

ACombo_Character::ACombo_Character()
{// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;const ConstructorHelpers::FObjectFinder<USkeletalMesh> SkeletalMesh = ConstructorHelpers::FObjectFinder<USkeletalMesh>(TEXT("SkeletalMesh'/Game/Frank_Slash_Pack/Frank_Assassin/Skeletal_Meshes/SK_Frank_Assassin_SkeletalMeshes.SK_Frank_Assassin_SkeletalMeshes'"));if (SkeletalMesh.Succeeded()){GetMesh()->SetSkeletalMesh(SkeletalMesh.Object, true);GetMesh()->SetRelativeLocation(FVector(0, 0, -88));GetMesh()->SetRelativeRotation(FRotator::MakeFromEuler(FVector(0, 0, -90)));}ConstructorHelpers::FClassFinder<UAnimInstance> AnimFinder = ConstructorHelpers::FClassFinder<UAnimInstance>(TEXT("AnimBlueprint'/Game/AdvCombo/C++/AdvAnimBP.AdvAnimBP_C'"));if (AnimFinder.Succeeded()){GetMesh()->AnimClass = AnimFinder.Class;}GetCharacterMovement()->MaxWalkSpeed = 300.0f;MontageSectionName = FName("Section1");const ConstructorHelpers::FObjectFinder<UAnimMontage> Combo1Finder = ConstructorHelpers::FObjectFinder<UAnimMontage>(TEXT("AnimMontage'/Game/AdvCombo/C++/Combo1.Combo1'"));if (Combo1Finder.Succeeded()){Combo1Montage = Combo1Finder.Object;}
}// Called when the game starts or when spawned
void ACombo_Character::BeginPlay()
{Super::BeginPlay();}// Called every frame
void ACombo_Character::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}// Called to bind functionality to input
void ACombo_Character::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{Super::SetupPlayerInputComponent(PlayerInputComponent);PlayerInputComponent->BindAxis(FName("Forward"), this, &ACombo_Character::MoveForward);PlayerInputComponent->BindAxis(FName("Right"), this, &ACombo_Character::MoveRight);PlayerInputComponent->BindAction(FName("Combo1"), IE_Pressed, this, &ACombo_Character::Combo1);PlayerInputComponent->BindAction(FName("Combo2"), IE_Pressed, this, &ACombo_Character::Combo2);PlayerInputComponent->BindAction(FName("Combo3"), IE_Pressed, this, &ACombo_Character::Combo3);
}void ACombo_Character::MoveForward(float Axis)
{if (!FMath::IsNearlyZero(Axis)){const FVector ForwardVector = GetActorForwardVector();AddMovementInput(ForwardVector, Axis);}
}void ACombo_Character::MoveRight(float Axis)
{if (!FMath::IsNearlyZero(Axis)){const FVector RightVector = GetActorRightVector();AddMovementInput(RightVector, Axis);}
}void ACombo_Character::Combo1()
{PlayAnimMontage(Combo1Montage, 1, MontageSectionName);
}void ACombo_Character::Combo2()
{}void ACombo_Character::Combo3()
{}

#3. 接口类

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UCombo_Interface : public UInterface
{GENERATED_BODY()
};/*** */
class MONTAGETEST_API ICombo_Interface
{GENERATED_BODY()// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:UFUNCTION(BlueprintNativeEvent)void SetNextComboSection(FName SectionName);UFUNCTION(BlueprintNativeEvent)void SetDefaultComboSection();
};

#4. AnimNotifyState类

void UCombo_AnimNotifyState::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
{Super::NotifyBegin(MeshComp, Animation, TotalDuration);// AActor* Actor = MeshComp->GetOwner();// ACombo_Character* Combo_Character = Cast<ACombo_Character>(Actor);// if (Combo_Character)// {//   Combo_Character->MontageSectionName = NextSectionName;// }UAnimInstance* TargetAnimBP = MeshComp->GetAnimInstance();if (TargetAnimBP->GetClass()->ImplementsInterface(UCombo_Interface::StaticClass()))//OK{//ICombo_Interface* Interface = Cast<ICombo_Interface>(TargetAnimBP);//NULL//Interface->SetNextComboSection(NextSectionName);                    //CrashICombo_Interface::Execute_SetNextComboSection(TargetAnimBP, NextSectionName);//Best}}void UCombo_AnimNotifyState::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{Super::NotifyEnd(MeshComp, Animation);// AActor* Actor = MeshComp->GetOwner();// ACombo_Character* Combo_Character = Cast<ACombo_Character>(Actor);// if (Combo_Character)// {//    Combo_Character->MontageSectionName = FName("Section1");// }UAnimInstance* TargetAnimBP = MeshComp->GetAnimInstance();if (TargetAnimBP->GetClass()->ImplementsInterface(UCombo_Interface::StaticClass()))//OK{ICombo_Interface::Execute_SetDefaultComboSection(TargetAnimBP);}
}

这里提供了2个方法可以使用,接口和取Pawn类对象,其他的制作同蓝图方式一致


UE Gameplay实例49(高级蒙太奇动画连招)相关推荐

  1. UE Gameplay入门47(骨架网格体的动画片段)

    动画片段是指原始的fbx切片动画 粉色的骨架网格体,浅绿色的动画片段 #1. 菜单功能区域 前面的Save.Browse.Preview Mesh和以往的都一样 Create Asset:该动画片段可 ...

  2. UE4C++ 调用蒙太奇动画

    UE4C++ 调用蒙太奇动画 直接调用函数PlayAnimMontage(), 通过函数定义可以知道,函数有三个参数:蒙太奇资源.播放速率和播放的蒙太奇sectionname 该函数在蓝图中的节点是P ...

  3. UE4动画系统,蒙太奇动画使用,添加动作

    提示:仅供学习参考 前言 一.什么是蒙太奇? 二.实现步骤 1.使用第三人称游戏c++模板创建一个项目,创建动画蒙太奇 2.在动画蓝图中添加蒙太奇 3.绑定鼠标左键输入 4.添加c++代码 5.设置动 ...

  4. JQuery-学习笔记05【高级——JQuery动画和遍历】

    Java后端 学习路线 笔记汇总表[黑马程序员] JQuery-学习笔记01[基础--JQuery基础]--[day01] JQuery-学习笔记02[基础--JQuery选择器] JQuery-学习 ...

  5. java day45【JQuery 高级:动画,遍历,事件绑定,案例,插件】

    第一章  JQuery 高级 1. 动画 1. 三种方式显示和隐藏元素 1. 默认显示和隐藏方式 1. show([speed,[easing],[fn]]) 1. 参数: 1. speed:动画的速 ...

  6. 13_ue4进阶_蒙太奇动画实现一边走一边攻击

    绿的是普通的动画,紫的是蒙太奇动画.但是动画的内容是一样的,所以我们为什么要用蒙太奇呢? 蒙太奇动画可以便捷的与动画蓝图发生作用,使他的动画产生比较润滑的过渡. 普通动画vs蒙塔奇动画 1.动画的播放 ...

  7. UE Gameplay Learning Record

    UE Gameplay Learning Type: #Learn Tags: #UnrealEngine #Gameplay Status: #Doing Time: 2022-12-10 11:2 ...

  8. HTML5+CSS3小实例:篮球弹跳动画

    实例:篮球弹跳动画 技术栈:HTML+CSS 效果: 源码: <!DOCTYPE html> <html><head><meta http-equiv=&qu ...

  9. UE4(unreal engine4)蒙太奇动画删除不想要的帧数

    UE4系列文章目录 文章目录 UE4系列文章目录 前言 一.问题原因 二.具体操作步骤 前言 UE4(unreal engine4)蒙太奇动画删除不想要的帧数.当我们在UE4中导入一个fbx骨骼动画. ...

最新文章

  1. 不允许使用不完整的类型_河南:限速标志不达标,坚决不允许通车!
  2. java面向对象(引用类型--参数传递)
  3. maya扇子动画_maya怎么制作一个万箭齐发的效果?
  4. matlab相机标定 外参数,相机外参数的标定.doc
  5. EasyUI系列学习(十一)-Accordion(分类)
  6. vs2013 没有ef mysql_vs2013 EF Mysql
  7. Android中onActivityResult/startActivityForResult用法
  8. php 编译原理,php编译原理 - Robin3D的个人页面 - OSCHINA - 中文开源技术交流社区
  9. java excel通用导入类_java excel 文件导入通用接口
  10. java 清空文件夹_java 删除文件夹中的所有文件及文件夹
  11. 计算机专业保研面试备考:数据库系统理论
  12. html2d动画,HTML5之SVG 2D入门11—用户交互性(动画)介绍及应用
  13. 单片机diy作品鉴赏,初学者进来膜拜
  14. vue安装及创建运行
  15. mysql binlog定点恢复锁表_MySQL -- binlog 操作与恢复
  16. hashcat跑握手包笔记
  17. true在php中代表,true是什么意思
  18. 说一说JS数据类型有哪些
  19. 图片局部无失真放大的两种方式
  20. python实现离散傅里叶变换

热门文章

  1. 阿里云的「香港」机房大陆访问速度怎么样?
  2. 攻防世界Reverse进阶区-EasyRE-writeup
  3. matlab中辗转相除法,基于Matlab的辗转相除法
  4. JavaScript自定义tirm方法
  5. [微信支付 ] prepay_id 为空,可能出现的问题?微信支付失败
  6. java fx eclipse_JavaFX环境搭建 - Eclipse的e(fx)clipse插件安装 创建JavaFX项目
  7. C++学习continue用法
  8. vite打包快几款基于vue3和vite的开箱即用的中后台管理模版
  9. 佛山 新型智慧城市建设实打实
  10. 条理清晰的入门:使用Java实现RSA加密解密