当前位置:首页 > 学海无涯 > 正文内容

Unity 开发实战:实现银行存取款功能系统

清羽天2周前 (11-27)学海无涯15
在许多游戏中,银行系统都是重要的经济组成部分,它能帮助玩家管理虚拟资产、实现安全存储。本文将详细介绍如何在 Unity 中设计并实现一个完整的银行存取款功能,包括数据结构设计、UI 交互逻辑和安全验证机制。

一、银行系统核心需求分析

一个基础的银行系统应包含以下核心功能:
  • 账户余额查询

  • 存款功能(将背包货币存入银行)

  • 取款功能(从银行取出货币到背包)

  • 交易记录保存

  • 安全验证(可选,如密码保护)

在开始编码前,我们需要明确两个关键数据实体:玩家背包货币和银行账户,以及它们之间的交互规则。

二、数据结构设计

首先设计存储货币和交易记录的数据结构,建议使用 ScriptableObject 存储配置数据,使用普通类存储运行时数据。

1. 货币类型定义

csharp
运行
// 定义货币类型枚举public enum CurrencyType{
    Gold,       // 金币
    Diamond,    // 钻石
    Crystal     // 水晶}// 货币数据类[System.Serializable]public class CurrencyData{
    public CurrencyType type;
    public int amount;

    public CurrencyData(CurrencyType type, int amount)
    {
        this.type = type;
        this.amount = amount;
    }}

2. 交易记录类

csharp
运行
// 交易类型public enum TransactionType{
    Deposit,    // 存款
    Withdraw    // 取款}// 交易记录[System.Serializable]public class TransactionRecord{
    public string transactionId;  // 交易ID
    public TransactionType type;  // 交易类型
    public CurrencyType currencyType; // 货币类型
    public int amount;            // 金额
    public DateTime timestamp;    // 时间戳

    public TransactionRecord(TransactionType type, CurrencyType currencyType, int amount)
    {
        transactionId = System.Guid.NewGuid().ToString();
        this.type = type;
        this.currencyType = currencyType;
        this.amount = amount;
        timestamp = DateTime.Now;
    }}

3. 银行账户数据类

csharp
运行
public class BankAccountData{
    public string accountId;  // 账户ID
    public List<CurrencyData> currencies = new List<CurrencyData>(); // 账户货币
    public List<TransactionRecord> transactionHistory = new List<TransactionRecord>(); // 交易记录
    public int maxTransactionRecords = 100; // 最大交易记录数

    public BankAccountData(string accountId)
    {
        this.accountId = accountId;
        // 初始化各种货币为0
        foreach (CurrencyType type in Enum.GetValues(typeof(CurrencyType)))
        {
            currencies.Add(new CurrencyData(type, 0));
        }
    }

    // 获取指定货币的余额
    public int GetCurrencyAmount(CurrencyType type)
    {
        var currency = currencies.Find(c => c.type == type);
        return currency != null ? currency.amount : 0;
    }

    // 更新货币余额
    private void UpdateCurrencyAmount(CurrencyType type, int newAmount)
    {
        var currency = currencies.Find(c => c.type == type);
        if (currency != null)
        {
            currency.amount = newAmount;
        }
    }

    // 添加交易记录
    private void AddTransactionRecord(TransactionRecord record)
    {
        transactionHistory.Add(record);
        // 保持记录数量不超过最大值
        if (transactionHistory.Count > maxTransactionRecords)
        {
            transactionHistory.RemoveAt(0);
        }
    }}

三、银行系统核心逻辑实现

接下来实现银行系统的核心管理类,负责处理存取款业务逻辑。

1. 银行管理器(单例模式)

