平台游戏:主要的游戏方式是在2D平面上使用各种方式在悬浮平台上进行移动和穿过各种障碍。
现如的平台游戏中的平台特性与种类是很多的,像马里奥(FC)的单向支撑不可穿透的,或魂斗罗(FC)单向支撑可穿透的…
\ | 超级玛丽 | 魂斗罗 | 空洞骑士 | 蔚蓝 | 本博文 |
---|---|---|---|---|---|
支撑 | 上 | 上 | 上 | 左、上、右 | 上 |
穿透 | 无 | 上、下 | 无 | 上(部分) | 上下 |
所以本文主要以向上支撑并上下穿透的平台特性来进行实现
需求分析
- 在关卡设计后,地形的数据需要自动识别
- 地形中的平台可上下穿透移动,并可向上站立
- 平台的移动路径可有,直线、曲线、虚线(跨越)
- 移动方式有,行走、跳跃
- 关卡场景可存在多个(动态加载)
预想过程
1.分级处理寻路过程
2.将平台以固定距离细分为点集,并定义其包裹边框
3.将多个平台进行同一管理,并合并为一个大的包裹边框,为一个关卡内容
4.单个关卡在寻路时,使用总的点集数据计算
AStar在获取周围点位时,可先利用平台边框探路,来提高效率
5.将输入位置载入关卡场景
以平台细分距离为半径向下获取最近的地图点位,作为输入的数据
下图A,B的圆圈,外圈为水平跳跃高度,内圈为垂直跳跃高度
|
|
6.利用转入的数据开始A*寻路
7.关卡在载入新地图时,整体寻路数据不变,只将新关卡的地图数据加入总的关卡队列
8.分级处理寻路块
在寻路时从关卡边框开始过滤路径,再从关卡边框过滤到平台边框,之后只在平台边框中的点位寻路
实现过程
1. 平台(预制体组件)中的移动位置标准化
分割为固定单位距离(space)的小点(脚本生成即可)
这里采用的是由Cinemachine的CinemachineSmoothPath来定义平台的形状,之后在根据space进行细分为小位点集
int count = Mathf.FloorToInt(path.PathLength / space);
Vector2[] vects = new Vector2[count];
for (int i = 0; i < count; i++)
{
vects[i] = path.EvaluatePositionAtUnit((float)i / (count - 1), CinemachinePathBase.PositionUnits.Normalized);
}
2. 将每个平台称作 [SubNavMesh] 子导航网格
用于记录单个平台的移动点集与平台存放区域(用边框定义,方便快速的过滤存在其周围的点位)
public class SubNavMesh
{
public Bounds bounds; //点位的边框
public int[] edges; //队列索引,指向位置队列的索引位置 - -点位ID
}
edges记录自身所有点位索引(数据源存放在NavMesh);只作为数据存放结构,判断一个点位是否存在与其中可先利用Bounds过滤。
具体表象如下
3. NavMesh 进行平台寻路导航
将SubNavMesh由NavMesh进行统一管理,一个NavMesh管理一个关卡中的所有平台的导航。并负责所有SubNavMesh的生成、销毁、读取。并根据其全部数据进行路径导航。
其导航过程为:
整体导航框架采用AStar寻路
点集寻路的过程中,在不同的分段中跨越时需要考虑跳跃限制
具体表象如下
因为代码太多所以只从类方法理解即可,源码在结尾自行查看
代码功能:
NavMesh的寻路方法:
将点位以 Point 结构体进行包装,以点位为基础寻路
NavMesh 的控制类,NavChunkPilot
4. NavMeshGroup 多关卡寻路
动态存储所有的NavMesh,利用NavMesh中的SubNavMesh.Bounds寻路再将所有的寻路的Bounds的SubNavMesh组合成新的NavMesh,再开始点位寻路
代码功能:
NavMeshGroup的寻路方法:
FindCellPath() 将平台Bounds 以 Cell 结构体进行包装,以Bounds为基础寻路;
FindPath() 将Bounds路径组合为NavMesh,进行寻路
这里不像NavMesh那样可以用Int 型直接记录点位信息,在多关卡时,起始点的信息因为存在与多个关卡之中所以格外将关卡名字进行标记
NavMeshGroup的控制类,NavPilot
5. 数据处理
将数据进行统一存放,数据调度上使用数据索引,加速数据处理速度。
框架设计
效果演示
单关卡寻路在 5毫秒内,三个关卡在20毫秒内