[Unity] ATM system 정리

Gee·2025년 3월 20일

STEP 1. ATM 화면 구성 (PopupBank)

using TMPro;
using UnityEngine;

public class Cash : MonoBehaviour
{
    public TextMeshProUGUI cashText;
    public int cash;
    
    void Start()
    {
        cashText.text = cash.ToString("N0");    // 1: To.String
        cashText.text = string.Format("{0:N0}", cash);  // 2: string.Format
    }
}
  • 현금의 천 원 단위마다 콤마(,)을 찍어주는 두 가지 방법

STEP 2. 유저 데이터 제작

public class UserData : MonoBehaviour
{
    public string userName;
    public int cash;
    public int balance;
}

public class UserInfo : MonoBehaviour
{
    public TextMeshProUGUI nameText;
    public TextMeshProUGUI cashText;
    public TextMeshProUGUI balanceText;
    
    public UserData userData;
    
    void Start()
    {
        nameText.text = userData.userName;
        cashText.text = userData.cash.ToString("N0");
        balanceText.text = userData.balance.ToString("N0");
    }
}
  • 유저 정보에다 받아온 유저 데이터를 넣어주는 코드

STEP 3. 게임 매니저 제작

[System.Serializable]
public class UserData
{
    public string name;
    public int cash;
    public int balance;
    
    public UserData(string name, int cash, int balance)
    {
        this.name = name;
        this.cash = cash;
        this.balance = balance;
    }
}
public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    public UserData userData;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
        
        userData = new UserData("Gee", 100000, 50000);
    }
}
public class UserInfo : MonoBehaviour
{
    public TextMeshProUGUI nameText;
    public TextMeshProUGUI cashText;
    public TextMeshProUGUI balanceText;
    
    void Start()
    {
        UserData userData = GameManager.Instance.userData;
        
        nameText.text = userData.name;
        cashText.text = userData.cash.ToString("N0");
        balanceText.text = userData.balance.ToString("N0");
    }
}
  • GameManager를 싱글톤화 한 후 UserData 클래스의 매개변수를 가져와 객체를 생성(초기화)
  • 이후 UserInfo를 통해 각 텍스트에 맞는 정보를 넣어준다. (출력하는 역할)
  • GameManager가 싱글톤으로 초기화되고, UserData 객체를 생성해 유저 데이터를 설정 -> UserInfo가 GameManager에서 UserData를 참조하여 UI에 데이터를 표시
  • GameManager가 게임 전반의 상태를 관리하며 UserData를 중앙에서 보유함
  • 다른 스크립트에서도 GameManager.Instance.userData로 접근 가능
  • UserInfo는 UI에 데이터를 표시하는 역할만 맡고 데이터는 직접 관리하지 않음
  • UserData 클래스는 Unity에서 인스펙터로 사용될 일이 없기 때문에 MonoBehaviour를 제거하고 순수 데이터 클래스로 변경 (데이터를 저장만 함)

STEP 4. 데이터와 UI 연동

[System.Serializable]
public class UserData
{
    public string name;
    public int cash;
    public int balance;
    
    public UserData(string name, int cash, int balance)
    {
        this.name = name;
        this.cash = cash;
        this.balance = balance;
    }
}
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    [field: SerializeField] public UserData userData { get; private set; }
    public UserInfo userInfo;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
            return;
        }
        
        userData = new UserData("Gee", 100000, 50000);
    }

    public void Start()
    {
        if (userInfo == null)
        {
            userInfo = FindObjectOfType<UserInfo>();
        }
        
        UpdateName("GEEEEE");
        UpdateCash(500);
        UpdateBalance(500);
    }
    
    public void UpdateName(string newName)
    {
        userData.name = newName;
        userInfo.Refresh();
    }
    
    public void UpdateCash(int amount)
    {
        userData.cash += amount;
        userInfo.Refresh();
    }

    public void UpdateBalance(int amount)
    {
        userData.balance += amount;
        userInfo.Refresh();
    }
}
public class UserInfo : MonoBehaviour
{
    public TextMeshProUGUI nameText;
    public TextMeshProUGUI cashText;
    public TextMeshProUGUI balanceText;
    
