GameRes游资网

 找回密码
 立即注册
返回列表
查看: 450|回复: 0

用Unity重现《空洞骑士》的苦痛之路(3)——地图篇

[复制链接]
发表于 2019-4-9 10:37:15 | 显示全部楼层 |阅读模式
游戏程序
平台类型:  
程序设计: 算法逻辑/智能AI 
编程语言:  
引擎/SDK: Unity3D 
v2-9be6e44ea578b9ccab483ea09aa19829_1200x500.jpg

前言:

大家好。虫虫大冒险(?)新的一期又双叒叕来了。

本期文章主要讲解《空洞骑士中》的地图搭建相关的内容,并会写一个简单的相机脚本。除此之外,还会涉及到如何制作背景、产生异于玩家移动的动态效果。好了,闲话不多说,直接开始。

地图模板

工欲善其事,必先利其器。在开始地图搭建之前,我们先制作好游戏需要的地图模板,这样在后续的地图搭建环节中可省去大量的时间与精力。

如何搭建地图模板就不多说了,通过图片资源对比实际游戏画面,然后再组合即可。大约有以下几种主要的模板:

image001.jpg
多个小图拼接的地图模板

值得注意的是,地面与墙壁的柱子形状的物体的纹理重复使用,是需要在精灵图片属性中编辑九宫格,并在SpriteRender-DrawMode中设置为Tiled,并调整参数才能够有正确的纹理重复的效果。如下:

image003.jpg
设置图片九宫格相关属性

这块技巧在使用Unity制作UI时会频繁用到,无论什么游戏。没接触过的童鞋可以留心一下。

由于机关陷阱物体是可以移动的,因而我们还需要写一个简单的机关移动脚本,来控制陷阱的移动。主要逻辑是:先获取陷阱能够到达的最上方点与最下方点,然后让陷阱在其中来回移动。其实现代码如下:

  1. public enum TrapMoveDir
  2. {
  3.     None,
  4.     Down,
  5.     Up,
  6. }
  7. public float moveSpeed;//移动速度
  8. public bool isMove=true;//是否进行移动
  9. public Transform startTrans;//开始的点
  10. public Transform endTrans;//结束的点
  11. public Transform trapObj;//陷阱物体
  12. TrapMoveDir moveDir;//移动方向枚举
  13. public void TrapMove()
  14. {
  15.     if (moveDir == TrapMoveDir.Down)
  16.     {
  17.         trapObj.transform.position += transform.up* moveSpeed * Time.deltaTime*-1;//朝下移动
  18.         float distance = Vector3.Distance(trapObj.transform.position, startTrans.position);
  19.         if (distance<0.5f)//如果现在距离最下面的点小于0.5f,重新设置移动方向
  20.         {
  21.             moveDir = TrapMoveDir.Up;
  22.         }
  23.     }
  24.     else if (moveDir == TrapMoveDir.Up)
  25.     {
  26.         trapObj.transform.position += transform.up * moveSpeed * Time.deltaTime;
  27.         float distance = Vector3.Distance(trapObj.transform.position, endTrans.position);
  28.         if (distance < 0.5f)
  29.         {
  30.             moveDir = TrapMoveDir.Down;
  31.         }
  32.     }
  33. }
复制代码

地图分层

为了实现原版游戏中景深的效果,我们需要正确的利用SpriteRender中提供的层级功能(Sorting Layer或者使用Order in Layer),以制作出想要的效果。其中主要的层级分布大致如下:(PS:原版游戏中的层级远远超出下面列举的这几种,这里只是简化版)

image005.jpg
大致层级图

优先级从上到下排列,最上面的的优先级最高。其中第一层半透明白色遮罩的主要效果,是用于模糊背景物体,产生人物与场景深度不一致的效果。对比如下:

image006.jpg
遮罩效果

第二层的白色半透明遮罩主要用于模糊黑色背景,让黑色背景跟后面的天空配色不产生冲突。

地图搭建

在开始搭建地图之前,推荐自己先画一个地图草图,然后按照草图进行搭建。搭建的时候可能不会跟草图一摸一样,但总是比毫无目标的乱搭一气好得多。如果你是想做到完全跟原版一样,那么这一步你就可以跳过了。类似如下:

image007.jpg

接下来就是按照草图,选择合适的地图模板和对应的图片搭建地图了。其中为了实现荆棘地形散乱生长的感觉,没有制作对应地形的模板,而是根据周围环境手动调整荆棘图片的旋转缩放参数来实现相应的效果。

image009.jpg

而地图中窗户的效果就需要花点心思来实现了。首先需要设置好需要通过窗户才能够看见的物体的层级,然后还需要添加比窗口物体层级小的黑色遮罩,来遮挡住其余不需要被查看的部分。如下:

image010.jpg

地图交互

关于玩家与地图之间的交互,此处就利用碰撞体的Tag属性,来进行不同的交互效果的触发。如下图:

image012.jpg
标签对应图

