前言
- 前一段时间看了siki的设计模式的课程,是根据游戏编程模式这本书写的代码
- 他的设计有些乱,我没有按照他的写,因此也走了不少坑
bug
导航组件
- Demo中的物体都是动态生成的,当物体身上挂载有导航组件的时候,生成之后如果距离导航组件太远就会禁用该组件,导致异常
- 两种解决办法,在生成物体的时候,传递生成点位置
- 在生成物体之后再添加导航组件(这种情况下,导航组件的参数需要事先配置好)
解析Json
- Json不支持float,因此我设置了两个属性,一个为double类型的,一个为float类型的。
public float Interval{set; get;}
public double IntervalD{ set{ Interval = (float)Value } }
- Unity无法解析Json文本。因为我没有把Json文本设置为utf-8编码,导致读取到的字符串为" "空,解析错误。我用notepad++转换编码格式就没问题了
初始化顺序问题
- 我有一个资源管理类AssetManager,他是一个分部类
AssetManager.cs
AssetManager.ResourcesLoad.cs
- 他有两个静态属性BaseFactory 实例与ResourcesLoad实例
- BaseFactory实例的初始化需要ResourcesLoad实例,所以需要控制他们两个的实例化顺序。
- 我原先是这样写的
private static BaseFactory mBaseFactory = new BaseFactory();
private static ResourcesLoad mLoad = new ResourcesLoad();
- 后来使用静态构造函数(不能有返回值,与类名相同,不能有参数,不能有访问修饰符,使用static关键字)
- 用来初始化类级别的项
- 在引用任何静态成员之前
- 创建任何实例之前
private static BaseFactory mBaseFactory ;
private static ResourcesLoad mLoad ;
static AssetManager( )
{
mBaseFactory = new BaseFactory();
mLoad = new ResourcesLoad();
}
构造函数初始化顺序问题
- 两个父类持有互相的引用,在构造函数时,传递互相的引用过去,但是子类需要使用的是子类的引用,因此需要向下转型
- 因为我在父类的构造函数里面使用了一个虚方法,因此在子类重写的虚方法里面可以向下转型
- 然后在子类里面用一个相对应的子类来保存字段
abstract Class ICharacter
{
protected BaseUnit mUnit;
public ICharacter(BaseUnit baseUnit){ mUnit = baseUnit; LoadModel(); }
protected abstract void LoadModel();
}
Class ISoldier:ICharacter
{
protected ISoldierData mData;
public ISoldier(BaseUnit baseUnit):base(baseUnit){ }
protected override void LoadModel()
{
mData = mUnit as ISoldierData;
//加载模型的代码
}
}
Anim与NavAgent无法暂停
- 每个Enemy和Soldier都持有一个状态机系统的引用 - mSys,状态机里面会播放动画,并设置持有者朝目标地点前进
public void Update( )
{
if(isGameOver || isGamePause){ return; }
mSys.CurrStateRun();
}
- 当点击暂停和游戏结束之后,物体依旧还在前进和播放动画
- 因为NavMeshAgent组件只要设置了目标点,就会一直运动。动画组件则是 Loop的缘故。除非禁用
- 我将mAgent.enabled = false,mAnim.enabled = false;
- 就完美暂停
无法阻碍射线
- 这个是层与层之间物理射线的问题,在这里我只想描述,不想深究
- 就是场景中的兵营模型身上有个检测点击的脚本
public void OnMouseDown(){ }
- 当游戏暂停后,UI将全部屏幕占满,点击兵营模型之后依旧会弹出兵营信息的显示
- 所以应该判断当前鼠标是否在UI上,如果在就不作出反应
EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
加载图片问题
- 我加载图片资源的时候用的下面方法
- 出现问题,我觉得这是Unity背后经过了什么处理,将图像处理成了sprite资源
object obj = Resources.Load(name);
Sprite mSpriteObj = obj as Sprite;
Sprite icon = Resources.Load<Sprite>(name);
从事件中删除方法
- 在Role的脚本中注册了GameOver事件,当该事件触发的时候将该物体暂停掉,禁用Anim与NavmeshAgent
- 当物体死掉的时候,会销毁模型对象与其身上的组件
- 当游戏结束的时候,调用这个脚本的方法,禁用其Anim就会导致错误,因此需要在角色死亡的时候,将其绑定的方法删除
GameEventManager.GameOverEvent -= SetGameOver
场景跳转问题
- 当游戏结束之后跳转到了主菜单场景,当点击开始之后,游戏再次开始
- 然后报出错误,在获得已经销毁的物体
- 我不知道为什么。还没有解决
触发问题
- 其实我也不知道为什么会出现这个问题
- 在init方法里面,FactoryManager.init()出错,导致从init开始到FactoryManager.init()的语句一直执行
- 所以就一直在报错。可是这些语句本身并不是放置在update方法里面的
- 而FactoryManager.Init()方法上面的语句并没有出现错误,但是多次执行就会出现问题,比如说,UIManager第一次执行完后会关掉界面,第二次执行查找不到就会报错
- 我查了半天,才发现这个异常是由下面的异常所引发的。如果不能一眼看出错误的话,还是老老实实单步调试吧
Tag
虚属性
- 在接口或者虚方法里面定义虚属性,然后在子类中进行重写
- new 与 override的区别
- new 是对父类的隐藏,override是重写。当用父类引用一个子类实例,调用父类的方法时候会得到不同的答案
- override 会调用子类自己重写的父类方法
- new 会调用继承关系上最近的一个被重写的方法
事件
- 对于定义的事件,只能在定义它的类内部调用,而委托就不用
子类可以用泛型
- 我定义了一个参数父类,然后继承于它,创建了一个带泛型的子类
- 根据里氏转换原则,用父类的接口引用子类的实例没有报错
- 只是在向下转型的时候,需要指明泛型
思考
属性
- 我将属性分为三种
- 共用的基本信息属性 比如名字,头像图片
- 每个实例都需要有的属性 血量
- 场景中的组件属性
- 共用的属性可以提取出来作为享元,需要的实例就保存一个该元数据的引用即可
- 属性与字段的使用思考
- 如果某个字段需要被外界访问,那就直接设置成属性
public T Attribute { get; private set ;}
- 不需要被访问,就设置字段
- 当属性的get与set方法内部需要填写方法体时,就添加一个与之相对应的字段设置为private
事件
- 对象与对象之间的交互是,我持有你的引用,然后调用你的方法
- 这一切都可以使用事件的形式完成,对象的某个方法订阅我的事件,需要调用的时候就激活相应的事件即可。事件是方法和方法之间的交互。
- 可一个对象持有另一个对象的引用,也是使用它的方法和属性。
- 因此如果两个对象关联不是很紧密的情况下,偶尔的情况下需要调用某个对象的一个方法,使用事件比较适合
- 但是关联紧密的话,就不适合 ,需要定义很多事件,很繁琐。
兵营和工厂
- 关于兵营和工厂
- 我一开始觉得每个兵营需要持有一个工厂实例化对象,因此兵营的构造函数需要一个工厂实例
- 后来我又思考,工厂只负责生产士兵,兵营负责给工厂下达命令生产士兵,可以解耦,而且可以使用享元模式
- 我们可以只创建三种士兵的三个工厂,创建很多个兵营,每当兵营需要创建士兵的时候就给相应的工厂发送消息即可
替换掉Switch case
高内聚与低耦合
- 不知道为什么我写到最后,越来越不明白这六个字了,高内聚和低耦合
- 高内聚是指模块应该完成自己的功能,不应该依赖其他模块,该模块内部的功能都是互相有联系的。这个需要划分功能
- 低耦合是指模块与模块之间的依赖程度低,通过接口就可以实现
- 只要贯彻了原则,基本就没问题。我这么认为
关于框架
- 我真的只想写写游戏逻辑就完事。可是事实不要求你这么做。
- 所以如果有一套很好用的脚手架工具,那我真是十分感谢了