vault backup: 2024-05-17 18:03:12
This commit is contained in:
parent
826dc1fced
commit
895adfbb89
@ -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. [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)
|
- UMovieSceneTrack::SetEvalDisabled(bool)
|
||||||
- ULevelSequence::MarkAsChanged()
|
- ULevelSequence::MarkAsChanged()
|
||||||
@ -52,9 +57,11 @@ UMovieSceneSequenceExtensions methods are not exported. They don’t have the PL
|
|||||||
- https://zhuanlan.zhihu.com/p/396353973
|
- https://zhuanlan.zhihu.com/p/396353973
|
||||||
- https://zhuanlan.zhihu.com/p/414179358
|
- https://zhuanlan.zhihu.com/p/414179358
|
||||||
- https://zhuanlan.zhihu.com/p/413151867
|
- 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
|
### UMovieSceneSignedObject
|
||||||
子类有UMovieScene、UMovieSceneSequence、UMovieSceneTrack、UMovieSceneSection。
|
子类有UMovieScene、UMovieSceneSequence、UMovieSceneTrack、UMovieSceneSection。
|
||||||
@ -74,5 +81,266 @@ UMovieSceneSignedObject -> UObject
|
|||||||
编辑器代码:**FSkeletalAnimationTrackEditor**
|
编辑器代码:**FSkeletalAnimationTrackEditor**
|
||||||
|
|
||||||
## Sequence绑定机制笔记
|
## Sequence绑定机制笔记
|
||||||
|
1. _SpawnableObject_(自动Spawn的Object)
|
||||||
|
表示该Object在Sequence被Evaluate的时候自动Spawn,并且由Sequence来管理生命周期
|
||||||
|
2. _PossessableObject_(可以被赋予的Object)
|
||||||
|
表示该Object可以被外部赋予,比如我手动赋予一个Actor给某个Track
|
||||||
### UMovieSceneSequence
|
### 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::BuildOutlinerEditWidget:MovieSceneSkeletalAnimationTrack下面生成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。
|
Loading…
x
Reference in New Issue
Block a user