csharp
运行
using UnityEngine;using System.Collections.Generic;public class BankManager : MonoBehaviour{
    public static BankManager Instance { get; private set; }

    // 银行账户字典(key: 玩家ID)
    private Dictionary<string, BankAccountData> playerAccounts = new Dictionary<string, BankAccountData>();

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // 获取或创建玩家银行账户
    public BankAccountData GetPlayerAccount(string playerId)
    {
        if (!playerAccounts.ContainsKey(playerId))
        {
            playerAccounts[playerId] = new BankAccountData(playerId);
        }
        return playerAccounts[playerId];
    }

    // 存款操作
    public bool Deposit(string playerId, CurrencyType type, int amount, out string message)
    {
        message = "";
        if (amount <= 0)
        {
            message = "存款金额必须大于0";
            return false;
        }

        // 检查玩家背包中是否有足够的货币(需要与你的背包系统对接)
        int playerCurrency = PlayerInventory.Instance.GetCurrencyAmount(type);
        if (playerCurrency < amount)
        {
            message = "背包中货币不足";
            return false;
        }

        // 获取玩家银行账户
        var account = GetPlayerAccount(playerId);
        
        // 更新银行余额
        int currentBankAmount = account.GetCurrencyAmount(type);
        account.UpdateCurrencyAmount(type, currentBankAmount + amount);
        
        // 扣除玩家背包货币
        PlayerInventory.Instance.SubtractCurrency(type, amount);
        
        // 记录交易
        account.AddTransactionRecord(new TransactionRecord(TransactionType.Deposit, type, amount));
        
        message = $"成功存入 {amount} {type}";
        return true;
    }

    // 取款操作
    public bool Withdraw(string playerId, CurrencyType type, int amount, out string message)
    {
        message = "";
        if (amount <= 0)
        {
            message = "取款金额必须大于0";
            return false;
        }

        // 获取玩家银行账户
        var account = GetPlayerAccount(playerId);
        
        // 检查银行余额
        int currentBankAmount = account.GetCurrencyAmount(type);
        if (currentBankAmount < amount)
        {
            message = "银行账户余额不足";
            return false;
        }

        // 更新银行余额
        account.UpdateCurrencyAmount(type, currentBankAmount - amount);
        
        // 增加玩家背包货币
        PlayerInventory.Instance.AddCurrency(type, amount);
        
        // 记录交易
        account.AddTransactionRecord(new TransactionRecord(TransactionType.Withdraw, type, amount));
        
        message = $"成功取出 {amount} {type}";
        return true;
    }

    // 获取交易记录
    public List<TransactionRecord> GetTransactionHistory(string playerId)
    {
        var account = GetPlayerAccount(playerId);
        return new List<TransactionRecord>(account.transactionHistory);
    }}

2. 玩家背包接口(示例)

上面的代码中用到了PlayerInventory,这里提供一个简单的接口示例:
csharp
运行
public class PlayerInventory : MonoBehaviour{
    public static PlayerInventory Instance { get; private set; }

    // 简化的玩家货币存储
    private Dictionary<CurrencyType, int> currencies = new Dictionary<CurrencyType, int>();

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            
            // 初始化货币
            foreach (CurrencyType type in Enum.GetValues(typeof(CurrencyType)))
            {
                currencies[type] = 0;
            }
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // 获取货币数量
    public int GetCurrencyAmount(CurrencyType type)
    {
        if (currencies.ContainsKey(type))
        {
            return currencies[type];
        }
        return 0;
    }

    // 添加货币
    public void AddCurrency(CurrencyType type, int amount)
    {
        if (currencies.ContainsKey(type))
        {
            currencies[type] += amount;
        }
        else
        {
            currencies[type] = amount;
        }
    }

    // 减少货币
    public void SubtractCurrency(CurrencyType type, int amount)
    {
        if (currencies.ContainsKey(type))
        {
            currencies[type] = Mathf.Max(0, currencies[type] - amount);
        }
    }}

四、银行 UI 界面实现

银行系统需要直观的 UI 界面让玩家进行操作,以下是一个基础的 UI 实现方案。

1. UI 结构设计