    void Start()
    {
        Refresh();
    }

    public void Refresh()
    {
        UserData userData = GameManager.Instance.userData;
        
        nameText.text = userData.name;
        cashText.text = userData.cash.ToString("N0");
        balanceText.text = userData.balance.ToString("N0");
    }
}
  • GameManager와 UserData에 { get; private set; }을 추가해 가져올 수만 있고 수정은 불가능하게 설정
  • 유니티 인스펙터에서 유저 데이터를 확인하기 위해 [field: SerializeField] 사용. 이러면 수정은 가능하나, 다른 UserData로 바꾸는 건 불가능하다.
    • 이는 { get; private set; }이 setter를 private으로 제한하기 때문
  • 이후 UserInfo의 Start()안에 있던 코드들을 Refresh()로 옮겨주고, GameManager에서 userInfo를 찾아 새로운 데이터를 업데이트 해 줄 수 있도록 설정.
  • 이렇게 하면 플레이 시 변경된 값인 GEEEEE, 100,500, 50,500이 출력된다.
  • GameManager Awake에서 Destroy한 다음에는 return을 해줘야 불필요한 코드 실행을 방지할 수 있다. (userData의 불필요한 초기화를 막음)
  • return은 메서드의 실행을 즉시 종료시켜서 Awake 메서드의 나머지 부분(예: userData = new UserData ~)이 실행되지 않도록 막아준다.
  • Destroy를 해줬다고 해도 Unity의 프레임 기반 업데이트 방식 때문에 Awake 메서드가 끝날 때까지는 즉시 사라지지 않는다는 점을 기억하자.
    • Awake 메서드가 끝날 때까지 객체가 메모리에 남아 있음 -> new UserData의 불필요한 초기화

STEP 5. 입금 UI 제작

  • Vertical Layout Group을 사용해 버튼마다 동일한 간격으로 배치

STEP 6. 출금 UI 제작

  • 5번과 동일함

STEP 7. 입출금 창 이동

public class PopupBank : MonoBehaviour
{
    public GameObject depositPanel;
    public GameObject withdrawPanel;
    public GameObject buttonPanel;

    public void OpenDepositPanel()
    {
        buttonPanel.SetActive(false);
        depositPanel.SetActive(true);
    }

    public void OpenWithdrawPanel()
    {
        buttonPanel.SetActive(false);
        withdrawPanel.SetActive(true);
    }

    public void ClosePanel()
    {
        depositPanel.SetActive(false);
        withdrawPanel.SetActive(false);
        buttonPanel.SetActive(true);
    }
}
  • 해당 스크립트를 PopupBank 오브젝트에 넣고, 각 버튼들의 OnClick에 메서드를 넣어 입출금 창이 활성화/비활성화 되도록 설정

STEP 8. 입금 기능 만들기

	public void DepositAmount(int amount)
    {
        if (GameManager.Instance.userData.cash >= amount)
        {
            GameManager.Instance.userData.cash -= amount;
            GameManager.Instance.UpdateBalance(amount);
        }
        else
        {
            errorPanel.SetActive(true);
        }
    }
    
    public void DepositInputAmount()
    {
        if(int.TryParse(depositInput.text, out int amount))
        {
            DepositAmount(amount);
        }
        else
        {
            depositInput.text = "";
        }
        
        depositInput.text = "";
    }

  • 각 금액 버튼들에 매개변수 amount를 지정해 주고, 해당 금액만큼의 cash를 보유하고 있다면 cash -= amount
  • 잔액도 amount 값만큼 추가하여 Update
  • depositInput에서 직접 입력한 값은 해당 금액만큼 amount에 추가하여 동일하게 Update 진행
  • 입금하려는 금액이 cash보다 크다면 errorPanel 활성화
  • 입력한 텍스트가 숫자가 아니라면 depositInput.text = "";으로 텍스트 창 초기화

