设计优化
2023年11月26日 2024年1月11日
Design Update / Refactoring
素材
字体
-
BarlowCondensed
- BarlowCondensed-Regular BarlowCondensed-SemiBold -
Morganite-Black
个人免费使用
武器图片
均来自 Pixabay
, 步枪和榴弹发射器长宽比接近 1:3
, 弹匣长宽比为 1:2
用于显示阵营颜色的图片
主菜单退出按钮图片
窗口部件背景图
-
按钮
300x100
普通状态
鼠标悬浮
-
生命条
430x40
-
武器
430x150
调整武器图片
参照武器背景图, 择定位置, 加上透明填充. 这样做的好处在于武器的显示不受文本变化的影响. 当然, 为文本添加大小盒也是可以解决的
430x150
导入素材
拖入文件夹, 移动作为 Content/ExternalContent/DesignUpdate
说明
修改繁多, 以适合为重, 用以参考
API导航
Button
1UPROPERTY( BlueprintAssignable, Category = "Button|Event" ) 2FOnButtonHoverEvent OnHovered; 3 4UPROPERTY( BlueprintAssignable, Category = "Button|Event" ) 5FOnButtonHoverEvent OnUnhovered;
Image
1/** */ 2UFUNCTION(BlueprintCallable, Category="Appearance") 3void SetColorAndOpacity(FLinearColor InColorAndOpacity);
FString
- ChrN
使用指定个数个前缀填充字符串
1/** 2 * Returns a string that is full of a variable number of characters 3 * 4 * @param NumCharacters Number of characters to put into the string 5 * @param Char Character to put into the string 6 * 7 * @return The string of NumCharacters characters. 8 */ 9 UE_NODISCARD static FString ChrN( int32 NumCharacters, TCHAR Char );
- Append
在尾部追加字符串
1/** Append a string and return a reference to this */ 2template <typename CharRangeType, typename TEnableIf<TIsCharRangeNotCArray<CharRangeType>::Value>::Type* = nullptr> 3FORCEINLINE FString& Append(CharRangeType&& Str) 4{ 5 AppendChars(GetData(Str), GetNum(Str)); 6 return *this; 7}
- Len
获取字符串长度
1/** Get the length of the string, excluding terminating character */ 2UE_NODISCARD FORCEINLINE int32 Len() const 3{ 4 return Data.Num() ? Data.Num() - 1 : 0; 5}
窗口部件整理
- WBP_HealthBar
- WBP_Menu
- WBP_LevelItem
- WBP_ReturnToMainMenu
- WBP_LevelItem
- WBP_PauseWidget
- WBP_ReturnToMainMenu
- WBP_ReturnToMainMenu
- WBP_GameOverWidget
- WBP_PlayerStatRow
- WBP_ReturnToMainMenu
- WBP_PlayerStatRow
- WBP_AboveHUD
已根据玩家是否存活, 玩家是否作为观察者来决定显示哪个HUD
- WBP_SpectatorHUD
- WBP_PlayerHUD
- WBP_GameData
- WBP_GameData
- WBP_SpectatorHUD
主菜单修改
移除 ExponentialHeightFog
使用烟雾特效
创建Niagara粒子系统
使用 Content/VFX/Projectile/NE_Smoke
, 命名为NS_MenuSmoke
移动到 Content/Menu/UI
, 双击打开
-
修改烟雾颜色变化: 由白色变为红色
X Y Z 颜色 起点 (0, 1) (0, 1) (0, 1) 白色 终点 (1, 1) (1, 0) (1, 0) 红色 -
设置粒子数
-
修改粒子生存期
-
设置特效循环
修改关卡
-
关卡中添加PlayerStart
-
添加粒子系统
修改WBP_Menu
-
把内容移动到左上方
-
添加文本框: 提示选择关卡
-
添加分隔符: 分隔提示和关卡选项
-
设置按钮左对齐
-
设置游戏标题
-
设置提示文本
-
修改按钮背景和点击填充
-
修改按钮文本
修改退出按钮
为大小盒添加下级 Overlay
Overlay由两部分组成, 按钮文本和提示图片组成的水平盒, 以及与水平盒重合的按钮
修改大小盒大小
-
水平盒
由三部分组成, 复用按钮文本, 分隔符, 和包裹提示图片的大小盒
-
设置水平盒
-
设置按钮文本样式
-
设置间隔
-
设置图片大小盒: 保证图片为正方形
-
设置图片
-
-
按钮
-
平铺, 取消样式
-
透明
点击时是没效果滴~
-
选中关卡时, 叠加红色透明图片, 鼠标在关卡之上悬浮时, 显示红框
选中时, 使选中项有红色阴影
推荐 方法二
-
方法一: 叠加红色透明图片
-
修改WBP_LevelItem
添加图片
-
修改STULevelItemWidget
- 添加属性: 叠加图片
protected
ShootThemUp: Menu/UI/STULevelItemWidget.h
1UPROPERTY(meta = (BindWidget)) 2UImage *CoverImage;
- 添加接口: 设置CoverImage可见性
public
ShootThemUp: Menu/UI/STULevelItemWidget.h
1void SetCoverImageVisibility(bool Visible);
ShootThemUp: Menu/UI/STULevelItemWidget.cpp
1void USTULevelItemWidget::SetCoverImageVisibility(bool Visible) 2{ 3 if (!CoverImage) return; 4 CoverImage->SetVisibility(Visible ? ESlateVisibility::Visible : ESlateVisibility::Hidden); 5}
- 添加属性: 叠加图片
-
修改STUMenuWIdget
ShootThemUp: Menu/UI/STUMenuWidget.cpp
1// OnLevelSelected 2 3// LevelItem->SetFrameImageVisibility(LevelItem->GetLevelData().LevelName == LevelData.LevelName); 4LevelItem->SetCoverImageVisibility(LevelItem->GetLevelData().LevelName == LevelData.LevelName);
-
-
方法二: 选中时, 设置关卡缩略图颜色
-
修改STULevelItemWidget
添加接口: 设置关卡缩略图颜色
public
ShootThemUp: Menu/UI/STULevelItemWidget.h
1void SetLevelImageColor(bool Selected);
ShootThemUp: Menu/UI/STULevelItemWidget.cpp
1void USTULevelItemWidget::SetLevelImageColor(bool Selected) 2{ 3 LevelImage->SetColorAndOpacity(Selected ? FLinearColor::Red : FLinearColor::White); 4}
-
修改STUMenuWIdget
ShootThemUp: Menu/UI/STUMenuWidget.cpp
1// OnLevelSelected 2 3// LevelItem->SetCoverImageVisibility(LevelItem->GetLevelData().LevelName == LevelData.LevelName); 4LevelItem->SetLevelImageColor(LevelItem->GetLevelData().LevelName == LevelData.LevelName);
-
鼠标悬浮时, 显示红框, 鼠标离开时, 移除红框
STULevelItemWidget
-
添加鼠标悬浮事件回调函数
private
ShootThemUp: Menu/UI/STULevelItemWidget.h
1UFUNCTION() 2void OnLevelHover(); 3 4UFUNCTION() 5void OnLevelUnHover();
ShootThemUp: Menu/UI/STULevelItemWidget.cpp
1void USTULevelItemWidget::OnLevelHover() 2{ 3 SetFrameImageVisibility(true); 4} 5 6void USTULevelItemWidget::OnLevelUnHover() 7{ 8 SetFrameImageVisibility(false); 9}
-
注册
ShootThemUp: Menu/UI/STULevelItemWidget.cpp
1// NativeOnInitialized 2 3LevelSelectButton->OnHovered.AddDynamic(this, &USTULevelItemWidget::OnLevelHover); 4LevelSelectButton->OnUnhovered.AddDynamic(this, &USTULevelItemWidget::OnLevelUnHover);
调整
WBP_LevelItem
按钮必须在最后, 才能正确触发悬浮和点击
查看
修改WBP_ReturnToMainMenu
-
设置按钮
按钮一般在右方
-
设置按钮文本
暂停菜单修改
-
修改暂停标题
-
修改按钮
-
修改按钮文本
-
修改间隔符
游戏结束菜单修改
- 把表头和表项窗口部件分开
- 既可以显示阵营编号, 也可以显示阵营颜色
分开记录和表头
修改WBP_PlayerStatRow
-
将水平盒最后的分隔符移到最前
-
设置高亮色
PlayerIndicatorImage
- Hex Linear 1C191E80 -
修改PlayerName默认文本为Name
-
设置文本框
对齐 字体 字号 阴影 阴影颜色 PLayerName 左对齐 BarlowCondensed-SemiBold 30 X = Y = 1.5 000000FF Kills 居中 同上 同上 同上 同上 Deaths 居中 同上 同上 同上 同上 Team 居中 同上 同上 同上 同上 善用Copy和Paste
拷贝WBP_PlayerStatRow获得表头, 命名为WBP_PlayerStatRowHead, 更改WBP_PlayerStatRowHead基类为UserWidget
修改WBP_PlayerStatRow, 更换字体
字体 | |
---|---|
PLayerName | BarlowCondensed-Regular |
Kills | 同上 |
Deaths | 同上 |
Team | 同上 |
替换WBP_GameOverWidget中表头为WBP_PlayerStatRowHead
使用图片标识阵营
修改WBP_PlayerStatRow
-
为TeamTextBlock添加上级
Widget Switcher
Widget Switcher
可以有多个下级元素, 同一时间只显示其中一个 -
在
Widget Switcher
中添加Image
-
设置当前使用图片标识Team, 即第二个元素, 索引为1
修改STUPlayerStatRowWidget
- 添加属性: 阵营图片
protected
ShootThemUp: UI/STUPlayerStatRowWidget.h
1UPROPERTY(meta = (BindWidget)) 2UImage *TeamImage;
- 添加接口: 设置图片颜色
public
ShootThemUp: UI/STUPlayerStatRowWidget.h
1void SetTeamImageColor(const FLinearColor &Color);
ShootThemUp: UI/STUPlayerStatRowWidget.cpp
1void USTUPlayerStatRowWidget::SetTeamImageColor(const FLinearColor &Color) 2{ 3 if (!TeamImage) return; 4 TeamImage->SetColorAndOpacity(Color); 5}
在STUGameOverWidget中设置记录中的阵营颜色
ShootThemUp: UI/STUGameOverWidget.cpp
- 方法一: 脑子抽了的人就会这么写
STUGameModeBase添加接口: 返回游戏规则
public
ShootThemUp: STUGameModeBase.h
1FGameData GetGameData() const { return GameData; }
之前使用该数据成员分量时, 提醒过建议自行取出分量
1#include "STUGameModeBase.h" 2 3// UpdatePlayersStat 4 5if (GetWorld()) 6{ 7 const auto GameMode = Cast<ASTUGameModeBase>(GetWorld()->GetAuthGameMode()); 8 if (GameMode) 9 { 10 const auto TeamID = PlayerState->GetTeamID(); 11 PlayerStatRowWidget->SetTeamImageColor(GameMode->GetGameData().TeamColors[TeamID - 1]); 12 } 13}
TeamID显示用, 从1开始计数 - 方法二
1PlayerStatRowWidget->SetTeamImageColor(PlayerState->GetTeamColor());
查看
BP_STUGameModeBase
- 修改阵营颜色: 如果Alpha通道为0, 无显示; 材质对于这类型配置认为Alpha通道为1
- 修改
Round Time
为3, 方便测试
修改WBP_GameOverWidget
-
修改标题
-
修改按钮
-
修改按钮文本
-
修改按钮间间隔
查看
弹药库显示
剩余子弹数由十位数变为个位数, 在视觉上有跳动. 保持字符串占3个字符, 使用0填充
这个还依赖于等宽字体, 只是减少感官上的跳动
添加辅助函数: 使得剩余子弹数不跳动
protected
ShootThemUp: UI/STUPlayerHUDWidget.h
1UFUNCTION() 2FString FormatBullets(int32 BulletsNum) const;
ShootThemUp: UI/STUPlayerHUDWidget.cpp
1FString USTUPlayerHUDWidget::FormatBullets(int32 BulletsNum) const 2{ 3 const int32 MaxLen = 3; 4 const TCHAR Prefix = '0'; /* PrefixSymbol */ 5 6 FString Str = FString::FromInt(BulletsNum); /* BulletStr */ 7 const auto Rest = MaxLen - Str.Len(); /* SymbolsNumToAdd */ 8 if (Rest > 0) 9 { 10 Str = FString::ChrN(Rest, Prefix).Append(Str); 11 } 12 13 return Str; 14}
设置武器示意图标
修改WBP_PlayerHUD弹药库显示
只讲结果, 自行组装
-
最外层是大小盒, 位于右下角, 高度和宽度与图片大小一致
430x150
-
第二层是Overlay, 因为图片要作为背景
Overlay由三部分组成, 背景图, 武器矢量图, 和包含弹药库信息的水平盒. 背景图作为最下层, 必须排在第一个
Overlay
-
背景图
-
武器矢量图
绑定函数
Get Weapon Icon
之前的实现里, 会修改图片颜色, 现在已不需要
-
水平盒
由两部分组成- 分隔: X = 25
- 存放弹药信息的垂直盒
- 分隔: X = 25
存放弹药信息的垂直盒
由三部分组成
- 间隔: Y = 20
- 子弹数
- 弹匣图片和弹匣数组成的水平盒
子弹数
绑定函数 Get_Ammo_Bullets
弹匣图片和弹匣数组成的水平盒
由四部分组成
- 分隔: X = 7
- 弹匣图片的上级大小盒
- 分隔: X = 5
- 弹匣数
弹匣图片的上级大小盒
-
大小盒
图片长宽比为1:2, 大小盒的长宽比与之一致 -
图片
金色 Hex Linear FF9940FF
弹匣数
文本绑定 Get_Ammo_Clips
回合数
WBP_GameData左上角显示回合数和当前回合倒计时
击杀数和死亡次数的显示稍后调整
垂直盒包含所有信息, 位于屏幕左上角
垂直盒由三部分组成
-
回合数
-
间隔 Y = 20
-
回合剩余时长
生命条显示
将生命条窗口部件从WBP_PlayerHUD移动到WBP_GameData
生命条和击杀死亡信息组合显示
实现生命条逻辑, 打包击杀死亡信息
修改STUGameDataWidget
提供接口: 返回生命百分比
public
ShootThemUp: UI/STUGameDataWidget.h
1UFUNCTION(BlueprintCallable) 2float GetHealthPercent() const;
ShootThemUp: UI/STUGameDataWidget.cpp
1#include "STUUtils.h" 2#include "Components/STUHealthComponent.h" 3 4float USTUGameDataWidget::GetHealthPercent() const 5{ 6 const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(GetOwningPlayerPawn()); 7 if (!HealthComponent) return 0.0f; 8 9 return HealthComponent->GetHealthPercent(); 10}
修改STUPlayerHUDWidget
屏蔽获取生命百分比逻辑
修改WBP_PlayerHUD
移除蓝图函数 Get Health Percent
中无效节点
修改WBP_GameData
-
包含生命条和击杀死亡信息的垂直盒
位于屏幕左下角
-
击杀死亡信息
顶级为水平盒, 由7部分组成. 中间为分隔, 左右两边的样式一样, 就不赘述了
-
击杀数文本
-
间隔: X = 10
-
包含提示信息的垂直盒
由两部分组成-
间隔: Y = 35
-
提示文本
-
-
间隔: X = 100
分隔击杀数和死亡数 -
死亡数文本
-
间隔: X = 10
-
包含提示信息的垂直盒
-
-
生命条
顶级为Overlay, 由两部分组成
-
包含背景图的大小盒
背景图
-
进度条
-
修改WBP_PlayerHUD
顶级Overlay, 由两部分组成
-
WBP_GameData
-
其他
注意受伤告警图片铺满屏幕
低血量提示
protected
ShootThemUp: UI/STUGameDataWidget.h
1class UProgressBar; 2 3UPROPERTY(meta = (BindWidget)) 4UProgressBar *HealthProgressBar; 5 6UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 7float PercentColorThreshold = 0.3f; 8 9UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 10FLinearColor GoodColor = FLinearColor::Green; 11 12UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 13FLinearColor BadColor = FLinearColor::Red;
ShootThemUp: UI/STUGameDataWidget.cpp
1#include "Components/ProgressBar.h" 2 3// GetHealthPercent 4 5if (HealthProgressBar) 6{ 7 HealthProgressBar->SetFillColorAndOpacity(HealthComponent->GetHealthPercent() > PercentColorThreshold ? GoodColor : BadColor); 8}
查看
打包设置
当前一共有4个关卡, 如果打包,需要指定主菜单之外的关卡路径
Edit > Project Setting.. > Project > Packaging
Packaging > Advanced
关卡数组