【unity实战】在unity实现一套可扩展的Model-View-Data (MVD) 系统架构设计

发布于:2025-08-16 ⋅ 阅读:(25) ⋅ 点赞:(0)

最终效果

在这里插入图片描述

前言

1、什么是MVD架构

Model-View-Data (MVD)是一种类似于MVC(Model-View-Controller)的架构模式,特别适合游戏开发。它将应用程序分为三个主要部分:
在这里插入图片描述

  • View: 处理游戏对象的视觉表现
  • Model: 管理游戏的核心数据和逻辑
  • Data: 存储和管理静态或配置数据

这种架构实现了

  • 数据与逻辑分离

  • 状态与表现分离

  • 支持灵活的实例化

  • 便于扩展和维护

2、组件关系示意图

在这里插入图片描述

3、Model模型和Data数据的区别

在这里插入图片描述
虽然模型和数据看起来很相似,但还是有一些差异。

  • 数据是静态模板(每个类型只有一份)
  • 模型是动态实例(可根据数据创建多个)

示例说明,以"史莱姆卡牌"为例:
在这里插入图片描述

  • 数据层定义

    • 基础攻击力:5

    • 基础生命值:10

    • 初始效果:治疗+2

  • 游戏运行时

    • 可以创建多个史莱姆卡牌实例

    • 每个实例继承数据层的初始值

    • 各实例的状态可独立变化(如一个生命值提升为11,另一个仍保持7)

    • 视图实时显示每个实例的当前状态

4、基础文件结构设计

Assets/
├── Scripts/
│ ├── Core/
│ │ ├── Models/ // 模型层
│ │ ├── Views/ // 视图层
│ │ ├── Data/ // 数据层
│ ├── Utilities/ // 工具类
│ └── GameManager.cs // 游戏入口

5、游戏开发中的扩展应用

5.1 可扩展卡牌系统

  • 数据层:存储基础属性(名称/图标/基础数值)、定义卡牌效果模板、提供卡牌生成蓝图
  • 模型层:维护卡牌当前属性(攻击力/生命值等)、管理临时状态和效果(增益/减益)、执行游戏逻辑运算
  • 视图层:显示卡牌的实时状态和数据、处理玩家交互(点击/拖拽等)、播放视觉特效和动画

5.2 角色系统

  • 数据层:角色基础属性(血量/攻击力)、技能模板
  • 模型层:实时状态(当前血量/装备加成)、Buff/Debuff
  • 视图层:3D模型、血条UI、技能特效

5.3 道具/装备系统

  • 数据层:武器基础伤害、耐久度、图标
  • 模型层:当前耐久度、强化等级、附魔效果
  • 视图层:背包图标、装备外观、粒子特效

5.4 技能系统

  • 数据层:技能CD、伤害公式、动画触发参数
  • 模型层:当前冷却时间、连击计数
  • 视图层:技能图标、施法特效、屏幕震动

5.5 场景交互对象

  • 数据层:可破坏物件的抗性参数
  • 模型层:当前耐久值、破坏状态
  • 视图层:裂纹贴图、破碎动画

5.6 UI系统

  • 数据层:窗口布局配置、文字内容
  • 模型层:打开/关闭状态、动态数据绑定
  • 视图层:实际渲染的UI元素

实例:实现一个可扩展卡牌系统

1、创建MVD架构

1.1 数据层 (Data)

using UnityEngine;

//存储卡牌数据
[CreateAssetMenu(menuName = "Card Data")]
public class CardData : ScriptableObject 
{
    [field: SerializeField] public Sprite Sprite { get; private set; } // 卡牌图片
    [field: SerializeField] public int Cost { get; private set; }      // 卡牌费用
    [field: SerializeField, TextArea] public string Effect { get; private set; }  // 卡牌效果描述
}
  • 功能:卡牌原始定义库

  • 职责:

    • 存储基础属性(名称/图标/基础数值)

    • 定义卡牌效果模板

    • 提供卡牌生成蓝图

  • 特性:

    • 使用ScriptableObject实现

    • 只读设计(运行时不可修改)

    • 单数据可生成多实例

1.2 模型层 (Model)

using UnityEngine;

public class Card
{
    private readonly CardData cardData; // 只读的卡牌基础数据
    
    // 构造函数:用CardData初始化卡牌
    public Card(CardData cardData)
    {
        this.cardData = cardData;
        Effect = cardData.Effect; // 初始化效果
        Cost = cardData.Cost;    // 初始化费用
    }
    
    // 属性
    public Sprite Sprite => cardData.Sprite; // 获取卡牌图片(简写属性)
    public string Title => cardData.name;    // 卡牌标题使用ScriptableObject的名称
    public int Cost { get; set; }           // 卡牌费用(可修改)
    public string Effect { get; set; }      // 卡牌效果(可修改)
    
