moba寻路_GitHub - aafasou/MoBaDemo: 用Unity做的一个类Moba游戏Demo

本文介绍了在Unity中开发MOBA游戏时的路径点寻路机制,用于控制小兵沿固定路线行走。文章详细讲解了小兵的出兵逻辑、对象池管理以及伪路径点寻路算法。此外,还讨论了游戏资源管理、输入输出系统、技能系统设计、AI行为树以及战争迷雾和UI设计等方面,提供了游戏数据的JSON组织和外部数据读取方式,强调了代码解耦和UI组件的自适应方法。
摘要由CSDN通过智能技术生成

游戏整体逻辑

出兵逻辑

游戏会在两个阵营的固定区域在固定的频率中出兵。小兵只能沿着规定好的轨道进行行走,并按照各个防守塔的顺序向敌方进行进攻。

需要注意的点

对于固定出现的小兵来说,不适合在其死亡之后删除GameObject对象,而应该使用对象池来管理他们,当小兵死亡时,只是将该单位的GameObject的active设为false,当要进行出兵的时候,从对象池中抽取active为false的单位出现在固定区域。

规定路径的寻路

对于小兵来说,他们会固定的走上中下三条路线,不会穿越其中的任何一条,而Unity本身的Navigation导航系统不能对于一个地形进行分区,所以使用area对各个单位行走轨迹进行分区的方法就失败了。

在此,决定用**(伪)路径点寻路**的方法来控制小兵的行走轨迹,下面简单描述一下:

因为小兵身上的行为树需要用到导航系统来对敌对目标进行导航,所以小兵的移动不能完全使用路径点寻路,所以这里加了个“伪”字

对于上中下三条路,他们有一系列这条路上的路径点,当小兵没有遭遇战斗时,会沿着这些路径点进行行走,当小兵遭遇战斗,并且,战斗结束后,小兵会找到离他最近的一个路径点,然后走过去,然后又是沿着 路径点一路走。

关键在于,如何判断小兵的战斗结束了,这里采用判断周围敌人的方法,当周围敌人数量为0,那么就是战斗结束了

走到终点也是一种情况,当走到了路径点的终点,小兵将会停在终点处(一般将终点设置为离敌方基地很近,这样小兵停在终点就会自动向敌方基地进攻)

如何实现路径点寻路

基于一个WayPointManager的MonoBehavior类来管理上中下三路的路径点(使用MonoBehavior的原因是可以在编辑器编辑路径点)

使用一个WayPointUnit来管理 单位上中下路的寻路 、 下一个路径点的寻找 、 寻找自己最近的路径点 功能。

理论上来说,只有要不断的攻打敌方基地的小兵才是需要路径点寻路的,其他野怪或者英雄依然使用unity自带的Navigation导航寻路。所以WayPointsUnit这个类是小兵独有的。但是,如果就此就为小兵设置一个单独的具体类,就未免太过麻烦,这里依旧把WayPointsUnit属性赋予CharacterMono类,只不过只有小兵才对其进行赋值,而其他单位这个属性值均为Null。

使用路径点寻路要改写单位的行为树。之前我的小兵行为树策略是指定敌人进行攻击的行为树,但是因为指定敌人的话,行走路线不能固定,所以要将指定攻击三塔、基地的行为树改造成 沿着路径点 指定行走的行为树,其基本逻辑用以下伪代码实现。

1 if(周围有敌人){

2 攻击、追击周围敌人

3 if(如果当前位置离最近的路径点距离大于X){

4 返回路径点,不管敌人

5 }

6 }else{

7 沿着下一个路径点行走

8 }

这个AI之所以可以顺利的进攻敌人的基地和三路塔,靠的是1-5行的第一个if,路径点规定一定会走到敌方塔或基地前,固小兵可以顺利进攻这几个位置.

5. 最近路径点算法: 最近路径点的寻找是整个AI性能的关键之处,在这里,我将这个算法定义为如下情况:

从当前下标往下找,一直找到一个离自己最近的位置,那么"最近路径点"等于离自己最近的位置的路径点的下标+1,这里主要是为了小兵不会走回头路(那会看起来很怪).下面用伪代码来描述:

for(int i=nowIndex+1;i

// 找到离自己最近的路径点的下标,赋值为 nearestPointIndex

}

// 将当前路径点(即下一步要走的位置)设置为最近的点的下一个坐标

nowPoint = nearestPointIndex+1

资源管理

概述

首先要明确在MOBA游戏中,有什么数据/资源是需要我们从外部进行动态读取和更新的.

初步观察,应该是如下数据:

每个单位的属性(hp/mp/攻击力等)

每个单位的模型(各类.obj/.fbx文件)

每个单位的各类贴图(漫反射贴图等)

特效对象,比如好几个GameObject组合起来的特效(如explosion特效)

技能/装备/状态属性(名称,各类属性的设置)

UI贴图纹理

各类公式(单位攻击伤害公式,技能伤害公式等)