建议的 UI 层级结构:
  • BankPanel(银行主面板)

    • HistoryScrollView

    • HistoryContent

    • CurrencyTypeDropdown(货币类型选择)

    • AmountInputField(金额输入)

    • DepositButton(存款按钮)

    • WithdrawButton(取款按钮)

    • GoldBalanceText

    • DiamondBalanceText

    • BalanceDisplay(余额显示区域)

    • TransactionPanel(交易操作区域)

    • TransactionHistoryPanel(交易记录区域)

    • CloseButton(关闭按钮)

    2. UI 逻辑代码

    csharp
    运行
    using UnityEngine;using UnityEngine.UI;using System.Collections.Generic;using TMPro;public class BankUI : MonoBehaviour{
        [Header("余额显示")]
        [SerializeField] private TextMeshProUGUI goldBalanceText;
        [SerializeField] private TextMeshProUGUI diamondBalanceText;
        
        [Header("交易操作")]
        [SerializeField] private Dropdown currencyTypeDropdown;
        [SerializeField] private InputField amountInputField;
        [SerializeField] private Button depositButton;
        [SerializeField] private Button withdrawButton;
        [SerializeField] private TextMeshProUGUI messageText;
        
        [Header("交易记录")]
        [SerializeField] private Transform historyContent;
        [SerializeField] private GameObject historyItemPrefab;
        
        private string currentPlayerId = "Player1"; // 实际项目中应使用真实玩家ID
        private CurrencyType selectedCurrencyType = CurrencyType.Gold;
    
        private void Start()
        {
            // 初始化下拉菜单
            InitializeCurrencyDropdown();
            
            // 绑定按钮事件
            depositButton.onClick.AddListener(OnDepositButtonClicked);
            withdrawButton.onClick.AddListener(OnWithdrawButtonClicked);
            currencyTypeDropdown.onValueChanged.AddListener(OnCurrencyTypeChanged);
            
            // 刷新UI
            RefreshUI();
        }
    
        // 初始化货币类型下拉菜单
        private void InitializeCurrencyDropdown()
        {
            currencyTypeDropdown.ClearOptions();
            List<string> options = new List<string>();
            
            foreach (CurrencyType type in Enum.GetValues(typeof(CurrencyType)))
            {
                options.Add(type.ToString());
            }
            
            currencyTypeDropdown.AddOptions(options);
            OnCurrencyTypeChanged(0); // 选中第一个
        }
    
        // 货币类型改变
        private void OnCurrencyTypeChanged(int index)
        {
            selectedCurrencyType = (CurrencyType)index;
        }
    
        // 存款按钮点击
        private void OnDepositButtonClicked()
        {
            if (int.TryParse(amountInputField.text, out int amount))
            {
                string message;
                bool success = BankManager.Instance.Deposit(currentPlayerId, selectedCurrencyType, amount, out message);
                messageText.text = message;
                messageText.color = success ? Color.green : Color.red;
                
                if (success)
                {
                    RefreshUI();
                    amountInputField.text = "";
                }
            }
            else
            {
                messageText.text = "请输入有效的金额";
                messageText.color = Color.red;
            }
        }
    
        // 取款按钮点击
        private void OnWithdrawButtonClicked()
        {
            if (int.TryParse(amountInputField.text, out int amount))
            {
                string message;
                bool success = BankManager.Instance.Withdraw(currentPlayerId, selectedCurrencyType, amount, out message);
                messageText.text = message;
                messageText.color = success ? Color.green : Color.red;
                
                if (success)
                {
                    RefreshUI();
                    amountInputField.text = "";
                }
            }
            else
            {
                messageText.text = "请输入有效的金额";
                messageText.color = Color.red;
            }
        }
    
        // 刷新UI显示
        public void RefreshUI()
        {
            var account = BankManager.Instance.GetPlayerAccount(currentPlayerId);
            
            // 更新余额显示
            goldBalanceText.text = $"银行金币: {account.GetCurrencyAmount(CurrencyType.Gold)} (背包: {PlayerInventory.Instance.GetCurrencyAmount(CurrencyType.Gold)})";
            diamondBalanceText.text = $"银行钻石: {account.GetCurrencyAmount(CurrencyType.Diamond)} (背包: {PlayerInventory.Instance.GetCurrencyAmount(CurrencyType.Diamond)})";
            
            // 更新交易记录
            UpdateTransactionHistory();
        }
    
        // 更新交易记录
        private void UpdateTransactionHistory()
        {
            // 清除现有记录
            foreach (Transform child in historyContent)
            {
                Destroy(child.gameObject);
            }
            
            // 获取交易记录
            var records = BankManager.Instance.GetTransactionHistory(currentPlayerId);
            // 倒序显示(最新的在上面)
            records.Reverse();
            
            // 创建记录项
            foreach (var record in records)
            {
                GameObject item = Instantiate(historyItemPrefab, historyContent);
                TextMeshProUGUI text = item.GetComponent<TextMeshProUGUI>();
                
                string typeStr = record.type == TransactionType.Deposit ? "存入" : "取出";
                text.text = $"{record.timestamp:yyyy-MM-dd HH:mm} {typeStr} {record.amount} {record.currencyType}";
            }
        }
    
        // 显示银行界面
        public void Show()
        {
            gameObject.SetActive(true);
            RefreshUI();
        }
    
        // 隐藏银行界面
        public void Hide()
        {
            gameObject.SetActive(false);
        }}

    五、数据持久化

    为了保证银行数据在游戏重启后不丢失,需要实现数据持久化功能。

    使用 PlayerPrefs 进行简单存储(适合小型项目)

    csharp
    运行
    public class BankDataPersistence : MonoBehaviour{
        private const string BankDataKey = "BankData";
    
        // 保存银行数据
        public void SaveBankData()
        {
            // 将银行数据转换为可序列化的格式
            string jsonData = JsonUtility.ToJson(BankManager.Instance.playerAccounts);
            PlayerPrefs.SetString(BankDataKey, jsonData);
            PlayerPrefs.Save();
        }
    
        // 加载银行数据
        public void LoadBankData()
        {
            if (PlayerPrefs.HasKey(BankDataKey))
            {
                string jsonData = PlayerPrefs.GetString(BankDataKey);
                // 从JSON加载数据到银行管理器(实际实现需要根据你的数据结构调整)
            }
        }}

    注意:

    对于复杂项目,建议使用:
    • 二进制序列化

    • SQLite 数据库

    • 服务器端存储(联网游戏)

    六、进阶功能扩展

    1. 银行密码保护
      • 添加密码设置和验证功能

      • 错误次数限制和冷却机制

    2. 利息系统
      csharp
      运行
      // 简单的利息计算示例public void CalculateInterest(string playerId, float interestRate){
          var account = BankManager.Instance.GetPlayerAccount(playerId);
          foreach (var currency in account.currencies)
          {
              if (currency.amount > 0)
              {
                  int interest = Mathf.RoundToInt(currency.amount * interestRate);
                  account.UpdateCurrencyAmount(currency.type, currency.amount + interest);
                  // 记录利息交易
              }
          }}
    3. 存款限额
      • 为每种货币设置存款上限

      • 可以通过升级银行账户提升限额

    4. 交易手续费
      • 在存取款时收取一定比例的手续费

      • 会员可以减免手续费

    七、常见问题与解决方案

    1. 数据同步问题
      • 确保 UI 在每次操作后都刷新

      • 多线程操作时注意加锁保护数据

    2. 输入验证
      • 严格验证输入金额的有效性

      • 防止负数和过大数值

    3. 性能优化
      • 交易记录过多时分页显示

      • 定期清理过旧的交易记录

    4. 安全问题
      • 防止客户端篡改数据(重要数据应在服务器验证)

      • 敏感操作添加二次确认

    结语

    银行系统作为游戏经济循环的重要环节,其设计需要兼顾功能性和安全性。本文实现的基础框架可以满足大多数游戏的需求,你可以根据具体项目特点进行扩展和优化。
    在实际开发中,建议将银行系统与游戏的整体经济系统紧密结合,设计合理的存取规则和激励机制,提升玩家的游戏体验。
    希望本文对你的 Unity 开发有所帮助,祝你的项目开发顺利!


    分享给朋友:

    “Unity 开发实战:实现银行存取款功能系统” 的相关文章

    Linux 忘记密码解决方法

    Linux 忘记密码解决方法

    很多朋友经常会忘记Linux系统的root密码,linux系统忘记root密码的情况该怎么办呢?重新安装系统吗?当然不用!进入单用户模式更改一下root密码即可。步骤如下:重启linux系统3 秒之内要按一下回车,出现如下界面然后输入e在 第二行最后边输入 single...

    Spring Boot 实现 MySQL 数据多选删除功能详解

    在实际的 Web 开发中,数据删除是常见操作,而多选删除能极大提升用户操作效率,比如批量删除商品、订单、用户信息等场景。本文将基于 Spring Boot 框架,结合 MySQL 数据库,从需求分析到代码实现,完整讲解多选删除功能的开发过程,包含前端页面交互、后端接口设计、数据库操作及异常处理,适合...

    Python 链接数据库与基础增删改查(CRUD)操作详解

    在 Python 开发中,数据库交互是后端开发、数据分析、自动化脚本等场景的核心能力 —— 无论是存储用户数据、处理业务逻辑,还是批量分析数据,都需要 Python 与数据库建立连接并执行操作。本文以 MySQL 数据库(Python 生态最常用的关系型数据库)为例,从环境准备、数据库连接...

    PHP 链接数据库与基础增删改查(CRUD)操作详解

    在 Web 开发中,PHP 与数据库的交互是动态网站的核心能力 —— 无论是用户登录注册、数据展示还是业务逻辑处理,都离不开 PHP 对数据库的增删改查操作。本文将以 MySQL 数据库(PHP 生态最常用的关系型数据库)为例,从环境准备、数据库连接、核心 CRUD 实现到安全优化,一步步...

    Java 链接数据库与基础增删改查操作详解

    在 Java 开发中,数据库交互是绝大多数应用的核心功能之一。无论是用户信息存储、业务数据统计还是日志记录,都需要通过 Java 程序与数据库建立连接并执行数据操作。本文将以 MySQL 数据库(最常用的关系型数据库之一)为例,从环境准备、数据库连接、基础增删改查(CRUD)操作到代码优化...

    Unity 开发实战:实现逼真的作物生长系统

    作物生长系统是农场类、生存类游戏的核心玩法之一,一个设计精良的作物生长系统能极大提升游戏的沉浸感。本文将详细介绍如何在 Unity 中构建一个完整的作物生长系统,包括生长周期、环境影响、交互逻辑和可视化表现。一、作物生长系统核心需求分析一个真实的作物生长系统应包含以下核心要素:多阶段生长周期(种子→...