六一的部落格


关关难过关关过,前路漫漫亦灿灿。



Show Animations


说明

  1. 在C++中播放受伤闪烁红屏
  2. 为窗口部件添加动画
    • 主菜单从黑屏到出现
    • 游戏状态从对角线平滑出场
    • 为暂停和游戏结局页面添加黑色阴影, 模糊过渡, 以及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++中播放窗口部件动画


回顾受伤播放闪烁红屏

  1. 为WBP_PlayerHUD中的DmageImage添加动画 Color and Opacity , Alpha通道逐渐变为0


  2. STUPlayerHUDWidget中添加事件函数OnTakeDamage, 游戏角色受伤时, 即在OnChangeHealth中调用

  3. 事件发生时, 若动画未在播放, 播放之



在C++中播放受伤动画

  1. 添加属性

    protected

    ShootThemUp: UI/STUPlayerHUDWidget.h
    1UPROPERTY(meta = (BindWidgetAnim), Transient)
    2UWidgetAnimation *DamageAnimation;
  2. 在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

  1. WBP_PlayerHUD > EventGraph中使用了DamageAnimation, 而代码中未将其声明为蓝图可用: 从EventGraph中移除



  2. 运行游戏, 游戏角色受伤时可见闪烁红屏动画


创建窗口部件基类: 提供播放出场动画接口

-
基类 UserWidget
名称 STUBaseWidget
路径 UI
属性 Public
  1. 添加属性: 保存动画

    protected

    ShootThemUp: UI/STUBaseWidget.h
    1UPROPERTY(meta = (BindWidgetAnim), Transient)
    2UWidgetAnimation *ShowAnimation;
  2. 添加接口: 播放动画

    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

  1. 添加图片, 设置平铺


  2. 设置可见性: 该组件不触发点击事件, 即可以点击覆盖的元素


  3. 为ShowImage添加动画: Color and Opacity



  4. 设置动画: 0-3s过程中, 背景色由黑色变透明



  5. 修改关键帧插值算法


    没看出来差别


为游戏状态, 暂停窗口和游戏结束窗口添加出场动画

  1. 游戏状态: STUAboveWidget
    • 弹药库数据: STUPlayerHUDWidget
    • 回合信息, 生命条和击杀死亡数据: STUPlayerHUDWidget > STUGameDataWidget
  2. 暂停窗口: STUPauseWidget
  3. 游戏结束窗口: 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

  1. 将WBP_GameData设置为变量, 修改变量名


  2. 0-0.75s这一过程中, 弹药信息从右下角沿对角线进入窗口

    使用的动画类型为变换 Transform , 设置其平移 Translation 参数



  3. 将WBP_PlayerHUD设置为变量, 修改变量名



暂停窗口

WBP_PauseWidget

  1. 添加黑色阴影


  2. 添加动画

    • 0-0.75s这一过程中, 模糊度由0变为4



    • 0-0.75s这一过程中, 垂直盒由偏上位置沿Y轴移动到屏幕中间




游戏结束窗口

WBP_GameOverWidget

同暂停窗口


验证Transient声明符

  1. 创建蓝图类


  2. 设置基类为Actor, 命名为TestActor


  3. 添加数据成员TestVar

    • Float类型
    • 可在实例中设置
    • 初始值11.0
    • 在屏幕中打印值


  4. 在主菜单添加TestActor实例, 设置实例中TestVar值为23



  5. 为TestVar标识Transient


    并未修改实例TestVar值, 运行时该值恢复为默认值11; 停止游戏, 该值仍显示为23


  6. 从关卡中移除TestActor实例


为用户界面添加动画


Show Animations


说明

  1. 在C++中播放受伤闪烁红屏
  2. 为窗口部件添加动画
    • 主菜单从黑屏到出现
    • 游戏状态从对角线平滑出场
    • 为暂停和游戏结局页面添加黑色阴影, 模糊过渡, 以及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++中播放窗口部件动画


回顾受伤播放闪烁红屏

  1. 为WBP_PlayerHUD中的DmageImage添加动画 Color and Opacity , Alpha通道逐渐变为0


  2. STUPlayerHUDWidget中添加事件函数OnTakeDamage, 游戏角色受伤时, 即在OnChangeHealth中调用

  3. 事件发生时, 若动画未在播放, 播放之



在C++中播放受伤动画

  1. 添加属性

    protected

    ShootThemUp: UI/STUPlayerHUDWidget.h
    1UPROPERTY(meta = (BindWidgetAnim), Transient)
    2UWidgetAnimation *DamageAnimation;
  2. 在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

  1. WBP_PlayerHUD > EventGraph中使用了DamageAnimation, 而代码中未将其声明为蓝图可用: 从EventGraph中移除



  2. 运行游戏, 游戏角色受伤时可见闪烁红屏动画


创建窗口部件基类: 提供播放出场动画接口

-
基类 UserWidget
名称 STUBaseWidget
路径 UI
属性 Public
  1. 添加属性: 保存动画

    protected

    ShootThemUp: UI/STUBaseWidget.h
    1UPROPERTY(meta = (BindWidgetAnim), Transient)
    2UWidgetAnimation *ShowAnimation;
  2. 添加接口: 播放动画

    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

  1. 添加图片, 设置平铺


  2. 设置可见性: 该组件不触发点击事件, 即可以点击覆盖的元素


  3. 为ShowImage添加动画: Color and Opacity



  4. 设置动画: 0-3s过程中, 背景色由黑色变透明



  5. 修改关键帧插值算法


    没看出来差别


为游戏状态, 暂停窗口和游戏结束窗口添加出场动画

  1. 游戏状态: STUAboveWidget
    • 弹药库数据: STUPlayerHUDWidget
    • 回合信息, 生命条和击杀死亡数据: STUPlayerHUDWidget > STUGameDataWidget
  2. 暂停窗口: STUPauseWidget
  3. 游戏结束窗口: 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

  1. 将WBP_GameData设置为变量, 修改变量名


  2. 0-0.75s这一过程中, 弹药信息从右下角沿对角线进入窗口

    使用的动画类型为变换 Transform , 设置其平移 Translation 参数



  3. 将WBP_PlayerHUD设置为变量, 修改变量名



暂停窗口

WBP_PauseWidget

  1. 添加黑色阴影


  2. 添加动画

    • 0-0.75s这一过程中, 模糊度由0变为4



    • 0-0.75s这一过程中, 垂直盒由偏上位置沿Y轴移动到屏幕中间




游戏结束窗口

WBP_GameOverWidget

同暂停窗口


验证Transient声明符

  1. 创建蓝图类


  2. 设置基类为Actor, 命名为TestActor


  3. 添加数据成员TestVar

    • Float类型
    • 可在实例中设置
    • 初始值11.0
    • 在屏幕中打印值


  4. 在主菜单添加TestActor实例, 设置实例中TestVar值为23



  5. 为TestVar标识Transient


    并未修改实例TestVar值, 运行时该值恢复为默认值11; 停止游戏, 该值仍显示为23


  6. 从关卡中移除TestActor实例