首先新建项目,并下载资源
将资源移到Assets文件夹下
将Sprites里的BirdHero的Sprite Mode改为Multiple,因为这是多幅图
点击Sprite Editor,进行图片切割
完成之后,可以看到这张图片右边有一个三角,点开可以看到三张图片
将天空和草地的图片拖到场景树下
把鸟也拖到场景树下,然后对天空、草地、鸟的图片设置层级
手动添加层级:在Soring Layer中选择Add Sorting Layer,添加背景和前景层
将天空和草地的Sorting Layer选择为BackGround,将鸟的设为ForeGround
将天空设为草地的子节点
给鸟添加RigidBody 2D
给草地添加碰撞器
点击Edit Collider,将碰撞范围压缩到地的厚度
给小鸟添加一个Polygon Collider
添加Scripts文件夹,新建C#脚本命名为Bird,并拖拽到Bird下
编写设置点击鼠标使得小鸟获得上升的力以及小鸟与地面碰撞后死亡的代码于Bird脚本中:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bird : MonoBehaviour
{
private bool isDead = false;
private Rigidbody2D rb;
private float upForce = 200;
// Start is called before the first frame update
void Start()
{
rb = this.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
print(rb.velocity); //打印速度
if (isDead)
return;
if (Input.GetMouseButton(0)) //button 值为 0 表示左按钮,1 表示右按钮,2 表示中间按钮。按下鼠标按钮时返回 true,释放时返回 false。
{
rb.velocity = Vector2.zero; //将速度置零
rb.AddForce(upForce * Vector2.up); //提供一个向上的力
}
}
private void OnCollisionEnter2D(Collision2D collision) //通过反射机制调用消息方法。其参数为与之碰撞的其他物体
{
isDead = true;
}
}
新建一个脚本Ground并拖拽到GrassGround对象下,编写地面被碰撞后让与之碰撞的物体消失的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ground : MonoBehaviour
{
public void OnCollisionEnter2D(Collision2D collision)
{
collision.gameObject.GetComponent<SpriteRenderer>().enabled = false;
}
}
也可以将小鸟经过碰撞后死亡的代码写在Ground对象中
创建小鸟飞翔的动画:
选中小鸟对象,选择Window——Animation
选择Create,新建文件夹Animation
选中Animation文件夹,并将文件名改为Idle
在Animation窗口中,将采样率设为1,滑动滚轮显示后面的帧,右键进行左右移动
点击红点开始记录关键帧,并把小鸟图片拖拽到1:0处
再新建一个关键帧命名为Flappy
将另一张图片拖到1:0处
再新建一个Die
将死亡的图片拖拽到1:0
选择小鸟,点击Animation——Animator打开动画编辑器
在Parameters里新建触发器
新建两个Trigger并命名为Flap和Die
右键Idle,选择Make transition,将箭头指向Flappy
点击Idle到Flappy的箭头,选择Conditions——+——Flap
打开Bird的脚本
添加动画对象和触发代码
运行游戏,发现先跑Idle动画再到Flappy,且跑完Idle的时间较长
解决方法是:勾掉Has Exit Time
为了使得动画能够切换,再新建一个transition从Flappy到Idle,开启Has Exit Time
在动画编辑器里添加一个Idle到Die的箭头,并把Conditions设置为Die,并勾掉Has Exit Time
在代码里设置死亡触发器
下面来显示得分和游戏结束
添加一个Text对象并改名为ScoreText
将font改为LuckiestGuy,颜色改为白色
设置字体大小为60,选择居中对齐,水平溢出方式为Overflow
设置锚点为底部中心
将Canvas Scaler设置成随画面缩放
修改内容为Score:0
按照同样的方式生成游戏结束的Text,并将几个Text重命名
因为这两个Text是游戏失败才出现,所以先将其隐藏
因此需要创建一个游戏控制器来控制游戏的逻辑,包括: 跟踪游戏状态、游戏开始和结束、游戏的逻辑处理可以都放在这里统一处理
新建一个GameObject对象,并改名为GameControl
新建一个GameControl脚本并挂载到GameControl对象上
在游戏控制器脚本中新建变量
在Unity中可以看到出现了两个变量
直接在Unity中将两个Text分别拖拽到对应位置
编写GameControl代码
Bird代码加入触发小鸟死亡的代码
使得小鸟死亡的时候能出现
制作卷轴背景
复制GrassGround,查看原GrassGround的Size的X值
将该X值粘贴到GrassGround(1)的Position的X中
第一种方法是通过动画的形式,由背景图向后运动构成小鸟向前运动的视觉效果
给GrassGround新建一个动画控制器,默认其名字为GrassGround
打开Animation,为GrassGround新建一个名为GroundScroll的动画,设置帧率为10,在15帧的位置记录关键帧,设置GrassGround的X坐标为-20.48
为GrassGround(1)也新建一个动画控制器,打开Animation创建一个名为GroundScroll(1)的动画,同样在15帧的位置记录关键帧,将GrassGround(1)的X坐标设置为0
运行,可以看到背景图片交替后退造成小鸟往前飞的视觉效果
但是发现两张背景的切换过渡不自然,所以点击Animation的Curves查看动画曲线,将曲线调整为斜率为1的直线
第二种方法是通过代码来实现
先隐藏动画控制器
给GrassGround和GrassGround(1)分别添加RigidBody 2D组件
并选择Body Type为Kinematic,表示排除重力影响
新建一个脚本ScrollingObject
将其拖到GrassGround和GrassGround(1)内,编写代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScrollingObject : MonoBehaviour
{
Rigidbody2D rb2d;
float width;
float startPosX;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
rb2d.velocity = new Vector2(-10, 0);
width = GetComponent<BoxCollider2D>().size.x;
startPosX = transform.position.x;
}
void Update()
{
if (GameControl.instance.gameOver)
{
rb2d.velocity = Vector2.zero;//清零速度
return;
}
//如果左移的距离大于等于图片本身的宽度,则回到初始位置
if (startPosX - transform.position.x >= width)
{
Vector3 pos = transform.position;
pos.x = startPosX;
transform.position = pos;
//注:不能直接修改transform.position.x
}
}
}
添加柱子障碍
创建空的GameObject并重命名如下
将Sprites的柱子图片拖拽到DownColumns下,并设置其Sorting Layer为ForeGround
复制DownColumns的ColumnSprites到upColumns,讲Scale的Y改为-1表示在Y轴反向,并往上移动到合适位置
新建一个文件夹命名为预置体
将柱子添加到Prefabs中
将ScrollingObject脚本添加到Columns中作为其组件,使其也具有滚动功能
给Columns添加Rigidbody 2D组件,并设置Kinematic
修改ScrollingObject代码:为了兼容GrassGround,通过对width的设置来使程序选择GrassGround还是Columns
来到Unity,发现Columns的ScrollingObject组件中出现了可手动设置数值的width,将其设为40
给DownColumns和UpClomns分别添加Collider 2D组件,并调节碰撞范围
给Columns新建一个Collider 2D组件,并勾上is Trigger使其成为触发器
点击Edit Collider来调节该触发器的位置和大小,将其控制在穿越两个柱子障碍后紧邻的一个空间,用于触发“通过障碍后得分”
下面编写代码:在GameControl中添加一个私有变量score(以防外界修改)和一个scoreText来显示在界面上
在Unity中将Canvas的ScoreText拖拽到GameControl的Score Text中
在GameControl中添加加分的方法(虽然在unity中通过拖拽已默认赋值了,但是我直接写scoreText.GetComponent
注意这个获取Text类型组件需要添加using UnityEngine.UI;
在Bird的脚本中添加触发加分的方法:Ctrl+Shift+M,选择OnTriggerEnter2D
编写该方法
运行游戏可以看到每通过障碍得分就会增加
下面来动态生成柱子
在Scenery下新建两个GameObject并命名为LowerBound和UpperBound,并将其向右拖到X坐标为30的位置,表示生成柱子的X坐标位置以及Y坐标的上下界
为了设定lowerBound和upperBound的大小,将场景中的Columns移到X为30的位置来进行比对
为了标记,给其选择颜色
并在Gizmos下调节大小
下图位置是我们希望Columns出现的最低的位置
查看此时的Postion的Y值为-1
于是将该Y值复制到lowerBound的Position的Y值
下图是我们希望Columns出现的最高的位置
查看此时的Postion的Y值为1.71
于是将该Y值复制到upperBound的Position的Y值
新建一个ColumnsSpawner脚本并绑定到Scenery上
在代码里添加预置体变量
把场景中已经设置好的Columns给Apply到预置体的Columns中,使得预置体的Columns具有场景中Columns的设定
在unity中把Columns拖进去
编写代码:希望每隔4秒就会生成Columns实例
保存后,在Unity中把lowerBound、upperBound拖到对应位置
继续编写代码:希望每次生成的位置是在upperBound和lowerbound之间的随机位置
因为Columns也绑了ScrollingObject插件,每次生成后会向后滚动,不会消失,所以需要在代码里对其进行管理。
方法一:手动销毁
在ScrollingObject中添加destroy变量用于区分Columns和其他对象(注:常见的同一个脚本复用于多个对象的方法——添加一个公有的布尔变量,通过在unity中是否勾选而区分)
在Unity中勾选destroy,表示在Columns中其值为true
缺点:不停地分配、释放消耗时间;容易造成内存碎片
方法二:对象池管理柱子生命周期
新建一个ColumnPool脚本并拖拽到Scenery下,隐藏掉ColumnsSpawner脚本,并将变量进行拖拽
改变ScrollingObject的代码
游戏的发布
File->Build Setting->Android->Open Download Page
下载好安卓支持运行文件后,双击后选择安装路径为Unity Editor平级的路径
再来到Unity,选择Android,Switch Platform
然后点击Build->Assets->新建一个Build文件夹,选择Build文件夹,文件名写作FlappyBird
提示要安装Android SDK
手动下载Android SDK后在unity里Build时发生了各种错误
所以按照下列博客的指示,通过Unity Hub重新安一个Unity,在下载的时候指定Android SDK
Unity打包Android最全攻略
在Unity Hub里记得设置安装路径
在下载前记得把之前下载的unity删干净(很重要!)
然后下载的时候选择安卓模块,可能下载的时候选择了但是开启Unity去Build的时候显示还没有配安卓支持,就按照指示再来添加,一直到下图为止
注意这里的NDK:unity打包有两种方式,一种是c#虚拟机格式(mono),一种是c++原生指令格式(ilcpp),只有打成第二种才需要NDK