Unity快速上手系列之番外篇:《2D横版跑酷》

作者:四五二十 2019-03-15

前文:
Unity快速上手教程(一):拉方块
Unity快速上手系列之2:2D物理弹球
Unity快速上手教程(三):《反应堆》实例开发

未尝试做过跑酷游戏,大约相当于没学过游戏开发。


玩笑至此,想传达的意思大家想必也清楚。跑酷游戏普遍非常简单,但简单并不代表简陋,跑酷是最能体现“麻雀虽小五脏俱全”的游戏类型。在当中开发者可以充分展示自己的设计才能,无论是奇思妙想的关卡设计还是灵光一现的功能创意。这样必然能获取到充足而高频的正反馈——而这一切都是建立在这样一个门槛较低的游戏体量上的。

因此对于初学者来说,没有比它更合适练手的类型了,这也是我专门写这样一篇番外篇的目的。

闲话少叙,直接开干。

创建地形

地面

标配的地面和天花板自不必说,跟着自己的喜好设计即可。

而移动地形则能稍微增加一点变数。以垂直升降为例,在自己设定的移动范围内,地形最低位置就向上移动,反之亦然。把脚本挂载到对应地形上即可:

  1. void Update() {
  2.     if (transform.position.y > 0) {
  3.         IsChange = false;
  4.     }
  5.     if (transform.position.y < -6) {
  6.         IsChange = true;
  7.     }
  8.     if (IsChange == false) {
  9.         //在世界坐标向下移动
  10.         transform.Translate(0, -4 * Time.deltaTime, 0, Space.World);
  11.     } else {
  12.         //在世界坐标向上移动
  13.         transform.Translate(0, 4 * Time.deltaTime, 0, Space.World);
  14.     }
  15. }
复制代码

水平移动同理。

背景

以下图为例,一张普通的砖墙背景:


为了展示出向前跑的效果,需要让图片每一帧向后移动。

transform.position=new Vector3(transform.position.x-0.03f,transform.position.y,transform.position.z);

顺便说一句,在Unity2018版本中可以使用纹理偏移的方法来达到类似的效果,这样就不用单独移动图片了:

  1. public float scrollSpeed = 0.5f;
  2.         public Renderer = rend;

  3.         //Use this for initialization
  4.         void Start()
  5.         {
  6.             rend = GetComponent<Renderer>();
  7.         }

  8.         //Update is called once per frame
  9.         void Update()
  10.         {
  11.             float offset = Time.time * scrollSpeed;
  12.             rend.material.mainTextureOffset = new Vector2(offset, 0); //纹理偏移
  13.         }
复制代码


与之对应的就不再是创建一个精灵,而是创建Quad:


角色

角色图片

把图片素材放在精灵上,并加上刚体和碰撞盒子:


强调一下,网上的图片素材一定只能供自己学习,一定不能用作商业用途!

角色动作

1、移动

我们给角色一个速度,使得游戏一开始角色就向右移动。

  1. myRigidbody.transform.Translate(speed*Time.deltaTime,0,0);
复制代码

2、跳跃

按下空格键给它一个向上的力,使得角色可以向上跳跃。有两个判断:只有当检测到玩家在地面上,或者在天花板上时,才能跳跃。在空中不能跳跃。当然,通过稍微的逻辑修改,还可以让角色实现二段跳功能。

  1. //按下空格键可以使方块跳跃
  2. if (Input.GetKeyDown(KeyCode.Space))
  3. {
  4.      if (Physics2D.Raycast(transform.position, Vector2.down,hight, LayerMask.GetMask("ground")))
  5.      {
  6.          myRigidbody.AddForce(Vector3.up * upspeed, ForceMode2D.Impulse);    //给它一个向上的力
  7.      }
  8.      if (Physics2D.Raycast(transform.position, Vector2.down, hight, LayerMask.GetMask("ceiling")))
  9.      {
  10.          myRigidbody.AddForce(Vector3.up * upspeed, ForceMode2D.Impulse);    //给它一个向上的力
  11.      }
  12. }
复制代码

3、放缩

在这个示例里,我给予了玩家一个放缩的特性,通过灵活地改变体型的大小来通过障碍物。根据体型不同,跳跃能力、奔跑速度会发生相应的变化。

  1. if (transform.localScale.x>0.3)
  2. {
  3.     //按下A键可以缩小方块
  4.     if (Input.GetKey(KeyCode.A))
  5.     {
  6.         transform.localScale = new Vector3(transform.localScale.x - 0.01f, transform.localScale.y - 0.01f, transform.localScale.z - 0.01f);
  7.         speed = speed + 0.05f;
  8.         upspeed = upspeed + 0.05f;
  9.     }
  10. }
