把核心框架实现了,没有做界面
PS:让我想起来以前用XNA写了半个暑假的简陋版抢滩登陆,老辛苦还丑,现在的可视化引擎比以前的强大太多了
框架是以前写的老框架的延伸,直接看UML图吧
TotalManager,LevelManager,UIManager我前面的文章已经做过一次了,标准的MVC中负责VC的部分,M就是下面的Level_N了。
Unity已经帮我们实现了组件模式,所以我们只需要建一个空物体,然后把EnemyCreate,Shoot,UIController三个脚本挂上去,再制成Prefab,在LevelManager调用即可。
因为这些工具类我都希望他们只会生成出一个,所以都做成了单例模式(多打一行重复代码都是罪孽)。细节上就是继承了一个UnitySingleton泛型,实现如下:
UnitySingleton.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class UnitySingleton<T> : MonoBehaviour where T : Component //约束T需要继承Component { //proteced保证了继承的子类可以访问 //不过也导致同样继承了UnitySingleton的别的子类也可以访问,不过问题不大 protected static T _instance; public static T Instance() { if (_instance == null) { //找到我们在场景里已经实例化的实例 _instance = FindObjectOfType(typeof(T)) as T; //如果我们场景里没有实例化,就创建一个空物体增加脚本返回这个实例 if (_instance == null) { GameObject obj = new GameObject(); obj.name = typeof(T).ToString(); _instance = obj.AddComponent<T>(); } } return _instance; } }
直接继承即可。注意我在泛型里并没有声明构造,需要在子类私有化防止被外部new实例化
public class UIController : UnitySingleton<UIController>{ private UIController() { } }
Shoot的逻辑处理没什么好说的,老样子一个raycast解决,当然其实这里还能玩很多东西的,例如不用射线检测,而是做成FreeFPS用子弹来打飞碟(直说了吧,还是抢滩登陆)
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); RaycastHit hit; //存储碰撞到的物体的信息 bool isCollider = Physics.Raycast(ray,out hit); //被检测物体要有collider才能检测到 if (isCollider) { hit.transform hit.collider hit.point }
ray里储存了方向变量,我们可以很方便地得到子弹的射击角度。
不过子弹也好,飞碟也好,都是频繁创建的物体,如果不断的实例化+销毁,一是会让内存碎片化(当然UNITY会帮你处理好这一块的内容),二是对性能的损耗是极高的,因为创建实例的复杂度是O(n)的(算法课没开小差吧?)
这种时候,我们就可以使用一种叫做“对象池”的类解决这个问题(工厂模式的衍生)。我们提前实例好一定数量的对象,使用的时候就拿出来,用完了就塞回对象池,避开了实例化和释放的操作,这时复杂度是O(1)的
对象池存储对象的方法可以任意选择,List,Stack,Dictionary,数组都没有问题,根据自己的需要选择合适的存储方式
我下面给出一个使用Stack(栈)的例子
public class ObjectPool<T> where T : class, new() { private Stack<T> m_objectStack = new Stack<T>(); public T Get() { return (m_objectStack.Count == 0) ? new T() : m_objectStack.Pop(); } public void Return(T t) { m_objectStack.Push(t); } }
很简单的代码,需要对象就调用Get方法,从Stack里拿一个实例去用,Stack里的对象如果已经用完了就当场实例化一个。
对象用完了就Return回来,压回Stack里等待下次使用
这段代码没有提前实例化,需要提前实例化的自己补一个Init方法,Return一定数量的对象实例即可
使用Stack的好处有两个,1.简单,用标准库,两分钟就能写好一个这样的对象池,2.对象池的大小是可拓展的,不会因为越界而报错。缺点也是因为可拓展,导致无法处理内存碎片化的问题(当然,使用UNITY就不用考虑这个了)
注意在多线程里使用对象池需要加上锁,不然无法保证线程安全(Unity依然不需要担心这个问题)
最后Unity有一个叫PoolManager的脚本插件,功能很强大,实际应用中用这个插件就可以满足九成九的需求了
github链接:https://github.com/keven2148/3D-Game-Programming-Design-Lesson-Work/tree/master/Lesson5
版本:Unity2017.3