关卡
本文将讲解如何构建关卡。先从“直线型道路”关卡开始。
Straight Paths Level
层级结构列表中的游戏对象:
Straight Paths Level中游戏对象的层级结构
所有的路径都是通过预制件(见上图蓝色的WideStraightPath)来复制的,直线型道路的路径要比旋转型道路更宽。场景中还有一些立方体状的3D对象(上图中的Cubes),作为路径的地板。另外还有一些简单的3D坐标,NewPathSpawn以及SpawnPoints。SpawnPoints用于指定生成新物体的位置,例如糖果、障碍物等。
为了节省内存资源,游戏不会在一开始就生成所有路径。实际上,游戏需要生成N+1条路径,N是当前Max所在的路径位置。可以利用一个简单的碰撞器BoxCollider,当Max与它发生碰撞, 则通过PathSpawnCollider脚本生成新路径。在直线型道路的关卡中,新生成的路径位于“NewPathSpawn”坐标,这个坐标就是当前路径的终点。
当玩家与PathSpawnCollider发生碰撞时,在NewPathSpawn位置生成新路径
NewPathSpawn的位置就是新路径的生成点
Character游戏对象下包含相机和Max模型,此外还有Character Controller 组件和CharacterSidewaysMovement脚本。通过该组件和脚本的控制,相机会始终保持在Max上方的位置,这是第三人称视角游戏中的常见角色控制方式。
Canvas包含两个文本对象。Directional Light就是很简单的平行光光照,Holder对象包含了本教程上半部分介绍的GameManager单例脚本。
PathSpawnCollider类——路径生成器
此脚本适用于两种关卡,主要功能是根据Max当前奔跑位置来生成下一段路径。
脚本首先设置了几个公共变量,可以在Unity编辑器中设置。
1、PositionY:用于将RedBorder放置到正确的Y轴位置。
2、数组PathSpawnPoints:用于保存下一段路径及边界的生成坐标。在“直线型道路”关卡中,数组内只有一个元素。但“旋转型道路“关卡中,该数组需要3个元素,其中一个是新路径的位置,另外两个是RedBorder障碍物(Max一旦碰撞到它,则游戏失败)。
3、Path:物体包含路径预制件。
4、数组DangerousBorder:包含了用于“旋转型道路”关卡的RedBorder预制件,在“直线型道路”关卡中为null。
当Max与PathSpawnCollider对象碰撞时,游戏会随机决定下一段路径是往左、往右还是笔直前行。在“直线型道路”关卡中,PathSpawnPoints数组只需一个路径对象(对应直行位置),将randomSpawnPoint参数设置成0,则会笔直生成下一段路径。在“旋转型道路”关卡中,在选定位置生成下一段路径,并在另外2个位置生成RedBorder障碍物,将其旋转90度以正确摆放。
Stuff Spawner类——道具生成器
Stuff Spawner脚本用于生成糖果、障碍物等游戏道具,两个关卡均有用到。首先声明一些可以在Unity编辑器中设置的公共变量。
1、StuffSpawnPoints:包含所有要被实例化物体的位置(糖果或者障碍物)。
2、数组Bonus:包含所有的糖果预制件。
3、数组Obstacles:中包含所有的障碍物预制件。
4、RandomX:该变量只有在“直线型道路”关卡中才会是true。它会随机在X轴的位置([minx, maxX]之间)生成各类糖果和障碍物,以显得每段路径都不一样。
CreateObject方法用于在选定位置实例化一个新的预制件,并且判定是否要沿X轴方向移动一段距离(仅“直线型道路”关卡)。
Start方法
决定是否在当前路径创建障碍物(50%概率)。如果判定通过,它将随机选取一个障碍物预制件并创建。
StuffSpawnPoints数组中的其它位置(即非obstacleIndex的位置),会决定是否创建糖果(33%的概率)。如果判定通过,它会随机在Bonus数组中选取一个预制件并创建。
CharacterSidewaysMovement类——角色横向移动控制类
该脚本仅用于在“直线型道路”关卡中移动Max。Max带有CharacterController组件。如果想了解如何对带有CharacterController组件的游戏对象进行移动控制,请查阅Unity文档。
声明一些变量,通过其名称就能很容易了解它们的用途。
在Start中设置moveDirection向量,用于帮助Max以给定的速度直线前进。然后为组件的相关变量设置正确的值,并更改游戏状态以开始游戏。
因为Max是可以进行跳跃操作的,所以很有可能造成人物跳跃出路径之外。CheckHeight方法检测Max是否掉落到平台的高度之下,如果是,则游戏失败。
DetectJumpOrSwipeLeftRight方法首先检测玩家是否转向或跳跃(即是否按左、右或者上方向键)。如果游戏已经检测到玩家按上方向键,则再检测当前Max是否正在落地或切换路径(这里禁止2次跳跃)。在通过了这些检测之后,再设置moveDirection向量的y值,并播放Max的跳跃动画。
然后,检测玩家是否左右滑动。如果Max当前不是跳跃状态,也未移动至道路边缘,则检测到左或右方向键输入后,让Max向左或右移动。接着,通过为moveDirection向量的x值加或减SideWaysSpeed变量值,来实现以上操作。在路径切换完成的同时,保存Max的最后位置。
接下来看看Update。这里通过switch语句处理3种不同的游戏状态。
在Start状态中,游戏会检测玩家是否点击屏幕。如果检测到点击后,将AnimationStarted的值设为true,将Max的状态切换为run,此时游戏状态也会切换为Playing。
在Playing状态中,首先检查Max的Y方向位置。然后根据玩家的具体操作,控制Max左右移动或跳跃。如果Max正在切换路径,则判断Max当前的X轴位置,并对比切换路径的目标位置。如果两者之间相差不大,则表示Max成功完成了移动,系统可以安全地将isChangingLane参数设置成false并防止Max移动过头。最后,使用CharacterController的Move方法将Max移动到下一段路径。
Update方法的最后一部分在Max死亡时调用,即Dead状态。当Max死亡,系统会将AnimationStarted参数设置成false,并提示玩家点击屏幕重新开始游戏。
OnControllerHit仅用于“直线型道路”关卡。路径的左右边界设置标签为WidePathBorder,当Max与其产生碰撞,系统会判定转向结束,因为已经碰到边缘了。如果这里不做判断,Max会继续移动直至穿墙,这就会导致严重Bug。
Rotated path关卡
“旋转型道路”关卡与“直线型道路”关卡有些相似,但路径的预制件不一样。对比“直线型道路” 关卡,“旋转型道路”关卡中包含了更多的NewPathSpawns,以及一个SwipeCollider。
Rotated path关卡的层级结构
PathSpawnCollider用作生成下一段路径和RedBorder预制件的触发器。SwipeCollider是唯一允许玩家进行滑动操作的地方。SpawnPoints就是糖果及障碍物的生成点。
当Max进入PathSpawnCollider时,从数组NewPathSpawn的3个位置中随机选择一个位置,实例化一段新的路径。
NewPathSpawnPoint左边
NewPathSpawnPoint右边
NewPathSpawnPoint前方
在这三个位置中随机选择一个作为下一段路径的起点,并在其它两个位置生成RedBorder障碍物。
Stuff Spawner 与Path Spawn Collider
这些脚本都仅用于“直线型道路”关卡,在此不多做描述。
Swipe Collider类——滑动碰撞体
在“旋转型道路”关卡中,当玩家位于路径中途时不允许滑动,只有到达某个特定空间才可以通过滑动让Max转向。可以在场景中放置一个隐形碰撞体Swipe Collider,当Max与Swipe Collider对象进行碰撞时,玩家才能进行滑动动作。代码如下:
SwipeCollider脚本只用于设置GameManager对象的公共变量,该变量控制Max是否可以左右滑动。当Max进入到碰撞体时执行OnTriggerEnter方法,当Max离开碰撞体时,执行OnTriggerExit方法。
CharacterRotateMovement类——角色转向移动控制类
该脚本与之前“直线型道路”关卡中的CharacterSidewaysMovement脚本非常相似,主要区别在于DetectJumpOrSwipeLeftRight方法,游戏通过Quaternion.AngleAxis来实现Max的转向。
从阅读代码片段可以发现,这里之所以没有使用OOP的继承来创建所有的移动类,是因为这里涉及的移动比较简单。如果您游戏的移动机制比较复杂,建议使用继承会更加便于代码管理。