写在前头
我学Unity的过程十分曲折,中间有多次中断,各种事情插进来让我成为了一个多边形平衡战士(可惜没有什么突出的)。连着搞了几个月其他事情,又学了一段时间图形学,接着去搞了一会技术美术,我意识到我的程序能力不能不管了,应该已经下降的不行了。
于是我打算重拾程序这项能力,脑海中突然蹦出这个一年前的项目。这个项目是和一个网上认识的团队做的,做到一半,我们就因为实力参差太大而解散了。说是和团队做的,其实这里的程序都是我写的,这已经是能追溯到的最近的我好好写的程序了,前两次都是gamejam,没有结构可言。上次好好写程序已经是一年前了,唉唉。
项目展示
地图无限延展
每一个生成的地图块上都有碰撞体,通过这个碰撞体检测玩家到了哪个地图块上
然后检测玩家与这个地图块中心点的距离,如果超过了某一阈值,就在玩家偏向的三个方向(比如玩家在左上放,那就是左边,左上,上边)生成地图,实现还需要检测那个位置是否已经存在地图块。
神秘的敌人代码框架
敌人死亡逻辑是在这个脚本实现的,但是是通过事件的方式在stat(属性)脚本里面调用的,因为属性里面记录了Health。
玩家受伤的逻辑也在stat中,通过调用stat.TakeDamage实现
经验球是直接被Instantiate的,生成后没有经过任何处理
上面那个神秘的SpeedMove是什么,之后讲。
经验球
自动飞到玩家的逻辑如下:
这里面又发现一个神秘的东西,IMoveMethod。
怪物生成
三种生成方法:
属性获取
获取对应属性的时候遍历挂载的脚本
这样做的好处是,我不需要在其他地方手动调用一次类似Equipments.Add方法。如果画一个系统图的话,那就是这样:
而如果不这么做的话,这里面的所有箭头就需要反过来了,我其实更希望底层代码只需要关注自己的事情,而不是记住这么多条条框框,这也是一种解耦。
试想一下如果在游戏中拾取了某件装备,那么我不仅需要AddComponent,还需要运行类似Component.AddThisToAllStat(),卸载的时候还需要再次调用Component.UnloadStatFromAllStat()
相当于少了一个约束吧,要不然的话就是写一个基类然后对所有装备都写一个固定操作。当然还有很多算法上的优化空间,这只是一个小Demo。
多次追踪的刺
这个功能涉及到以下难点:平滑移动、多次追踪、刺的转向、攻击完成后平滑地回到玩家等,最主要的是这些状态的融合、先后顺序。
效果就是视频中的蓝色的刺呈现的那样
先调用public static GameObject Find(GameObject thisObject, ObjectType objectType)来取得最近的怪物,然后击中一个怪物就把它放到链表中,然后调用这个:FindExcept(GameObject thisObject, List<GameObject> exceptObject, ObjectType objectType)。达到击中次数之后就返回玩家身边,这里又使用到了moveMethod。
后续如果要优化的话,我会提前给每个刺附上碰撞体,Find方法就只遍历碰撞体内的怪物。
各种移动方法
接口IMoveMethod:
PID平滑旋转方法:
实现了IMoveMethod的类:
SpeedMove:(炮弹)以恒定速度移动
PID:以PID运动曲线移动
Flash:闪现到目标地点
CustomCurve:使用Unity内置的曲线进行差值
存在于另一个游戏中的无缝转场
可以看到在转场过程中,能够看到第二个Scene中的运动,并不是普通地遮罩转场。写完之后才发现,为了实现这个功能,需要增加许多耦合,这完全没必要,干脆直接拿下一个场景的截图来实现就可以了。
存在于另一个游戏中的对话系统,使用ink
展示出来证明我有研究过对话系统qwq
程序分析
属性
现状
属性是单独用了一个脚本。一开始用的枚举,但是发现不能在编辑器里面做到一个枚举对应一个数值那样修改,Dictionary不能被序列化。所以就先用List存下来,然后在Awake里面把List的内容赋值给Dictionary。用哈希表来存储,这样查询的时候时间复杂度就是O1了。
修改
也许可以用ScriptableObject来存数值,然后在原来的Excel读取工具的基础上修改,实现直接在Excel中调整数值。
UI
现状
先前看的一个野生教程做的游戏框架,做到后面发现不太好用。有一个专门的UIManager,然后通过调用UIManager.ShowUI(string name, Transform parent = null)来展示UI。具体是通过name找到Prefab,然后通过反射找到UI对应的UIController脚本,并AddComponent给UI对象,然后返回UIController。
修改
之后有空去学一下MVC然后再来修改。
游戏状态
现状
没用上,只有单单的一个变量存着状态。
修改
先写一个与状态强相关的基类,里面序列化一段字段来设置这里面的某个函数的行为只能在哪几状态下执行,然后所有的游戏逻辑继承这个基类?
武器类型架构
现状
把跟随性武器想的太复杂,拆成了一个Manager,用来规定生成数量和生成方法。
生成方法就是Target Spawner来管理,这么叫是因为他会生成一些空物体在对应位置,然后把跟随性武器的跟随点设置为这些空物体。之所以用空物体而不是简单的数值是因为,空物体是带有transform的,它是一个引用值,这样的话执行moveMethod.Attach函数执行一次即可,之后物体会一直跟着这个transform移动。
这里的Centor Point不一定是玩家,所以单独列出来了,但是我希望有一个设置可以调整其种类,如果选择跟随玩家,那就隐藏这个字段,那应该要涉及到Editor脚本了。
也就是说,生成一个这样的跟随性武器,需要牵扯到三个脚本以及一个Prefab:SummonManager.cs、Spawner.cs、对应召唤物的cs、以及召唤物的Prefab。
修改
优化这方面逻辑,尽量使用一个脚本。可以通过ScriptableObject来进行配置,然后由程序自动完成。