STEP 9. 출금 기능 만들기

	public void WithdrawAmount(int amount)
    {
        if (GameManager.Instance.userData.balance >= amount)
        {
            GameManager.Instance.userData.balance -= amount;
            GameManager.Instance.UpdateCash(amount);
        }
        else
        {
            errorPanel.SetActive(true);
        }
    }
    
    public void WithdrawInputAmount()
    {
        if (int.TryParse(withdrawInput.text, out int amount))
        {
            WithdrawAmount(amount);
        }
        else
        {
            withdrawInput.text = "";
        }
        
        withdrawInput.text = "";
    }
  • 8번의 입금 기능과 동일

STEP 10. 저장 및 로드 기능 만들기

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    [field: SerializeField] public UserData userData { get; private set; }
    public UserInfo userInfo;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
            return;
        }
        
        // PlayerPrefs.DeleteAll();
        LoadUserData();
    }

    public void Start()
    {
        if (userInfo == null)
        {
            userInfo = FindObjectOfType<UserInfo>();
        }
        
        SaveUserData();
    }
    
    public void UpdateName(string newName)
    {
        userData.name = newName;
        userInfo.Refresh();
        SaveUserData();
    }
    
    public void UpdateCash(int amount)
    {
        userData.cash += amount;
        userInfo.Refresh();
        SaveUserData();
    }

    public void UpdateBalance(int amount)
    {
        userData.balance += amount;
        userInfo.Refresh();
        SaveUserData();
    }

    public void SaveUserData()
    {
        PlayerPrefs.SetString("Name", userData.name);
        PlayerPrefs.SetInt("Cash", userData.cash);
        PlayerPrefs.SetInt("Balance", userData.balance);
        PlayerPrefs.Save();
    }

    public void LoadUserData()
    {
        if (PlayerPrefs.HasKey("Name"))
        {
            string name = PlayerPrefs.GetString("Name");
            int cash = PlayerPrefs.GetInt("Cash");
            int balance = PlayerPrefs.GetInt("Balance");
            userData = new UserData(name, cash, balance);
        }
        else
        {
            userData = new UserData("Gee", 100000, 50000);
        }
    }
}
  • PlayerPrefs를 활용한 userData 저장
  • Awake에서 new UserData로 생성하던 객체를 LoadUserData();로 변경
  • PlayerPrefs.SetString, PlayerPrefs.SetInt로 Name, Cash, Balance 저장
  • if (PlayerPrefs.HasKey("Name"))로 저장한 데이터를 찾고 string, int 변수에 담아 로드
  • 저장된 데이터가 없다면 new UserData -> 기본값 "Gee", 100000, 50000으로 객체 생성

