代码更新
https://github.com/BAOfanTing/ARPG_Game_Code/commit/c629270e49496ba1bcbaf03780d23c1842ca5e7a
Animation Montages动画蒙太奇
蒙太奇的工作流程
新建一个鼠标左键的按键映射,下载一些攻击动画,重定向给我们的人物,新建一个动画蒙太奇,放入动画
然后在动画蓝图里插入一个Slot节点再在人物的蓝图里这样设置,就能点击产生攻击动画
使用C++来播放蒙太奇
新建函数Attack,绑定,定义一个蒙太奇变量
首先获取动画实例,判断动画实例和蒙太奇动画是不是空指针,用一个随机数来选择播放的蒙太奇动画,最后还需要在蓝图里设置攻击蒙太奇
优化攻击动画
现在一直点击鼠标会打断上一次的攻击重复播放需要更改。把播放蒙太奇的代码封装成一个函数,随后新定义一个角色动作状态的枚举,先判断能否进行攻击,进入后设置攻击状态无法重置攻击。
UENUM(BlueprintType)
enum class EActionState:uint8
{
EAS_Unoccupied UMETA(DisplayName = "Unoccupied"),
EAS_Attacking UMETA(DisplayName = "Attacking"),
EAS_Equipping UMETA(DisplayName = "Equipping")
};
void ACharacter01::Attack()
{
if (CanAttack())
{
PlayAttackMontage();
ActionState = EActionState::EAS_Attacking;
}
}
void ACharacter01::PlayAttackMontage()
{
// 获取角色的骨架并检查是否存在动画实例以及攻击蒙太奇(Montage)
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance && AttackMontage)
{ // 在动画实例上播放攻击蒙太奇
AnimInstance->Montage_Play(AttackMontage);
// 生成一个随机数,选择攻击动作的不同部分
const int32 Selection = FMath::RandRange(0, 1);
FName SectionName = FName();
// 根据随机数选择不同的攻击部分
switch (Selection)
{
case 0:
SectionName = FName("Attack1");
break;
case 1:
SectionName = FName("Attack2");
break;
default:
break;
}
// 跳转到所选的攻击部分
AnimInstance->Montage_JumpToSection(SectionName, AttackMontage);
}
}
但是上边的方法只能攻击一次,使用动画通知来知道动画已经播放完了,在蒙太奇里边新建两个notify
新建一个攻击结束函数,将函数和变量都暴露给蓝图,在动画蓝图中
在动画蓝图中当动画结束时直接调用这个函数,这样我们就能多次攻击不卡顿
武器漂浮,走动不攻击
新建一个枚举类来判断武器是否被拿在手上,定义初始转态
UENUM(BlueprintType)
enum class EItemState : uint8
{
EIS_UnOnHand UMETA(DisplayName="UnOnHand"),
EIS_OnHand UMETA(DisplayName = "OnHand")
};
在tick函数里添加判断当物体不被拿起时漂浮,在回到武器cpp在equip的函数里将ItemState就设置完成了
// 每帧都会被调用
void Aitem::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 记录经过的总时间
RunningTime += DeltaTime;
//当物体不被拿起时漂浮
if (ItemState == EItemState::EIS_UnOnHand)
{
AddActorWorldOffset(FVector(0.f,0.f,TransformedSin()));
}
}
走动不攻击只需要判断是否在攻击状态,如果在的话就不接收移动的值
挥剑声音、喘息声音
进入攻击的动画蓝图添加一个播放声音的notify,也可以在蒙太奇中放入,
为了更改声音,可以创建一个soundcue
在插件里打开MetaSound,新建一个
新建一个变量,修改type改为whoosh,使用随机函数来让时长和声音变大变小
创建一个人物的喘息声音,传入的声音是一个数组,选中10个声音,最后把两个声音都加入蒙太奇
脚步声和粒子效果
跟喘息声一样,导入18个声音文件进行制作,进入跑步动画,加上脚步和袋子碰撞的声音,并且从包里将粒子效果copy并且绑定左右脚。把跳跃等动作都加上音效
修改一下脚的位置
攻击时脚会挪开是因为ik——foot没有对应上脚的位置,所以要进行修改
将每个脚位置的transform给ik就可以改正
收剑,拿出剑
下载拿收剑和不拿剑待机的动画,导入重定向,新建蒙太奇。回到人物头文件新建进入条件,装备蒙太奇变量,播放函数
//能够卸下武器
bool CanDisarm();
bool Canarm();
void PlayEquipMontage(FName SectionName);
UPROPERTY(EditDefaultsOnly, Category = Montages)
UAnimMontage* EquipMontage;
播放函数很简单,只需要判断实例存在,传入sectionname。
void ACharacter01::PlayEquipMontage(FName SectionName)
{
// 获取角色的骨架并检查是否存在动画实例以及蒙太奇(Montage)
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance && EquipMontage)
{ // 在动画实例上播放蒙太奇
AnimInstance->Montage_Play(EquipMontage);
// 跳转到所选的攻击部分
AnimInstance->Montage_JumpToSection(SectionName, EquipMontage);
}
}
回到按键E的函数,简单修改,这是还应该添加一个武器状态,看他是不是被我们拿起来。这样才能装备和卸下,在重叠拿起时把这个变量设置重叠物,最后在人物蓝图里吧equipmontage设置为我们的装备蒙太奇。
void ACharacter01::EKeyPressed()
{ //当重叠的物体为武器类获取改物体
AWeapon* OverlappingWeapon = Cast<AWeapon>(OverlappingItem);
if (OverlappingWeapon)
{
OverlappingWeapon->Equip(GetMesh(),FName("RightHandSocket"));
CharacterState = ECharacterState::ECS_EquipedOneHandWeapon;
}
else
{ //收起武器,并设置人物转态
if(CanDisarm())
{
PlayEquipMontage(FName("Unequip"));
CharacterState = ECharacterState::ECS_Unequiped;
}
//拿出武器
else if (Canarm())
{
PlayEquipMontage(FName("Equip"));
CharacterState = ECharacterState::ECS_EquipedOneHandWeapon;
}
}
}
完成后发现装备武器后还会卸下武器,排查后发现是蒙太奇里边没有断开动画连续播放了
让剑粘在背部
和放在手上一样,我们需要在背部也上一个socket,在spine05插入一个socket,插入剑的模型调整位置,差不多就行。在蒙太奇里边插入一个武器脱离手的通知notify。
将这段函数的上两句选中右键重构,可以拿出一个附加到socket的函数,会自动为我们创建函数并替换
void AWeapon::Equip(USceneComponent* InParent, FName InSocketName)
{
// 创建一个 FAttachmentTransformRules 对象,规定附着的规则。
FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget, true);
// 将武器的 ItemMesh 附着到InParent的 Mesh 上,并使用Socket作为附着点
ItemMesh->AttachToComponent(InParent, TransformRules, InSocketName);
ItemState = EItemState::EIS_OnHand;
}
void AWeapon::AttachMeshToSocket(USceneComponent* InParent, const FName& InSocketName)
{
// 创建一个 FAttachmentTransformRules 对象,规定附着的规则。
FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget, true);
// 将武器的 ItemMesh 附着到InParent的 Mesh 上,并使用Socket作为附着点
ItemMesh->AttachToComponent(InParent, TransformRules, InSocketName);
}
现在我们需要一个蓝图可读的函数来让notify执行Disarm函数(在人物cpp里)这样我们就可以把剑放在背部,拿起剑也一样
void ACharacter01::Disarm()
{
if (EquippedWeapon)
{
EquippedWeapon->AttachMeshToSocket(GetMesh(),FName("SpineSocket"));
}
}
此时我们在移动的时候还能收剑,想要取消。添加一个动作状态正在装备就可以,同时还需要在动画结束时在添加一个notify来重置ActionState状态为未被占用
在移动里修改判断条件
装备武器的音效
使用metasound制作一个音效,然后使用代码来播放,对于不同的武器使用不同的声音。在武器头文件新建一个变量
// 武器装备时播放的音效
UPROPERTY(EditAnywhere, Category = "Weapon Properties")
USoundBase* EquipSound;
void AWeapon::Equip(USceneComponent* InParent, FName InSocketName)
{
// 调用 AttachMeshToSocket 函数将武器的 Mesh 附加到指定的骨骼插槽上
AttachMeshToSocket(InParent, InSocketName);
// 设置武器的状态为在手上
ItemState = EItemState::EIS_OnHand;
// 如果设置了装备音效,就在武器的位置播放音效
if (EquipSound)
{
UGameplayStatics::PlaySoundAtLocation(this, EquipSound, GetActorLocation());
}
}
然后进入剑的蓝图为它设置单独的音效
此时装备武器后还按e还会发出声音,因为两个胶囊体一直在重叠,因此我们要在拿起武器后关闭它的重叠,在上边的代码里在加上下边这一行
if (Sphere)
{
Sphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
优化动画
在调试的过程中可以an“~”输入slomo 0.1,这样所有的东西都会按0.1秒来播放方便看,在动画蓝图总可以使用key来修改部分动画而不会影响整体。
首先找到要修改的开始和结尾,选定骨骼节点分别创建一个key,然后在中间位置调整骨骼节点的位置在添加一个就完成了