UMVC——Unity的MVC思想
UMVC——Unity的MVC思想
本章应当在完成了程序基础小框架,初步理解了框架的作用之后,再来学习
要注意!MVC的部分思想与我们之前所学习的小框架的使用初衷有所冲突!MVC思想并不是为了做游戏而生的,它只是一种软件开发的编程思想,
MVC不是必备的内容,且MVC主要用于开发游戏UI系统的逻辑
在实际开发中,不应该直接硬套mvc框架,而是吸收后。自己做自己的实现,这样才比较好用
取其思想用法,用合适的度对应自己合适的项目,至于通用程度,这是需要多个项目的迭代的
——来自某日群聊内某饼老师的谆谆教诲
MVC
MVC全名是Model View Controller
是模型(model)-视图(view)-控制器(controller)的缩写
它是一种软件设计规范,用一种将业务逻辑、数据、界面显示这三者分离的方法来组织代码
将业务逻辑聚集到一个部件里面,再改进和个性化定制
界面及用户交互的同时,不需要重新编写业务逻辑
本系列学习内容
((20230829024950-89hkl1u “MVC基础”))
基本概念
基本实例
不使用MVC思想制作UI逻辑
使用MVC思想制作 ...
UF_OLDL9——UI管理模块
UF_OLDL9——UI管理模块
注意!该UI模块是基于UGUI的! 请先学习UGUI再学习这里的内容
在游戏中,各种游戏对象可能会打开或者隐藏各种UI面板
如果让它们直接相互关联,那这些对象和面板的联系会变成错综复杂难以管理,也就是说程序的耦合性变高了
制作UI模块的目的,就是为了降低这种耦合性
首先,对于 游戏对象(也有可能是一个面板) 显示隐藏一个UI面板的需求
我们可以构建一个UI管理器,通过调用UI管理器来控制各个UI面板的显示隐藏
对于各个UI面板(Panel),
它们的共同点在于:它们需要管理控制自己面板内的各个控件,需要显示隐藏等
这时我们就可以抽象出这些共同行为,将其写入一个UI基类,让面板对象继承这个UI基类
UI基类
首先可以利用.GetComponentsInChildren<>()获取所有的组件(它们都有一个父类UIBehaviour),再用字典存储
我们可以通过输入控件对象名和控件类型来获取某个控件
其次,每个UI都有隐藏显示的方法,所以我们需要首先在基类声明显示和隐藏的方法,到之后具体到某个子类在重写
使用方法:
让UI面板类继承它
1234 ...
UF_OLDL8——音效管理模块
UF_OLDL8——音效管理模块
音效管理模块
在游戏里,各个对象可能都会有各种音效,很明显,为每个对象都单独添加音效源难以统一管理的
我们需要写一个独立的音效管理模块来统一管理它们
通过这个模块,使得游戏对象不再需要自己控制音效源,只需要调用该模块即可
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135using System.Collections;using System.Collections.Generic;using UnityEngine ...
UF_OLDL7——输入控制模块
UF_OLDL7——输入控制模块
输入控制模块
注意!该输入控制模块主要用于PC端,其它操作方式需要作特别的处理,但思路大致一致
在使用输入控制模块前,可能都是在操控角色的Update()内写输入操作逻辑
但是,如果我们要切换操控的对象,或者多操控一些对象,每一个对象的类里都要写输入操作逻辑,
这样较为麻烦且不能统一管理,也不利于降低代码耦合性(不同的类都在引用Input类,但是它们输入的逻辑其实是类似的)
通过事件中心模块,封装一个输入控制模块,它只需要对事件中心发送输入事件,由事件中心触发对象执行监听输入事件的方法
这样即可缩减代码量,且便于统一管理输入控制逻辑,降低耦合性
(要控制的对象只需要通过事件中心监听输入控制模块发送的输入事件即可)
知识点:Input,事件中心模块,公共Mono模块
最基本的输入模块
使用方法:
检测键盘某按键按下检测,向事件中心发送监听“键盘某键按下”“键盘某键抬起”,可以检测
需要加键就在InputUpdate加调用方法
12345678910111213141516171819202122232425262728293031323334353637 ...
UF_OLDL6——资源加载模块
UF_OLDL6——资源加载模块
资源加载模块
注意!该资源加载模块不包含AB包相关内容
在之前的学习里我们已经大量的使用了Resource公共类动态读取资源
将其封装出一个资源加载模块可以更好的使用它
知识点:异步加载,委托和lambda表达式,协同程序,泛型
使用方法:
资源同步加载,如果加载的是游戏对象就直接实例化并返回该游戏对象
资源异步加载,如果加载的是游戏对象就直接实例化并返回该游戏对象,需要传入有参数的回调函数,加载完毕后执行,参数为读取到的资源
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849using System.Collections;using UnityEngine;using UnityEngine.Events;public class ResourcesManager : BaseManager<ResourcesManager>{ /// <summary> /// 同步加载资源 ...
UF_OLDL5——场景切换模块
UF_OLDL5——场景切换模块
关于场景切换
在游戏项目里,切换场景并不是切换到一个已经放好东西的场景上
而是切换到一个空场景,通过配置文件去动态的加载场景上的物件,通过玩家消息去动态的创建玩家
所以,我们需要一个场景切换模块来提供一个场景切换的公共接口来指定切换场景后应该做什么
知识点涉及到场景异步加载、协同程序、委托
场景切换模块
使用方法:
场景同步切换,传入场景名和切换场景结束后要执行的方法
场景异步切换,传入场景名和切换场景结束后要执行的方法,可以用这个来更新进度条等内容
异步切换时,事件中心会得到“进度表更新”事件,参数包含场景切换进度,可以使用这个来更新进度条
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950using System.Collections;using UnityEngine;using UnityEngine.Events;using UnityEngine.SceneManagement;/// <summary ...
UF_OLDL4——公共Mono模块
UF_OLDL4——公共Mono模块
公共Mono模块
Unity协程和Update()帧更新等只有在继承MonoBehaviour的类里才能使用,
但我们有不少类是不继承MonoBehaviour的,那我们应该如何让它们能够使用Update()和开启协程呢?
使用公共Mono模块,来让不继承MonoBehaviour的类来使用上一些MonoBehaviour的内容,
例如开启协程,可以Update更新,并统一管理Update()
知识点:生命周期函数,事件,协同程序
对外开放部分——公共Mono模块管理者
单例公共Mono模块的管理者,封装一个唯一的Mono模块控制者用于对外开放和统一管理
可以给外部添加帧更新事件方法,也可以通过该模块为非继承MonoBehavior的类开启协程
封装了一个唯一的公共MonoBehaviour模块控制者
对外实现了帧更新和开启协程的方法
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585 ...
UF_OLDL3——事件中心模块
UF_OLDL3——事件中心模块
事件中心
事件中心是基于观察者设计模式来设计的,目的是降低程序耦合性,减少程序复杂度,会使用委托,字典
为何要用事件中心
在以前,没有事件中心的时候,
面对一个对象被修改时其它对象要做自动更新的问题:
例如一个怪物死亡,需要让其它对象例如玩家执行什么方法,
我们可能就会让怪物关联这些对象,然后在死亡的方法里调用这些对象
但是,这种方法让几个不相关的类之间的耦合度增加了,修改其中一个类,就有可能影响另几个类,这显然不利于我们程序后续的编写和维护
基于观察者设计模式来设计的事件中心可以解决这种一个对象状态改变给其他对象通知的问题
观察者设计模式
观察者模式 | 菜鸟教程 (runoob.com)
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。
比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时 ...
UF_OLDL2——缓存池模块
UF_OLDL2——缓存池模块
缓存池
为何要用缓存池
节选自:Unity —— 缓存池简单理解 - 知乎 (zhihu.com)
来理解缓存池之前,我们先了解一下C#的内存回收机制:
每次实例化一个对象(在场景上创建一个对象),都会分配一个内存空间;
当这个对象被删除时,仅仅时断开了对这片空间的引用,此内存空间并没有被释放掉。再次创建对象时,会继续分配其他的内存空间,直到内存被全部被分配满。
当内存满了再回过头看有哪些是不用的"垃圾",再回收释放。
这样的一次释放,叫做 “一次GC”。
所谓垃圾,就是没有被任何变量、对象引用的内容。
通过是否被引用来确定哪些对象是"垃圾"。
正是因为每次GC需要经过大量的计算来判断是否需要回收,对CPU的消耗较大,
所以每次GC都可能会造成卡顿,GC次数一旦多了会严重影响玩家的使用体验,由此出现了缓存池。
在以前我们不使用缓存池时,例如子弹,我们一旦发射出去,打中什么了之后,就不再使用它,就会将其直接销毁
然而实际上,这些子弹对象仍然占用着内存空间,只是没有被引用
而这种对象的多次创建,内存空间被占满 ...
UF_OLDL1——单例模式基类
UF_OLDL1——单例模式基类
单例模式
一般的单例模式差不多都会写类似于下面的这种形式,区别几乎只在于类名
123456public class GameManager{ private static GameManager instance = new GameManager(); //为该类写一个私有静态变量,并调用私有构造函数,装载该类的唯一的实例化对象 public static GameManager Instance => instance; //为该类写一个静态属性,使唯一装载该类的实例化对象的变量在外部只读,以可以调用该类的各种成员 private GameManager() { } //私有的无参构造函数,确保只有该类的私有静态变量可以构造该类的实例化对象,而外部不能,在外部第一次调用静态属性时会执行}
或者用静态方法来也可以获取私有静态变量
123456789public class BaseManager{ private static BaseManager i ...