first commit

This commit is contained in:
zhangmiao 2025-08-25 15:12:17 +08:00
commit 0f9619fef0
17 changed files with 755 additions and 0 deletions

View File

@ -0,0 +1,29 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "0.2",
"FriendlyName": "AdvancedMovieTracks",
"Description": "",
"Category": "Other",
"CreatedBy": "",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "AdvancedMovieTracks",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "AdvancedMovieTracksEditor",
"Type": "Editor",
"LoadingPhase": "Default"
}
]
}

View File

@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,53 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class AdvancedMovieTracks : ModuleRules
{
public AdvancedMovieTracks(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core","MovieSceneTracks","MovieScene","CinematicCamera"
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore", "LevelSequence",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}

View File

@ -0,0 +1,107 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "CameraCutCameraCompHook.h"
#include "LevelSequence.h"
#include "LevelSequencePlayer.h"
#include "Camera/CameraComponent.h"
#include "Sections/MovieSceneCameraCutSection.h"
#include "Tracks/MovieSceneCameraCutTrack.h"
// Sets default values for this component's properties
UCameraCutCameraCompHook::UCameraCutCameraCompHook()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UCameraCutCameraCompHook::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UCameraCutCameraCompHook::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!MyLevelSequencePlayer) return;
// 获取 CameraCut 轨道
ULevelSequence* LevelSequence = Cast<ULevelSequence>(MyLevelSequencePlayer->GetSequence());
if (!LevelSequence) return;
UMovieSceneCameraCutTrack* CameraCutTrack = Cast<UMovieSceneCameraCutTrack>(LevelSequence->GetMovieScene()->GetCameraCutTrack());
if (!CameraCutTrack) return;
//FQualifiedFrameTime CurrentTime = MyLevelSequencePlayer->GetCurrentTime(); // 获取当前时间
//auto CurrentFrame = CurrentTime.Time.FrameNumber*800;//不够精确
// 获取当前播放帧
FFrameRate TickResolution = LevelSequence->GetMovieScene() ->GetTickResolution();
FQualifiedFrameTime QualifiedTime = MyLevelSequencePlayer->GetCurrentTime();
FFrameTime TimeInTick = ConvertFrameTime(QualifiedTime.Time, QualifiedTime.Rate, TickResolution);
FFrameNumber CurrentTickFrame = TimeInTick.GetFrame();
// 查找当前激活的 CameraCut 段
UCameraComponent* ActiveCamComp = nullptr;
for (UMovieSceneSection* Section : CameraCutTrack->GetAllSections())
{
if (UMovieSceneCameraCutSection* CamSection = Cast<UMovieSceneCameraCutSection>(Section))
{
auto StartFrame = CamSection->GetInclusiveStartFrame();
auto EndFrame = CamSection->GetExclusiveEndFrame();
if (CurrentTickFrame >= StartFrame && CurrentTickFrame < EndFrame)
{
// 获取绑定的对象
TArray<UObject*> BoundObjs = MyLevelSequencePlayer->GetBoundObjects(CamSection->GetCameraBindingID());
for (UObject* Obj : BoundObjs)
{
if (AActor* Actor = Cast<AActor>(Obj))
{
ActiveCamComp = Actor->FindComponentByClass<UCameraComponent>();
}
else if (UCameraComponent* CamComp = Cast<UCameraComponent>(Obj))
{
ActiveCamComp = CamComp;
}
if (ActiveCamComp) break;
}
break;
}
}
}
FoundCamera = nullptr;
// 如果找到摄像机组件,就可以获取其位置
if (ActiveCamComp)
{
FoundCamera = ActiveCamComp;
FVector CamLocation = ActiveCamComp->GetComponentLocation();
FString CamName = ActiveCamComp->GetOwner()-> GetName();
if(bNeedDebug)
{
UE_LOG(LogTemp, Log, TEXT("Current Camera position: %s,Name: %s"), *CamLocation.ToString(), *CamName);
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Orange, FString::Printf(TEXT("Current Camera position: %s,Name: %s"), *CamLocation.ToString(), *CamName));
DrawDebugSphere(GetOwner()->GetWorld(), CamLocation, 36.f, 12, FColor::Cyan, false, 0.2f, 0, 1.f);
}
// 可以在这里对相机位置进行处理
}
}
bool UCameraCutCameraCompHook::FoundCameraComp(UCameraComponent*& FoundCameraComp)
{
FoundCameraComp = FoundCamera;
return IsValid(FoundCamera);
}

View File

