【UE4】Montage NextSection 问题记录

本文使用 UE4.26,以及 GAS 系统,记录一下 Montage Next Section 的一个问题

一、问题背景和表现

1.1 问题背景

  在 UE4 中,有时候需要在播放 Montage 的同时修改 Section 的连接关系(关于 Montage 和 Section 的含义和用法可以参考 这里),比如有时候想第 1 段后边接第 2 段,有时候想接第 3 段,这时候可以用 UGameplayAbility::MontageSetNextSectionName 设置第一段的下一段是哪个,然后就可以达到第一段结束之后,直接从哪一段继续播的效果。

  如果不想当前段播完,而是直接跳转到下一段,从接口上看,没有一个接口是 “JumpToNextSection” 这种功能,只有 “MontageJumpToSection” 这样的接口,所以需要一个 “GetNextSection” 这样的接口拿到当前 Section 的下一个 Section 是哪个。

1.2 问题表现

  问题表现一句话总结就是:FAnimMontageInstance::GetNextSection()FAnimMontageInstance::GetNextSectionID 结果不一致。

FName FAnimMontageInstance::GetNextSection() const
{
    
    
	if (Montage)
	{
    
    
		float CurrentPosition;
		const int32 CurrentSectionIndex = Montage->GetAnimCompositeSectionIndexFromPos(Position, CurrentPosition);
		if (Montage->IsValidSectionIndex(CurrentSectionIndex))
		{
    
    
			FCompositeSection& CurrentSection = Montage->GetAnimCompositeSection(CurrentSectionIndex);
			return CurrentSection.NextSectionName;
		}
	}
	return NAME_None;
}

int32 FAnimMontageInstance::GetNextSectionID(int32 const & CurrentSectionID) const
{
    
    
	return NextSections.IsValidIndex(CurrentSectionID) ? NextSections[CurrentSectionID] : INDEX_NONE;
}

  两个函数如上所示:

  1. GetNextSection 是通过 Position,算出当前 Section,然后通过 FCompositeSection 类型里的 NextSectionName 变量得到 NextSection。
  2. GetNextSectionID 是通过 NextSections 数组,通过当前 Section 的 ID 得到 NextSection 的 ID。

  通过第二种方法得到 NextSection 的 ID 之后可以得到 NextSection 的 Name:

FName FAnimMontageInstance::GetSectionNameFromID(int32 const & SectionID) const
{
    
    
	if (Montage && Montage->IsValidSectionIndex(SectionID))
	{
    
    
		FCompositeSection const & CurrentSection = Montage->GetAnimCompositeSection(SectionID);
		return CurrentSection.SectionName;
	}
	return NAME_None;
}

  从函数名上看,两种应该只是返回名字和 ID 的区别(当当前所在 Section ID 作为参数传给 GetNextSectionID 时),但是由于用过两种完全不同的方式获得 NextSection(一种是 FCompositeSection 成员变量,一种是 NextSections 数组),实际结果可能不一样。

1.3 问题复现

  配置一个如下所示的 Montage:
在这里插入图片描述

  其中有四个 Section,每一段的下一段是后边一个 Section。
  每一段里都配置了一个 “PrintString” 的动画通知,作用就是输出一个配置的字符串,每一段配置的就是对应的 “2”、“3”、“4”。
  运行游戏,并播放这个 Montage,可以看到按顺序输出了四个,没有问题:

LogAbilitySystem: Error: Print String 1
LogAbilitySystem: Error: Print String 2
LogAbilitySystem: Error: Print String 3
LogAbilitySystem: Error: Print String 4

  在第一段中配置一个动画通知,在通知触发时,进行 “SetNextSection” 和 “GetNextSection” 的操作(为了测试,虽然可以直接 Jump),
在这里插入图片描述

  代码如下:

ACharacter* const Character = Cast<ACharacter>(GetOwningActorFromActorInfo());
if (!Character)
{
    
    
	return;
}

UAbilitySystemComponent* const ASC = GetAbilitySystemComponentFromActorInfo_Checked();
if (!ASC)
{
    
    
	return;
}

UAnimInstance* const AnimInstance = Character->GetMesh()->GetAnimInstance();
if (!AnimInstance)
{
    
    
	return;
}

FAnimMontageInstance* const AnimMontageInstance = AnimInstance->GetActiveInstanceForMontage(ASC->GetCurrentMontage());
if (!AnimMontageInstance)
{
    
    
	return;
}

MontageSetNextSectionName("1", "3");

FName const CurSection = AnimMontageInstance->GetCurrentSection();
FName const NextSection1 = AnimMontageInstance->GetNextSection();
FName const NextSection2 = 
	AnimMontageInstance->GetSectionNameFromID(AnimMontageInstance->GetNextSectionID(ASC->GetCurrentMontageSectionID()));
UE_LOG(LogAbilitySystem, Error, 
	TEXT("CurSection is %s; NextSection1 is %s; NextSection2 is %s;"), 
	*CurSection.ToString(), 
	*NextSection1.ToString(), 
	*NextSection2.ToString());

  重点其实就是 MontageSetNextSectionName("1", "3"); 设置了第 “1” 段的下一段是第 “3” 段而不是默认的第 “2” 段。

  其中 MontageSetNextSectionName("1", "3");AnimInstance->Montage_SetNextSection("1", "3", ASC->GetCurrentMontage());AnimMontageInstance->SetNextSectionName("1", "3"); 三种方式是一样的。

  然后通过两种不同的方式 Get 出 NextSection,结果如下:

LogAbilitySystem: Error: CurSection is 1; NextSection1 is 2; NextSection2 is 3;
LogAbilitySystem: Error: Print String 1
LogAbilitySystem: Error: Print String 3
LogAbilitySystem: Error: Print String 4

  从实际动画表现,和输出看,可以看到设置 NextSection 是生效了的,第 “1” 段结束接的是第 “3” 段,但是通过 AnimMontageInstance->GetCurrentSection() 得到的却是默认的第 “2” 段。

二、问题原因

UGameplayAbility::MontageSetNextSectionName(FName FromSectionName, FName ToSectionName)

  上边这个函数最终会调到下边:

bool FAnimMontageInstance::SetNextSectionID(int32 const & SectionID, int32 const & NewNextSectionID)

  而上边这个函数只有对成员变量 PrevSectionsNextSections 这两个数组的修改,并没有真正修改 Montage 上的 Section 的 NextSection,如下图所示:
在这里插入图片描述

  修改这个相当于修改资源的配置了,本身也不应该修改,所以个人感觉 FAnimMontageInstance::GetNextSection() 这个函数写的有点问题,应该返回 Montage Instance 上的 NextSection 而不是资源本身的未调整的配置。

猜你喜欢

转载自blog.csdn.net/Bob__yuan/article/details/117357991