Unity | 框架MVC

目录

一、MVC介绍

二、搭建UI界面

三、代码实现

1.Model层

2.View层

3.Controller层

四、MVC框架测试

五、知识补充


一、MVC介绍

  1. model:数据层。界面展示的数据(需要进行初始化、更新、保存、事件通知等操作),单例模式,不必继承MonoBehaviour。
  2. view:界面层。寻找UI,提供更新UI控件的方法,供controller调用。要挂在预制体上。
    (1)MVC是对M和V层进行了解耦,但没有完全解耦,从而诞生了MVP:对M和V层完全解耦。
    (2)MVC里view还是具备直接得到model的权限的,MVP框架是完全把视图跟模型分离了。
  3. controller:处理业务逻辑。控制View的显隐及更新,事件监听及取消。要挂在预制体上。
    (1)注意:物体隐藏时,OnDestroy不会被调用。
    (2)一个view对应一个controller。

二、搭建UI界面

        搭建如下的UI界面,并制作成预制体,并放于Resources\Prefabs目录。

三、代码实现

        创建三个脚本:Model.cs、View.cs、Controller.cs。并将View.cs、Controller.cs挂于预制体上。

1.Model层

using System;
using UnityEngine;

public class Model
{
    // 单例
    private static Model instance;
    public static Model Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Model();
                instance.Init();
            }
            return instance;
        }
    }


    // 数据
    private int coinCount;
    public int CoinCount  
    {
        get { return coinCount; }
    }


    // 数据操作:初始化、更新、保存
    private void Init()
    {
        coinCount = PlayerPrefs.GetInt("CoinCount", 0);
    }
    public void UpdateCoinCount()
    {
        ++coinCount;
        Save();
    }
    private void Save()
    {
        PlayerPrefs.SetInt("CoinCount", coinCount);
        CallEvent();
    }


    // 事件:用于通知Controller进行更新View
    private event Action<Model> ModelEvent;
    public void AddEventListener(Action<Model> function)
    {
        ModelEvent += function;
    }
    public void RemoveEventListener(Action<Model> function)
    {
        ModelEvent -= function;
    }
    private void CallEvent()
    {
        ModelEvent?.Invoke(this);
    }
}

2.View层

using UnityEngine;
using UnityEngine.UI;

public class View : MonoBehaviour
{
    //提供UI控件
    public Text coinText;

    public Button addCoinButton;

    //提供更新UI控件的方法
    public void UpdateCoin(Model model)
    {
        coinText.text = model.CoinCount.ToString();
    }
}

3.Controller层

        一个UI预制体对应一个View、一个Controller。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Controller : MonoBehaviour
{
    //controller控制view
    private View view;

    private static Controller controller;
    public static Controller Instance
    {
        get
        {
            return controller;
        }
    }

    //1.面板显示隐藏
    public static void Show()
    {
        if (controller == null)
        {
            var temp = Resources.Load<GameObject>("Prefabs/UIPanel");
            var go = Instantiate(temp);
            go.transform.parent = GameObject.Find("Canvas").transform;
            go.transform.localPosition = Vector3.zero;
            go.transform.localScale = Vector3.one;
            controller = go.GetComponent<Controller>();
        }
        controller.gameObject.SetActive(true);

    }

    public static void Hide()
    {
        if (controller != null)
        {
            controller.gameObject.SetActive(false);
        }
    }

    void Start()
    {
        view = GetComponent<View>();

        //2.首次更新面板数据
        view.UpdateCoin(Model.Instance);


        //3.事件监听,更新数据
        view.addCoinButton.onClick.AddListener(() =>
        {
            Model.Instance.UpdateCoinCount();
        });
        Model.Instance.AddEventListener(UpdateCoin);
    }

    private void UpdateCoin(Model model)
    {
        //2.更新面板数据
        view.UpdateCoin(model);
    }


    private void OnDestroy()
    {
        Model.Instance.RemoveEventListener(UpdateCoin);
    }
}

四、MVC框架测试

using UnityEngine;

public class TestMVC : MonoBehaviour
{
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Controller.Show();
        }
        if (Input.GetMouseButtonDown(1))
        {
            Controller.Hide();
        }
    }
}