@ -0,0 +1,44 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CameraCutCameraCompHook.generated.h"
class UCameraComponent;
class ULevelSequencePlayer;
/**
* This Component Must Tick while you're using FoundCameraComp()
*/
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class ADVANCEDMOVIETRACKS_API UCameraCutCameraCompHook : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UCameraCutCameraCompHook();
protected:
// Called when the game starts
virtual void BeginPlay() override;
UPROPERTY(BlueprintReadWrite,VisibleAnywhere,meta=(AllowPrivateAccess=true))
ULevelSequencePlayer* MyLevelSequencePlayer;
UPROPERTY()
UCameraComponent* FoundCamera;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(BlueprintReadWrite, VisibleAnywhere, meta = (AllowPrivateAccess = true))
bool bNeedDebug = false;
UFUNCTION(BlueprintPure)
bool FoundCameraComp(UCameraComponent*& FoundCameraComp);
};

View File

@ -0,0 +1,39 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "CineCustomedCameraActor.h"
//#include "CineCameraActor.h"
ACineCustomedCameraActor::ACineCustomedCameraActor(const FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ACineCustomedCameraActor::BeginPlay()
{
Super::BeginPlay();
}
void ACineCustomedCameraActor::NotifyCameraCut()
{
FString a(FString::Printf(TEXT("CameraCutIn:%s"),*GetName()));
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Cyan, a);
}
UE_LOG(LogTemp, Warning, TEXT("%s"),*a);
Super::NotifyCameraCut();
}
// Called every frame
void ACineCustomedCameraActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}

View File

@ -0,0 +1,31 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "CineCameraActor.h"
#include "CineCustomedCameraActor.generated.h"
UCLASS()
class ADVANCEDMOVIETRACKS_API ACineCustomedCameraActor : public ACineCameraActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ACineCustomedCameraActor(const FObjectInitializer& ObjectInitializer);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void NotifyCameraCut() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};

View File

@ -0,0 +1,71 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "CustomAudioTrack.h"
void UCustomAudioTrack::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
}
bool UCustomAudioTrack::Rename(const TCHAR* NewName, UObject* NewOuter, ERenameFlags Flags)
{
RuntimeName = NewName;
return Super::Rename(NewName, NewOuter, Flags);
}
bool UCustomAudioTrack::SupportsType(TSubclassOf<UMovieSceneSection> SectionClass) const
{
return Super::SupportsType(SectionClass);
}
void UCustomAudioTrack::RemoveAllAnimationData()
{
Super::RemoveAllAnimationData();
}
bool UCustomAudioTrack::HasSection(const UMovieSceneSection& Section) const
{
return Super::HasSection(Section);
}
void UCustomAudioTrack::AddSection(UMovieSceneSection& Section)
{
Super::AddSection(Section);
}
void UCustomAudioTrack::RemoveSection(UMovieSceneSection& Section)
{
Super::RemoveSection(Section);
}
void UCustomAudioTrack::RemoveSectionAt(int32 SectionIndex)
{
Super::RemoveSectionAt(SectionIndex);
}
bool UCustomAudioTrack::IsEmpty() const
{
return Super::IsEmpty();
}
const TArray<UMovieSceneSection*>& UCustomAudioTrack::GetAllSections() const
{
return Super::GetAllSections();
}
bool UCustomAudioTrack::SupportsMultipleRows() const
{
return Super::SupportsMultipleRows();
}
UMovieSceneSection* UCustomAudioTrack::CreateNewSection()
{
return Super::CreateNewSection();
}

View File