STEP 11. 로그인 창 만들기

    public UserInfo userInfo;
    public GameObject popupBank;
    public GameObject popupLogin;

    public const string nameKey = "Name";
    public const string cashKey = "Cash";
    public const string balanceKey = "Balance";
    public const string idKey = "Id";
    public const string passwordKey = "Password";
    
    public void Start()
    {
        if (userInfo == null)
        {
            userInfo = FindObjectOfType<UserInfo>();
        }
        
        SaveUserData();
        popupBank.SetActive(false);
        popupLogin.SetActive(true);
    }

    public void SaveUserData()
    {
        PlayerPrefs.SetString(nameKey, userData.name);
        PlayerPrefs.SetInt(cashKey, userData.cash);
        PlayerPrefs.SetInt(balanceKey, userData.balance);
        PlayerPrefs.SetString(idKey, userData.id);
        PlayerPrefs.SetString(passwordKey, userData.password);
        PlayerPrefs.Save();
    }

    public void LoadUserData()
    {
        if (PlayerPrefs.HasKey(nameKey))
        {
            string name = PlayerPrefs.GetString(nameKey);
            int cash = PlayerPrefs.GetInt(cashKey);
            int balance = PlayerPrefs.GetInt(balanceKey);
            string id = PlayerPrefs.GetString(idKey);
            string password = PlayerPrefs.GetString(passwordKey);
            
            userData = new UserData(name, cash, balance, id, password);
        }
        else
        {
            userData = new UserData("Gee", 100000, 50000, "", "");
        }
    }
}
  • PlayerPrefs에 id와 password를 추가하고, public const string nameKey = "Name";과 같이 저장 값을 상수로 지정
    • 오타를 방지하고 유지보수를 편하게 하기 위함 (+가독성 향상, 메모리 성능 최적화)
  • 게임 시작 시 popupBank 창은 꺼주고 popupLogin 창을 활성화 시켜줌
  • PSInputField 오브젝트는 Content Type을 Password로 변경해 텍스트 입력 시 ***로 표시되게 설정

STEP 12. 회원가입 만들기

public class PopupSignUp : MonoBehaviour
{
    public TMP_InputField inputId;
    public TMP_InputField inputName;
    public TMP_InputField inputPassword;
    public TMP_InputField inputPasswordConfirm;

    public GameObject popupSignUp;
    public GameObject errorPanel;
    public TextMeshProUGUI errorText;

    public void SignUp()
    {
        if (string.IsNullOrWhiteSpace(inputId.text))
        {
            errorText.text = "ID를 확인해주세요.";
            OpenErrorPanel();
            return;
        }

        if (string.IsNullOrWhiteSpace(inputName.text))
        {
            errorText.text = "Name을 확인해주세요.";
            OpenErrorPanel();
            return;
        }

        if (string.IsNullOrWhiteSpace(inputPassword.text))
        {
            errorText.text = "Password를 확인해주세요.";
            OpenErrorPanel();
            return;
        }
        
        if (string.IsNullOrEmpty(inputPasswordConfirm.text))
        {
            errorText.text = "Password Confirm을 확인해주세요.";
            OpenErrorPanel();
            return;
        }

        if (inputPassword.text != inputPasswordConfirm.text)
        {
            errorText.text = "Password가 같지 않습니다.";
            OpenErrorPanel();
            return;
        }

        if (GameManager.Instance != null)
        {
            GameManager.Instance.SetUserData(new UserData(name: inputName.text, cash: 100000, balance: 50000,
                id: inputId.text, password: inputPassword.text));
        }
        
        ClosePopupSignUp();
    }
    
    public void OpenPopupSignUp()
    {
        popupSignUp.SetActive(true);
    }
    
    public void ClosePopupSignUp()
    {
        popupSignUp.SetActive(false);
    }

    public void OpenErrorPanel()
    {
        errorPanel.SetActive(true);
    }

    public void CloseErrorPanel()
    {
        errorPanel.SetActive(false);
    }
}
  • 입력 정보들이 비어있는지 확인 후 순차적으로 오류 메세지 출력(ErrorPanel은 항상 활성화)
  • 모든 내용을 맞게 입력했고, GameManager가 있다면 SetUserData에 new UserData 객체를 넣어줌(입력한 정보 + 기본 cash, balance값)