复制代码
  1. if(transform.localScale.x <=1)
  2. {
  3.     //按下D键可以变大方块
  4.     if (Input.GetKey(KeyCode.D))
  5.     {
  6.         //检测到方块上面是天花板则不能变大
  7.         if (Physics2D.Raycast(transform.position, Vector2.up, 0.5f*hight, LayerMask.GetMask("ceiling")))
  8.         {

  9.         }
  10.         else
  11.         {
  12.             transform.localScale = new Vector3(transform.localScale.x + 0.01f, transform.localScale.y + 0.01f, transform.localScale.z + 0.01f);
  13.             speed = speed - 0.05f;
  14.             upspeed = upspeed - 0.05f;
  15.         }
  16.     }
复制代码

效果如图:


摄像机

这一部分的处理较为简单,只需要让镜头跟随玩家移动即可。

  1. //让相机跟着玩家移动
  2. transform.position = new Vector3(player.transform.position.x + 5, 0 , transform.position.z);
复制代码

金币

金币是场景中散落的收集要素,就跟《超级马里奥》中的金币一样。

图片资源随便用一个:


我们让金币带上一个旋转的样式:

  1. transform.Rotate(Vector3.up * 4, Space.World);    //原地旋转
复制代码

效果如下图所示:


根据收集逻辑,场景中的金币一旦被玩家所触碰到,就会自动移动至UI中金币数量位置。

金币的移动函数如下:

  1. //金币向目标点移动
  2. public void CoinMove()
  3. {
  4.     //UI坐标转换成世界坐标
  5.     UIcoin = Camera.main.ScreenToWorldPoint(AllCoin.transform.position);

  6.     //当前物体向这某一个物体移动
  7.     transform.position = Vector3.MoveTowards(transform.position, UIcoin + Vector3.forward, 25 * Time.deltaTime);

  8.     //两个坐标相减是方向,用sqrMagnitude获取方向的距离
  9.     if ((transform.position - (UIcoin + Vector3.forward)).sqrMagnitude < 0.1f)
  10.     {
  11.         player.GetComponent<PlayerMove>().money++;
  12.         player.GetComponent<PlayerMove>().SetMoney();
  13.         Destroy(gameObject);
  14.     }
  15. }
复制代码

当然,要触发这个移动函数,还需要增加一个检测碰撞的过程,在过程中调用移动函数。不要忘了给金币添加碰撞盒子。

  1. void Update ()
  2. {
  3.     transform.Rotate(Vector3.up * 4, Space.World);    //原地旋转

  4.     if (IsRun == true)
  5.     {
  6.         CoinMove();
  7.     }
  8. }

  9. public void OnTriggerEnter2D(Collider2D coll)
  10. {
  11.     if (coll.gameObject.CompareTag("Player"))
  12.     {
  13.         IsRun = true;
  14.     }
  15. }
复制代码

在角色脚本中获取该数量text。

  1. void Start ()
  2. {
  3.     myRigidbody = this.GetComponent<Rigidbody2D>();
  4.     money = 0;
  5.     SetMoney();
  6.     Hp.gameObject.SetActive(false);
  7. }

  8. public void SetMoney()      //改变金币数量
  9. {
  10.     MoneyText.text = money.ToString();
  11. }
复制代码

金币时,金币在移动至相应位置的同时,数量+1,并销毁自己。

  1. player.GetComponent<PlayerMove>().money++;
  2. player.GetComponent<PlayerMove>().SetMoney();
  3. Destroy(gameObject);
复制代码

机关

角色一旦碰到场景中的各种机关,就宣告game over。机关分为固定机关和移动机关两种。

移动机关当检测到角色靠近时,就自动向下掉落,碰到地板后停止运动,在掉落过程中如果碰到玩家则game over。

  1. void Update ()
  2. {
  3.     if (isRun==true)
  4.     {
  5.         //玩家靠近绝对值距离小于4时火焰下落
  6.         if (Mathf.Abs(player.transform.position.x-transform.position.x)<4)
  7.         {
  8.             transform.Translate(0, -9 * Time.deltaTime, 0, Space.World);
  9.         }
  10.     }
  11. }

  12. public void OnTriggerEnter2D(Collider2D coll)
  13. {
  14.     if (coll.gameObject.CompareTag("ground"))
  15.     {
  16.         isRun = false;
  17.     }
复制代码

磁铁功能

很多跑酷游戏里都会有“磁铁”这样的powerup道具,吃到后,会在一定时间内让场景中的金币自动被吸附到玩家身上。

碰撞盒

要实现这样的功能,我们首先需要给磁铁加一个可任意调整的碰撞盒。


随意改变一下形状,能碰到前方的金币即可。


拾取金币

  1. public void OnTriggerEnter2D(Collider2D coll)
  2. {
  3.     if(coll.gameObject.CompareTag("coin"))
  4.     {
  5.         coll.GetComponent<Coin>().IsRun = true;
  6.     }
  7. }
复制代码

碰撞盒碰到金币的盒子时,把金币的IsRun改为True,此时金币就会自动跑向目标点,达到磁铁吸金的效果。

跟随角色

角色碰到磁铁后,为了让吸金效果跟随角色,需要让磁铁本身也跟随角色一起移动。

  1. if (coll.gameObject.CompareTag("Player"))
  2. {
  3.     IsFollow = true;
  4. }

  5. void Update ()
  6. {
  7.     if (IsFollow == true)
  8.     {
  9.         //跟着玩家移动
  10.         transform.position = new Vector3(player.transform.position.x - 0.5f, player.transform.position.y + 1, transform.position.z);
  11.     }
  12. }
复制代码

时间条

我们首先创建两张图片,分别为白色和绿色的,绿色图片在白色图片之上,两张大小一样。


找到面板上的绿色时间条,然后以帧为单位逐渐缩小尺寸。在减少为0的时候销毁磁铁和时间条,意味着磁铁失效。

  1. //找到玩家
  2. public GameObject player;
  3. //找到血量条
  4. public GameObject MagnetHp;
  5. //找到HP图片
  6. public Image HpPhoto;
  7. //HP数值
  8. public float Hp;
  9. //是否移动
  10. public bool IsFollow = false;

  11. void Update ()
  12. {
  13.     if (IsFollow==true)
  14.     {
  15.         //改变Rotation
  16.         transform.eulerAngles = new Vector3(0,0,0);
  17.         transform.localScale = new Vector3(0.17f, 0.17f, transform.localScale.z);
  18.         transform.position = new Vector3(player.transform.position.x-0.5f, player.transform.position.y+1, transform.position.z);
  19.         player.GetComponent<PlayerMove>().Hp.gameObject.SetActive(true);
  20.         Hp = Hp - 0.002f;
  21.         if(Hp<0)
  22.         {
  23.             Hp = 0;
  24.             Destroy(transform.parent.gameObject);     //销毁磁铁
  25.             Destroy(MagnetHp);      //销毁血条
  26.         }

  27.         //实时更新面板上的HP数值
  28.         HpPhoto.rectTransform.localScale = new Vector3(Hp, HpPhoto.rectTransform.localScale.y, HpPhoto.rectTransform.localScale.z);
  29.     }
  30. }
复制代码

切换属性

这是在本篇示例中另外添加的一个特性:属性切换。

场景中有时会出现类似于水墙的障碍,玩家需要切换角色的属性后才能通过。

水属性图片:


当角色触碰到上图时,切换角色外形为如下:


  1. player.GetComponent<SpriteRenderer>().sprite = tupian;
复制代码

当检测到玩家属性已切换时,将水墙的碰撞盒变为可穿越即可。
  1. rainbowwall.GetComponent<BoxCollider2D>().isTrigger = true;
复制代码

游戏结束

goal标志

当玩家到达goal标志时,判定玩家闯关成功。


反之,玩家碰到机关、摔下悬崖或是未能穿过水墙时,判定游戏失败。


That's it。实际运行的效果如下:


完整的工程如下:

wushupei/RunGamegithub.com

这样一个简单的游戏却作用多多,不光可以应对类似于课程设计之类的学习任务,还足以作为开发者的入门第一次实践。希望能藉由此文,让尽量多的盆友们踏上游戏开发令人激动的旅程中去。

照样怒宣传一波:想系统性学习游戏开发、学习Unity开发的,欢迎围观:
http://levelpp.com/
在线的教学视频:
https://space.bilibili.com/38043731/#/
以及QQ交流群:610475807


作者:四五二十
专栏地址:https://zhuanlan.zhihu.com/p/38476477

最新评论
暂无评论
参与评论

商务合作 查看更多

编辑推荐 查看更多