代码比较简单,就是获取碰撞物体的标签来执行不同的函数,就不贴出了。

简易相机

到了这一步,地图、玩家都准备好了。只需要一个相机游戏就可以试玩了。

现在我们来写一个简单的相机控制脚本。由于我们是2D游戏,可以忽略Z轴上的位移来进行相机的移动控制,于是我们的相机的控制原理如下:

v2-200d216c676c66db1380a6217b05b5b3_b.gif
相机移动原理

其实现代码如下:

  1.    public Transform player;   //玩家
  2.     Camera playerCamera;        //主相机
  3.     Vector2 boxSize;            //视野范围
  4.     public bool cameraMove;
  5.     public void FollowPlayer()
  6.     {
  7.         Vector3 targetPos = new Vector3(player.position.x, player.position.y, transform.position.z);
  8.         transform.position = Vector3.Lerp(transform.position, targetPos, 0.08f);//插值进行移动
  9.         float distance = Vector3.Distance(targetPos,transform.position);
  10.         if (distance<0.5f)//距离小于0.5f时 停止移动
  11.         {
  12.             cameraMove = false;
  13.         }
  14.     }

  15.     public void CheckBoundary()
  16.     {
  17.         float leftDistance = 0; //左右距离
  18.         if (player.position.x < transform.position.x) //在左边
  19.         {
  20.             leftDistance = transform.position.x - player.position.x;
  21.         }
  22.         else
  23.         {
  24.             leftDistance = player.position.x - transform.position.x;
  25.         }
  26.         if (leftDistance > boxSize.x * 0.5f)//如果左右距离大于设置好的矩形宽度的一半
  27.         {
  28.             cameraMove = true;//相机开始移动
  29.         }
  30.         float uDDistance = 0;   //上下距离
  31.         if (player.position.y < transform.position.y)
  32.         {
  33.             uDDistance = transform.position.y - player.position.y;
  34.         }
  35.         else
  36.         {
  37.             uDDistance = player.position.y - transform.position.y;
  38.         }

  39.         if (uDDistance > boxSize.y * 0.5f)
  40.         {
  41.             cameraMove = true;
  42.         }
  43.     }
复制代码

背景移动

在现实世界中,人移动时肉眼看远方,远处的物体总是移动的比自己慢。而我们就是需要模拟这种特性,来完善我们的景深效果。

实现原理:游戏一开始获取玩家的位置并保存,然后每一帧计算出玩家已经移动的距离,然后使用计算出来的移动位移,来计算出背景此时的坐标。代码如下:

  1.   public Transform player;
  2.     Vector3 backStartPoint;     //初始背景的位置
  3.     Vector3 playeStartPoint;    //初始玩家的位置
  4.     public void BackMoveFunc()
  5.     {
  6.         float tempX = player.position.x - playeStartPoint.x;
  7.         float tempY = player.position.y - playeStartPoint.y;    //计算X,Y两轴的历史位移
  8.         float xVaule = tempX * 0.08f;
  9.         float yVaule = tempY * 0.06f;//计算出背景的X,Y轴位移
  10.         tra
复制代码

这里只让一层的背景进行移动,如果想要更好的效果,可以多加几层,使移动的速度不同,这样的效果将会更加逼真。

演示

敲黑板,项目到了这终于算能够拿出来瞅瞅了,下面就是现在的演示:(在原文观看)

结语

新的一期又肝完了。虽然通过这个项目我知道樱桃游戏工作室将会比我更肝,但我还是想说:快,马上,赶紧,quick,哈压库,把丝之歌交出来。没有丝之歌玩我要死了啊。

最后再多说一句。游戏中还有许多小物体上都是有帧动画的,包括不限于血条,灯光,陷阱等等。如果你的身体机能满足下图:

image014.png

可以考虑全都要。(咕咕咕)

工程下载链接
链接:https://pan.baidu.com/s/1DD2_yJh-rulqpNgIDAuZjA提取码:7gw1
相关链接,很(mai)重(mai)要(mai)
空洞骑士购买链接:https://store.steampowered.com/app/367520/Hollow_Knight/
有线下学习游戏开发打算的童鞋,欢迎访问http://levelpp.com/。
线上课程的传送门如下:简明易懂的C#入门指南-网易云课堂study.163.com
另有专业开发交(gao)流(ji)群等待大家强势插入:869551769

系列文章:
用Unity重现《空洞骑士》的苦痛之路(1):动作篇
用Unity重现《空洞骑士》的苦痛之路(2)——人物控制篇
用Unity重现《空洞骑士》的苦痛之路(3)——地图篇
用Unity重现《空洞骑士》的苦痛之路(4)——特效篇

作者:繁华如梦
专栏地址:https://zhuanlan.zhihu.com/p/60295559

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|作品发布|文章投稿|广告合作|关于本站|GameRes游资网 ( 闽ICP备05005107-1 )

GMT+8, 2019-5-26 23:12

快速回复 返回顶部 返回列表