为用户界面添加动画
2023年12月8日 2024年1月11日
Show Animations
说明
- 在C++中播放受伤闪烁红屏
- 为窗口部件添加动画
- 主菜单从黑屏到出现
- 游戏状态从对角线平滑出场
- 为暂停和游戏结局页面添加黑色阴影, 模糊过渡, 以及Y轴平滑出场
- 主菜单从黑屏到出现
API导航
BindWidgetAnim
和 BindWidget
同为枚举类型成员, 用以绑定窗口部件动画
Transient
枚举类型成员. 使用该标记后, 即使修改实例的该属性值, 最终生效的仍为类的默认属性值
即往硬盘写入不生效
IsAnimationPlaying
窗口部件动画是否正在播放
1/** 2 * Gets whether an animation is currently playing on this widget. 3 * 4 * @param InAnimation The animation to check the playback status of 5 * @return True if the animation is currently playing 6 */ 7UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category="User Interface|Animation") 8bool IsAnimationPlaying(const UWidgetAnimation* InAnimation) const;
PlayAnimation
播放窗口部件动画
1/** 2 * Plays an animation in this widget a specified number of times 3 * 4 * @param InAnimation The animation to play 5 * @param StartAtTime The time in the animation from which to start playing, relative to the start position. For looped animations, this will only affect the first playback of the animation. 6 * @param NumLoopsToPlay The number of times to loop this animation (0 to loop indefinitely) 7 * @param PlaybackSpeed The speed at which the animation should play 8 * @param PlayMode Specifies the playback mode 9 * @param bRestoreState Restores widgets to their pre-animated state when the animation stops 10 */ 11UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category = "User Interface|Animation") 12UUMGSequencePlayer* PlayAnimation(UWidgetAnimation* InAnimation, float StartAtTime = 0.0f, int32 NumLoopsToPlay = 1, EUMGSequencePlayMode::Type PlayMode = EUMGSequencePlayMode::Forward, float PlaybackSpeed = 1.0f, bool bRestoreState = false);
在C++中播放窗口部件动画
回顾受伤播放闪烁红屏
-
为WBP_PlayerHUD中的DmageImage添加动画
Color and Opacity
, Alpha通道逐渐变为0 -
STUPlayerHUDWidget中添加事件函数OnTakeDamage, 游戏角色受伤时, 即在OnChangeHealth中调用
-
事件发生时, 若动画未在播放, 播放之
在C++中播放受伤动画
- 添加属性
protected
ShootThemUp: UI/STUPlayerHUDWidget.h
1UPROPERTY(meta = (BindWidgetAnim), Transient) 2UWidgetAnimation *DamageAnimation;
- 在OnChangeHealth屏蔽OnTakeDamage, 播放动画
ShootThemUp: UI/STUPlayerHUDWidget.cpp
1void USTUPlayerHUDWidget::OnChangeHealth(float HealthDelta) 2{ 3 if (HealthDelta < 0.0f) 4 { 5 // OnTakeDamage(); 6 if (!IsAnimationPlaying(DamageAnimation)) 7 { 8 PlayAnimation(DamageAnimation); 9 } 10 } 11}
修改WBP_PlayerHUD
-
WBP_PlayerHUD > EventGraph中使用了DamageAnimation, 而代码中未将其声明为蓝图可用: 从EventGraph中移除
-
运行游戏, 游戏角色受伤时可见闪烁红屏动画
创建窗口部件基类: 提供播放出场动画接口
- | |
---|---|
基类 | UserWidget |
名称 | STUBaseWidget |
路径 | UI |
属性 | Public |
- 添加属性: 保存动画
protected
ShootThemUp: UI/STUBaseWidget.h
1UPROPERTY(meta = (BindWidgetAnim), Transient) 2UWidgetAnimation *ShowAnimation;
- 添加接口: 播放动画
public
ShootThemUp: UI/STUBaseWidget.h
1virtual void Show();
ShootThemUp: UI/STUBaseWidget.cpp
1void USTUBaseWidget::Show() 2{ 3 PlayAnimation(ShowAnimation); 4}
为主菜单添加出场动画
修改主菜单窗口部件基类: STUMenuWidget
ShootThemUp: Menu/UI/STUMenuWidget.h
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4class SHOOTTHEMUP_API USTUMenuWidget : public USTUBaseWidget 5{ 6 // ... 7};
修改创建主菜单逻辑
ShootThemUp: Menu/UI/STUMenuHUD.h
1class USTUBaseWidget; 2 3TSubclassOf<USTUBaseWidget> MenuWidgetClass;
ShootThemUp: Menu/UI/STUMenuHUD.cpp
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4// BeginPlay 5const auto MenuWidget = CreateWidget<USTUBaseWidget>(GetWorld(), MenuWidgetClass); 6if (MenuWidget) 7{ 8 MenuWidget->AddToViewport(); 9 MenuWidget->Show(); 10}
设置ShowAnimation
WBP_Menu
-
添加图片, 设置平铺
-
设置可见性: 该组件不触发点击事件, 即可以点击覆盖的元素
-
为ShowImage添加动画: Color and Opacity
-
设置动画: 0-3s过程中, 背景色由黑色变透明
-
修改关键帧插值算法
没看出来差别
为游戏状态, 暂停窗口和游戏结束窗口添加出场动画
- 游戏状态: STUAboveWidget
- 弹药库数据: STUPlayerHUDWidget
- 回合信息, 生命条和击杀死亡数据: STUPlayerHUDWidget > STUGameDataWidget
- 弹药库数据: STUPlayerHUDWidget
- 暂停窗口: STUPauseWidget
- 游戏结束窗口: STUGameOverWidget
修改窗口部件基类
游戏状态
封装可以使相关逻辑不牵扯其他, 但层级较深是开发设计中比较集会的一点: 扁平化更优
-
最下层: STUGameDataWidget
ShootThemUp: UI/STUGameDataWidget.h
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4class SHOOTTHEMUP_API USTUGameDataWidget : public USTUBaseWidget 5{ 6 // ... 7};
-
中间层: STUPlayerHUDWidget
包含回合信息, 生命条和击杀数据; 需覆写Show方法
ShootThemUp: UI/STUPlayerHUDWidget.h
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4class SHOOTTHEMUP_API USTUPlayerHUDWidget : public USTUBaseWidget 5{ 6 // ... 7};
覆写Show方法: 需调用STUGameDataWidget的Show方法
ShootThemUp: UI/STUPlayerHUDWidget.h
1class USTUGameDataWidget; 2 3// public 4virtual void Show() override; 5 6// protected 7UPROPERTY(meta = (BindWidget)) 8USTUGameDataWidget *GameData;
ShootThemUp: UI/STUPlayerHUDWidget.cpp
1#include "UI/STUGameDataWidget.h" 2 3void USTUPlayerHUDWidget::Show() 4{ 5 if (GameData) 6 { 7 GameData->Show(); 8 } 9 Super::Show(); 10}
-
最上层: STUAboveWidget
包含所有游戏状态; 需覆写Show方法
ShootThemUp: UI/STUAboveWidget.h
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4class SHOOTTHEMUP_API USTUAboveWidget : public USTUBaseWidget 5{ 6 // ... 7}
覆写Show方法: 需调用STUPlayerHUDWidget的Show方法
ShootThemUp: UI/STUAboveWidget.h
1class USTUPlayerHUDWidget; 2 3// public 4virtual void Show() override; 5 6// protected 7UPROPERTY(meta = (BindWidget)) 8USTUPlayerHUDWidget *PlayerHUD;
ShootThemUp: UI/STUAboveWidget.cpp
1#include "UI/STUPlayerHUDWidget.h" 2 3void USTUAboveWidget::Show() 4{ 5 if (PlayerHUD) 6 { 7 PlayerHUD->Show(); 8 } 9 Super::Show(); 10}
暂停窗口
ShootThemUp: UI/STUPauseWidget.h
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4class SHOOTTHEMUP_API USTUPauseWidget : public USTUBaseWidget 5{ 6 // ... 7};
游戏结束窗口
ShootThemUp: UI/STUGameOverWidget.h
1// #include "Blueprint/UserWidget.h" 2#include "UI/STUBaseWidget.h" 3 4class SHOOTTHEMUP_API USTUGameOverWidget : public USTUBaseWidget 5{ 6 // ... 7};
修改创建窗口逻辑
ShootThemUp: UI/STUGameHUD.h
1class USTUBaseWidget; 2 3TSubclassOf<USTUBaseWidget> PlayerHUDWidgetClass; 4 5TSubclassOf<USTUBaseWidget> PauseWidgetClass; 6 7TSubclassOf<USTUBaseWidget> GameOverWidgetClass; 8 9TMap<ESTUMatchState, USTUBaseWidget *> GameWidgets; 10 11USTUBaseWidget *CurrentWidget = nullptr;
ShootThemUp: UI/STUGameHUD.cpp
1#include "UI/STUBaseWidget.h" 2 3// InitGameWidgets 4 5GameWidgets.Add(ESTUMatchState::InProgress, CreateWidget<USTUBaseWidget>(GetWorld(), PlayerHUDWidgetClass)); 6GameWidgets.Add(ESTUMatchState::Pause, CreateWidget<USTUBaseWidget>(GetWorld(), PauseWidgetClass)); 7GameWidgets.Add(ESTUMatchState::GameOver, CreateWidget<USTUBaseWidget>(GetWorld(), GameOverWidgetClass)); 8 9// OnMatchStateChanged 10 11if (CurrentWidget) 12{ 13 CurrentWidget->Show(); 14 CurrentWidget->SetVisibility(ESlateVisibility::Visible); 15}
设置ShowAnimation
游戏状态 - 回合信息, 生命条和击杀死亡数据
WBP_GameData
修改回合信息垂直盒名称为 RoundsVerticalBox
, 生命条垂直盒名称为 HealthVerticalBox
0-0.75s这一过程中, 回合信息垂直盒从左上角沿对角线进入窗口, 生命条垂直盒从左下角沿对角线进入窗口
使用的动画类型为变换 Transform
, 设置其平移 Translation
参数
-
回合信息
-
生命条和击杀死亡数据
游戏状态 - 弹药信息
WBP_PlayerHUD
-
将WBP_GameData设置为变量, 修改变量名
-
0-0.75s这一过程中, 弹药信息从右下角沿对角线进入窗口
使用的动画类型为变换Transform
, 设置其平移Translation
参数 -
将WBP_PlayerHUD设置为变量, 修改变量名
暂停窗口
WBP_PauseWidget
-
添加黑色阴影
-
添加动画
-
0-0.75s这一过程中, 模糊度由0变为4
-
0-0.75s这一过程中, 垂直盒由偏上位置沿Y轴移动到屏幕中间
-
游戏结束窗口
WBP_GameOverWidget
同暂停窗口
验证Transient声明符
-
创建蓝图类
-
设置基类为Actor, 命名为TestActor
-
添加数据成员TestVar
- Float类型
- 可在实例中设置
- 初始值11.0
- 在屏幕中打印值
- Float类型
-
在主菜单添加TestActor实例, 设置实例中TestVar值为23
-
为TestVar标识Transient
并未修改实例TestVar值, 运行时该值恢复为默认值11; 停止游戏, 该值仍显示为23
-
从关卡中移除TestActor实例