五、知识补充

    private event Action<Model> ModelEvent;
    public void AddEventListener(Action<Model> function)
    {
        ModelEvent += function;
    }
    public void RemoveEventListener(Action<Model> function)
    {
        ModelEvent -= function;
    }
    private void CallEvent()
    {
        ModelEvent?.Invoke(this);
    }

        在C#中,event关键字用于声明一个事件,它本质上是一种特殊的多播委托。`ModelEvent`在这里是一个事件,当你对其使用`+=`操作符时,你实际上是将一个回调方法添加到委托的调用列表中。如果你注册了多个回调,那么这些回调方法就会被添加到`ModelEvent`的调用链中。

        当调用`ModelEvent?.Invoke(this);`时,以下是在底层发生的事情:

  1. 空值检查:`?.`是C# 6.0中引入的空条件运算符。它在尝试调用方法或访问成员之前会检查左侧的对象是否为`null`。如果`ModelEvent`不为`null`,那么调用继续;如果为`null`,那么调用就不会执行,整个表达式的结果也是`null`。
  2. Invoke方法:`Invoke`是`MulticastDelegate`类的一个方法,它负责同步地调用委托链中的每个回调方法。`this`关键字是传递给每个回调方法的参数,表示事件的发起者。
  3. 多播委托的调用链:`ModelEvent`作为一个多播委托,可以持有对多个方法的引用。当你调用`Invoke`时,它按照注册的顺序调用这些方法。如果调用链中的任何一个方法抛出异常,那么后续的方法调用将不会执行,除非你捕获并处理了这个异常。
  4. 线程安全性:虽然`?.`操作符提供了对`null`的检查,但这并不保证线程安全性。如果在检查`ModelEvent`之后和调用`Invoke`之前,另一个线程将`ModelEvent`设置为`null`,那么仍然会抛出`NullReferenceException`。在多线程环境中,通常需要额外的同步机制来确保线程安全。
  5. 事件的触发:如果`ModelEvent`不为空,`Invoke`会触发事件,即调用所有注册的回调方法。这些方法将按照它们被添加到委托中的顺序被调用。

        在C#中,事件的使用是一种很好的设计模式,它允许对象通知其他对象发生了某些事情,而不需要知道这些对象是谁或者它们要做什么。这有助于保持代码的解耦和灵活性。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/644119.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

flutter 实现旋转星球

先看效果 planet_widget.dart import dart:math; import package:flutter/material.dart; import package:vector_math/vector_math_64.dart show Vector3; import package:flutter/gestures.dart; import package:flutter/physics.dart;class PlanetWidget extends StatefulW…

内网穿透--Spp-特殊协议-上线

免责声明:本文仅做技术交流与学习... 目录 spp项目: 一图通解: 1-下载spp 2-服务端执行命令 3-客户端执行命令 4-服务端cs监听&生马 spp项目: GitHub - esrrhs/spp: A simple and powerful proxy 支持的协议&#xff1a;tcp、udp、udp、icmp、http、kcp、quic 支持的…

什么是健康信息卡

健康档案信息卡是交由居民本人保管的个人健康信息卡片。 其内容包括&#xff1a;居民个人主要基本信息、健康档案编码、患有的重要疾病、过敏史以及紧急情况下的联系人及联系方式&#xff0c;还有所属基层医疗机构的责任医生、护士及联系电话等。它主要用于居民在复诊、转诊或接…

HTML+CSS 玻璃按钮

效果演示 Code <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>玻璃按钮</title><li…

Distributed Transactions Mit 6.824

Topic1&#xff1a;distributed transactions concurrency control atomic commit 传统计划&#xff1a;事务 程序员标记代码序列的开始/结束作为事务。 事务示例 x 和 y 是银行余额——数据库表中的记录。x 和 y 位于不同的服务器上&#xff08;可能在不同的银行&#x…

【Linux网络】端口及UDP协议

文章目录 1.再看四层2.端口号2.1引入linux端口号和进程pid的区别端口号是如何生成的传输层有了pid还设置端口号端口号划分 2.2问题2.3netstat 3.UDP协议3.0每学一个协议 都要讨论一下问题3.1UDP协议3.2谈udp/tcp实际上是在讨论什么&#xff1f; 1.再看四层 2.端口号 端口号(Po…

Servlet 的 API

HttpServlet init&#xff1a;当 tomcat 收到了 /hello 这样的路径是请求后就会调用 HelloServlet&#xff0c;于是就需要对 HelloServlet 进行实例化&#xff08;只实例一次&#xff0c;后续再有请求也不实例了&#xff09;。 destory&#xff1a;如果是通过 smart tomcat 的停…