各类动画、动作文件

音效文件

其中1/5/7可以归类为游戏中的各类数据对象,在外部存储时可以以文本格式存储(即json/xml/excel等格式),这些数据是需要进行动态更新与读取的.(方便对游戏中的各类对象的属性进行实时编辑与修改,方便版本更新)

其中2/3/4/6/8/9为资源文件,可以将它们打包为Unity可以读取的形式(assetbundle或其他打包方法).

综上所述,本游戏主要有两类资源,他们分别是文本类型的资源(json/xml/excel等格式)和assetbunlde类型(或其他)的资源,其中特效对象(prefab)/贴图(texture)/模型(mesh)通过assetbundle打包到外部,方便策划添加/编辑/修改.

最后再通过Unity加载这两类资源到游戏中,通过输入框架,将这些数据/资源文件变为游戏中的各个对象.

输入系统架构与描述

在本游戏中,我模仿了一部分魔兽争霸单位编辑器的方法来对单位、物品、技能对象进行编辑。

也就是,所有单位的数据都是先在编辑器中编辑好的,当编辑好后,这个数据就以(JSON、CSV、Sqlite、xml)的形式存储到了本地文件中。

在游戏代码中,通过读取这些本地文件,得到了一个个已经编辑好的单位、技能、物品的数据,通过这些数据来生成对象。

在外部如何组织数据

我使用JSON来进行外部组织数据,技能、单位、物品,都依据JSON的方式进行存储。

如何将外部数据输入进Unity

采用了类似RPG Maker的笨办法

在游戏开始时对游戏数据进行初始化,即在游戏开始时,读取数据库,对所有游戏数据生成一个模板对象,用一个数组或字典存储。然后在游戏中对这些模板对象进行深拷贝到一个个实例游戏对象中去.

主要步骤有以下几步:

读取JSON文件

根据JSON文件生成模板游戏对象

生成的模板游戏对象加入一个列表,以列表中的下标作为模板对象的编号,在游戏中需要引用模板对象时,使用该模板对象的下标进行引用。

如何初始化

如何初始化也是一个问题,这里我采用一个单例的MonoBehavior类来对外部数据进行初始化.

对于一个游戏来说,进入游戏后,有一个开始菜单,只有当点击开始游戏后,游戏才会开始.

在游戏开始的地方设置一个进度条(类似于魔兽、Dota那样),在进度条时对外部数据进行读取并初始化.

最终将外部数据(JSON数据)读取到一个字典中去,每个游戏对象需要使用到外部数据时,就读取该字典.

举个例子:

在红方阵营出现的小兵为在单位编辑器中编辑的003号单位.

这时,已经有了一个读取了所有单位信息的字典(或列表)

UnitDataList , 只需将红方阵营出现的小兵的CharacterModel赋值为 UnitDataList[003].DeepCopy()就好了.

DeepCopy是深拷贝的意思,不能直接赋值,因为在列表(或字典)中的是模板对象,直接赋值(也就是引用啦),会导致模板对象的值被外部修改(这是不允许的).

JSON数据组织

输出系统架构与描述

由于游戏中的大部分数据用JSON进行组织,而直接写JSON比较繁琐,所以在Unity扩展编辑器中编写一个用于输出编辑好的JSON数据的扩展编辑器。

对于这个扩展编辑器的编写,有以下几点要求:

使用反射获得要设计的对象的所有基础字段(int、string、bool、float、battleState),并通过编辑器对其进行填充

技能

emmmmmmmmmmmmm,如何设计技能表绝对是一个难题了,各个技能抽象的程度不一样,对外开放的接口也不一样.

在这里,我决定采用JSON的方式来存技能数据.

在JSON数据中,有一行数据,SkillType,技能类型,用于表示这个技能是什么类型的技能.在技能编辑器中(使用Unity Editor进行制作)根据这个SkillType来进行反射,生成对应的技能类,并在编辑器中生成这些技能类的可编辑字段.

而对于存储的JSON数据,则使用该技能开放的接口对应的属性进行存储.

简单的举个例子,目前我有两个技能,分别是召唤技能a和指定目标伤害技能b. 存储到JSON中格式就是这样的.

{

"SkillList":[

{

// 此处是零号技能,伤害技能

"Damge": 100,

"selfEffect":"001"// 这里存储的是对应prefab在AssetBundle下的名称

"targetEffect":"002",

"skillName": "伤害技能",

"iconPath": "001",// 这里存储的是对应texture在AssetBundle下的名称

"description":"这是一个伤害技能",// 存储描述

"mp" : 100,// 消耗mp

"skillType": 0// 整数(对应枚举),描述这个技能的类型,用于反射

},

{

// 一号技能,召唤技能

"Summoner":"001",// 这里存储的是对应prefab在AssetBundle下的名称

"skillName": "召唤技能",

"iconPath": "002",// 这里存储的是对应texture在AssetBundle下的名称

"description":"这是一个召唤技能",// 存储描述

"mp" : 100// 消耗mp

"skillType": "SummerSkill"

}

],

"SkillCount":2

}

