[专栏作家]使用Unity制作3D无尽跑酷游戏(下)

  关卡

  本文将讲解如何构建关卡。先从“直线型道路”关卡开始。

  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的继承来创建所有的移动类,是因为这里涉及的移动比较简单。如果您游戏的移动机制比较复杂,建议使用继承会更加便于代码管理。

声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
推荐阅读