You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MVC的纸牌游戏:如何基于非MonoBehaviour类创建对象?

实现思路与建议

1. 采用分层架构解耦逻辑与视图

将核心游戏逻辑与Unity视图层完全分离:

  • 逻辑层:Card类作为纯数据/逻辑类,只维护卡牌的核心属性(花色、点数、翻面状态、归属标识等),不调用任何Unity API,专注于游戏规则相关的逻辑处理。
  • 视图层:创建继承MonoBehaviour的CardView脚本,挂载在卡牌UI预制体上,负责接收逻辑层的数据并同步UI状态(切换正反面Sprite、调整位置/旋转等)。

2. 通过预制体实例化视图对象

  • 先在Unity编辑器中制作卡牌UI预制体,包含显示用的Image/SpriteRenderer、交互用的Collider等组件,并挂载CardView脚本。
  • 当需要基于逻辑层的Card数据生成场景对象时,用Object.Instantiate()实例化预制体,再通过CardView的绑定方法关联对应的Card实例,完成数据与视图的映射。

示例代码片段:

// 逻辑层纯数据Card类
public class Card
{
    public enum Suit { Hearts, Diamonds, Clubs, Spades }
    public enum Rank { Ace, Two, ..., King }

    public Suit CardSuit { get; }
    public Rank CardRank { get; }
    public bool IsFaceUp { get; private set; }

    public Card(Suit suit, Rank rank)
    {
        CardSuit = suit;
        CardRank = rank;
        IsFaceUp = false;
    }

    public void Flip() => IsFaceUp = !IsFaceUp;
}

// 视图层CardView脚本
public class CardView : MonoBehaviour
{
    [SerializeField] private Sprite faceSprite;
    [SerializeField] private Sprite backSprite;
    [SerializeField] private Image cardImage;

    private Card _logicCard;

    // 绑定逻辑层Card实例
    public void BindToCard(Card logicCard)
    {
        _logicCard = logicCard;
        UpdateVisualState();
    }

    // 同步UI状态
    public void UpdateVisualState()
    {
        cardImage.sprite = _logicCard.IsFaceUp ? faceSprite : backSprite;
        // 可扩展:同步位置、缩放、层级等
    }
}

// 控制器类负责实例化与绑定
public class DeckController : MonoBehaviour
{
    [SerializeField] private CardView cardPrefab;
    [SerializeField] private Transform spawnRoot;

    private List<Card> _logicDeck = new List<Card>();

    void Start()
    {
        InitLogicDeck();
        SpawnCardViews();
    }

    // 初始化逻辑层卡组
    private void InitLogicDeck()
    {
        foreach (Card.Suit suit in Enum.GetValues(typeof(Card.Suit)))
        {
            foreach (Card.Rank rank in Enum.GetValues(typeof(Card.Rank)))
            {
                _logicDeck.Add(new Card(suit, rank));
            }
        }
    }

    // 生成所有卡牌视图并绑定
    private void SpawnCardViews()
    {
        float offsetX = 0.2f;
        int index = 0;
        foreach (var logicCard in _logicDeck)
        {
            CardView cardView = Instantiate(cardPrefab, spawnRoot.position + new Vector3(index * offsetX, 0, 0), Quaternion.identity);
            cardView.BindToCard(logicCard);
            index++;
        }
    }
}

3. 用事件驱动实现状态自动同步

避免视图层轮询逻辑层数据,给Card类添加事件,当数据变化时触发事件,视图层监听事件并自动更新UI:

public class Card
{
    public event Action OnStateChanged;
    public bool IsFaceUp { get; private set; }

    public void Flip()
    {
        IsFaceUp = !IsFaceUp;
        OnStateChanged?.Invoke();
    }
}

// 在CardView中绑定事件
public void BindToCard(Card logicCard)
{
    _logicCard = logicCard;
    // 先解绑避免重复注册
    _logicCard.OnStateChanged -= UpdateVisualState;
    _logicCard.OnStateChanged += UpdateVisualState;
    UpdateVisualState();
}

void OnDestroy()
{
    if (_logicCard != null)
    {
        _logicCard.OnStateChanged -= UpdateVisualState;
    }
}

4. 适配非UI场景的核心优势

由于逻辑层完全独立于Unity的视图组件,你可以直接在机器人逻辑模块等非Unity环境中实例化Card、Deck类,运行洗牌、发牌、规则判断等核心逻辑,无需依赖任何视图代码。当需要同步到UI时,再通过上述绑定机制更新视图即可。

额外优化建议

  • 将卡组的洗牌、发牌等逻辑封装在非MonoBehaviour的Deck类中,仅暴露必要接口给视图控制器调用。
  • 逻辑层用抽象坐标(如Vector2Int表示列/行)管理卡牌位置,视图层将抽象坐标转换为Unity世界空间坐标,进一步解耦位置逻辑。

内容的提问来源于stack exchange,提问作者Eduardo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:40:38