UiGameOverComponent.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  3. *
  4. * SPDX-License-Identifier: Apache-2.0 OR MIT
  5. *
  6. */
  7. #include <AzCore/Serialization/EditContext.h>
  8. #include <AzCore/std/sort.h>
  9. #include <LyShine/Bus/UiElementBus.h>
  10. #include <LyShine/Bus/UiCursorBus.h>
  11. #include <LyShine/Bus/UiTextBus.h>
  12. #include <Source/Components/UI/UiGameOverComponent.h>
  13. namespace MultiplayerSample
  14. {
  15. void UiGameOverComponent::Reflect(AZ::ReflectContext* context)
  16. {
  17. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  18. {
  19. serializeContext->Class<UiGameOverComponent, AZ::Component>()
  20. ->Version(2)
  21. ->Field("Game Over root element", &UiGameOverComponent::m_gameOverRootElement)
  22. ->Field("Rank Numbers Elements", &UiGameOverComponent::m_rankNumbersUIContainer)
  23. ->Field("Top Player Elements", &UiGameOverComponent::m_topRankPlayersUIElements)
  24. ->Field("Time Remaining Until New Match UI Elements", &UiGameOverComponent::m_timeRemainingUntilNewMatchUIContainer)
  25. ;
  26. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  27. {
  28. editContext->Class<UiGameOverComponent>("Ui Game Over", "Shows Game Over screen and results")
  29. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  30. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("CanvasUI"))
  31. ->DataElement(AZ::Edit::UIHandlers::Default, &UiGameOverComponent::m_gameOverRootElement,
  32. "Game Over root element", "The root element of all Game Over specific UI.")
  33. ->DataElement(AZ::Edit::UIHandlers::Default, &UiGameOverComponent::m_rankNumbersUIContainer,
  34. "Rank Numbers", "Used to display the rank of this client's autonomous player. The parent container UI element containing ui images for each possible rank. Example 1st-10th place.")
  35. ->DataElement(AZ::Edit::UIHandlers::Default, &UiGameOverComponent::m_topRankPlayersUIElements,
  36. "Top Players", "A sorted list of rows to display the top players (player rank 1-3). Each row should have 2 children: name and score.")
  37. ->DataElement(AZ::Edit::UIHandlers::Default, &UiGameOverComponent::m_timeRemainingUntilNewMatchUIContainer,
  38. "Time Remaining Until New Match UI Elements", "A container sorted list number images to display the remaining time until the new match starts.")
  39. ;
  40. }
  41. }
  42. }
  43. void UiGameOverComponent::Activate()
  44. {
  45. m_waitForActiveNetworkMatchComponent.Enqueue(AZ::TimeMs{ 1000 }, true);
  46. UiGameOverBus::Handler::BusConnect(GetEntityId());
  47. }
  48. void UiGameOverComponent::Deactivate()
  49. {
  50. UiGameOverBus::Handler::BusDisconnect();
  51. m_waitForActiveNetworkMatchComponent.RemoveFromQueue();
  52. m_onSecondsRemainingChanged.RemoveFromQueue();
  53. m_onRoundNumberChangedHandler.Disconnect();
  54. }
  55. void UiGameOverComponent::DisplaySecondsRemainingUI()
  56. {
  57. const AZ::TimeMs currentHostTime = AZ::Interface<Multiplayer::IMultiplayer>::Get()->GetCurrentHostTimeMs();
  58. const AZ::TimeMs matchStartTime = AZ::Interface<INetworkMatch>::Get()->GetMatchStartHostTime();
  59. const uint16_t secondsRemaining = aznumeric_cast<uint16_t>(AZStd::floor(AZ::TimeMsToSeconds(matchStartTime - currentHostTime)));
  60. LyShine::EntityArray rankNumberUIElements;
  61. UiElementBus::EventResult(rankNumberUIElements, m_timeRemainingUntilNewMatchUIContainer, &UiElementBus::Events::GetChildElements);
  62. const uint16_t rankNumberUiCount = aznumeric_cast<uint16_t>(rankNumberUIElements.size());
  63. for(uint16_t uiNumbersIdx = 0; uiNumbersIdx < rankNumberUiCount; ++uiNumbersIdx)
  64. {
  65. UiElementBus::Event(rankNumberUIElements[uiNumbersIdx]->GetId(), &UiElementBus::Events::SetIsEnabled, secondsRemaining == uiNumbersIdx);
  66. }
  67. // Remove this event to update the timer ui once the time reaches 0
  68. if (secondsRemaining == 0)
  69. {
  70. m_onSecondsRemainingChanged.RemoveFromQueue();
  71. }
  72. }
  73. void UiGameOverComponent::SetGameOverScreenEnabled(bool enabled)
  74. {
  75. if (enabled)
  76. {
  77. DisplaySecondsRemainingUI();
  78. m_onSecondsRemainingChanged.Enqueue(AZ::TimeMs{ 1000 }, true);
  79. }
  80. else
  81. {
  82. m_onSecondsRemainingChanged.RemoveFromQueue();
  83. }
  84. UiElementBus::Event(m_gameOverRootElement, &UiElementBus::Events::SetIsEnabled, enabled);
  85. }
  86. void UiGameOverComponent::DisplayResults(MatchResultsSummary results)
  87. {
  88. // Sort the players by score (highest score is 1st)
  89. // If scores are matching, then sort by remaining armor.
  90. AZStd::sort(results.m_playerStates.begin(), results.m_playerStates.end(), [](const PlayerState& a, const PlayerState& b)
  91. {
  92. if (a.m_score == b.m_score)
  93. {
  94. return a.m_remainingArmor > b.m_remainingArmor;
  95. }
  96. return a.m_score > b.m_score;
  97. });
  98. // Display the top 3 players
  99. for (uint i = 0; i < m_topRankPlayersUIElements.size(); ++i)
  100. {
  101. AZ::EntityId& topRankPlayerRow = m_topRankPlayersUIElements[i];
  102. LyShine::EntityArray playerStatsUiElements;
  103. UiElementBus::EventResult(playerStatsUiElements, topRankPlayerRow, &UiElementBus::Events::GetChildElements);
  104. if (playerStatsUiElements.size() >= 2)
  105. {
  106. if (i < results.m_playerStates.size())
  107. {
  108. UiTextBus::Event(playerStatsUiElements[0]->GetId(), &UiTextBus::Events::SetText, results.m_playerStates[i].m_playerName.c_str());
  109. UiTextBus::Event(playerStatsUiElements[1]->GetId(), &UiTextBus::Events::SetText, AZStd::string::format("%u", results.m_playerStates[i].m_score));
  110. }
  111. else
  112. {
  113. UiTextBus::Event(playerStatsUiElements[0]->GetId(), &UiTextBus::Events::SetText, "N/A");
  114. UiTextBus::Event(playerStatsUiElements[1]->GetId(), &UiTextBus::Events::SetText, "N/A");
  115. }
  116. }
  117. else
  118. {
  119. AZ_Warning("UiGameOverComponent", false, "Failed to display player rank %i. Please update ui canvas so the top player rows have at least 2 children: name and score.", i + 1)
  120. }
  121. }
  122. // Display the rank on this client's player
  123. const char* playerIdentityName = nullptr;
  124. PlayerIdentityRequestBus::BroadcastResult(playerIdentityName, &PlayerIdentityRequestBus::Events::GetPlayerIdentityName);
  125. if (playerIdentityName)
  126. {
  127. // Find this client player's rank by matching their name
  128. uint clientPlayerRank = 0;
  129. for (; clientPlayerRank < results.m_playerStates.size(); ++clientPlayerRank)
  130. {
  131. if (results.m_playerStates[clientPlayerRank].m_playerName == playerIdentityName)
  132. {
  133. break;
  134. }
  135. }
  136. // Display the proper rank number UI
  137. LyShine::EntityArray rankNumbers;
  138. UiElementBus::EventResult(rankNumbers, m_rankNumbersUIContainer, &UiElementBus::Events::GetChildElements);
  139. for (uint i = 0; i < rankNumbers.size(); ++i)
  140. {
  141. UiElementBus::Event(rankNumbers[i]->GetId(), &UiElementBus::Events::SetIsEnabled, i == clientPlayerRank);
  142. }
  143. }
  144. }
  145. }