using System.IO;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    [field: SerializeField] public UserData userData { get; private set; }
    
    public UserInfo userInfo;
    
    public GameObject popupBank;
    public GameObject popupLogin;

    // public const string nameKey = "Name";
    // public const string cashKey = "Cash";
    // public const string balanceKey = "Balence";
    // public const string idKey = "Id";
    // public const string passwordKey = "Password";

    private string savePath;
    
    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
            return;
        }
        
        savePath = Path.Combine(Application.persistentDataPath, "userData.json");   // 저장 경로 설정
        Debug.Log($"Json 파일 경로: {savePath}");
        
        LoadUserData();
    }

    public void SetUserData(UserData newUserData)
    {
        userData = newUserData;
        SaveUserData();
        if (userInfo != null)
        {
            userInfo.Refresh();
        }
    }

    public void SaveUserData()
    {
        // PlayerPrefs.SetString(nameKey, userData.name);
        // PlayerPrefs.SetInt(cashKey, userData.cash);
        // PlayerPrefs.SetInt(balanceKey, userData.balance);
        // PlayerPrefs.SetString(idKey, userData.id);
        // PlayerPrefs.SetString(passwordKey, userData.password);
        // PlayerPrefs.Save();
        
        string json = JsonUtility.ToJson(userData); // userData를 Json 형식 문자열로 직렬화
        File.WriteAllText(savePath, json);  // Json 문자열을 경로에 저장
        Debug.Log($"저장 성공: {json}");
    }

    public void LoadUserData()
    {
        // if (PlayerPrefs.HasKey(nameKey))
        // {
        //     string name = PlayerPrefs.GetString(nameKey);
        //     int cash = PlayerPrefs.GetInt(cashKey);
        //     int balance = PlayerPrefs.GetInt(balanceKey);
        //     string id = PlayerPrefs.GetString(idKey);
        //     string password = PlayerPrefs.GetString(passwordKey);
        //     
        //     userData = new UserData(name, cash, balance, id, password);
        // }
        // else
        // {
        //     userData = new UserData("Gee", 100000, 50000, "", "");
        // }

        if (File.Exists(savePath))  // json 파일이 있으면 그 안의 데이터를 UserData 객체로 변환
        {
            string json = File.ReadAllText(savePath);   // 경로의 파일에서 Json 문자열을 읽음
            userData = JsonUtility.FromJson<UserData>(json);    // Json 문자열을 userData 객체로 역직렬화
            Debug.Log($"불러온 데이터: {json}");
        }
        else
        {
            Debug.Log("저장된 데이터가 없습니다.");
        }
    }
}
  • 데이터 저장 방식을 PlayerPrefs에서 JSON으로 변경
  • Awake에서 저장 경로를 설정 -> (받아온 newUserData를 SetUserData에서 저장) -> newUserData를 JSON 경로에 저장(직렬화)
  • 데이터를 로드한 땐 JSON 파일을 찾아 문자열을 읽은 후 userData에 저장(역직렬화)

STEP 13. 로그인하기

    public UserData LoadUserData()
    {
        if (File.Exists(savePath))  // json 파일이 있으면 그 안의 데이터를 UserData 객체로 변환
        {
            string json = File.ReadAllText(savePath);   // 경로의 파일에서 Json 문자열을 읽음
            userData = JsonUtility.FromJson<UserData>(json);    // Json 문자열을 userData 객체로 역직렬화
            Debug.Log($"불러온 데이터: {json}");
            return userData;
        }
        else
        {
            Debug.Log("저장된 데이터가 없습니다.");
            return null;
        }
    }
  • 기존의 public void LoadUserData()public UserData LoadUserData()로 변경. 반환값을 바꿔줌
public class PopupLogin : MonoBehaviour
{
    public TMP_InputField inputID;
    public TMP_InputField inputPS;

    public GameObject PopupLoginPanel;
    public GameObject PopupBank;
    public GameObject errorPanel;
    public TextMeshProUGUI errorText;

    private string savePath;

    private void Awake()
    {
        savePath = Path.Combine(Application.persistentDataPath, "userData.json");
        Debug.Log($"파일 경로: {savePath}");
    }

