游戏结束显示玩家得分
2023年11月23日 2024年1月11日
Game Over Widget
API导航
PlayerState
修改和获取游戏角色名称
AIController控制的游戏角色, 名称通常为空, 而PlayerController控制的游戏角色, 其名称为主机名
1/** set the player name to S */ 2virtual void SetPlayerName(const FString& S); 3 4/** returns current player name */ 5UFUNCTION(BlueprintPure, Category = PlayerState) 6FString GetPlayerName() const;
Pawn
检查上级控制器是否为PlayerController, 意味着是否为玩家操控的游戏角色
1/** See if this actor is currently being controlled */ 2UE_DEPRECATED(4.24, "IsControlled is deprecated. To check if this pawn is controlled by anything, then call IsPawnControlled. To check if this pawn is controlled only by the player then call IsPlayerControlled") 3UFUNCTION(BlueprintCallable, Category=Pawn) 4bool IsControlled() const; 5 6bool APawn::IsControlled() const 7{ 8 APlayerController* const PC = Cast<APlayerController>(Controller); 9 return(PC != nullptr); 10}
Controller
判断控制器类型是否为PlayerController
1/** Returns whether this Controller is a PlayerController. */ 2UFUNCTION(BlueprintCallable, Category=Pawn) 3bool IsPlayerController() const;
TextBlock
设置文本内容
1/** 2 * Directly sets the widget text. 3 * Warning: This will wipe any binding created for the Text property! 4 * @param InText The text to assign to the widget 5 */ 6UFUNCTION(BlueprintCallable, Category="Widget", meta=(DisplayName="SetText (Text)")) 7virtual void SetText(FText InText);
Image
1class UMG_API UImage : public UWidget 2{ 3 // ... 4};
Widget
UImage是其派生类
设置窗口部件可见性
1/** Sets the visibility of the widget. */ 2UFUNCTION(BlueprintCallable, Category="Widget") 3virtual void SetVisibility(ESlateVisibility InVisibility);
VerticalBox
1/** 2 * A vertical box widget is a layout panel allowing child widgets to be automatically laid out 3 * vertically. 4 * 5 * * Many Children 6 * * Flows Vertical 7 */ 8UCLASS(meta = (ShortTooltip = "A layout panel for automatically laying child widgets out vertically")) 9class UMG_API UVerticalBox : public UPanelWidget 10{ 11 // ... 12};
PanelWidget
UVerticalBox的基类
移除所有子节点
1/** Remove all child widgets from the panel widget. */ 2UFUNCTION(BlueprintCallable, Category="Widget|Panel") 3virtual void ClearChildren();
添加子节点
1/** 2 * Adds a new child widget to the container. Returns the base slot type, 3 * requires casting to turn it into the type specific to the container. 4 */ 5UFUNCTION(BlueprintCallable, Category="Widget|Panel") 6UPanelSlot* AddChild(UWidget* Content);
STUGameHUD创建游戏结束窗口
添加属性: 游戏结束窗口部件类型
ShootThemUp: UI/STUGameHUD.h
1UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 2TSubclassOf<UUserWidget> GameOverWidgetClass;
创建该类型窗口部件对象, 加入窗口部件对象容器
ShootThemUp: UI/STUGameHUD.cpp
1// InitGameWidgets 2GameWidgets.Add(ESTUMatchState::GameOver, CreateWidget<UUserWidget>(GetWorld(), GameOverWidgetClass));
设计游戏结束窗口
由两部分组成:
- 游戏结束窗口
- 单条玩家得分
创建两个C++类
- | |
---|---|
基类 | UserWidget |
路径 | UI |
名称 | STUGameOverWidget |
属性 | Public |
- | |
---|---|
基类 | UserWidget |
路径 | UI |
名称 | STUPlayerStatRowWidget |
属性 | Public |
创建两个蓝图窗口部件
Content/UI
基类 | |
---|---|
WBP_PlayerStatRow | STUPlayerStatRowWidget |
WBP_GameOverWidget | STUGameOverWidget |
设计WBP_PlayerStatRow
既用作玩家得分记录, 也用作记录表头
-
添加
Canvas Panel
-
添加
Vertical Box
一条记录中的所有元素都包含在Vertical Box
中勾选
Size To Content
Vertical Box
由两部分组成
-
添加
Overlay
-
添加
Spacer
- Appearance > Size > Y 10, 纵向间隔
Overlay
由两部分组件; 其存在使得图片作为记录的背景
-
添加
Image
用以高亮玩家信息修改变量名为
PlayerIndicatorImage
, 之后会在代码中设置可见性- Appearance > Color and Opacity > Hex Linear 0028FFFF Appearance > Color and Opacity > A 0.5, 透明度 Horizontal Alignment Fill Vertical Alignment Fill Behavior > Visibility Hidden, 默认不可见(不高亮) -
添加
Horizontal Box
记录文本
Horizontal Box
由5部分组成; 所有文本外均套有一个 Size Box
; 为所有文本设置对齐方式; 为所有文本勾选 IsVariable
, 并设置变量名
文本框默认内容 | Size Box > Width Override | Text > Appearance > Justification 对齐方式 | 变量名 |
---|---|---|---|
PlayerName | 400 | 默认左对齐 | PlayerNameTextBlock |
Kills | 200 | 右对齐 | KillsTextBlock |
Deaths | 200 | 右对齐 | DeathsTextBlock |
Team | 200 | 右对齐 | TeamTextBlock |
-
添加
Text
玩家名
建议将文本框默认内容设为Name -
添加
Text
击杀数 -
添加
Text
死亡次数 -
添加
Text
阵营ID -
添加
分隔符
- Appearance > Size > X 50, 水平间隔
设计WBP_GameOverWidget
从WBP_PauseWidget拷贝已有设计
-
设置
Game Pause
文本居中 -
拷贝整个
Canvas Panel
设计WBP_GameOverWidget
-
粘贴
Canvas Panel
-
修改文本框内容
-
移除
Size Box
-
添加表头: WBP_PlayerStatRow
-
添加分隔符
Spacer
- Appearance > Size > Y 40, 纵向间隔 -
添加垂直盒
Vertical Box
, 用来存放玩家得分
之后会在代码中往里添加记录: 勾选Is Variable
, 修改变量名为PlayerStatBox
修改STUGameModeBase设置游戏角色名称
如果是玩家操控的游戏角色, 设置名称为 Player
, 将NPC命名为 Bot
ShootThemUp: STUGameModeBase.cpp
1// CreateTeamsInfo 2PlayerState->SetPlayerName(Controller->IsPlayerController() ? "Player" : "Bot");
另一种实现, 不建议使用 , 因为IsControlled已经过时了
1const auto Pawn = Controller->GetPawn(); 2if (!Pawn) continue; 3 4if (Pawn->IsControlled()) 5{ 6 PlayerState->SetPlayerName("Player"); 7} 8else 9{ 10 PlayerState->SetPlayerName("Bot"); 11}
STUUtils添加静态函数: 将int32转换为FText
PlayerState中一些属性为int32类型, 而文本框内容设置接受FText类型
ShootThemUp: STUUtils.h
1static FText TextFromInt(int32 Number) { return FText::FromString(FString::FromInt(Number)); }
实现STUPlayerStatRowWidget
添加属性
变量: 文本框和图片
protected
ShootThemUp: UI/STUPlayerStatRowWidget.h
1class UImage; 2class UTextBlock; 3 4UPROPERTY(meta = (BindWidget)) 5UImage *PlayerIndicatorImage; 6 7UPROPERTY(meta = (BindWidget)) 8UTextBlock *PlayerNameTextBlock; 9 10UPROPERTY(meta = (BindWidget)) 11UTextBlock *KillsTextBlock; 12 13UPROPERTY(meta = (BindWidget)) 14UTextBlock *DeathsTextBlock; 15 16UPROPERTY(meta = (BindWidget)) 17UTextBlock *TeamTextBlock;
添加接口: 设置文本框内容, 设置高亮可见性
public
ShootThemUp: UI/STUPlayerStatRowWidget.h
1void SetPlayerIndicatorImageVisibility(bool Visible); 2void SetPlayerNameTextBlock(FString PlayerName); 3void SetKillsTextBlock(int32 Kills); 4void SetDeathsTextBlock(int32 Deaths); 5void SetTeamTextBlock(int32 TeamID);
ShootThemUp: UI/STUPlayerStatRowWidget.cpp
1#include "STUUtils.h" 2#include "Components/Image.h" 3#include "Components/TextBlock.h" 4 5void USTUPlayerStatRowWidget::SetPlayerIndicatorImageVisibility(bool Visible) 6{ 7 if (!PlayerIndicatorImage) return; 8 PlayerIndicatorImage->SetVisibility(Visible ? ESlateVisibility::Visible : ESlateVisibility::Hidden); 9} 10 11void USTUPlayerStatRowWidget::SetPlayerNameTextBlock(FString PlayerName) 12{ 13 if (!PlayerNameTextBlock) return; 14 PlayerNameTextBlock->SetText(FText::FromString(PlayerName)); 15} 16 17void USTUPlayerStatRowWidget::SetKillsTextBlock(int32 Kills) 18{ 19 if (!KillsTextBlock) return; 20 KillsTextBlock->SetText(STUUtils::TextFromInt(Kills)); 21} 22 23void USTUPlayerStatRowWidget::SetDeathsTextBlock(int32 Deaths) 24{ 25 if (!DeathsTextBlock) return; 26 DeathsTextBlock->SetText(STUUtils::TextFromInt(Deaths)); 27} 28 29void USTUPlayerStatRowWidget::SetTeamTextBlock(int32 TeamID) 30{ 31 if (!TeamTextBlock) return; 32 TeamTextBlock->SetText(STUUtils::TextFromInt(TeamID)); 33}
实现STUGameOverWidget
覆写Initialize, 注册游戏状态改变通知
ShootThemUp: UI/STUGameOverWidget.h
1#include "STUCoreTypes.h" 2 3// public 4virtual bool Initialize() override; 5 6// private 7void OnMatchStateChanged(ESTUMatchState NewState);
ShootThemUp: UI/STUGameOverWidget.cpp
1#include "STUGameModeBase.h" 2 3bool USTUGameOverWidget::Initialize() 4{ 5 if (GetWorld()) 6 { 7 const auto GameMode = Cast<ASTUGameModeBase>(GetWorld()->GetAuthGameMode()); 8 if (GameMode) 9 { 10 GameMode->OnMatchStateChanged.AddUObject(this, &USTUGameOverWidget::OnMatchStateChanged); 11 } 12 } 13 14 return Super::Initialize(); 15}
添加属性: 记录盒
protected
ShootThemUp: UI/STUGameOverWidget.h
1class UVerticalBox; 2 3UPROPERTY(meta = (BindWidget)) 4UVerticalBox *PlayerStatBox;
添加属性: 记录类型
protected
ShootThemUp: UI/STUGameOverWidget.h
1UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 2TSubclassOf<UUserWidget> PlayerStatRowWidgetClass;
添加美化函数: 往记录盒添加记录
private
ShootThemUp: UI/STUGameOverWidget.h
1void UpdatePlayersStat();
ShootThemUp: UI/STUGameOverWidget.cpp
1#include "Player/STUPlayerState.h" 2#include "UI/STUPlayerStatRowWidget.h" 3#include "Components/VerticalBox.h" 4 5void USTUGameOverWidget::UpdatePlayersStat() 6{ 7 if (!GetWorld() || !PlayerStatBox) return; 8 9 PlayerStatBox->ClearChildren(); 10 11 for (auto It = GetWorld()->GetControllerIterator(); It; ++It) 12 { 13 const auto Controller = It->Get(); 14 if (!Controller) continue; 15 16 const auto PlayerState = Cast<ASTUPlayerState>(Controller->PlayerState); 17 if (!PlayerState) continue; 18 19 const auto PlayerStatRowWidget = CreateWidget<USTUPlayerStatRowWidget>(GetWorld(), PlayerStatRowWidgetClass); 20 if (!PlayerStatRowWidget) continue; 21 22 PlayerStatRowWidget->SetPlayerNameTextBlock(PlayerState->GetPlayerName()); 23 PlayerStatRowWidget->SetKillsTextBlock(PlayerState->GetKillsNum()); 24 PlayerStatRowWidget->SetDeathsTextBlock(PlayerState->GetDeathsNum()); 25 PlayerStatRowWidget->SetTeamTextBlock(PlayerState->GetTeamID()); 26 PlayerStatRowWidget->SetPlayerIndicatorImageVisibility(Controller->IsPlayerController()); 27 28 PlayerStatBox->AddChild(PlayerStatRowWidget); 29 } 30}
在处理函数中调用
ShootThemUp: UI/STUGameOverWidget.cpp
1void USTUGameOverWidget::OnMatchStateChanged(ESTUMatchState NewState) 2{ 3 if (NewState == ESTUMatchState::GameOver) 4 { 5 UpdatePlayersStat(); 6 } 7}
查看
验证
-
设置BP_STUGameHUD
- GameOverWidgetClass WBP_GameOverWidget -
设置WBP_GameOverWidget
- PlayerStatRowWidgetClass WBP_PlayerStatRow -
设置BP_STUGameModeBase
- Rounds Num 1 Round Time 3
存在问题
设置BP_STUGameModeBase
- | |
---|---|
Players Num | 20 |
为记录盒添加滚动条
-
为记录盒添加滚动盒
此时运行, 无改变, 因为垂直盒勾选了
Size To Content
-
为滚动盒添加大小盒
设置最大高度- Max Desired Height 300 -
查看
记录盒上方和下方都有阴影, 滚动条轨迹不可见 -
优化滚动盒阴影
-
始终显示滚动条轨迹, 设置滚动条宽度
再次查看
-
滚动条效果
-
恢复玩家数
BP_STUGameModeBase
- Players Num 4 因为大小盒高度不足300, 所以不显示滚动条