vault backup: 2024-05-17 18:03:12

This commit is contained in:
BlueRose 2024-05-17 18:03:12 +08:00
parent 826dc1fced
commit 895adfbb89

View File

@ -9,6 +9,11 @@ rating: ⭐
参考:
1. [sequencer mute tracks at runtime from c](https://forums.unrealengine.com/t/sequencer-mute-tracks-at-runtime-from-c/476278/3)
可行思路:
1. 给导播创建一个快捷键Hotkey之后在触发根据Tag切换对应角色动画蓝图中的逻辑。
2. 使用Sequence的EventTrack设置若干事件。
3. 模改UMovieSceneSkeletalAnimationTrack实现一个可以自动根据Tag捕获对应角色并且播发动画的功能。
# 方案一
- UMovieSceneTrack::SetEvalDisabled(bool)
- ULevelSequence::MarkAsChanged()
@ -52,9 +57,11 @@ UMovieSceneSequenceExtensions methods are not exported. They dont have the PL
- https://zhuanlan.zhihu.com/p/396353973
- https://zhuanlan.zhihu.com/p/414179358
- https://zhuanlan.zhihu.com/p/413151867
- LevelSequence分析
- https://zhuanlan.zhihu.com/p/157892605
## 继承关系
UMovieSceneSignedObject -> UObject
UMovieSceneSignedObject -> UObject。一个UMovieScene由若干个MasterTrack(UMovieSceneTrack)组成UMovieScene相当于是UMovieSceneTracks的容器那么UMovieSceneTrack又由UMovieSceneSections组成UMovieSceneSection就是一个Track中间的某一段。
### UMovieSceneSignedObject
子类有UMovieScene、UMovieSceneSequence、UMovieSceneTrack、UMovieSceneSection。
@ -74,5 +81,266 @@ UMovieSceneSignedObject -> UObject
编辑器代码:**FSkeletalAnimationTrackEditor**
## Sequence绑定机制笔记
1. _SpawnableObject_(自动Spawn的Object)
表示该Object在Sequence被Evaluate的时候自动Spawn并且由Sequence来管理生命周期
2. _PossessableObject_(可以被赋予的Object)
表示该Object可以被外部赋予比如我手动赋予一个Actor给某个Track
### UMovieSceneSequence
UMovieSceneSequence::CreatePossessable()
UMovieSceneSequence::BindPossessableObject()
绑定过程可以参考:
ULevelSequenceEditorSubsystem::AddActorsToBinding(const TArray<AActor*>& Actors, const FMovieSceneBindingProxy& ObjectBinding)
### ActorToSequencer
菜单出现逻辑位于FLevelSequenceEditorActorBinding::BuildSequencerAddMenu(FMenuBuilder& MenuBuilder)。
`FLevelSequenceEditorActorBinding::AddPossessActorMenuExtensions(FMenuBuilder& MenuBuilder)`
=>
`FLevelSequenceEditorActorBinding::AddActorsToSequencer(AActor*const* InActors, int32 NumActors)`
=>
`TArray<FGuid> FSequencer::AddActors(const TArray<TWeakObjectPtr<AActor> >& InActors, bool bSelectActors)`
=>
`TArray<FGuid> FSequencerUtilities::AddActors(TSharedRef<ISequencer> Sequencer, const TArray<TWeakObjectPtr<AActor> >& InActors)`
```c++
TArray<FGuid> FSequencerUtilities::AddActors(TSharedRef<ISequencer> Sequencer, const TArray<TWeakObjectPtr<AActor> >& InActors)
{
TArray<FGuid> PossessableGuids;
UMovieSceneSequence* Sequence = Sequencer->GetFocusedMovieSceneSequence();
if (!Sequence)
{
return PossessableGuids;
}
UMovieScene* MovieScene = Sequence->GetMovieScene();
if (!MovieScene)
{
return PossessableGuids;
}
if (MovieScene->IsReadOnly())
{
ShowReadOnlyError();
return PossessableGuids;
}
const FScopedTransaction Transaction(LOCTEXT("AddActors", "Add Actors"));
Sequence->Modify();
for (TWeakObjectPtr<AActor> WeakActor : InActors)
{
if (AActor* Actor = WeakActor.Get())
{
FGuid ExistingGuid = Sequencer->FindObjectId(*Actor, Sequencer->GetFocusedTemplateID());
if (!ExistingGuid.IsValid())
{
FGuid PossessableGuid = CreateBinding(Sequencer, *Actor, Actor->GetActorLabel());
PossessableGuids.Add(PossessableGuid);
if (ACameraActor* CameraActor = Cast<ACameraActor>(Actor))
{
NewCameraAdded(Sequencer, CameraActor, PossessableGuid);
}
Sequencer->OnActorAddedToSequencer().Broadcast(Actor, PossessableGuid);
}
}
}
return PossessableGuids;
}
```
### FAudioTrackEditor
FAudioTrackEditor::HandleAssetAdded(UObject* Asset, const FGuid& TargetObjectGuid) => FAudioTrackEditor::AddNewSound() => UMovieSceneAudioTrack::AddNewSoundOnRow(USoundBase* Sound, FFrameNumber Time, int32 RowIndex)
## MovieSceneSkeletalAnimationTrack
- UMovieSceneSkeletalAnimationTrack
- UMovieSceneSkeletalAnimationSection
- FSkeletalAnimationTrackEditor
- UMovieSceneSkeletalAnimationSystem
- FSkeletalAnimationTrackEditMode
Track的编辑器注册位于MovieSceneToolsModule.cpp
```c++
AnimationTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FSkeletalAnimationTrackEditor::CreateTrackEditor ) );
```
只对UMovieSceneSkeletalAnimationTrack生效
```c++
bool FSkeletalAnimationTrackEditor::SupportsSequence(UMovieSceneSequence* InSequence) const
{
ETrackSupport TrackSupported = InSequence ? InSequence->IsTrackSupported(UMovieSceneSkeletalAnimationTrack::StaticClass()) : ETrackSupport::NotSupported;
return TrackSupported == ETrackSupport::Supported;
}
FEditorModeRegistry::Get().RegisterMode<FSkeletalAnimationTrackEditMode>(
FSkeletalAnimationTrackEditMode::ModeName,
NSLOCTEXT("SkeletalAnimationTrackEditorMode", "SkelAnimTrackEditMode", "Skeletal Anim Track Mode"),
FSlateIcon(),
false);
```
### FSkeletalAnimationTrackEditor
FSkeletalAnimationTrackEditor::BuildTrackContextMenu()在MovieSceneSkeletalAnimationTrack的Animation上右键出现的菜单。
FSkeletalAnimationTrackEditor::BuildOutlinerEditWidgetMovieSceneSkeletalAnimationTrack下面生成Animtion那一列。
## 播放逻辑
```c++
ALevelSequenceActor::InitializePlayer()
ALevelSequenceActor->SequencePlayer->Play();
ALevelSequenceActor->SequencePlayer->Update(DeltaSeconds);
```
具体的逻辑位于UMovieSceneSequencePlayer::PlayInternal()
```c++
void UMovieSceneSequencePlayer::PlayInternal()
{
if (NeedsQueueLatentAction())
{
QueueLatentAction(FMovieSceneSequenceLatentActionDelegate::CreateUObject(this, &UMovieSceneSequencePlayer::PlayInternal));
return;
}
if (!IsPlaying() && Sequence && CanPlay())
{
const FString SequenceName = GetSequenceName(true);
UE_LOG(LogMovieScene, Verbose, TEXT("PlayInternal - %s (current status: %s)"), *SequenceName, *UEnum::GetValueAsString(Status));
// Set playback status to playing before any calls to update the position
Status = EMovieScenePlayerStatus::Playing;
float PlayRate = bReversePlayback ? -PlaybackSettings.PlayRate : PlaybackSettings.PlayRate;
// If at the end and playing forwards, rewind to beginning
if (GetCurrentTime().Time == GetLastValidTime())
{
if (PlayRate > 0.f)
{
SetPlaybackPosition(FMovieSceneSequencePlaybackParams(FFrameTime(StartTime), EUpdatePositionMethod::Jump));
}
}
else if (GetCurrentTime().Time == FFrameTime(StartTime))
{
if (PlayRate < 0.f)
{
SetPlaybackPosition(FMovieSceneSequencePlaybackParams(GetLastValidTime(), EUpdatePositionMethod::Jump));
}
}
// Update now
if (PlaybackSettings.bRestoreState)
{
RootTemplateInstance.EnableGlobalPreAnimatedStateCapture();
}
bPendingOnStartedPlaying = true;
Status = EMovieScenePlayerStatus::Playing;
TimeController->StartPlaying(GetCurrentTime());
if (PlayPosition.GetEvaluationType() == EMovieSceneEvaluationType::FrameLocked)
{
if (!OldMaxTickRate.IsSet())
{
OldMaxTickRate = GEngine->GetMaxFPS();
}
GEngine->SetMaxFPS(1.f / PlayPosition.GetInputRate().AsInterval());
}
//播放的核心逻辑估计在这里。
if (!PlayPosition.GetLastPlayEvalPostition().IsSet() || PlayPosition.GetLastPlayEvalPostition() != PlayPosition.GetCurrentPosition())
{
UpdateMovieSceneInstance(PlayPosition.PlayTo(PlayPosition.GetCurrentPosition()), EMovieScenePlayerStatus::Playing);
}
RunLatentActions();
UpdateNetworkSyncProperties();
if (bReversePlayback)
{
if (OnPlayReverse.IsBound())
{
OnPlayReverse.Broadcast();
}
}
else
{
if (OnPlay.IsBound())
{
OnPlay.Broadcast();
}
}
}
}
void UMovieSceneSequencePlayer::UpdateMovieSceneInstance(FMovieSceneEvaluationRange InRange, EMovieScenePlayerStatus::Type PlayerStatus, bool bHasJumped)
{
FMovieSceneUpdateArgs Args;
Args.bHasJumped = bHasJumped;
UpdateMovieSceneInstance(InRange, PlayerStatus, Args);
}
void UMovieSceneSequencePlayer::UpdateMovieSceneInstance(FMovieSceneEvaluationRange InRange, EMovieScenePlayerStatus::Type PlayerStatus, const FMovieSceneUpdateArgs& Args)
{
if (Observer && !Observer->CanObserveSequence())
{
return;
}
UMovieSceneSequence* MovieSceneSequence = RootTemplateInstance.GetSequence(MovieSceneSequenceID::Root);
if (!MovieSceneSequence)
{
return;
}
#if !NO_LOGGING
if (UE_LOG_ACTIVE(LogMovieScene, VeryVerbose))
{
const FQualifiedFrameTime CurrentTime = GetCurrentTime();
const FString SequenceName = GetSequenceName(true);
UE_LOG(LogMovieScene, VeryVerbose, TEXT("Evaluating sequence %s at frame %d, subframe %f (%f fps)."), *SequenceName, CurrentTime.Time.FrameNumber.Value, CurrentTime.Time.GetSubFrame(), CurrentTime.Rate.AsDecimal());
}
#endif
if (PlaybackClient)
{
PlaybackClient->WarpEvaluationRange(InRange);
}
// Once we have updated we must no longer skip updates
bSkipNextUpdate = false;
// We shouldn't be asked to run an async update if we have a blocking sequence.
check(!Args.bIsAsync || !EnumHasAnyFlags(MovieSceneSequence->GetFlags(), EMovieSceneSequenceFlags::BlockingEvaluation));
// We shouldn't be asked to run an async update if we don't have a tick manager.
check(!Args.bIsAsync || TickManager != nullptr);
FMovieSceneContext Context(InRange, PlayerStatus);
Context.SetHasJumped(Args.bHasJumped);
TSharedPtr<FMovieSceneEntitySystemRunner> Runner = RootTemplateInstance.GetRunner();
if (Runner)
{
Runner->QueueUpdate(Context, RootTemplateInstance.GetRootInstanceHandle());
if (Runner == SynchronousRunner || !Args.bIsAsync)
{
Runner->Flush();
}
}
}
```
暂停的核心逻辑:
```c++
FMovieSceneEvaluationRange CurrentTimeRange = PlayPosition.GetCurrentPositionAsRange();
const FMovieSceneContext Context(CurrentTimeRange, EMovieScenePlayerStatus::Stopped);
Runner->QueueUpdate(Context, RootTemplateInstance.GetRootInstanceHandle(), FSimpleDelegate::CreateWeakLambda(this, FinishPause));
```
## SequenceRecorder
SequenceRecorder模块的基类是ISequenceRecorder。