@ -0,0 +1,64 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
//"CustomAudioTrack.h"
#include "CoreMinimal.h"
#include "Evaluation/MovieSceneEvalTemplate.h"
#include "Tracks/MovieSceneAudioTrack.h"
#include "CustomAudioTrack.generated.h"
/**
*
*/
UCLASS()
class ADVANCEDMOVIETRACKS_API UCustomAudioTrack : public UMovieSceneAudioTrack
{
GENERATED_BODY()
public:
// ... 之前的代码 ...
// 重写显示名称
#if WITH_EDITOR
virtual FText GetDisplayName() const override
{
if (!RuntimeName.IsNone())
{
return FText::FromName(RuntimeName);
}
return Super::GetDisplayName();
}
#endif
#if WITH_EDITORONLY_DATA
virtual FText GetDefaultDisplayName() const override { return FText::FromString(TEXT("CustomAudioTrack")); };
virtual void SetDisplayName(const FText& NewDisplayName)override {
RuntimeName = *NewDisplayName.ToString();
Super::SetDisplayName(NewDisplayName);
}
#endif
// UMovieSceneTrack interface
virtual bool SupportsType(TSubclassOf<UMovieSceneSection> SectionClass) const override;
virtual void RemoveAllAnimationData() override;
virtual bool HasSection(const UMovieSceneSection& Section) const override;
virtual void AddSection(UMovieSceneSection& Section) override;
virtual void RemoveSection(UMovieSceneSection& Section) override;
virtual void RemoveSectionAt(int32 SectionIndex) override;
virtual bool IsEmpty() const override;
virtual const TArray<UMovieSceneSection*>& GetAllSections() const override;
virtual bool SupportsMultipleRows() const override;
virtual UMovieSceneSection* CreateNewSection() override;
// 支持序列化
virtual void Serialize(FArchive& Ar) override;
virtual bool Rename(const TCHAR* NewName = nullptr, UObject* NewOuter = nullptr, ERenameFlags Flags = REN_None)override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Identification", meta = (DisplayName = "Track Name"))
FName RuntimeName;
/*UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Identification", meta = (DisplayName = "Track Name"))
FName RuntimeNameA;*/
};

View File

@ -0,0 +1,35 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AdvancedMovieTracks.h"
#define LOCTEXT_NAMESPACE "FAdvancedMovieTracksModule"
void FAdvancedMovieTracksModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
//ISequencerModule& SequencerModule = FModuleManager::Get().LoadModuleChecked<ISequencerModule>("Sequencer");
//// ×¢²á×Ô¶¨Òå¹ìµÀ±à¼­Æ÷
//TrackEditorBindingHandle = SequencerModule.RegisterTrackEditor(
// FOnCreateTrackEditor::CreateStatic(&FCustomAudioTrackEditor::CreateTrackEditor)
//);
}
void FAdvancedMovieTracksModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
/*ISequencerModule* SequencerModule = FModuleManager::Get().GetModulePtr<ISequencerModule>("Sequencer");
if (SequencerModule)
{
SequencerModule->UnRegisterTrackEditor(TrackEditorBindingHandle);
}*/
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FAdvancedMovieTracksModule, AdvancedMovieTracks)

View File

@ -0,0 +1,16 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FAdvancedMovieTracksModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
FDelegateHandle TrackEditorBindingHandle;
};

View File

@ -0,0 +1,64 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class AdvancedMovieTracksEditor : ModuleRules
{
public AdvancedMovieTracksEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core","MovieSceneTracks","MovieScene","AdvancedMovieTracks"
// ... add other public dependencies that you statically link with here ...
}
);
if (Target.bBuildWithEditorOnlyData && Target.bBuildEditor)
{
PublicDependencyModuleNames.AddRange(new string[]
{
"UnrealEd"
});
PrivateDependencyModuleNames.AddRange(new string[]
{
"UnrealEd", "Sequencer"
});
}
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}

View File