    public void Login()
    {
        UserData loadUserData = GameManager.Instance.LoadUserData();

        if (string.IsNullOrWhiteSpace(inputID.text))
        {
            errorText.text = "ID를 입력해주세요.";
            OpenErrorPanel();
            return;
        }

        if (string.IsNullOrWhiteSpace(inputPS.text))
        {
            errorText.text = "Password를 입력해주세요.";
            OpenErrorPanel();
            return;
        }

        if (loadUserData == null)
        {
            errorText.text = "해당 데이터가 없습니다.";
            OpenErrorPanel();
            inputID.text = "";
            inputPS.text = "";
            return;
        }

        if (inputID.text == loadUserData.id && inputPS.text == loadUserData.password)
        {
            PopupLoginPanel.SetActive(false);
            PopupBank.SetActive(true);
            
            Debug.Log("로그인 성공");
        }
        else
        {
            errorText.text = "ID 또는 Password가 일치하지 않습니다.";
            OpenErrorPanel();
            inputID.text = "";
            inputPS.text = "";
        }
    }

    public void OpenErrorPanel()
    {
        errorPanel.SetActive(true);
    }

    public void CloseErrorPanel()
    {
        errorPanel.SetActive(false);
    }
}
  • Login()에서 UserData loadUserData = GameManager.Instance.LoadUserData();를 통해 GameManager에 저장된 UserData를 접근해 로그인 체크
  • JSON에 저장되어 있는 정보와 일치하면 로그인 창을 닫고 PopupBank 창을 활성화 시켜줌

STEP 14. 송금하기

[System.Serializable]
public class UserData
{
    public string name;
    public int cash;
    public int balance;
    public string id;
    public string password;
    
    public UserData(string name, int cash, int balance, string id, string password)
    {
        this.name = name;
        this.cash = cash;
        this.balance = balance;
        this.id = id;
        this.password = password;
    }
}

