游戏中常用的设计类型
- 静态方法
- 静态类
- 继承
- 抽象类
- 接口
- 设计模式 (单例模式 观察者模式 工厂模式 组合模式)
- 设计模块/设计系统
框架开发路线
- 知识库
- 方法库/工具集
- 类库/框架
- Manager Of Manager
- 资源管理/热更新
- UI/编码效率提高
- 整合必备的第三方库
- 模块/组件
- 脚本Lua支持
游戏库的作用
- 知识学习&收集
- API收集
- C#语法实践
- 库本身的功能
- 规则实现
- 使用流程提供和优化
- 效率提升(编码体验、逻辑复用)
- 项目使用工具收集
开发中的知识点
- Unity中Screen.height和Screen.width可以获得当前设备的高和宽
- 类的第一个作用,方法的集合
- System.Obselete的作用及用法
- partial关键字
- C#中所有类型都继承object类
- 泛型:结构复用利器
- C#脚本只有继承了MonoBehaviour才能作为脚本挂到GaneObject上
- 代码复用
- 方法重载
- 泛型
- 继承
- 不继承MonoBehaviour的脚本具有函数周期吗(Awake Start)
- 给类添加abstract关键字,这样用户在使用该类的时候就不能自己创建出来实例
11.脚本访问脚本的几种形式- A GameObject 是 B GameObject 的 Parent,或者中间隔着几个层级的Parent。
- A脚本想调用B脚本,直接通过transform.Find(“XXX/YYY/ZZZ”).GetComponent().DoSomething()就可以了
- B脚本想调用A脚本,比较好的方式是在B脚本中声明委托,然后在A中注册特定的方法。当B想调用A脚本的方法的时候,通过委托通知就好。除此之外,也可以使用消息机制。Unity本身实现了一套消息机制。比如在B脚本中可以使用thsi.SendMessageUpward(“MethedName”)这样的方式,特点是使用字符串,并且可能用到了反射。
- A GameObject 和 B GameObject是同级的,比如他们有共同的Parent。这种情况下,比较好的方式还是使用消息机制
- A GameObject 和 B GameObject 不在同一个GameObject树下,这种情况大部分是跨模块通信,推荐使用的还是消息机制
- A GameObject 是 B GameObject 的 Parent,或者中间隔着几个层级的Parent。
-
消息机制涉及到的知识点:
- List或LinkedList或者自己实现的链表
- Dictionary
- 委托
一般的消息机制会提供3个API:
- 注册事件
- 注销事件
- 发送事件
-
框架:提供一个架构(文件结构、约定等等),你必须遵守它,只要你遵守,那剩下的就全部处理通用需求了。
MsgDispatcher.Register("消息名", (obj) =>{});
MsgDispatcher.Send("消息名", "消息内容");
MsgDispatcher.UnRegister("消息名");
--存放全局消息的容器
private static Dictionary<string, Action<object>> RegisteredMsgs = new Dictionary<string, Action<object>>();
--注册功能
public static void Register(string msgName, Action<object> onMsgReceived){
RegisteredMsgs.Add(msgName, onMsgReceived);
}
--注销功能
public static void UnRegister(string msgName)
{
RegisteredMsgs.Remove(msgName);
}
--发送功能
public static void Send(string msgName, object data){
RegisteredMsgs[msgName](data);
}
Manager Of Managers结构:
- MainManager:作为入口管理器
- EventManager:消息管理
- GUIManager:图形视图管理
- AudioManager:音效管理
- PoolManager:GameObject管理(减少动态开辟内存消耗,减少GC)
- LevelManager:关卡管理
- GameManager:游戏管理
- SaveManager:配置&存储管理
- MenuManaager:菜单管理
静态变量和成员变量的区别:
- 使用角度,通过类名访问静态方法,通过实例化对象访问成员变量
- 用途角度,静态变量更多的对整个项目配置或者是记录,成员变量记录对象本身的状态
关于泛型
对以下代码进行优化
public static object GetRandomValueFrom(object[] value){
return values[Random.Range(0, value.Length)];
}
测试代码:
var intRandomValue = (int)GetRandomValueFrom(new int[]{1, 2, 3});
var stringRandomValue = (string)GetRandomValueFrom(new string[]{"asdasd", "123123"});
var floatRandomValue = (float)GetRandomValueFrom(new float[]{0.1f, 0.2f});
将object改成泛型
public static T GetRandomValueForm<T>(T[] values){
return Random.Range(0, values.Length);
}
测试代码:
var intRandomValue = GetRandomValueFrom(new int[]{1, 2, 3});
var stringRandomValue = GetRandomValueFrom(new string[]{"asdasd", "123123"});
var floatRandomValue = GetRandomValueFrom(new float[]{0.1f, 0.2f});
使用params参数优化, params可以传递任意数量的参数
public static T GetRandomValueFrom<T>(params T[] values){
return values[Random.Range(0, values.Length)];
}
测试代码:
var intRandomValue = GetRandomValueFrom(1,2,3);
var stringRandomValue = GetRandomValueFrom("asdasd", "123123");
var floatRandomValue = GetRandomValueFrom(0.1f, 0.2f);
- 父类的变量可以接收子类的实例
- 要说方法是逻辑上的复用,那么泛型就是结构上的复用。两大复用法宝。
设计对象池
-
内存碎片
- 设备内存堆中的空余空间被打碎成了很多小的内存碎片,而不是大的连续内存块。总的可用内存也许很大,但是最长的连续空间很小
- 内存过小导致无法运行程序
-
对象池 定义一个池对象,初始化是就创建了整个对象集合(通常使用一次连续的分配),然后设置对象池中对象的状态。使用流程不再是创建和销毁,而是分配和回收
-
使用 游戏实体和视觉效果
- 需要频繁创建和销毁对象
- 对象大小相仿
- 在堆上进行内存分配十分缓慢或者会导致内存碎片
- 每个对象都封装了想数据库或者网络连接这样很昂贵又可以重用的资源
-
总结
- 通常依赖垃圾回收机制或者new和delete来处理内存管理,通过使用对象池,相当于将处理内存的责任交到自己手里
-
关于对象池
- 警惕池可能在不需要的对象上浪费内存,把握好对池大小的掌握
- 同时只能激活固定数量的对象
- 灵活改变对象池的大小
- 不要创建新的对象
- 强制干掉一个已有的对象
- 每个对象的内存大小是固定的
- 重用对象不会自动清除,初始化对象很重要
- 未使用的对象会保留在内存中
-
PoolManager知识点
- Func/Action 委托
- 工厂
- 堆栈 Stack
对象之间的交互
1. 最佳的单向依赖就是自顶向下的调用
- Panel(父脚本)通过Button(子控件)的onClick注册事件
- GameManager(顶端模块)通过调用LevelManager(子模块)的方法
- 车子调用轮胎让它转动
2.依赖的类型
- 自顶向下的单向依赖(最佳)
- 父节点持有子节点的引用
namespace Master
{
public class Parent
{
Child mChild = new Child()
}
public class Child{}
}
- 同级之间的单向依赖(避免)
- 自底向上的单向依赖(避免)
单例模式
- 懒加载
GameObject mSharedUIRoot = null;
public GameObject UIRoot
{
get
{
if(!mSharedUIRoot)
{
mSharedUIRoot = new GameObject("UIRoot");
return mSharedUIRoot;
}
}
}
- Property Get
- 字典使用
- DontDestronLoad(加载新场景是,不销毁当前场景指定对象)