    // 执行卡牌效果
    public void PerformEffect()
    {
        Debug.Log($"触发卡牌效果:{Effect},费用:${Cost}");
        // 这里可以扩展实际游戏效果逻辑
    }
}
  • 功能:卡牌运行时动态状态容器

  • 职责:

    • 维护卡牌当前属性(攻击力/生命值等)

    • 管理临时状态和效果(增益/减益)

    • 执行游戏逻辑运算

  • 特性:

    • 基于数据层实例化

    • 支持运行时状态修改

    • 每个卡牌实例拥有独立模型

1.3 视图层 (View)

using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;

public class CardView : MonoBehaviour, IPointerClickHandler // 实现点击接口
{
    [SerializeField] private SpriteRenderer cardImage; // 卡牌图片渲染器
    [SerializeField] private TMP_Text title;         // 卡牌标题文本(TextMeshPro)
    [SerializeField] private TMP_Text cost;          // 卡牌费用文本(TextMeshPro)
    [SerializeField] private TMP_Text effect;          // 卡牌效果描述文本(TextMeshPro)
    
    private Card card; // 关联的卡牌逻辑对象

    // 初始化卡牌视图
    public void Setup(Card card)
    {
        this.card = card;
        cardImage.sprite = card.Sprite; // 设置卡牌图片
        title.text = card.Title;        // 设置标题
        cost.text = card.Cost.ToString(); // 设置费用
        effect.text = card.Effect;  //设置卡牌效果描述
    }
    
    // 点击卡牌时的处理
    public void OnPointerClick(PointerEventData eventData)
    {
        card.PerformEffect(); // 执行卡牌效果
        Destroy(gameObject);  // 使用后销毁卡牌(可根据需求修改)
    }
}
  • 功能:卡牌在游戏世界中的可视化表现

  • 职责:

    • 显示卡牌的实时状态和数据

    • 处理玩家交互(点击/拖拽等)

    • 播放视觉特效和动画

  • 特性:纯表现层,不存储业务逻辑

2、测试调用

2.1 游戏管理器脚本

这里我们创建一个小型的游戏管理器脚本,以便我们可以测试调用前面创建的MVD架构

using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    [SerializeField] private List<CardData> cardDatas; // 所有卡牌数据列表
    [SerializeField] private CardView cardView;       // 卡牌视图预制体
    
    private List<Card> deck; // 卡组
    
    private void Start()
    {
        InitializeDeck(); // 初始化卡组
    }
    
    // 初始化卡组(创建10张随机卡牌)
    private void InitializeDeck()
    {
        deck = new List<Card>();
        for (int i = 0; i < 10; i++)
        {
            // 随机选择卡牌数据
            CardData data = cardDatas[Random.Range(0, cardDatas.Count)];
            // 创建新卡牌并加入卡组
            Card card = new Card(data);
            deck.Add(card);
        }
    }
    
    // 抽卡方法
    public void DrawCard()
    {
        if (deck.Count == 0) return; // 卡组为空时不抽卡
        
        // 随机选择一张卡
        Card drawnCard = deck[Random.Range(0, deck.Count)];
        deck.Remove(drawnCard); // 从卡组移除
        
        // 实例化卡牌视图
        CardView view = Instantiate(cardView);
        view.Setup(drawnCard); // 初始化卡牌视图
    }
}

2.2 配置不同的卡牌模板数据

在这里插入图片描述

2.3 创建CardView预制体

在这里插入图片描述

2.4 配置GameManager

在这里插入图片描述

2.5 配置按钮调用DrawCard抽卡方法方法

在这里插入图片描述

2.6 2D场景中的 UI 和物体交互

注意,前面CardView代码里我们使用EventSystem 系统的IPointerClickHandler接口的OnPointerClick方法实现点击卡牌时触发卡牌效果。要使用它处理 2D 场景中的 UI 和物体交互,我们还需要注意下面的准备:

  • 确保物体上有Collider2D组件

  • 场景中需要有EventSystem对象(新建UI时会自动创建)

  • 主相机需要有Physics2DRaycaster组件

3、运行效果

在这里插入图片描述

参考

https://www.youtube.com/@thecodeotter


专栏推荐

地址
【unity游戏开发入门到精通——C#篇】
【unity游戏开发入门到精通——unity通用篇】
【unity游戏开发入门到精通——unity3D篇】
【unity游戏开发入门到精通——unity2D篇】
【unity实战】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架/工具集开发】
【unity游戏开发——模型篇】
【unity游戏开发——InputSystem】
【unity游戏开发——Animator动画】
【unity游戏开发——UGUI】
【unity游戏开发——联网篇】
【unity游戏开发——优化篇】
【unity游戏开发——shader篇】
【unity游戏开发——编辑器扩展】
【unity游戏开发——热更新】
【unity游戏开发——网络】

完结

好了,我是向宇,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到