[System.Serializable]
public class UserDataList
{
    public List<UserData> users = new List<UserData>();
}
  • UserData를 리스트화 시켜서 여러 데이터를 저장할 수 있게 함
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    [field: SerializeField] public UserData userData { get; private set; }
    
    private UserDataList userDataList = new UserDataList();
    private UserData loginUser;
    
    public UserInfo userInfo;
    
    public GameObject popupBank;
    public GameObject popupLogin;

    private string savePath;
    
    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
            return;
        }
        
        savePath = Path.Combine(Application.persistentDataPath, "users.json");   // 저장 경로 설정
        Debug.Log($"Json 파일 경로: {savePath}");
        
        LoadUserData();
    }

    public void Start()
    {
        if (userInfo == null)
        {
            userInfo = FindObjectOfType<UserInfo>();
        }
        
        SaveUserData();
        
        popupBank.SetActive(false);
        popupLogin.SetActive(true);
    }

    public UserData GetCurrentUser()    // 현재 로그인한 유저 반환
    {
        return loginUser;
    }

    public void SetUserData(UserData newUserData)   // 새로운 유저 데이터 설정
    {
        // UserData existingUser = userDataList.users.Find(user => user.id == newUserData.id);
        
        // 중복 유저 확인
        UserData existingUser = null;
        foreach (UserData user in userDataList.users)
        {
            if (user.id == newUserData.id)
            {
                existingUser = user;
                break;
            }
        }
        
        if (existingUser != null)
        {
            // 기존 유저 업데이트(덮어씌우기)
            existingUser.name = newUserData.name;
            existingUser.cash = newUserData.cash;
            existingUser.balance = newUserData.balance;
            existingUser.password = newUserData.password;
        }
        else
        {
            userDataList.users.Add(newUserData);    // 새 유저 추가
        }
        
        userData = newUserData;
        loginUser = newUserData;
        userInfo?.Refresh();
        SaveUserData();
    }

    // 유저 데이터를 JSON 파일에 저장
    public void SaveUserData()
    {
        string json = JsonUtility.ToJson(userDataList, true); // userDataList를 JSON 문자열로 직렬화
        File.WriteAllText(savePath, json);  // JSON 문자열을 경로에 저장
        Debug.Log($"유저 데이터리스트 저장 성공: {json}");
    }
    
    // JSON 파일에서 유저 데이터를 로드
    public void LoadUserData()
    {
        if (File.Exists(savePath))  // JSON 파일 존재여부 확인
        {
            string json = File.ReadAllText(savePath);   // 경로의 파일에서 JSON 문자열을 읽음
            Debug.Log($"불러온 데이터: {json}");
            
            userDataList = JsonUtility.FromJson<UserDataList>(json);    // Json 문자열을 userDataList 객체로 역직렬화
    
            Debug.Log($"불러온 유저 수: {userDataList.users.Count}");
        }
        else
        {
            Debug.Log("저장된 데이터가 없습니다.");
            userDataList = new UserDataList();
        }
    }

    public UserData FindUserID(string userID)
    {
        foreach (UserData user in userDataList.users)   // ID가 일치하는 유저를 찾아서 반환
        {
            if (user.id == userID)
            {
                return user;
            }
        }

        return null;
    }
    
    public void UpdateName(string newName)
    {
        userData.name = newName;
        userInfo.Refresh();
        SaveUserData();
    }
    
    public void UpdateCash(int amount)
    {
        userData.cash += amount;
        userInfo.Refresh();
        SaveUserData();
    }

    public void UpdateBalance(int amount)
    {
        userData.balance += amount;
        userInfo.Refresh();
        SaveUserData();
    }
}
  • FindUserID: userDataList.users에서 ID가 일치하는 유저를 찾아 반환
  • 송금 기능을 위해 GetCurrentUser()에는 현재 로그인한 유저 정보를 저장
    public void TransferAmount()
    {
        string targetID = transferUserInput.text; // 송금 대상의 ID 가져오기

        if (string.IsNullOrWhiteSpace(targetID))
        {
            ShowError("송금 대상을 입력하세요.");
            return;
        }
        
        UserData sender = GameManager.Instance.GetCurrentUser();  // 발신자(로그인 유저) 정보
        UserData receiver = GameManager.Instance.FindUserID(targetID);  // 수신자 정보
        
        if (receiver == null)
        {
            ShowError("송금 대상이\n존재하지 않습니다.");
            return;
        }

        if (string.IsNullOrWhiteSpace(transferAmountInput.text))
        {
            ShowError("송금 금액을 입력하세요.");
            return;
        }

        if (!int.TryParse(transferAmountInput.text, out int amount) || amount <= 0)
        {
            ShowError("송금할 수 없습니다.");
            return;
        }
        
        if (sender.balance < amount)
        {
            ShowError("잔액이 부족합니다.");
            return;
        }
        
        if (targetID == sender.id)
        {
            ShowError("자신에게 송금할 수 없습니다.");
            return;
        }
        
        sender.balance -= amount;   // 발신 처리
        receiver.balance += amount; // 수신 처리

        if (GameManager.Instance.userInfo != null)
        {
            GameManager.Instance.userInfo.Refresh();
        }
        
        GameManager.Instance.SaveUserData();
        Debug.Log($"송금 완료: {sender.name}이 {receiver.name}에게 {amount}원 송금");
    
        transferUserInput.text = "";
        transferAmountInput.text = "";
    }
    
    private void ShowError(string massage)
    {
        errorText.text = massage;
        errorPanel.SetActive(true);
    }
  • 이후 PopupBank 스크립트에서 송금 기능을 구현
  • 입력된 금액을 int.Tryparse로 반환해 주고, GameManager.Instance.FindUserID(targetID)를 호출해서 송금할 대상을 찾음
  • 잔액이 amount보다 크거나 작다면 sender.balance -= amount;으로 송금 처리
  • 수신자에게는 receiver.balance += amount;로 잔액 추가
  • 이후 GameManager.Instance.SaveUserData();를 통해 변경된 데이터를 저장
profile
...

0개의 댓글