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

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

清羽天2个月前 (11-27)学海无涯27
在许多游戏中,银行系统都是重要的经济组成部分,它能帮助玩家管理虚拟资产、实现安全存储。本文将详细介绍如何在 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是开发与运维工作中不可或缺的工具,掌握常用命令能显著提升效率。本篇整理了一些高频使用的命令,覆盖文件操作、系统监控、网络调试等核心场景,适合入门学习或作为日常参考使用。以下是一些常用的Linux命令:1. ls:列出当前目录中的文件和子目录ls2. pwd:显示当前工作目录的路径pwd3....

    Spring Boot 过滤器入门:从概念到实战配置

    在 Web 开发中,过滤器(Filter)是处理 HTTP 请求和响应的重要组件,它能在请求到达控制器前、响应返回客户端前进行拦截和处理。比如日志记录、权限验证、字符编码转换等场景,都离不开过滤器的身影。本文将带大家从零开始,掌握 Spring Boot 中过滤器的入门知识和完整设置流程。一、过滤器...

    Python 自定义鼠标样式完全指南:从基础到实战(Tkinter/PyQt 双方案)

    Python 自定义鼠标样式完全指南:从基础到实战(Tkinter/PyQt 双方案)在 Python GUI 开发中,默认鼠标样式往往难以满足个性化界面设计需求。无论是打造创意工具、游戏界面,还是品牌化桌面应用,自定义鼠标样式都能显著提升用户体验与视觉质感。本文将结合 Python 主流 GUI...

    PHP 自定义鼠标样式完全指南:Web 场景实战(CSS 核心 + PHP 动态适配)

    在 PHP 开发的 Web 应用中,自定义鼠标样式是提升界面个性化与用户体验的有效手段 —— 无论是电商平台的商品预览、创意官网的交互设计,还是后台管理系统的功能区分,合适的鼠标样式都能让操作逻辑更清晰、视觉效果更出彩。与 Java/Python 的桌面端 GUI 不同,PHP 作为服务器端语言,无...

    PHP 实现在线视频播放完整方案:从后端存储到前端适配

    在 Web 开发中,在线视频播放是电商展示、教育平台、企业宣传等场景的核心需求。PHP 作为主流的后端脚本语言,具备开发高效、部署简单、生态完善的优势,配合前端播放器组件,可快速实现跨浏览器、高兼容性的视频播放功能。本文将从技术选型、后端核心实现、前端集成、优化部署四个维度,手把手教你搭建 PHP...

    Java 实现在线视频播放完整方案:从后端服务到前端播放

    在 Web 开发中,在线视频播放是常见需求(如教育平台、视频网站、企业培训系统等)。Java 作为成熟的后端技术,能提供稳定的视频资源管理、权限控制、流式传输能力;配合前端播放器组件,可实现流畅的跨浏览器视频播放体验。本文将从技术选型、后端实现、前端集成、功能优化四个维度,手把手教你完成 Java...