├── Resources └── Icon128.png ├── Content └── Editor │ └── PaperFlipbookUserWidget_64x.png ├── README.md ├── Config └── FilterPlugin.ini ├── .gitignore ├── Source ├── PaperFlipbookWidget │ ├── Public │ │ ├── PaperFlipbookWidgetStats.h │ │ ├── PaperFlipbookWidget.h │ │ ├── SPaperFlipbookWidget.h │ │ └── PaperFlipbookUserWidget.h │ ├── Private │ │ ├── PaperFlipbookWidget.cpp │ │ ├── PaperFlipbookUserWidget.cpp │ │ └── SPaperFlipbookWidget.cpp │ └── PaperFlipbookWidget.Build.cs └── PaperFlipbookWidgetEditor │ ├── Public │ └── PaperFlipbookWidgetEditor.h │ ├── Private │ ├── PaperFlipbookWidgetEditorStyle.h │ ├── PaperFlipbookWidgetEditor.cpp │ └── PaperFlipbookWidgetEditorStyle.cpp │ └── PaperFlipbookWidgetEditor.Build.cs ├── PaperFlipbookWidget.uplugin └── LICENSE /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HoussineMehnik/UE4-PaperFlipbookWidgetPlugin/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /Content/Editor/PaperFlipbookUserWidget_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HoussineMehnik/UE4-PaperFlipbookWidgetPlugin/HEAD/Content/Editor/PaperFlipbookUserWidget_64x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # To Download Examples project or Compiled plugin version 2 | Visit https://unrealengineresources.com/plugins 3 | 4 | 5 | # Paper Flipbook Widget 6 | 7 | [![Project](https://img.youtube.com/vi/o-NE4kOKh1E/0.jpg)](https://youtu.be/o-NE4kOKh1E) 8 | -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # # a comment - this is ignored 2 | # *.a # no .a files 3 | # !lib.a # but do track lib.a, even though you're ignoring .a files above 4 | # /TODO # only ignore the root TODO file, not subdir/TODO 5 | # build/ # ignore all files in the build/ directory 6 | # doc/*.txt # ignore doc/notes.txt, but not doc/server/arch.txt 7 | 8 | !* 9 | *.obj 10 | *.dll 11 | *.pdb 12 | *.generated.h 13 | Intermediate/ 14 | Saved/ 15 | Binaries/ 16 | .vs/ -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Public/PaperFlipbookWidgetStats.h: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #pragma once 6 | 7 | #include "CoreMinimal.h" 8 | #include "Stats/Stats.h" 9 | 10 | DECLARE_STATS_GROUP(TEXT("FlipbookWidget"), STATGROUP_FlipbookWidget, STATCAT_Advanced); 11 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Public/PaperFlipbookWidget.h: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #pragma once 6 | 7 | #include "CoreMinimal.h" 8 | #include "Modules/ModuleManager.h" 9 | 10 | class FPaperFlipbookWidgetModule : public IModuleInterface 11 | { 12 | public: 13 | 14 | /** IModuleInterface implementation */ 15 | virtual void StartupModule() override; 16 | virtual void ShutdownModule() override; 17 | }; 18 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidgetEditor/Public/PaperFlipbookWidgetEditor.h: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #pragma once 6 | 7 | #include "CoreMinimal.h" 8 | #include "Modules/ModuleManager.h" 9 | 10 | class FPaperFlipbookWidgetEditorModule : public IModuleInterface 11 | { 12 | public: 13 | 14 | /** IModuleInterface implementation */ 15 | virtual void StartupModule() override; 16 | virtual void ShutdownModule() override; 17 | }; 18 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidgetEditor/Private/PaperFlipbookWidgetEditorStyle.h: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #pragma once 6 | 7 | #include "CoreMinimal.h" 8 | #include "Styling/ISlateStyle.h" 9 | 10 | class FPaperFlipbookWidgetEditorStyle 11 | { 12 | 13 | private: 14 | static TSharedPtr< class FSlateStyleSet > StyleSet; 15 | 16 | public: 17 | static void Initialize(); 18 | static void Shutdown(); 19 | static TSharedPtr Get(); 20 | static FName GetAppStyleSetName(); 21 | 22 | private: 23 | static FString InContent(const FString& RelativePath, const ANSICHAR* Extension); 24 | }; -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Private/PaperFlipbookWidget.cpp: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #include "PaperFlipbookWidget.h" 6 | 7 | #define LOCTEXT_NAMESPACE "PaperFlipbookWidgetModule" 8 | 9 | void FPaperFlipbookWidgetModule::StartupModule() 10 | { 11 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 12 | } 13 | 14 | void FPaperFlipbookWidgetModule::ShutdownModule() 15 | { 16 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 17 | // we call this function before unloading the module. 18 | } 19 | 20 | #undef LOCTEXT_NAMESPACE 21 | 22 | IMPLEMENT_MODULE(FPaperFlipbookWidgetModule, PaperFlipbookWidget) -------------------------------------------------------------------------------- /PaperFlipbookWidget.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Paper Flipbook Widget", 6 | "Description": "Paper flipbook widget allows you to display a flipbook asset in the UI.", 7 | "Category": "User Interface", 8 | "CreatedBy": "Elhoussine Mehnik", 9 | "CreatedByURL": "https://twitter.com/HoussineMehnik", 10 | "DocsURL": "https://unrealengineresources.com/documentation", 11 | "MarketplaceURL": "https://unrealengineresources.com/plugins", 12 | "SupportURL": "mailto:ue4resources@gmail.com", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "EnabledByDefault": false, 18 | "Modules": [ 19 | { 20 | "Name": "PaperFlipbookWidget", 21 | "Type": "Runtime", 22 | "LoadingPhase": "PreDefault" 23 | }, 24 | { 25 | "Name": "PaperFlipbookWidgetEditor", 26 | "Type": "Editor", 27 | "LoadingPhase": "PostEngineInit" 28 | } 29 | ], 30 | "Plugins": [ 31 | { 32 | "Name": "Paper2D", 33 | "Enabled": true 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Source/PaperFlipbookWidgetEditor/Private/PaperFlipbookWidgetEditor.cpp: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #include "PaperFlipbookWidgetEditor.h" 6 | #include "PaperFlipbookWidgetEditorStyle.h" 7 | 8 | #define LOCTEXT_NAMESPACE "PaperFlipbookWidgetEditorModule" 9 | 10 | void FPaperFlipbookWidgetEditorModule::StartupModule() 11 | { 12 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 13 | FPaperFlipbookWidgetEditorStyle::Initialize(); 14 | } 15 | 16 | void FPaperFlipbookWidgetEditorModule::ShutdownModule() 17 | { 18 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 19 | // we call this function before unloading the module. 20 | FPaperFlipbookWidgetEditorStyle::Shutdown(); 21 | } 22 | 23 | #undef LOCTEXT_NAMESPACE 24 | 25 | IMPLEMENT_MODULE(FPaperFlipbookWidgetEditorModule, PaperFlipbookWidgetEditor) -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/PaperFlipbookWidget.Build.cs: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | using UnrealBuildTool; 6 | 7 | public class PaperFlipbookWidget : ModuleRules 8 | { 9 | public PaperFlipbookWidget(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; /*PCHUsage = ModuleRules.PCHUsageMode.NoPCHs; bUseUnity = false;*/ 12 | 13 | PrivateIncludePaths.Add("PaperFlipbookWidget/Private"); 14 | 15 | PublicDependencyModuleNames.AddRange( 16 | new string[] 17 | { 18 | "Core", 19 | // ... add other public dependencies that you statically link with here ... 20 | } 21 | ); 22 | 23 | 24 | PrivateDependencyModuleNames.AddRange( 25 | new string[] 26 | { 27 | "CoreUObject", 28 | "Engine", 29 | "Slate", 30 | "SlateCore", 31 | "UMG", 32 | "Paper2D", 33 | // ... add private dependencies that you statically link with here ... 34 | } 35 | ); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidgetEditor/PaperFlipbookWidgetEditor.Build.cs: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | using UnrealBuildTool; 6 | 7 | public class PaperFlipbookWidgetEditor : ModuleRules 8 | { 9 | public PaperFlipbookWidgetEditor(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; /*PCHUsage = ModuleRules.PCHUsageMode.NoPCHs; bUseUnity = false;*/ 12 | 13 | PrivateIncludePaths.Add("PaperFlipbookWidgetEditor/Private"); 14 | 15 | PublicDependencyModuleNames.AddRange( 16 | new string[] 17 | { 18 | "Core", 19 | // ... add other public dependencies that you statically link with here ... 20 | } 21 | ); 22 | 23 | 24 | PrivateDependencyModuleNames.AddRange( 25 | new string[] 26 | { 27 | "CoreUObject", 28 | "Engine", 29 | "Slate", 30 | "SlateCore", 31 | "Projects", 32 | "EditorFramework", 33 | // ... add private dependencies that you statically link with here ... 34 | } 35 | ); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | //====================== Editor Scripting Tools ===========================// 4 | // Copyright 2020 Elhoussine Mehnik (Mhousse1247). All Rights Reserved. 5 | // It's free of charge and can be used for any project and any purpose as long as it is not violating the EULA. 6 | //====================== http://ue4resources.com/ ========================// 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidgetEditor/Private/PaperFlipbookWidgetEditorStyle.cpp: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #include "PaperFlipbookWidgetEditorStyle.h" 6 | #include "Interfaces/IPluginManager.h" 7 | #include "Styling/SlateStyleRegistry.h" 8 | #include "SlateOptMacros.h" 9 | #include "Styling/SlateTypes.h" 10 | #include "Styling/SlateStyle.h" 11 | 12 | 13 | 14 | 15 | 16 | #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( FPaperFlipbookWidgetEditorStyle::InContent( RelativePath, ".png" ), __VA_ARGS__ ) 17 | 18 | 19 | FString FPaperFlipbookWidgetEditorStyle::InContent(const FString& RelativePath, const ANSICHAR* Extension) 20 | { 21 | static FString IconsDir = IPluginManager::Get().FindPlugin(TEXT("PaperFlipbookWidget"))->GetContentDir() / TEXT("Editor"); 22 | return (IconsDir / RelativePath) + Extension; 23 | } 24 | 25 | TSharedPtr< FSlateStyleSet > FPaperFlipbookWidgetEditorStyle::StyleSet = NULL; 26 | TSharedPtr< class ISlateStyle > FPaperFlipbookWidgetEditorStyle::Get() { return StyleSet; } 27 | 28 | 29 | FName FPaperFlipbookWidgetEditorStyle::GetAppStyleSetName() 30 | { 31 | return StyleSet->GetStyleSetName(); 32 | } 33 | 34 | 35 | BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 36 | 37 | void FPaperFlipbookWidgetEditorStyle::Initialize() 38 | { 39 | 40 | // Const icon & thumbnail sizes 41 | const FVector2D Icon16x16(16.0f, 16.f); 42 | const FVector2D Icon64x64(64.f, 64.f); 43 | 44 | 45 | // Only register once 46 | if (StyleSet.IsValid()) 47 | { 48 | return; 49 | } 50 | 51 | // Style Set Allocation 52 | StyleSet = MakeShareable(new FSlateStyleSet("PaperFlipbookWidgetStyle")); 53 | FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("PaperFlipbookWidget"))->GetContentDir(); 54 | StyleSet->SetContentRoot(ContentDir); 55 | 56 | 57 | // Class Thumbnails 58 | StyleSet->Set("ClassIcon.PaperFlipbookUserWidget", new IMAGE_BRUSH("PaperFlipbookUserWidget_64x", Icon16x16)); 59 | StyleSet->Set("ClassThumbnail.PaperFlipbookUserWidget", new IMAGE_BRUSH("PaperFlipbookUserWidget_64x", Icon64x64)); 60 | 61 | 62 | // Register Style Set 63 | FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); 64 | }; 65 | 66 | END_SLATE_FUNCTION_BUILD_OPTIMIZATION 67 | 68 | #undef IMAGE_BRUSH 69 | 70 | 71 | void FPaperFlipbookWidgetEditorStyle::Shutdown() 72 | { 73 | if (StyleSet.IsValid()) 74 | { 75 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); 76 | ensure(StyleSet.IsUnique()); 77 | StyleSet.Reset(); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Public/SPaperFlipbookWidget.h: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #pragma once 6 | 7 | #include "CoreMinimal.h" 8 | #include "Misc/Attribute.h" 9 | #include "Input/Reply.h" 10 | #include "Widgets/DeclarativeSyntaxSupport.h" 11 | #include "Styling/SlateColor.h" 12 | #include "Styling/CoreStyle.h" 13 | #include "Widgets/SLeafWidget.h" 14 | 15 | class FPaintArgs; 16 | class FSlateWindowElementList; 17 | class UPaperFlipbook; 18 | class UPaperSprite; 19 | 20 | /** 21 | * Implements a widget that displays a paper flipbook. 22 | */ 23 | class PAPERFLIPBOOKWIDGET_API SPaperFlipbookWidget 24 | : public SLeafWidget 25 | { 26 | public: 27 | SLATE_BEGIN_ARGS(SPaperFlipbookWidget) 28 | : _ColorAndOpacity(FLinearColor::White) 29 | , _OnMouseButtonDown() 30 | { 31 | } 32 | 33 | 34 | /** Color and opacity */ 35 | SLATE_ATTRIBUTE(FSlateColor, ColorAndOpacity) 36 | 37 | /** Invoked when the mouse is pressed in the widget. */ 38 | SLATE_EVENT(FPointerEventHandler, OnMouseButtonDown) 39 | 40 | /** Called when the button is pressed */ 41 | SLATE_EVENT(FSimpleDelegate, OnFinishedPlaying) 42 | 43 | SLATE_END_ARGS() 44 | 45 | /** Constructor */ 46 | SPaperFlipbookWidget(); 47 | 48 | /** 49 | * Construct this widget 50 | * 51 | * @param InArgs The declaration data for this widget 52 | */ 53 | void Construct(const FArguments& InArgs); 54 | 55 | public: 56 | 57 | /** See the ColorAndOpacity attribute */ 58 | void SetColorAndOpacity(const TAttribute& InColorAndOpacity); 59 | 60 | /** See the ColorAndOpacity attribute */ 61 | void SetColorAndOpacity(FLinearColor InColorAndOpacity); 62 | 63 | /** See OnMouseButtonDown event */ 64 | void SetOnMouseButtonDown(FPointerEventHandler EventHandler); 65 | 66 | /** Set OnFinishedPlaying event */ 67 | void SetOnFinishedPlaying(FSimpleDelegate InOnFinishedPlaying); 68 | 69 | void OverrideBrushSize(FVector2D InDesiredSize, bool bOverride); 70 | 71 | /** */ 72 | void SetBrushTintColor(FSlateColor TintColor); 73 | 74 | /** */ 75 | void SetBrushTiling(TEnumAsByte InTiling); 76 | 77 | /** */ 78 | void SetBrushMirroring(TEnumAsByte InMirroring); 79 | 80 | FSlateBrush* GetBrush(); 81 | 82 | public: 83 | 84 | // SWidget overrides 85 | virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; 86 | virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; 87 | virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; 88 | 89 | protected: 90 | // Begin SWidget overrides. 91 | virtual FVector2D ComputeDesiredSize(float) const override; 92 | // End SWidget overrides. 93 | 94 | protected: 95 | 96 | FSlateBrush DefaultBrush; 97 | 98 | uint8 bUseSpriteSize : 1; 99 | 100 | /** Color and opacity scale for this image */ 101 | TAttribute ColorAndOpacity; 102 | 103 | /** Invoked when the mouse is pressed in the image */ 104 | FPointerEventHandler OnMouseButtonDownHandler; 105 | 106 | /** The delegate to execute when a non-looping flipbook finishes playing */ 107 | FSimpleDelegate OnFinishedPlaying; 108 | 109 | /** Flipbook currently being played */ 110 | UPaperFlipbook* SourceFlipbook; 111 | 112 | /** Current play rate of the flipbook */ 113 | float PlayRate; 114 | 115 | /** Whether the flipbook should loop when it reaches the end, or stop */ 116 | uint8 bLooping : 1; 117 | 118 | /** If playback should move the current position backwards instead of forwards */ 119 | uint8 bReversePlayback : 1; 120 | 121 | /** Are we currently playing (moving Position) */ 122 | uint8 bPlaying : 1; 123 | 124 | /** Current position in the timeline */ 125 | float AccumulatedTime; 126 | 127 | /** Last frame index calculated */ 128 | int32 CachedFrameIndex; 129 | 130 | public: 131 | /** Change the flipbook used by this instance (will reset the play time to 0 if it is a new flipbook). */ 132 | virtual bool SetFlipbook(class UPaperFlipbook* NewFlipbook); 133 | 134 | /** Gets the flipbook used by this instance. */ 135 | virtual UPaperFlipbook* GetFlipbook(); 136 | 137 | /** Start playback of flipbook */ 138 | void Play(); 139 | 140 | /** Start playback of flipbook from the start */ 141 | void PlayFromStart(); 142 | 143 | /** Start playback of flipbook in reverse */ 144 | void Reverse(); 145 | 146 | /** Start playback of flipbook in reverse from the end */ 147 | void ReverseFromEnd(); 148 | 149 | /** Stop playback of flipbook */ 150 | void Stop(); 151 | 152 | /** Get whether this flipbook is playing or not. */ 153 | bool IsPlaying() const; 154 | 155 | /** Get whether we are reversing or not */ 156 | bool IsReversing() const; 157 | 158 | /** Jump to a position in the flipbook (expressed in frames). If bFireEvents is true, event functions will fire, otherwise they will not. */ 159 | void SetPlaybackPositionInFrames(int32 NewFramePosition, bool bFireEvents); 160 | 161 | /** Get the current playback position (in frames) of the flipbook */ 162 | int32 GetPlaybackPositionInFrames() const; 163 | 164 | /** Jump to a position in the flipbook (expressed in seconds). If bFireEvents is true, event functions will fire, otherwise they will not. */ 165 | void SetPlaybackPosition(float NewPosition, bool bFireEvents); 166 | 167 | /** Get the current playback position (in seconds) of the flipbook */ 168 | float GetPlaybackPosition() const; 169 | 170 | /** true means we should loop, false means we should not. */ 171 | void SetLooping(bool bNewLooping); 172 | 173 | /** Get whether we are looping or not */ 174 | bool IsLooping() const; 175 | 176 | /** Sets the new play rate for this flipbook */ 177 | void SetPlayRate(float NewRate); 178 | 179 | /** Get the current play rate for this flipbook */ 180 | float GetPlayRate() const; 181 | 182 | /** Set the new playback position time to use */ 183 | void SetNewTime(float NewTime); 184 | 185 | /** Get length of the flipbook (in seconds) */ 186 | float GetFlipbookLength() const; 187 | 188 | /** Get length of the flipbook (in frames) */ 189 | int32 GetFlipbookLengthInFrames() const; 190 | 191 | /** Get the nominal framerate that the flipbook will be played back at (ignoring PlayRate), in frames per second */ 192 | float GetFlipbookFramerate() const; 193 | 194 | protected: 195 | 196 | void CalculateCurrentFrame(); 197 | UPaperSprite* GetSpriteAtCachedIndex() const; 198 | 199 | void TickFlipbook(float DeltaTime); 200 | }; 201 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Private/PaperFlipbookUserWidget.cpp: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #include "PaperFlipbookUserWidget.h" 6 | #include "Slate/SlateBrushAsset.h" 7 | #include "SPaperFlipbookWidget.h" 8 | 9 | #define LOCTEXT_NAMESPACE "Paper2D" 10 | 11 | ///////////////////////////////////////////////////// 12 | // UPaperFlipbookUserWidget 13 | 14 | UPaperFlipbookUserWidget::UPaperFlipbookUserWidget(const FObjectInitializer& ObjectInitializer) 15 | : Super(ObjectInitializer) 16 | , SourceFlipbook(nullptr) 17 | , bAutoPlay(true) 18 | , PlayRate(1.0f) 19 | , bLooping(true) 20 | , ColorAndOpacity(FLinearColor::White) 21 | , bOverrideContentSize(false) 22 | , DesiredSize(0.f, 0.f) 23 | { 24 | 25 | } 26 | 27 | void UPaperFlipbookUserWidget::ReleaseSlateResources(bool bReleaseChildren) 28 | { 29 | Super::ReleaseSlateResources(bReleaseChildren); 30 | 31 | MyFlipbookWG.Reset(); 32 | } 33 | 34 | TSharedRef UPaperFlipbookUserWidget::RebuildWidget() 35 | { 36 | MyFlipbookWG = SNew(SPaperFlipbookWidget); 37 | return MyFlipbookWG.ToSharedRef(); 38 | } 39 | 40 | void UPaperFlipbookUserWidget::SynchronizeProperties() 41 | { 42 | Super::SynchronizeProperties(); 43 | 44 | TAttribute ColorAndOpacityBinding = PROPERTY_BINDING(FSlateColor, ColorAndOpacity); 45 | 46 | if (MyFlipbookWG.IsValid()) 47 | { 48 | MyFlipbookWG->OverrideBrushSize(DesiredSize, bOverrideContentSize); 49 | MyFlipbookWG->SetBrushMirroring(Mirroring); 50 | MyFlipbookWG->SetBrushTiling(Tiling); 51 | MyFlipbookWG->SetColorAndOpacity(ColorAndOpacityBinding); 52 | MyFlipbookWG->SetOnMouseButtonDown(BIND_UOBJECT_DELEGATE(FPointerEventHandler, HandleMouseButtonDown)); 53 | MyFlipbookWG->SetOnFinishedPlaying(BIND_UOBJECT_DELEGATE(FSimpleDelegate, OnFlipbookFinishedPlaying)); 54 | MyFlipbookWG->SetFlipbook(SourceFlipbook); 55 | MyFlipbookWG->SetPlayRate(PlayRate); 56 | MyFlipbookWG->SetLooping(bLooping); 57 | 58 | if (bAutoPlay) 59 | { 60 | MyFlipbookWG->Play(); 61 | } 62 | else 63 | { 64 | MyFlipbookWG->Stop(); 65 | } 66 | } 67 | } 68 | 69 | void UPaperFlipbookUserWidget::SetColorAndOpacity(FLinearColor InColorAndOpacity) 70 | { 71 | ColorAndOpacity = InColorAndOpacity; 72 | if (MyFlipbookWG.IsValid()) 73 | { 74 | MyFlipbookWG->SetColorAndOpacity(ColorAndOpacity); 75 | } 76 | } 77 | 78 | void UPaperFlipbookUserWidget::SetOpacity(float InOpacity) 79 | { 80 | ColorAndOpacity.A = InOpacity; 81 | if (MyFlipbookWG.IsValid()) 82 | { 83 | MyFlipbookWG->SetColorAndOpacity(ColorAndOpacity); 84 | } 85 | } 86 | 87 | void UPaperFlipbookUserWidget::OverrideBrushSize(FVector2D InDesiredSize) 88 | { 89 | if (!bOverrideContentSize || DesiredSize != InDesiredSize) 90 | { 91 | bOverrideContentSize = true; 92 | if (MyFlipbookWG.IsValid()) 93 | { 94 | MyFlipbookWG->OverrideBrushSize(DesiredSize, bOverrideContentSize); 95 | } 96 | } 97 | } 98 | 99 | void UPaperFlipbookUserWidget::SetBrushTintColor(FSlateColor TintColor) 100 | { 101 | if (MyFlipbookWG.IsValid()) 102 | { 103 | MyFlipbookWG->SetBrushTintColor(TintColor); 104 | } 105 | } 106 | 107 | void UPaperFlipbookUserWidget::SetBrushTiling(TEnumAsByte InTiling) 108 | { 109 | if (Tiling != InTiling) 110 | { 111 | Tiling = InTiling; 112 | if (MyFlipbookWG.IsValid()) 113 | { 114 | MyFlipbookWG->SetBrushTiling(InTiling); 115 | } 116 | } 117 | } 118 | 119 | void UPaperFlipbookUserWidget::SetBrushMirroring(TEnumAsByte InMirroring) 120 | { 121 | if (Mirroring != InMirroring) 122 | { 123 | Mirroring = InMirroring; 124 | if (MyFlipbookWG.IsValid()) 125 | { 126 | MyFlipbookWG->SetBrushMirroring(InMirroring); 127 | } 128 | } 129 | } 130 | 131 | FReply UPaperFlipbookUserWidget::HandleMouseButtonDown(const FGeometry& Geometry, const FPointerEvent& MouseEvent) 132 | { 133 | if (OnMouseButtonDownEvent.IsBound()) 134 | { 135 | return OnMouseButtonDownEvent.Execute(Geometry, MouseEvent).NativeReply; 136 | } 137 | 138 | return FReply::Unhandled(); 139 | } 140 | 141 | bool UPaperFlipbookUserWidget::SetFlipbook(class UPaperFlipbook* NewFlipbook) 142 | { 143 | if (NewFlipbook != SourceFlipbook) 144 | { 145 | SourceFlipbook = NewFlipbook; 146 | if (MyFlipbookWG.IsValid()) 147 | { 148 | MyFlipbookWG->SetFlipbook(NewFlipbook); 149 | } 150 | return true; 151 | } 152 | 153 | return false; 154 | } 155 | 156 | UPaperFlipbook* UPaperFlipbookUserWidget::GetFlipbook() 157 | { 158 | return SourceFlipbook; 159 | } 160 | 161 | void UPaperFlipbookUserWidget::Play() 162 | { 163 | bAutoPlay = true; 164 | if (MyFlipbookWG.IsValid()) 165 | { 166 | MyFlipbookWG->Play(); 167 | } 168 | } 169 | 170 | void UPaperFlipbookUserWidget::PlayFromStart() 171 | { 172 | bAutoPlay = true; 173 | if (MyFlipbookWG.IsValid()) 174 | { 175 | MyFlipbookWG->PlayFromStart(); 176 | } 177 | } 178 | 179 | void UPaperFlipbookUserWidget::Reverse() 180 | { 181 | bAutoPlay = true; 182 | if (MyFlipbookWG.IsValid()) 183 | { 184 | MyFlipbookWG->Reverse(); 185 | } 186 | } 187 | 188 | void UPaperFlipbookUserWidget::ReverseFromEnd() 189 | { 190 | bAutoPlay = true; 191 | if (MyFlipbookWG.IsValid()) 192 | { 193 | MyFlipbookWG->ReverseFromEnd(); 194 | } 195 | } 196 | 197 | void UPaperFlipbookUserWidget::Stop() 198 | { 199 | bAutoPlay = false; 200 | if (MyFlipbookWG.IsValid()) 201 | { 202 | MyFlipbookWG->Stop(); 203 | } 204 | } 205 | 206 | bool UPaperFlipbookUserWidget::IsPlaying() const 207 | { 208 | if (MyFlipbookWG.IsValid()) 209 | { 210 | return MyFlipbookWG->IsPlaying(); 211 | } 212 | return false; 213 | } 214 | 215 | bool UPaperFlipbookUserWidget::IsReversing() const 216 | { 217 | if (MyFlipbookWG.IsValid()) 218 | { 219 | return MyFlipbookWG->IsReversing(); 220 | } 221 | return false; 222 | } 223 | 224 | void UPaperFlipbookUserWidget::SetPlaybackPositionInFrames(int32 NewFramePosition) 225 | { 226 | if (MyFlipbookWG.IsValid()) 227 | { 228 | MyFlipbookWG->SetPlaybackPositionInFrames(NewFramePosition, false); 229 | } 230 | } 231 | 232 | int32 UPaperFlipbookUserWidget::GetPlaybackPositionInFrames() const 233 | { 234 | if (MyFlipbookWG.IsValid()) 235 | { 236 | return MyFlipbookWG->GetPlaybackPositionInFrames(); 237 | } 238 | return 0; 239 | } 240 | 241 | void UPaperFlipbookUserWidget::SetPlaybackPosition(float NewPosition) 242 | { 243 | if (MyFlipbookWG.IsValid()) 244 | { 245 | MyFlipbookWG->SetPlaybackPosition(NewPosition, false); 246 | } 247 | } 248 | 249 | float UPaperFlipbookUserWidget::GetPlaybackPosition() const 250 | { 251 | if (MyFlipbookWG.IsValid()) 252 | { 253 | return MyFlipbookWG->GetPlaybackPosition(); 254 | } 255 | return 0.0f; 256 | } 257 | 258 | void UPaperFlipbookUserWidget::SetLooping(bool bNewLooping) 259 | { 260 | if (MyFlipbookWG.IsValid()) 261 | { 262 | MyFlipbookWG->SetLooping(bNewLooping); 263 | } 264 | } 265 | 266 | bool UPaperFlipbookUserWidget::IsLooping() const 267 | { 268 | if (MyFlipbookWG.IsValid()) 269 | { 270 | return MyFlipbookWG->IsLooping(); 271 | } 272 | return false; 273 | } 274 | 275 | void UPaperFlipbookUserWidget::SetPlayRate(float NewRate) 276 | { 277 | PlayRate = NewRate; 278 | if (MyFlipbookWG.IsValid()) 279 | { 280 | MyFlipbookWG->SetPlayRate(NewRate); 281 | } 282 | } 283 | 284 | float UPaperFlipbookUserWidget::GetPlayRate() const 285 | { 286 | if (MyFlipbookWG.IsValid()) 287 | { 288 | return MyFlipbookWG->GetPlayRate(); 289 | } 290 | return 0.0f; 291 | } 292 | 293 | void UPaperFlipbookUserWidget::SetNewTime(float NewTime) 294 | { 295 | if (MyFlipbookWG.IsValid()) 296 | { 297 | MyFlipbookWG->SetNewTime(NewTime); 298 | } 299 | } 300 | 301 | float UPaperFlipbookUserWidget::GetFlipbookLength() const 302 | { 303 | if (MyFlipbookWG.IsValid()) 304 | { 305 | return MyFlipbookWG->GetFlipbookLength(); 306 | } 307 | return 0.0f; 308 | } 309 | 310 | int32 UPaperFlipbookUserWidget::GetFlipbookLengthInFrames() const 311 | { 312 | if (MyFlipbookWG.IsValid()) 313 | { 314 | return MyFlipbookWG->GetFlipbookLengthInFrames(); 315 | } 316 | return 0; 317 | } 318 | 319 | float UPaperFlipbookUserWidget::GetFlipbookFramerate() const 320 | { 321 | if (MyFlipbookWG.IsValid()) 322 | { 323 | return MyFlipbookWG->GetFlipbookFramerate(); 324 | } 325 | return 0.0f; 326 | } 327 | 328 | void UPaperFlipbookUserWidget::OnFlipbookFinishedPlaying() 329 | { 330 | OnFinishedPlaying.Broadcast(); 331 | } 332 | 333 | #if WITH_EDITOR 334 | 335 | const FText UPaperFlipbookUserWidget::GetPaletteCategory() 336 | { 337 | return LOCTEXT("Paper2D", "Paper2D"); 338 | } 339 | 340 | #endif 341 | 342 | 343 | ///////////////////////////////////////////////////// 344 | 345 | #undef LOCTEXT_NAMESPACE 346 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Public/PaperFlipbookUserWidget.h: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #pragma once 6 | 7 | #include "CoreMinimal.h" 8 | #include "UObject/ObjectMacros.h" 9 | #include "Misc/Attribute.h" 10 | #include "Styling/SlateBrush.h" 11 | #include "Input/Reply.h" 12 | #include "Widgets/SWidget.h" 13 | #include "Components/Widget.h" 14 | #include "UObject/ScriptInterface.h" 15 | #include "PaperFlipbookUserWidget.generated.h" 16 | 17 | 18 | // Event for a non-looping flipbook finishing play 19 | DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnFlipbookFinishedPlayingEvent); 20 | 21 | class SPaperFlipbookWidget; 22 | class UPaperFlipbook; 23 | 24 | /** 25 | * The paper flipbook widget allows you to display a flipbook asset in the UI. 26 | * 27 | * * No Children 28 | */ 29 | UCLASS(meta = (DisplayName = "Paper Flipbook")) 30 | class PAPERFLIPBOOKWIDGET_API UPaperFlipbookUserWidget : public UWidget 31 | { 32 | GENERATED_UCLASS_BODY() 33 | 34 | public: 35 | 36 | /** Flipbook currently being played */ 37 | UPROPERTY(Category = Appearance, EditAnywhere, meta = (DisplayThumbnail = "true")) 38 | UPaperFlipbook* SourceFlipbook; 39 | 40 | /** Are we currently playing . */ 41 | UPROPERTY(Category = Appearance, EditAnywhere) 42 | uint8 bAutoPlay : 1; 43 | 44 | /** Current play rate of the flipbook */ 45 | UPROPERTY(Category = Appearance, EditAnywhere) 46 | float PlayRate; 47 | 48 | /** Whether the flipbook should loop when it reaches the end, or stop */ 49 | UPROPERTY(Category = Appearance, EditAnywhere) 50 | uint8 bLooping : 1; 51 | 52 | /** Color and opacity */ 53 | UPROPERTY(Category = Appearance, EditAnywhere, BlueprintReadOnly, meta = (sRGB = "true")) 54 | FLinearColor ColorAndOpacity; 55 | 56 | /** How to tile the image in Image mode */ 57 | UPROPERTY(Category = Appearance, EditAnywhere, BlueprintReadWrite) 58 | TEnumAsByte Tiling; 59 | 60 | /** How to mirror the image in Image mode. This is normally only used for dynamic image brushes where the source texture 61 | comes from a hardware device such as a web camera. */ 62 | UPROPERTY(Category = Appearance, EditAnywhere, BlueprintReadWrite) 63 | TEnumAsByte Mirroring; 64 | 65 | /** */ 66 | UPROPERTY(Category = Appearance, EditAnywhere, BlueprintReadWrite) 67 | uint8 bOverrideContentSize : 1; 68 | 69 | UPROPERTY(Category = Appearance, EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bOverrideContentSize")) 70 | FVector2D DesiredSize; 71 | 72 | 73 | /** A bindable delegate for the ColorAndOpacity. */ 74 | UPROPERTY() 75 | FGetLinearColor ColorAndOpacityDelegate; 76 | 77 | public: 78 | 79 | UPROPERTY(EditAnywhere, Category = Events, meta = (IsBindableEvent = "True")) 80 | FOnPointerEvent OnMouseButtonDownEvent; 81 | 82 | public: 83 | 84 | /** */ 85 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget|Appearance") 86 | void SetColorAndOpacity(FLinearColor InColorAndOpacity); 87 | 88 | /** */ 89 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget|Appearance") 90 | void SetOpacity(float InOpacity); 91 | 92 | /** */ 93 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget|Appearance") 94 | void OverrideBrushSize(FVector2D InDesiredSize); 95 | 96 | /** */ 97 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget|Appearance") 98 | void SetBrushTintColor(FSlateColor TintColor); 99 | 100 | /** */ 101 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget|Appearance") 102 | void SetBrushTiling(TEnumAsByte InTiling); 103 | 104 | /** */ 105 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget|Appearance") 106 | void SetBrushMirroring(TEnumAsByte InMirroring); 107 | 108 | //~ Begin UWidget Interface 109 | virtual void SynchronizeProperties() override; 110 | //~ End UWidget Interface 111 | 112 | //~ Begin UVisual Interface 113 | virtual void ReleaseSlateResources(bool bReleaseChildren) override; 114 | //~ End UVisual Interface 115 | 116 | 117 | #if WITH_EDITOR 118 | //~ Begin UWidget Interface 119 | virtual const FText GetPaletteCategory() override; 120 | //~ End UWidget Interface 121 | #endif 122 | 123 | protected: 124 | //~ Begin UWidget Interface 125 | virtual TSharedRef RebuildWidget() override; 126 | //~ End UWidget Interface 127 | 128 | // 129 | FReply HandleMouseButtonDown(const FGeometry& Geometry, const FPointerEvent& MouseEvent); 130 | 131 | public: 132 | /** Event called whenever a non-looping flipbook finishes playing (either reaching the beginning or the end, depending on the play direction) */ 133 | UPROPERTY(BlueprintAssignable, Category = "PaperFlipbook|Widget|Event") 134 | FOnFlipbookFinishedPlayingEvent OnFinishedPlaying; 135 | 136 | public: 137 | /** Change the flipbook used by this instance (will reset the play time to 0 if it is a new flipbook). */ 138 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 139 | virtual bool SetFlipbook(class UPaperFlipbook* NewFlipbook); 140 | 141 | /** Gets the flipbook used by this instance. */ 142 | UFUNCTION(BlueprintPure, Category = "PaperFlipbook|Widget") 143 | virtual UPaperFlipbook* GetFlipbook(); 144 | 145 | /** Start playback of flipbook */ 146 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 147 | void Play(); 148 | 149 | /** Start playback of flipbook from the start */ 150 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 151 | void PlayFromStart(); 152 | 153 | /** Start playback of flipbook in reverse */ 154 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 155 | void Reverse(); 156 | 157 | /** Start playback of flipbook in reverse from the end */ 158 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 159 | void ReverseFromEnd(); 160 | 161 | /** Stop playback of flipbook */ 162 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 163 | void Stop(); 164 | 165 | /** Get whether this flipbook is playing or not. */ 166 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 167 | bool IsPlaying() const; 168 | 169 | /** Get whether we are reversing or not */ 170 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 171 | bool IsReversing() const; 172 | 173 | /** Jump to a position in the flipbook (expressed in frames). */ 174 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 175 | void SetPlaybackPositionInFrames(int32 NewFramePosition); 176 | 177 | /** Get the current playback position (in frames) of the flipbook */ 178 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 179 | int32 GetPlaybackPositionInFrames() const; 180 | 181 | /** Jump to a position in the flipbook (expressed in seconds). */ 182 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 183 | void SetPlaybackPosition(float NewPosition); 184 | 185 | /** Get the current playback position (in seconds) of the flipbook */ 186 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 187 | float GetPlaybackPosition() const; 188 | 189 | /** true means we should loop, false means we should not. */ 190 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 191 | void SetLooping(bool bNewLooping); 192 | 193 | /** Get whether we are looping or not */ 194 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 195 | bool IsLooping() const; 196 | 197 | /** Sets the new play rate for this flipbook */ 198 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 199 | void SetPlayRate(float NewRate); 200 | 201 | /** Get the current play rate for this flipbook */ 202 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 203 | float GetPlayRate() const; 204 | 205 | /** Set the new playback position time to use */ 206 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 207 | void SetNewTime(float NewTime); 208 | 209 | /** Get length of the flipbook (in seconds) */ 210 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 211 | float GetFlipbookLength() const; 212 | 213 | /** Get length of the flipbook (in frames) */ 214 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 215 | int32 GetFlipbookLengthInFrames() const; 216 | 217 | /** Get the nominal framerate that the flipbook will be played back at (ignoring PlayRate), in frames per second */ 218 | UFUNCTION(BlueprintCallable, Category = "PaperFlipbook|Widget") 219 | float GetFlipbookFramerate() const; 220 | 221 | protected: 222 | TSharedPtr MyFlipbookWG; 223 | 224 | protected: 225 | void OnFlipbookFinishedPlaying(); 226 | 227 | 228 | protected: 229 | 230 | PROPERTY_BINDING_IMPLEMENTATION(FSlateColor, ColorAndOpacity); 231 | }; 232 | -------------------------------------------------------------------------------- /Source/PaperFlipbookWidget/Private/SPaperFlipbookWidget.cpp: -------------------------------------------------------------------------------- 1 | //==========================================================================// 2 | // Copyright Elhoussine Mehnik (ue4resources@gmail.com). All Rights Reserved. 3 | //================== http://unrealengineresources.com/ =====================// 4 | 5 | #include "SPaperFlipbookWidget.h" 6 | #include "Rendering/DrawElements.h" 7 | #include "PaperFlipbook.h" 8 | #include "PaperSprite.h" 9 | #include "PaperSpriteBlueprintLibrary.h" 10 | #include "Slate/SlateTextureAtlasInterface.h" 11 | #include "PaperFlipbookWidgetStats.h" 12 | 13 | 14 | DECLARE_CYCLE_STAT(TEXT("Tick Flipbook Widget"), STAT_TickFlipbookWidget, STATGROUP_FlipbookWidget); 15 | DECLARE_CYCLE_STAT(TEXT("On Paint Flipbook Widget"), STAT_OnPaint, STATGROUP_FlipbookWidget); 16 | 17 | 18 | SPaperFlipbookWidget::SPaperFlipbookWidget() 19 | { 20 | SetCanTick(true); 21 | bCanSupportFocus = false; 22 | 23 | DefaultBrush.DrawAs = ESlateBrushDrawType::Image; 24 | DefaultBrush.Tiling = ESlateBrushTileType::NoTile; 25 | DefaultBrush.Mirroring = ESlateBrushMirrorType::NoMirror; 26 | 27 | CachedFrameIndex = INDEX_NONE; 28 | AccumulatedTime = 0.0f; 29 | PlayRate = 1.0f; 30 | 31 | bLooping = true; 32 | bReversePlayback = false; 33 | bPlaying = true; 34 | bUseSpriteSize = true; 35 | } 36 | 37 | void SPaperFlipbookWidget::Construct(const FArguments& InArgs) 38 | { 39 | ColorAndOpacity = InArgs._ColorAndOpacity; 40 | OnMouseButtonDownHandler = InArgs._OnMouseButtonDown; 41 | } 42 | 43 | int32 SPaperFlipbookWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const 44 | { 45 | SCOPE_CYCLE_COUNTER(STAT_OnPaint); 46 | 47 | if (DefaultBrush.DrawAs != ESlateBrushDrawType::NoDrawType) 48 | { 49 | const bool bIsEnabled = ShouldBeEnabled(bParentEnabled); 50 | const ESlateDrawEffect DrawEffects = bIsEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; 51 | 52 | const FLinearColor FinalColorAndOpacity(InWidgetStyle.GetColorAndOpacityTint() * ColorAndOpacity.Get().GetColor(InWidgetStyle) * DefaultBrush.GetTint(InWidgetStyle)); 53 | 54 | 55 | FSlateDrawElement::MakeBox(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), &DefaultBrush, DrawEffects, FinalColorAndOpacity); 56 | } 57 | return LayerId; 58 | } 59 | 60 | FReply SPaperFlipbookWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) 61 | { 62 | if (OnMouseButtonDownHandler.IsBound()) 63 | { 64 | // If a handler is assigned, call it. 65 | return OnMouseButtonDownHandler.Execute(MyGeometry, MouseEvent); 66 | } 67 | else 68 | { 69 | // otherwise the event is unhandled. 70 | return FReply::Unhandled(); 71 | } 72 | 73 | } 74 | 75 | void SPaperFlipbookWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) 76 | { 77 | SLeafWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); 78 | 79 | { 80 | SCOPE_CYCLE_COUNTER(STAT_TickFlipbookWidget); 81 | 82 | // Advance time 83 | TickFlipbook(InDeltaTime); 84 | 85 | // Update the frame and push it to the renderer if necessary 86 | CalculateCurrentFrame(); 87 | } 88 | } 89 | 90 | FVector2D SPaperFlipbookWidget::ComputeDesiredSize(float) const 91 | { 92 | return DefaultBrush.ImageSize; 93 | } 94 | 95 | void SPaperFlipbookWidget::SetColorAndOpacity(const TAttribute& InColorAndOpacity) 96 | { 97 | if (!ColorAndOpacity.IdenticalTo(InColorAndOpacity)) 98 | { 99 | ColorAndOpacity = InColorAndOpacity; 100 | Invalidate(EInvalidateWidget::PaintAndVolatility); 101 | } 102 | } 103 | 104 | void SPaperFlipbookWidget::SetColorAndOpacity(FLinearColor InColorAndOpacity) 105 | { 106 | if (!ColorAndOpacity.IdenticalTo(InColorAndOpacity)) 107 | { 108 | ColorAndOpacity = InColorAndOpacity; 109 | Invalidate(EInvalidateWidget::PaintAndVolatility); 110 | } 111 | } 112 | 113 | void SPaperFlipbookWidget::SetOnMouseButtonDown(FPointerEventHandler EventHandler) 114 | { 115 | OnMouseButtonDownHandler = EventHandler; 116 | } 117 | 118 | void SPaperFlipbookWidget::SetOnFinishedPlaying(FSimpleDelegate InOnFinishedPlaying) 119 | { 120 | OnFinishedPlaying = InOnFinishedPlaying; 121 | } 122 | 123 | void SPaperFlipbookWidget::OverrideBrushSize(FVector2D InDesiredSize, bool bOverride) 124 | { 125 | bUseSpriteSize = !bOverride; 126 | if (bOverride) 127 | { 128 | DefaultBrush.ImageSize = InDesiredSize; 129 | Invalidate(EInvalidateWidget::LayoutAndVolatility); 130 | } 131 | } 132 | 133 | void SPaperFlipbookWidget::SetBrushTintColor(FSlateColor TintColor) 134 | { 135 | if (DefaultBrush.TintColor != TintColor) 136 | { 137 | DefaultBrush.TintColor = TintColor; 138 | Invalidate(EInvalidateWidget::PaintAndVolatility); 139 | } 140 | } 141 | 142 | void SPaperFlipbookWidget::SetBrushTiling(TEnumAsByte InTiling) 143 | { 144 | DefaultBrush.Tiling = InTiling; 145 | Invalidate(EInvalidateWidget::LayoutAndVolatility); 146 | } 147 | 148 | void SPaperFlipbookWidget::SetBrushMirroring(TEnumAsByte InMirroring) 149 | { 150 | DefaultBrush.Mirroring = InMirroring; 151 | Invalidate(EInvalidateWidget::LayoutAndVolatility); 152 | } 153 | 154 | FSlateBrush* SPaperFlipbookWidget::GetBrush() 155 | { 156 | return &DefaultBrush; 157 | } 158 | 159 | UPaperSprite* SPaperFlipbookWidget::GetSpriteAtCachedIndex() const 160 | { 161 | UPaperSprite* SpriteToSend = nullptr; 162 | if ((SourceFlipbook != nullptr) && SourceFlipbook->IsValidKeyFrameIndex(CachedFrameIndex)) 163 | { 164 | SpriteToSend = SourceFlipbook->GetKeyFrameChecked(CachedFrameIndex).Sprite; 165 | } 166 | return SpriteToSend; 167 | } 168 | 169 | void SPaperFlipbookWidget::CalculateCurrentFrame() 170 | { 171 | const int32 LastCachedFrame = CachedFrameIndex; 172 | CachedFrameIndex = (SourceFlipbook != nullptr) ? SourceFlipbook->GetKeyFrameIndexAtTime(AccumulatedTime) : INDEX_NONE; 173 | 174 | if (CachedFrameIndex != LastCachedFrame) 175 | { 176 | if (UPaperSprite* Sprite = GetSpriteAtCachedIndex()) 177 | { 178 | const FSlateAtlasData SpriteAtlasData = Sprite->GetSlateAtlasData(); 179 | const FVector2D SpriteSize = SpriteAtlasData.GetSourceDimensions(); 180 | DefaultBrush.SetResourceObject(Sprite); 181 | 182 | if (bUseSpriteSize) 183 | { 184 | DefaultBrush.ImageSize = SpriteSize; 185 | } 186 | } 187 | else 188 | { 189 | DefaultBrush.SetResourceObject(nullptr); 190 | } 191 | 192 | // Indicate we need to send new dynamic data. 193 | Invalidate(EInvalidateWidget::LayoutAndVolatility); 194 | } 195 | } 196 | 197 | 198 | void SPaperFlipbookWidget::TickFlipbook(float DeltaTime) 199 | { 200 | bool bIsFinished = false; 201 | 202 | if (bPlaying) 203 | { 204 | const float TimelineLength = GetFlipbookLength(); 205 | const float EffectiveDeltaTime = DeltaTime * (bReversePlayback ? (-PlayRate) : (PlayRate)); 206 | 207 | float NewPosition = AccumulatedTime + EffectiveDeltaTime; 208 | 209 | if (EffectiveDeltaTime > 0.0f) 210 | { 211 | if (NewPosition > TimelineLength) 212 | { 213 | if (bLooping) 214 | { 215 | // If looping, play to end, jump to start, and set target to somewhere near the beginning. 216 | SetPlaybackPosition(TimelineLength, true); 217 | SetPlaybackPosition(0.0f, false); 218 | 219 | if (TimelineLength > 0.0f) 220 | { 221 | while (NewPosition > TimelineLength) 222 | { 223 | NewPosition -= TimelineLength; 224 | } 225 | } 226 | else 227 | { 228 | NewPosition = 0.0f; 229 | } 230 | } 231 | else 232 | { 233 | // If not looping, snap to end and stop playing. 234 | NewPosition = TimelineLength; 235 | Stop(); 236 | bIsFinished = true; 237 | } 238 | } 239 | } 240 | else 241 | { 242 | if (NewPosition < 0.0f) 243 | { 244 | if (bLooping) 245 | { 246 | // If looping, play to start, jump to end, and set target to somewhere near the end. 247 | SetPlaybackPosition(0.0f, true); 248 | SetPlaybackPosition(TimelineLength, false); 249 | 250 | if (TimelineLength > 0.0f) 251 | { 252 | while (NewPosition < 0.0f) 253 | { 254 | NewPosition += TimelineLength; 255 | } 256 | } 257 | else 258 | { 259 | NewPosition = 0.0f; 260 | } 261 | } 262 | else 263 | { 264 | // If not looping, snap to start and stop playing. 265 | NewPosition = 0.0f; 266 | Stop(); 267 | bIsFinished = true; 268 | } 269 | } 270 | } 271 | 272 | SetPlaybackPosition(NewPosition, true); 273 | } 274 | 275 | // Notify user that the flipbook finished playing 276 | if (bIsFinished) 277 | { 278 | OnFinishedPlaying.ExecuteIfBound(); 279 | } 280 | } 281 | 282 | bool SPaperFlipbookWidget::SetFlipbook(class UPaperFlipbook* NewFlipbook) 283 | { 284 | if (NewFlipbook != SourceFlipbook) 285 | { 286 | // Don't allow changing the sprite if we are "static". 287 | /*AActor* ComponentOwner = GetOwner(); 288 | if ((ComponentOwner == nullptr) || AreDynamicDataChangesAllowed())*/ 289 | { 290 | SourceFlipbook = NewFlipbook; 291 | 292 | // We need to also reset the frame and time also 293 | AccumulatedTime = 0.0f; 294 | CalculateCurrentFrame(); 295 | 296 | Invalidate(EInvalidateWidget::LayoutAndVolatility); 297 | return true; 298 | } 299 | } 300 | 301 | return false; 302 | } 303 | 304 | UPaperFlipbook* SPaperFlipbookWidget::GetFlipbook() 305 | { 306 | return SourceFlipbook; 307 | } 308 | 309 | 310 | void SPaperFlipbookWidget::Play() 311 | { 312 | bReversePlayback = false; 313 | bPlaying = true; 314 | } 315 | 316 | void SPaperFlipbookWidget::PlayFromStart() 317 | { 318 | SetPlaybackPosition(0.0f, /*bFireEvents=*/ false); 319 | Play(); 320 | } 321 | 322 | void SPaperFlipbookWidget::Reverse() 323 | { 324 | bReversePlayback = true; 325 | bPlaying = true; 326 | } 327 | 328 | void SPaperFlipbookWidget::ReverseFromEnd() 329 | { 330 | SetPlaybackPosition(GetFlipbookLength(), /*bFireEvents=*/ false); 331 | Reverse(); 332 | } 333 | 334 | void SPaperFlipbookWidget::Stop() 335 | { 336 | bPlaying = false; 337 | } 338 | 339 | bool SPaperFlipbookWidget::IsPlaying() const 340 | { 341 | return bPlaying; 342 | } 343 | 344 | bool SPaperFlipbookWidget::IsReversing() const 345 | { 346 | return bPlaying && bReversePlayback; 347 | } 348 | 349 | void SPaperFlipbookWidget::SetPlaybackPositionInFrames(int32 NewFramePosition, bool bFireEvents) 350 | { 351 | const float Framerate = GetFlipbookFramerate(); 352 | const float NewTime = (Framerate > 0.0f) ? (NewFramePosition / Framerate) : 0.0f; 353 | SetPlaybackPosition(NewTime, bFireEvents); 354 | } 355 | 356 | int32 SPaperFlipbookWidget::GetPlaybackPositionInFrames() const 357 | { 358 | const float Framerate = GetFlipbookFramerate(); 359 | const int32 NumFrames = GetFlipbookLengthInFrames(); 360 | if (NumFrames > 0) 361 | { 362 | return FMath::Clamp(FMath::TruncToInt(AccumulatedTime * Framerate), 0, NumFrames - 1); 363 | } 364 | else 365 | { 366 | return 0; 367 | } 368 | } 369 | 370 | void SPaperFlipbookWidget::SetPlaybackPosition(float NewPosition, bool bFireEvents) 371 | { 372 | float OldPosition = AccumulatedTime; 373 | AccumulatedTime = NewPosition; 374 | 375 | // If we should be firing events for this track... 376 | if (bFireEvents) 377 | { 378 | float MinTime; 379 | float MaxTime; 380 | if (!bReversePlayback) 381 | { 382 | // If playing sequence forwards. 383 | MinTime = OldPosition; 384 | MaxTime = AccumulatedTime; 385 | 386 | // Slight hack here.. if playing forwards and reaching the end of the sequence, force it over a little to ensure we fire events actually on the end of the sequence. 387 | if (MaxTime == GetFlipbookLength()) 388 | { 389 | MaxTime += (float)KINDA_SMALL_NUMBER; 390 | } 391 | } 392 | else 393 | { 394 | // If playing sequence backwards. 395 | MinTime = AccumulatedTime; 396 | MaxTime = OldPosition; 397 | 398 | // Same small hack as above for backwards case. 399 | if (MinTime == 0.0f) 400 | { 401 | MinTime -= (float)KINDA_SMALL_NUMBER; 402 | } 403 | } 404 | 405 | #if 0 406 | // See which events fall into traversed region. 407 | for (int32 i = 0; i < Events.Num(); i++) 408 | { 409 | float EventTime = Events[i].Time; 410 | 411 | // Need to be slightly careful here and make behavior for firing events symmetric when playing forwards of backwards. 412 | bool bFireThisEvent = false; 413 | if (!bReversePlayback) 414 | { 415 | if ((EventTime >= MinTime) && (EventTime < MaxTime)) 416 | { 417 | bFireThisEvent = true; 418 | } 419 | } 420 | else 421 | { 422 | if ((EventTime > MinTime) && (EventTime <= MaxTime)) 423 | { 424 | bFireThisEvent = true; 425 | } 426 | } 427 | 428 | if (bFireThisEvent) 429 | { 430 | Events[i].EventFunc.ExecuteIfBound(); 431 | } 432 | } 433 | #endif 434 | } 435 | 436 | #if 0 437 | // Execute the delegate to say that all properties are updated 438 | TimelinePostUpdateFunc.ExecuteIfBound(); 439 | #endif 440 | 441 | if (OldPosition != AccumulatedTime) 442 | { 443 | CalculateCurrentFrame(); 444 | } 445 | } 446 | 447 | float SPaperFlipbookWidget::GetPlaybackPosition() const 448 | { 449 | return AccumulatedTime; 450 | } 451 | 452 | void SPaperFlipbookWidget::SetLooping(bool bNewLooping) 453 | { 454 | bLooping = bNewLooping; 455 | } 456 | 457 | bool SPaperFlipbookWidget::IsLooping() const 458 | { 459 | return bLooping; 460 | } 461 | 462 | void SPaperFlipbookWidget::SetPlayRate(float NewRate) 463 | { 464 | PlayRate = NewRate; 465 | } 466 | 467 | float SPaperFlipbookWidget::GetPlayRate() const 468 | { 469 | return PlayRate; 470 | } 471 | 472 | void SPaperFlipbookWidget::SetNewTime(float NewTime) 473 | { 474 | // Ensure value is sensible 475 | //@TODO: PAPER2D: NewTime = FMath::Clamp(NewTime, 0.0f, Length); 476 | 477 | SetPlaybackPosition(NewTime, /*bFireEvents=*/ false); 478 | } 479 | 480 | float SPaperFlipbookWidget::GetFlipbookLength() const 481 | { 482 | return (SourceFlipbook != nullptr) ? SourceFlipbook->GetTotalDuration() : 0.0f; 483 | } 484 | 485 | int32 SPaperFlipbookWidget::GetFlipbookLengthInFrames() const 486 | { 487 | return (SourceFlipbook != nullptr) ? SourceFlipbook->GetNumFrames() : 0; 488 | } 489 | 490 | float SPaperFlipbookWidget::GetFlipbookFramerate() const 491 | { 492 | return (SourceFlipbook != nullptr) ? SourceFlipbook->GetFramesPerSecond() : 15.0f; 493 | } 494 | 495 | --------------------------------------------------------------------------------