@ -0,0 +1,105 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "CustomAudioTrackEditor.h"
#include "AdvancedMovieTracks/CustomAudioTrack.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#if WITH_EDITOR
#include "Sections/MovieSceneAudioSection.h"
#define LOCTEXT_NAMESPACE "FAudioTrackEditor"
FCustomAudioTrackEditor::FCustomAudioTrackEditor(TSharedRef<ISequencer> InSequencer)
: FMovieSceneTrackEditor(InSequencer)
{
}
//UMovieSceneTrack* FCustomAudioTrackEditor::AddTrack(UMovieScene* FocusedMovieScene, const FGuid& ObjectHandle, TSubclassOf<UMovieSceneTrack> TrackClass, FName UniqueTypeName) const
//{
// //// 确保是自定义轨道类型
// //if (TrackClass == UCustomAudioTrack::StaticClass())
// //{
// // // 创建轨道
// // UCustomAudioTrack* NewTrack = FocusedMovieScene->AddMasterTrack<UCustomAudioTrack>();
// // NewTrack->SetDisplayName(FText::FromString("Custom Audio Track"));
// // return NewTrack;
// //}
// //return nullptr;
// return FocusedMovieScene->AddTrack(TrackClass, ObjectHandle);
//}
void FCustomAudioTrackEditor::BuildAddTrackMenu(FMenuBuilder& MenuBuilder)
{
MenuBuilder.AddMenuEntry(
FText::FromString("Custom Audio Track"),
FText::FromString("Adds a custom audio track"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateRaw(this, &FCustomAudioTrackEditor::HandleAddAudioTrackMenuEntryExecute))
);
}
void FCustomAudioTrackEditor::HandleAddAudioTrackMenuEntryExecute()
{
UMovieScene* FocusedMovieScene = GetFocusedMovieScene();
if (FocusedMovieScene == nullptr)
{
return;
}
if (FocusedMovieScene->IsReadOnly())
{
return;
}
const FScopedTransaction Transaction(NSLOCTEXT("Sequencer", "AddAudioTrack_Transaction", "Add My Audio Track"));
FocusedMovieScene->Modify();
auto NewTrack = FocusedMovieScene->AddTrack<UCustomAudioTrack>();
ensure(NewTrack);
NewTrack->SetDisplayName(FText::FromString(TEXT("My Audio")));
if (GetSequencer().IsValid())
{
GetSequencer()->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::TrackValueChanged);
// 或者 NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemsChanged) 视场景而定
GetSequencer()->OnAddTrack(NewTrack, FGuid());
}
}
bool FCustomAudioTrackEditor::SupportsType(TSubclassOf<UMovieSceneTrack> TrackClass) const
{
return TrackClass == UCustomAudioTrack::StaticClass();
}
TSharedRef<ISequencerTrackEditor> FCustomAudioTrackEditor::CreateTrackEditor(TSharedRef<ISequencer> InSequencer)
{
return MakeShared<FCustomAudioTrackEditor>(InSequencer);
}
//
//void FCustomAudioTrackEditor::HandleAddTrack()
//{
// UMovieScene* FocusedMovieScene = GetFocusedMovieScene();
// if (FocusedMovieScene)
// {
// // 添加轨道
// UCustomAudioTrack* NewTrack = FocusedMovieScene->AddMasterTrack<UCustomAudioTrack>();
// if (NewTrack)
// {
// // 添加默认音频片段
// NewTrack->AddNewSound(nullptr, 0.0f);
//
// // 通知Sequencer刷新
// GetSequencer()->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemAdded);
// }
// }
//}
#undef LOCTEXT_NAMESPACE
#endif

View File

@ -0,0 +1,32 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
// "CustomAudioTrackEditor.h"
#include "CoreMinimal.h"
#if WITH_EDITOR
#include "MovieSceneTrackEditor.h"
class FCustomAudioTrackEditor : public FMovieSceneTrackEditor
{
public:
FCustomAudioTrackEditor(TSharedRef<ISequencer> InSequencer);
// 创建轨道实例
//virtual UMovieSceneTrack* AddTrack(UMovieScene* FocusedMovieScene, const FGuid& ObjectHandle, TSubclassOf<UMovieSceneTrack> TrackClass, FName UniqueTypeName) const override;
// 在菜单中显示
virtual void BuildAddTrackMenu(FMenuBuilder& MenuBuilder) override;
// 检查是否支持添加轨道
virtual bool SupportsType(TSubclassOf<UMovieSceneTrack> TrackClass) const override;
// 工厂创建函数
static TSharedRef<ISequencerTrackEditor> CreateTrackEditor(TSharedRef<ISequencer> InSequencer);
private:
virtual void HandleAddAudioTrackMenuEntryExecute();
};
#endif

View File

@ -0,0 +1,41 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AdvancedMovieTracksEditor.h"
#if WITH_EDITOR
#include "AdvancedMovieTracks.h"
#include "ISequencerModule.h"
#include "AdvancedMovieTracksEditor/CustomAudioTrackEditor.h"
#define LOCTEXT_NAMESPACE "FAdvancedMovieTracksEditorModule"
void FAdvancedMovieTracksEditorModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
ISequencerModule& SequencerModule = FModuleManager::Get().LoadModuleChecked<ISequencerModule>("Sequencer");
// ×¢²á×Ô¶¨Òå¹ìµÀ±à¼­Æ÷
TrackEditorBindingHandle = SequencerModule.RegisterTrackEditor(
FOnCreateTrackEditor::CreateStatic(&FCustomAudioTrackEditor::CreateTrackEditor)
);
}
void FAdvancedMovieTracksEditorModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
ISequencerModule* SequencerModule = FModuleManager::Get().GetModulePtr<ISequencerModule>("Sequencer");
if (SequencerModule)
{
SequencerModule->UnRegisterTrackEditor(TrackEditorBindingHandle);
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FAdvancedMovieTracksEditorModule, AdvancedMovieTracksEditor)
#endif

View File

@ -0,0 +1,16 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FAdvancedMovieTracksEditorModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
FDelegateHandle TrackEditorBindingHandle;
};