存在重复元素 II[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个整数数组nums和一个整数k&#xff0c;判断数组中是否存在两个不同的索引i和j&#xff0c;满足nums[i] nums[j]且abs(i - j) < k。如果存在&#xff0c;返回true&#xff1b;否则&#xff0c;返回false。 示例 1&#…

Netty初识Hello World 事件循环对象(EventLoop) 事件循环组 (EventLoopGroup)

初始Netty-HelloWorld Netty在网络通信中的地位就如同Spring框架在JavaEE开发中的地位。 基于Netty网络通信开发简易的服务端、客户端&#xff0c;以实现客户端向服务端发送hello world&#xff0c;服务端仅接收不返回数据。 服务端代码&#xff1a; Slf4j public class Hell…

ICML2024 定义新隐私保护升级:DP-BITFIT新型微调技术让AI模型学习更安全

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;差分隐私在大模型微调中的重要性和挑战 在当今的深度学习领域&#xff0c;大型预训练模型的微调已成为提高各种任务性能的关键技术。然而&am…

开放式耳机哪个品牌音质好用又实惠耐用?五大公认卷王神器直入!

​在现今耳机市场&#xff0c;开放式耳机凭借其舒适的佩戴体验和独特的不入耳设计&#xff0c;备受消费者追捧。它们不仅让你在享受音乐时&#xff0c;仍能察觉周围的声音&#xff0c;确保与人交流无障碍&#xff0c;而且有利于耳朵的卫生与健康。对于运动爱好者和耳机发烧友而…

AI大模型探索之路-实战篇7:Function Calling技术实战:自动生成函数

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5&#xff1a;探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6&#xff1a;掌握Function Calling的详细流程 目录 系列篇章&#x1f4a…

Centos修改系統語言

一、使用命令行修系统语言 1、显示系统当前语言环 [rootkvm-suma ~]# localectl System Locale: LANGen_US.utf8 VC Keymap: cn X11 Layout: cn 2、查看系统支持字符集 [rootkvm-suma ~]# locale -a 2、设置系统语言环境 [rootkvm-suma ~]# localectl set-locale LANGz…

Gradio 搭建yolov8 分类系统

代码 import gradio as gr import pandas as pd from ultralytics import YOLO from skimage import data from PIL import Imagemodel YOLO(yolov8n-cls.pt) def predict(img):logging.info("Gradio 调用开始")result model.predict(sourceimg)logging.info("…

机器学习预测-CNN手写字识别

介绍 这段代码是使用PyTorch实现的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;用于在MNIST数据集上进行图像分类。让我一步步解释&#xff1a; 导入库&#xff1a;代码导入了必要的库&#xff0c;包括PyTorch&#xff08;torch&#xff09;、神经网络模块&#xff0…

【Linux】Linux的安装

文章目录 一、Linux环境的安装虚拟机 镜像文件云服务器&#xff08;可能需要花钱&#xff09; 未完待续 一、Linux环境的安装 我们往后的学习用的Linux版本为——CentOs 7 &#xff0c;使用 Ubuntu 也可以 。这里提供几个安装方法&#xff1a; 电脑安装双系统&#xff08;不…

LeetCode热题100——矩阵

73.矩阵清零 题目 给定一个 *m* x *n* 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 示例…

OpenAI撤回有争议的决定:终止永久性非贬损协议

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Docker提示某网络不存在如何解决,添加完网络之后如何删除?

Docker提示某网络不存在如何解决&#xff1f; 创建 Docker 网络 假设现在需要创建一个名为my-mysql-network的网络 docker network create my-mysql-network运行容器 创建网络之后&#xff0c;再运行 mysqld_exporter 容器。完整命令如下&#xff1a; docker run -d -p 9104…

力扣刷题---2283. 判断一个数的数字计数是否等于数位的值【简单】

题目描述 给你一个下标从 0 开始长度为 n 的字符串 num &#xff0c;它只包含数字。 如果对于 每个 0 < i < n 的下标 i &#xff0c;都满足数位 i 在 num 中出现了 num[i]次&#xff0c;那么请你返回 true &#xff0c;否则返回 false 。 示例 1&#xff1a; 输入&a…