设计模式Demo制作bug以及反思集锦

前言
  • 前一段时间看了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
  • 替换掉switch case
    • 士兵和敌人都有三种,但是他们都没有独特的字段需要定义,属性类型完全相同,只是数据不同而已
    • 完全可以定义为两个类ISoldier与IEnemy 即可
    • 可是在实际开发中又需要区分三种对象,两种解决
    • 第一是在ISoldier里面再定义一个字段,SoldierType,可是这样会导致很多的switch case 语句,判断类型,然后做出行为。
    • 第二是继承自ISoldier再衍生出三种士兵对象,Sergeant,Rookie,Captain
    • 然后将原本的case语句,放置在了各个继承类里面书写
    • 第二种方法可扩展,如果新增加了一种兵种,直接继承自ISoldier,然后编写自己的语句即可。如果是case,则需要修改很多处地方,而且很容易改错
    • 下面的两篇博客也是同样的思想
      https://blog.csdn.net/taotao1414924381/article/details/84557937
      https://www.cnblogs.com/dzh1990/p/8276053.html
高内聚与低耦合
  • 不知道为什么我写到最后,越来越不明白这六个字了,高内聚和低耦合
  • 高内聚是指模块应该完成自己的功能,不应该依赖其他模块,该模块内部的功能都是互相有联系的。这个需要划分功能
  • 低耦合是指模块与模块之间的依赖程度低,通过接口就可以实现
  • 只要贯彻了原则,基本就没问题。我这么认为
关于框架
  • 我真的只想写写游戏逻辑就完事。可是事实不要求你这么做。
  • 所以如果有一套很好用的脚手架工具,那我真是十分感谢了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值