用Unity3D仿造一个FlappyBird


Unity3D 4.3版本之后提供了官方的2D工具包,可以很方便的用来开发2D游戏。我昨天在官网学习了Unity2D的tutorial,今天下午花了3个小时使用Unity2D制作了一个简化版的Flappy Bird。

  • 准备工作
    • 带alpha通道的小鸟图片一张。可以通过PS对这张图片中的小鸟的翅膀做小小的修改,如下图所示。这两张图片会用来制作小鸟的飞行动画;
    • Bird Fly
    • 使用PS对小鸟图片进行旋转,得到如下三幅图片,用于制作小鸟的死亡动画;
    • Bird Dying
    • 水管图片一张;
    • 背景图片一张;
    • GameOver、Start、0~9的数字等用来做GUI的图片若干。
  • 资源导入

    • 首先新建一个Unity3D的工程,注意在新建时将默认的工程类型改成2D。
    • Unity3D支持直接导入psd文件,所以上面的那些素材直接拖进Project面板即可。
    • 对于用于制作动画psd文件,在导入后,需要将Inspector面板中的Sprite Mode改成Multiple,并点击Sprite Editor->Slice,Type选择Grid,并设置好Size,Slice并Apply之后,这些psd文件就被切分成了一帧一帧的图像,如图所示。
    • FrameAnim
    • 将背景图片、水管等图片拖拽进场景中,并新建一个EmptyObject,重命名为Bird。为其添加Sprite Renderer这个组件。在Sprite Renderer的Sprite中可以加入小鸟飞行动画的第一帧,方便调试。
  • 制作动画

    • 依次点击Window->Animation,打开Animation窗口。在Hierarchy中选中Bird的情况下,点击Sample左边的双向箭头,新建一个Clip,命名为Fly。
    • 将前面得到的小鸟飞行动画资源的所有帧选中,拖进Animation窗格,并且将Sample改为5,表示每秒播放五帧。
    • 这是Bird对象已经自动添加了Animator组件,双击Animator Controller,可以打开Animator窗口。其中Fly用橙色表示,代表这是默认的动画。
    • 同样的步骤,我们为小鸟创建死亡的动画,Death在Animator窗口中是灰色的,表示非默认动画。
    • 同样的,我们创建一个GameOver动画,这个动画只有一帧图像,图像中小鸟肚皮朝上,表示已经死亡。
    • 我们为三个动画建立如图所示的状态转移图,Fly->Death的转移条件是Dead==true,而另一个转移条件则使用默认的Exit Time。
    • StateMachine
  • 控制小鸟

    • 首先为小鸟添加RigidBody2D和Box Collider 2D两个组件,调整其大小和位置,并勾选RigidBody 2D 的Fixed Angle。并且,将小鸟的tag改成Player。
    • 为了不让小鸟飞出(掉出)屏幕,可以建立两个Empty Object,命名为cell和floor,并为他们添加Box Collider 2D组件,将其放置于屏幕的上方和下方。如图所示。
    • cell&floor
    • 为小鸟对象添加脚本BirdControl.cs。
    • 对于小鸟的控制主要是每次点击鼠标左键,都会为小鸟施加向上的力,使用AddForce容易实现。

      1
      2
      3
      4
      if (Input.GetButtonUp("Fire1"))
      {
      rigidBody.AddForce(Vector2.up * JumpForce);
      }
    • 另外,小鸟需要提供一个Death方法,供小鸟碰撞水管时调用。其目前的主要功能是播放Dead动画。

      1
      2
      3
      4
      5
      public void Death()
      {
      isDead = true;
      anim.SetBool("Dead", true);
      }
  • 控制水管

    • 以下过程可以通过生成Prefab的方式提高效率。
    • 我们手工的将水管拖拽如场景,生成地图。这里水管都放在小鸟的右边,之后会使得水管匀速向左移动。为水管添加Box Collider 2D,添加tag Bucket,并添加脚本Bucket.cs。
    • 在Bucket.cs中我们主要重写了OnCollisionEnter2D方法,用于检测小鸟是否撞上了水管。

      1
      2
      3
      4
      5
      6
      7
      void OnCollisionEnter2D(Collision2D other)
      {
      if (other.gameObject.tag == "Player")
      {
      other.gameObject.GetComponent<BirdControl>().Death();
      }
      }
    • 在场景中新建一个Empty Object,将其更名为GameLogic,为其添加脚本GameLogic.cs。我们将在此脚本中实现水管的左移。

    • 首先我们找到场景中的所有水管。

      1
      buckets = GameObject.FindGameObjectsWithTag("Bucket");
    • 其次,在每一此Update方法中,将所有水管的x坐标左移固定值。

      1
      2
      3
      4
      5
      6
      for (int i = 0; i < numBucket; ++i)
      {
      Vector3 pos = buckets[i].transform.position;
      pos.x -= Time.deltaTime * BucketMoveSpeed;
      buckets[i].transform.position = pos;
      }
  • 计分

    • 每通过一对水管,增加一分。
    • 首先在GameLogic.cs中用Sprite[]的形式保存了所有的0~9的数字图片。
    • 然后在场景中新建一个Empty Object,为其添加Sprite Renderer,默认显示数字0的图片。
    • 在GameLogic的Update过程中,统计在小鸟左边一共有多少个水管,将这个数目除以2就是当前的分数。
    • 由于目前场景中只制作了7关,所以可以直接以分数为下标索引,找出对应的数字图片,替换掉上面Sprite Renderer中的Sprite即可。
  • GameOver

    • 当小鸟死亡时,显示GameOver字样,并显示一个按钮,用于重新开始。
    • 先将GameOver图片和按钮图片导入场景,但是不勾选Sprite Renderer。即默认不显示。
    • 当调用BirdControl.Death方法时,通过gameObject.renderer.enabled=true,显示这两个图片。
    • 为重启按钮添加Box Collider 2D和脚本,并重写OnMouseDown方法,在其中调用Application.LoadLevel(0)重新开始游戏。
  • 踩过的坑

    • 当要重写OnMouseDown方法时,对应的gameObject必须存在一个collider组件,否则该方法无效。
    • 如果勾选了一个RigidBody的isKinematice选项,那么对于这个RigidBody的所有碰撞检测都将被忽略。对于计分,我开始的想法是在小鸟的左侧建立一个只包含BoxCollider2D的Empty Object,然后检测这个Object和水管的碰撞,每发生一次碰撞,分数就加一。这里我不希望这个Empty Object受碰撞影响,也不希望水管受碰撞影响,所以选中了BoxCollider的isTrigger选项,而isTrigger的触发必须要有一方包含RigidBody,无论给水管还是Empty Object添加RigidBody,我都不希望这个RigidBody受碰撞影响,因此必须要勾选isKinematic,这样的话碰撞检测就失效了。所以这种方案不可行,因此还是采用了上面提到的数数的方案。

游戏工程下载:点此下载

游戏截图
1分
2分

Comments