所有JSON数据的数据格式

为了和游戏的输入框架一一对应,在这里要严格规定JSON数据的形式,下面列出一个合格JSON数据的几项标准:

对象的第一项属性必定是XXType,XXType属性表示该对象对应的类。以便后续使用反射为该对象注入属性

JSON中所有有关GameObject对象的属性均为字符串,这些字符串在对应类中的属性名为xxxPath,表示这些GameObject对象的地址,当xxxxPath属性被赋值的时候,自动对AsstesBundle读取资源。

JSON中的所有属性严格对应目标类(Type属性)的属性名,以便反射。

当JSON对象中存在数组时(包围整个JSON对象的数组不算),数组元素必须为int,这个int表示某个对象的编号在数据库中的ID。

当某个对象内嵌另一个对象的时候,那另一个对象的标准同1、2、3、4。

一个正确的JSON数据如下:

{

"Type":"",// 技能类型

"SkillName":"伤害技能",// 技能名

"IconPath":"default-itemIconImage",// 技能图标地址

"SkillLevel":1,// 技能等级

"Mp":100,// 魔力值

"BaseDamage":100,// 基础伤害

"KeyCode":113,// 按键

"SpellDistance":3.3,// 技能施法范围

"CD":5.5,// 技能CD时间

"SelfEffect":"001",// 施加在自身的特效的地址

"additionalState" : { // 此技能附加的状态,是一个JSON对象,包含了这个状态的类型,状态名,持续时间等等

Type: "", // 状态类型

name : "", // 状态名

description : "",// 状态描述

iconPath : "", // 状态图标地址

isStackable = false,//状态是否可叠加

statePassiveSkills = [1,3], // 状态被动技能列表,保存一系列的编号,这些编号用于指代技能编辑器中某个技能的ID

stateHolderEffect = "" // 当某个单位保持这个状态时,此单位会产生的特效

}

}

一个用于输入的XX表(技能表、人物表等)JSON数据的格式如下:

[

{

对象1

},

{

对象2

},

{

对象3

},

]

其中的对象同上面所说的规定的格式。

GamePlay数据类

简单说明一下,在GamePlay数据类中,有许多数据都是要由外部输入的,到时会有一个全局的 “字典/列表/队列” 等来管理所有数据。

对于一个由外部输入的数据,在定义属性时,使用注释 "<==" 来说明,对于在游戏中依据某些数据动态生成的数据,则不使用此注释.

GamePlay数据类中的Model类和Mono类

在我设计的GamePlay游戏逻辑类中,有Model类和Mono类这两种,其中Model类是相当于Java里面的JavaBean那种感觉,主要就是用来存储从外部导入进来的游戏数据,比如技能、物品、人物数据等。而Mono类对应的是Unity里面的MonoBehaviour类,主要用来管理游戏进程的。

举个例子,对于投射物,有projectileModel和projectileMono类,其中Model类是用来存储从外部导入的投射物数据,比如在外部数据库编辑好的某个投射物,他的射速、射线弧度、投射物到达目标地点后产生的屏幕特效、投射物与敌人碰撞后产生的屏幕特效等。

而对于Mono类,则用来管理一个具体的游戏里面的投射物的生命周期,在Mono类中,管理投射物造成的伤害计算、向目标飞过去的逻辑等等。

每个Mono类中都有一个对应的Model类充当他的属性。

为什么要引入看起来变扭的Model类和Mono类

因为平常都是从游戏外部通过 sqlite、csv、json 来导入数据进Unity的,然后导入的数据需要用一个新建的对象来保存它,但是MonoBehavior对象是不能随意创建的,但是游戏逻辑的部分又关乎MonoBehavior类,就是挂载到游戏对象上的类必须继承自MonoBehavior,所以只好在中间新增一个Model类来充当外部数据与MonoBehavior类的中介者。

Model类和Mono类应该遵循怎样的设计原则

一般来说,Model类只需要存储从外部导入的游戏数据就好了,相当于一个中间的存储容器.

但是,我觉得稍微漂亮一点的写法是把一些关乎游戏数据计算的方法也写在Model类中,比如:伤害计算公式等等.

关于一些非数值计算的游戏逻辑,写在Mono类中,比如音效、动画的播放.

技能设置描述

所有技能都在外处使用编辑器进行编辑,以(.json\ .csv\ .xml\ .sqllit)的方式进行保存。

所有技能都会有特效动画的产生,特效动画还可分为立即释放型特效,投射型特效等,特效动画由外部进行指定,类型为GameObject,暂定以JSON的形式保存一个技能,其动画特效(释放时敌方产生的特效及我方产生的特效)是一个字符串类型,该字符串指定了在Resource/Prefabs/文件夹下的粒子特效预制体的名称。

技能

基类 BaseSkill

属性

技能名称 &#

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值