538 lines
21 KiB
C#
538 lines
21 KiB
C#
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Threading.Tasks;
|
||
|
using TMPro;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.Events;
|
||
|
using UnityEngine.UI;
|
||
|
|
||
|
namespace BulletHellTemplate
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Manages the profile menu UI, including displaying player information and managing icon, frame selections, and audio settings.
|
||
|
/// </summary>
|
||
|
public class UIProfileMenu : MonoBehaviour
|
||
|
{
|
||
|
[Header("UI Elements")]
|
||
|
[Tooltip("Text component to display the player's name.")]
|
||
|
public TextMeshProUGUI playerName;
|
||
|
|
||
|
[Tooltip("Image component to display the player's icon.")]
|
||
|
public Image playerIcon;
|
||
|
|
||
|
[Tooltip("Image component to display the player's frame.")]
|
||
|
public Image playerFrame;
|
||
|
|
||
|
[Header("UI Prefabs")]
|
||
|
[Tooltip("Prefab for creating icon entries.")]
|
||
|
public IconsEntry iconsEntryPrefab;
|
||
|
|
||
|
[Tooltip("Prefab for creating frame entries.")]
|
||
|
public FramesEntry framesEntryPrefab;
|
||
|
|
||
|
[Tooltip("Container to hold dynamically created icon and frame entries.")]
|
||
|
public Transform container;
|
||
|
|
||
|
[Tooltip("Container GameObject for icons/frames.")]
|
||
|
public GameObject containerPref;
|
||
|
|
||
|
[Header("Change Name Components")]
|
||
|
[Tooltip("Currency ID used for name change.")]
|
||
|
public string changeNameTick = "TKN";
|
||
|
|
||
|
[Tooltip("Minimum length for the player's name.")]
|
||
|
public int minNameLength = 3;
|
||
|
|
||
|
[Tooltip("Maximum length for the player's name.")]
|
||
|
public int maxNameLength = 20;
|
||
|
|
||
|
[Tooltip("Number of tickets required to change the player's name.")]
|
||
|
public int ticketsToChange = 1;
|
||
|
|
||
|
[Tooltip("Indicates if a ticket is required for name change.")]
|
||
|
public bool needTicket;
|
||
|
|
||
|
[Tooltip("UI prefab for the name change input screen.")]
|
||
|
public GameObject changeNamePref;
|
||
|
|
||
|
[Tooltip("Input field for entering a new name.")]
|
||
|
public TMP_InputField changeNameInput;
|
||
|
|
||
|
[Tooltip("UI component to display status messages (success/error).")]
|
||
|
public TextMeshProUGUI statusText;
|
||
|
|
||
|
[Header("Favorite character")]
|
||
|
[Tooltip("Image to display mastery icon of the favorite character.")]
|
||
|
public Image masteryIcon;
|
||
|
|
||
|
[Tooltip("Image fill component to display mastery progress.")]
|
||
|
public Image masteryProgressBar;
|
||
|
|
||
|
[Tooltip("UI text to display current mastery experience.")]
|
||
|
public TextMeshProUGUI currentMasteryExp;
|
||
|
|
||
|
[Tooltip("UI text to display the mastery level name.")]
|
||
|
public TextMeshProUGUI masteryName;
|
||
|
|
||
|
[Tooltip("Container to hold the temporary favorite character model.")]
|
||
|
public Transform tempCharacterContainer;
|
||
|
|
||
|
[Header("UI Translations")]
|
||
|
[Tooltip("Fallback message when there are not enough tickets to change the name.")]
|
||
|
public string notEnoughTickets = "Not enough tickets to change the name.";
|
||
|
[Tooltip("Translations for the message when there are not enough tickets to change the name.")]
|
||
|
public NameTranslatedByLanguage[] notEnoughTicketsTranslated;
|
||
|
|
||
|
[Tooltip("Fallback message when name length is invalid.")]
|
||
|
public string nameLengthError = "Name must be between 3 and 14 characters.";
|
||
|
[Tooltip("Translations for the message when name length is invalid.")]
|
||
|
public NameTranslatedByLanguage[] nameLengthErrorTranslated;
|
||
|
|
||
|
[Tooltip("Fallback message when the name is already taken.")]
|
||
|
public string nameAlreadyTaken = "Name already taken.";
|
||
|
[Tooltip("Translations for the message when the name is already taken.")]
|
||
|
public NameTranslatedByLanguage[] nameAlreadyTakenTranslated;
|
||
|
|
||
|
[Tooltip("Fallback message when the name is changed successfully.")]
|
||
|
public string nameChangeSuccess = "Name changed successfully.";
|
||
|
[Tooltip("Translations for the message when the name is changed successfully.")]
|
||
|
public NameTranslatedByLanguage[] nameChangeSuccessTranslated;
|
||
|
|
||
|
[Tooltip("Fallback message when there's an error changing the name.")]
|
||
|
public string nameChangeFail = "Error changing name.";
|
||
|
[Tooltip("Translations for the message when there's an error changing the name.")]
|
||
|
public NameTranslatedByLanguage[] nameChangeFailTranslated;
|
||
|
|
||
|
[Header("Events")]
|
||
|
[Tooltip("Event invoked when the menu is opened.")]
|
||
|
public UnityEvent OnOpenMenu;
|
||
|
|
||
|
[Tooltip("Event invoked when the menu is closed.")]
|
||
|
public UnityEvent OnCloseMenu;
|
||
|
|
||
|
[Tooltip("Event invoked when the nickname is successfully changed.")]
|
||
|
public UnityEvent OnChangeNickname;
|
||
|
|
||
|
[Tooltip("Event invoked when the icon is successfully changed.")]
|
||
|
public UnityEvent OnChangeIcon;
|
||
|
|
||
|
[Tooltip("Event invoked when the frame is successfully changed.")]
|
||
|
public UnityEvent OnChangeFrame;
|
||
|
|
||
|
[Tooltip("Event invoked when a coupon is redeemed successfully.")]
|
||
|
public UnityEvent OnRedeemCoupon;
|
||
|
|
||
|
[Header("Audio Settings")]
|
||
|
[Tooltip("Slider for adjusting master volume.")]
|
||
|
public Slider masterVolumeSlider;
|
||
|
|
||
|
[Tooltip("Slider for adjusting VFX volume.")]
|
||
|
public Slider vfxVolumeSlider;
|
||
|
|
||
|
[Tooltip("Slider for adjusting ambience volume.")]
|
||
|
public Slider ambienceVolumeSlider;
|
||
|
|
||
|
private string currentLang;
|
||
|
private List<IconsEntry> iconsEntries = new List<IconsEntry>();
|
||
|
private List<FramesEntry> framesEntries = new List<FramesEntry>();
|
||
|
public static UIProfileMenu Singleton;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Subscribes to language change events and sets up the singleton reference.
|
||
|
/// </summary>
|
||
|
private void Awake()
|
||
|
{
|
||
|
LanguageManager.LanguageManager.onLanguageChanged += UpdateText;
|
||
|
Singleton = this;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Unsubscribes from language change events.
|
||
|
/// </summary>
|
||
|
private void OnDestroy()
|
||
|
{
|
||
|
LanguageManager.LanguageManager.onLanguageChanged -= UpdateText;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes the UI menu when enabled.
|
||
|
/// </summary>
|
||
|
private void OnEnable()
|
||
|
{
|
||
|
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
|
||
|
OnOpenMenu.Invoke();
|
||
|
DestroyTemporaryModel();
|
||
|
LoadProfile();
|
||
|
LoadFavoriteCharacter();
|
||
|
UpdateText();
|
||
|
SetupSliders();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Cleans up the UI menu when disabled.
|
||
|
/// </summary>
|
||
|
private void OnDisable()
|
||
|
{
|
||
|
DestroyTemporaryModel();
|
||
|
OnCloseMenu.Invoke();
|
||
|
UIMainMenu.Singleton.LoadPlayerInfo();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when the language is changed, forcing the UI to update translations.
|
||
|
/// </summary>
|
||
|
private void UpdateText()
|
||
|
{
|
||
|
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
|
||
|
LoadFavoriteCharacter();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Loads and displays the player's profile information, including name, icon, and frame.
|
||
|
/// </summary>
|
||
|
public void LoadProfile()
|
||
|
{
|
||
|
containerPref.SetActive(!containerPref.activeSelf);
|
||
|
string name = PlayerSave.GetPlayerName();
|
||
|
playerName.text = !string.IsNullOrEmpty(name) ? name : "Unknown Player";
|
||
|
|
||
|
string iconId = PlayerSave.GetPlayerIcon();
|
||
|
string frameId = PlayerSave.GetPlayerFrame();
|
||
|
|
||
|
SetPlayerIcon(iconId);
|
||
|
SetPlayerFrame(frameId);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets initial slider values from the AudioManager (if available) and adds listeners.
|
||
|
/// </summary>
|
||
|
private void SetupSliders()
|
||
|
{
|
||
|
if (masterVolumeSlider != null)
|
||
|
{
|
||
|
masterVolumeSlider.onValueChanged.RemoveAllListeners();
|
||
|
masterVolumeSlider.value = AudioManager.Singleton.masterVolume;
|
||
|
masterVolumeSlider.onValueChanged.AddListener(OnMasterVolumeChanged);
|
||
|
}
|
||
|
|
||
|
if (vfxVolumeSlider != null)
|
||
|
{
|
||
|
vfxVolumeSlider.onValueChanged.RemoveAllListeners();
|
||
|
vfxVolumeSlider.value = AudioManager.Singleton.vfxVolume;
|
||
|
vfxVolumeSlider.onValueChanged.AddListener(OnVFXVolumeChanged);
|
||
|
}
|
||
|
|
||
|
if (ambienceVolumeSlider != null)
|
||
|
{
|
||
|
ambienceVolumeSlider.onValueChanged.RemoveAllListeners();
|
||
|
ambienceVolumeSlider.value = AudioManager.Singleton.ambienceVolume;
|
||
|
ambienceVolumeSlider.onValueChanged.AddListener(OnAmbienceVolumeChanged);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Handles changes in the master volume slider.
|
||
|
/// </summary>
|
||
|
/// <param name="value">Volume value from 0 to 1.</param>
|
||
|
public void OnMasterVolumeChanged(float value)
|
||
|
{
|
||
|
AudioManager.Singleton.masterVolume = value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Handles changes in the VFX volume slider.
|
||
|
/// </summary>
|
||
|
/// <param name="value">Volume value from 0 to 1.</param>
|
||
|
public void OnVFXVolumeChanged(float value)
|
||
|
{
|
||
|
AudioManager.Singleton.vfxVolume = value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Handles changes in the ambience volume slider.
|
||
|
/// </summary>
|
||
|
/// <param name="value">Volume value from 0 to 1.</param>
|
||
|
public void OnAmbienceVolumeChanged(float value)
|
||
|
{
|
||
|
AudioManager.Singleton.ambienceVolume = value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates and displays icon entries in the container, clearing old entries first.
|
||
|
/// </summary>
|
||
|
public void LoadIcons()
|
||
|
{
|
||
|
foreach (Transform child in container)
|
||
|
{
|
||
|
Destroy(child.gameObject);
|
||
|
}
|
||
|
|
||
|
containerPref.SetActive(true);
|
||
|
|
||
|
foreach (IconItem item in GameInstance.Singleton.iconItems)
|
||
|
{
|
||
|
if (MonetizationManager.Singleton.IsIconPurchased(item.iconId) || item.isUnlocked)
|
||
|
{
|
||
|
IconsEntry iconEntry = Instantiate(iconsEntryPrefab, container);
|
||
|
iconEntry.SetIconInfo(item.iconId);
|
||
|
iconEntry.icon.sprite = item.icon;
|
||
|
string iconNameTranslatedName = GetTranslatedString(item.iconNameTranslated, item.iconName, currentLang);
|
||
|
iconEntry.iconName.text = iconNameTranslatedName;
|
||
|
iconsEntries.Add(iconEntry);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates and displays frame entries in the container, clearing old entries first.
|
||
|
/// </summary>
|
||
|
public void LoadFrames()
|
||
|
{
|
||
|
foreach (Transform child in container)
|
||
|
{
|
||
|
Destroy(child.gameObject);
|
||
|
}
|
||
|
|
||
|
containerPref.SetActive(true);
|
||
|
|
||
|
foreach (FrameItem item in GameInstance.Singleton.frameItems)
|
||
|
{
|
||
|
if (MonetizationManager.Singleton.IsFramePurchased(item.frameId) || item.isUnlocked)
|
||
|
{
|
||
|
FramesEntry frameEntry = Instantiate(framesEntryPrefab, container);
|
||
|
frameEntry.SetFrameInfo(item.frameId);
|
||
|
string frameNameTranslatedName = GetTranslatedString(item.frameNameTranslated,item.frameName, currentLang);
|
||
|
frameEntry.icon.sprite = item.icon;
|
||
|
frameEntry.frameName.text = frameNameTranslatedName;
|
||
|
framesEntries.Add(frameEntry);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Loads the player's favorite character data including mastery progress and 3D model.
|
||
|
/// </summary>
|
||
|
public void LoadFavoriteCharacter()
|
||
|
{
|
||
|
int favouriteCharacterId = PlayerSave.GetFavouriteCharacter();
|
||
|
int currentMasteryLevel = PlayerSave.GetCharacterMasteryLevel(favouriteCharacterId);
|
||
|
int currMasteryExp = PlayerSave.GetCharacterCurrentMasteryExp(favouriteCharacterId);
|
||
|
|
||
|
CharacterMasteryLevel masteryInfo = GameInstance.Singleton.GetMasteryLevel(currentMasteryLevel);
|
||
|
string masteryTranslatedName = GetTranslatedString(masteryInfo.masteryNameTranslated, masteryInfo.masteryName, currentLang);
|
||
|
|
||
|
if (masteryName != null) masteryName.text = masteryTranslatedName;
|
||
|
if (masteryIcon != null) masteryIcon.sprite = masteryInfo.masteryIcon;
|
||
|
|
||
|
if (currentMasteryLevel < GameInstance.Singleton.characterMastery.maxMasteryLevel)
|
||
|
{
|
||
|
int requiredMasteryExp = GameInstance.Singleton.GetMasteryExpForLevel(currentMasteryLevel);
|
||
|
if (currentMasteryExp != null) currentMasteryExp.text = $"{currMasteryExp}/{requiredMasteryExp}";
|
||
|
if (masteryProgressBar != null) masteryProgressBar.fillAmount = (float)currMasteryExp / requiredMasteryExp;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (currentMasteryExp != null) currentMasteryExp.text = $"{currMasteryExp}/MAX";
|
||
|
if (masteryProgressBar != null) masteryProgressBar.fillAmount = 1f;
|
||
|
}
|
||
|
|
||
|
foreach (Transform child in tempCharacterContainer)
|
||
|
{
|
||
|
Destroy(child.gameObject);
|
||
|
}
|
||
|
|
||
|
CharacterData cd = GetCharacterDataById(favouriteCharacterId);
|
||
|
if (cd != null && tempCharacterContainer != null)
|
||
|
{
|
||
|
int skinIndex = PlayerSave.GetCharacterSkin(cd.characterId);
|
||
|
if (cd.characterSkins != null && cd.characterSkins.Length > 0 && skinIndex >= 0 && skinIndex < cd.characterSkins.Length)
|
||
|
{
|
||
|
CharacterSkin skin = cd.characterSkins[skinIndex];
|
||
|
if (skin.skinCharacterModel != null)
|
||
|
Instantiate(skin.skinCharacterModel, tempCharacterContainer);
|
||
|
}
|
||
|
else if (cd.characterModel != null)
|
||
|
{
|
||
|
Instantiate(cd.characterModel, tempCharacterContainer);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Handles the click event to open the name change UI.
|
||
|
/// </summary>
|
||
|
public void OnClickChangeName()
|
||
|
{
|
||
|
if (needTicket && MonetizationManager.GetCurrency(changeNameTick) < ticketsToChange)
|
||
|
{
|
||
|
DisplayStatusMessage(GetTranslatedString(notEnoughTicketsTranslated, notEnoughTickets, currentLang), Color.red);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
changeNamePref.SetActive(true);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Processes the new name input and starts the coroutine to change the player's name.
|
||
|
/// </summary>
|
||
|
public void ProcessChangeName()
|
||
|
{
|
||
|
string newName = changeNameInput.text;
|
||
|
|
||
|
if (string.IsNullOrEmpty(newName) || newName.Length < minNameLength || newName.Length > maxNameLength)
|
||
|
{
|
||
|
DisplayStatusMessage(GetTranslatedString(nameLengthErrorTranslated, nameLengthError, currentLang), Color.red);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
StartCoroutine(ChangePlayerName(newName));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Coroutine to change the player's name, including validation and database update.
|
||
|
/// </summary>
|
||
|
/// <param name="newName">The new name to set.</param>
|
||
|
private IEnumerator ChangePlayerName(string newName)
|
||
|
{
|
||
|
Task<bool> checkNameTask = BackendManager.Singleton.CheckIfNameExists(newName);
|
||
|
yield return new WaitUntil(() => checkNameTask.IsCompleted);
|
||
|
|
||
|
if (checkNameTask.Result)
|
||
|
{
|
||
|
DisplayStatusMessage(GetTranslatedString(nameAlreadyTakenTranslated, nameAlreadyTaken, currentLang), Color.red);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Task updateNameTask = BackendManager.Singleton.UpdatePlayerName(newName);
|
||
|
yield return new WaitUntil(() => updateNameTask.IsCompleted);
|
||
|
|
||
|
if (updateNameTask.Exception == null)
|
||
|
{
|
||
|
DisplayStatusMessage(GetTranslatedString(nameChangeSuccessTranslated, nameChangeSuccess, currentLang), Color.white);
|
||
|
playerName.text = newName;
|
||
|
UIMainMenu.Singleton.LoadPlayerInfo();
|
||
|
OnChangeNickname.Invoke(); // Invoke event for successful name change
|
||
|
|
||
|
if (needTicket)
|
||
|
{
|
||
|
int currentTickets = MonetizationManager.GetCurrency(changeNameTick);
|
||
|
int newTickets = currentTickets - ticketsToChange;
|
||
|
MonetizationManager.SetCurrency(changeNameTick, newTickets);
|
||
|
}
|
||
|
changeNamePref.SetActive(false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DisplayStatusMessage(GetTranslatedString(nameChangeFailTranslated, nameChangeFail, currentLang), Color.red);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Retrieves character data by its ID.
|
||
|
/// </summary>
|
||
|
/// <param name="characterId">The ID of the character to retrieve.</param>
|
||
|
/// <returns>Character data if found, otherwise null.</returns>
|
||
|
private CharacterData GetCharacterDataById(int characterId)
|
||
|
{
|
||
|
if (GameInstance.Singleton == null || GameInstance.Singleton.characterData == null)
|
||
|
return null;
|
||
|
|
||
|
foreach (CharacterData item in GameInstance.Singleton.characterData)
|
||
|
if (item.characterId == characterId)
|
||
|
return item;
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Destroys any temporary character models currently instantiated.
|
||
|
/// </summary>
|
||
|
private void DestroyTemporaryModel()
|
||
|
{
|
||
|
if (tempCharacterContainer == null) return;
|
||
|
for (int i = tempCharacterContainer.childCount - 1; i >= 0; i--)
|
||
|
Destroy(tempCharacterContainer.GetChild(i).gameObject);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the translated string for the current language, or a fallback value if no translation is found.
|
||
|
/// </summary>
|
||
|
/// <param name="translations">Array of NameTranslatedByLanguage entries.</param>
|
||
|
/// <param name="fallback">Fallback text in case the translation is not found.</param>
|
||
|
/// <param name="currentLang">The current language ID.</param>
|
||
|
/// <returns>A translated string if found, otherwise the fallback.</returns>
|
||
|
private string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string currentLang)
|
||
|
{
|
||
|
if (translations != null)
|
||
|
{
|
||
|
foreach (var translation in translations)
|
||
|
{
|
||
|
if (!string.IsNullOrEmpty(translation.LanguageId)
|
||
|
&& translation.LanguageId.Equals(currentLang)
|
||
|
&& !string.IsNullOrEmpty(translation.Translate))
|
||
|
{
|
||
|
return translation.Translate;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return fallback;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Displays a status message with the specified color.
|
||
|
/// </summary>
|
||
|
/// <param name="message">The message to display.</param>
|
||
|
/// <param name="color">The color of the message text.</param>
|
||
|
private void DisplayStatusMessage(string message, Color color)
|
||
|
{
|
||
|
statusText.text = message;
|
||
|
statusText.color = color;
|
||
|
StartCoroutine(HideStatusMessageAfterDelay(statusText, 2f));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Coroutine to hide the status message after a specified delay.
|
||
|
/// </summary>
|
||
|
/// <param name="statusText">The UI text component used to display the message.</param>
|
||
|
/// <param name="delay">Time in seconds before hiding the message.</param>
|
||
|
private IEnumerator HideStatusMessageAfterDelay(TextMeshProUGUI statusText, float delay)
|
||
|
{
|
||
|
yield return new WaitForSeconds(delay);
|
||
|
statusText.text = string.Empty;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the player's icon based on the provided iconId.
|
||
|
/// </summary>
|
||
|
/// <param name="iconId">The ID of the icon to set.</param>
|
||
|
private void SetPlayerIcon(string iconId)
|
||
|
{
|
||
|
foreach (IconItem item in GameInstance.Singleton.iconItems)
|
||
|
{
|
||
|
if (item.iconId == iconId)
|
||
|
{
|
||
|
playerIcon.sprite = item.icon;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the player's frame based on the provided frameId.
|
||
|
/// </summary>
|
||
|
/// <param name="frameId">The ID of the frame to set.</param>
|
||
|
private void SetPlayerFrame(string frameId)
|
||
|
{
|
||
|
foreach (FrameItem item in GameInstance.Singleton.frameItems)
|
||
|
{
|
||
|
if (item.frameId == frameId)
|
||
|
{
|
||
|
playerFrame.sprite = item.icon;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|