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. 给导播创建一个快捷键(Hotkey),之后在触发根据Tag切换对应角色动画蓝图中的逻辑。
|
||||
2. 使用Sequence的EventTrack设置若干事件。
|
||||
3. 模改UMovieSceneSkeletalAnimationTrack,实现一个可以自动根据Tag捕获对应角色并且播发动画的功能。
|
||||
|
||||
# 方案一
|
||||
- UMovieSceneTrack::SetEvalDisabled(bool)
|
||||
- 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/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::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