该游戏火爆于2013年。
游戏 比较简单。
打开Unity 新建项目 把相机调成正交,
再 把 工具栏改成 上 有个2D的按钮,点它
导入素材,制作背景
、
带有透明背景的,最好是PNG格式
然后把 Texture Type改为 sprite (2D and UI), 如果一张图片里面有很多个素材就把
Sprite Mode 改为multiple 点击 sprite edit
再点slice 切割 auto 自动切割,就可以把素材切开,切开后点apply,应用一下
安卓版前期工作
将Game视图下的 这个比例 调好,这里是安卓版,再File下面 有个Bulid Setting,打开,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class HeroControl : MonoBehaviour {
// Use this for initialization
public float UpFloPower = 1f;
Rigidbody2D m_Rig2D;
AudioSource m_source;
public AudioClip m_fly;
public AudioClip Death;
Vector3 oriPos;
void Awake()
{
oriPos = transform.position;
m_Rig2D = GetComponent<Rigidbody2D>();
m_source = GetComponent<AudioSource>();
}
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (Input.GetButton("Fire1") && pipeMove.Instance.GameStart)
{
m_Rig2D.bodyType = RigidbodyType2D.Dynamic;
if(m_Rig2D.IsSleeping())
m_Rig2D.WakeUp();
m_source.clip = m_fly;
if (!m_source.isPlaying)
m_source.Play();
m_Rig2D.velocity = Vector2.up * UpFloPower;
}
if (!pipeMove.Instance.GameStart)
{
m_source.clip = Death;
m_source.Play();
m_Rig2D.isKinematic = true;
m_Rig2D.Sleep();
transform.position = oriPos;
enabled = false;
}else
{
m_Rig2D.bodyType = RigidbodyType2D.Dynamic;
if(m_Rig2D.IsSleeping())
m_Rig2D.WakeUp();
}
}
}
这个是主角的代码,把它加到鸟的身上去, 这个代码 就是 实现小鸟的控制 关键的语句是
m_Rig2D.bodyType = RigidbodyType2D.Dynamic;
if(m_Rig2D.IsSleeping())
m_Rig2D.WakeUp();
m_source.clip = m_fly;
if (!m_source.isPlaying)
m_source.Play();
m_Rig2D.velocity = Vector2.up * UpFloPower;
m_Rig2D.WakeUp();
是把组件唤醒
Rigbody组件 十分好用,
m_Rig2D.velocity = Vector2.up * UpFloPower; 就是给它加向上的速度
然后把它飞的声音 和死亡的声音在 unity的Inspector
中托给它。 接着我们再 实现 下管道的移动
这里用单例模式来实现,使用单例模式要注意,该脚本在场景中只能有一个实例,也就是说只能挂载在1个物体上,不能有多的。否则会造成混乱。
其实逻辑很简单。 如果 管子 的位置超过了我们希望得到的位置,就让它重置为原来的位置就可以, 原来的位置我们可以在Awake的时候进行保存。 这里用一个 空物体,作为他们的父物体就可以控制他们的移动了。
控制物体 移动的方法 有很多, 可以给他们加rigbody 组件,然后用 rigbody里的moveToPosition,velocity,也可以用 Vectory.Lerp, 还可以直接暴力的 Trsanform.position+=Vectoty.left*Time.deltime;
最后 就是比较常用的 transform.Translate
Time.deltatime; 是上一帧完成的时间, 这样子乘就得到每秒的速度,如果不乘 就是每一帧的速度了,update是 每帧执行的函数。
然后需要给 管子 加Y轴的随机值,我这里是把脚本挂载在 父物体上,这样比较好管理,用getChild方面将其保存起来。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class pipeMove : MonoBehaviour {
// Use this for initialization
//脚本实例
public static pipeMove Instance;
public float floCirclePos= -10f; //临界点
public float floSpeed=1f; //飘行速度
//GameObject GoBroadCastMsg;
Vector2 m_vectorOriPos;
bool m_isStartGame=false;
List<Transform> childTransf = new List<Transform>();
List<Vector2> childPos = new List<Vector2>();
void Awake()
{
Instance = this;
m_vectorOriPos = transform.position;
//GetComponentsInChildren<Transform>();
for (int i = 0; i < transform.childCount; i++)
{
Transform temp = transform.GetChild(i);
childTransf.Add(temp);
childPos.Add(temp.position);
}
}
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (m_isStartGame)
{
if (transform.position.x < floCirclePos)
{
transform.position = m_vectorOriPos;
// 随机子物体Y位置
SetChildRanmPos();
//RamdomPos;
}
transform.Translate(Vector2.left * floSpeed * Time.deltaTime);
}
}
void SetChildRanmPos()
{
float ReSult = Random.Range(-3f, 2f);
for (int i = 0; i < childTransf.Count; i++)
{
childTransf[i].position = childPos[i] + new Vector2(0, ReSult);
}
}
public bool GameStart
{
get
{
return m_isStartGame;
}
set
{
m_isStartGame = true;
}
}
public void GameOver()
{
transform.position = m_vectorOriPos;
UIManager.Instance.Ready();
m_isStartGame = false;
UIManager.Instance.RestScore();
}
}
土地的移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LandMove : MonoBehaviour {
// Use this for initialization
//Transform[] chilTrans;
//Vector3[] ChildOriPosS;
Vector3 OriPos;
void Awake()
{
//chilTrans = GetComponentsInChildren<Transform>();
//ChildOriPosS = new Vector3[chilTrans.Length];
//for (int i = 0; i < chilTrans.Length; i++)
//{
// ChildOriPosS[i] = new Vector3(chilTrans[i].position.x, chilTrans[i].position.y);
//}
OriPos = transform.position;
}
void Start ()
{
}
void SetChildRandm()
{
}
// Update is called once per frame
void Update ()
{
if (pipeMove.Instance.GameStart)
{
if (transform.position.x < pipeMove.Instance.floCirclePos)
{
transform.position = OriPos;
}
transform.Translate(Vector2.left * pipeMove.Instance.floSpeed * Time.deltaTime);
}
}
是和上面的一样 放在物体上,进行控制
这里碰撞检测比较简单,因为管理游戏输赢的逻辑放在了pipMove类里, 又是用了单例,因此 直接访问就OK了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 碰撞检测类
/// </summary>
public class pipe : MonoBehaviour {
// Use this for initialization
void Start()
{
}
void OnCollisionEnter2D( Collision2D other)
{
if (other.transform.CompareTag("Player"))
{
AudioManager.PlayAudioEffectA("Dead");
pipeMove.Instance.GameOver();
}
}
// Update is called once per frame
void Update ()
{
}
这里 要注意的是 小鸟 要手动加个Tag,点击主角,然后再Inspector面板中有个Tag 然后选择player就可以了这个是 自带 的。管子要加,主角也要加box collider 2d的组件。
另外, 管子下边再加多一个空物体,作为检测 得分的。叫check
属性面板。 然后加个简单的处理得分的脚本 check Reword
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CheckReword : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
/// <summary>
/// 检测是否 奖励
/// </summary>
/// <param name="other"></param>
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log("1");
if (other.transform.CompareTag("Player"))
{
UIManager.Instance.Reword();
}
}
}
得分这些一般是属于UI类的管理, 因此把游戏输赢 排行的UI 先做好。
这里要注意 Canvas 的 UI Scale mode要改为Scale with Screen size 做下 自适应
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIManager : MonoBehaviour {
// Use this for initialization
public static UIManager Instance;
public static int Score=0;
AudioSource m_souce;
public AudioClip RewordClip;
//开始界面UI
public GameObject Start_UI;
//提升UI
public GameObject Hit_UI;
public Text ScoreText;
void Awake()
{
m_souce = GetComponent<AudioSource>();
Instance = this;
}
void Start ()
{
}
public void Ready()
{
if(Score>PlayerPrefs.GetInt("Score"))
{
PlayerPrefs.SetInt("Score", Score);
}
Hit_UI.gameObject.SetActive(true);
}
public void StartGame()
{
pipeMove.Instance.GameStart = true;
}
// Update is called once per frame
void Update ()
{
}
public void Reword()
{
m_souce.clip = RewordClip;
m_souce.Play();
Score++;
ScoreText.text=Score.ToString();
}
public void RestScore()
{
Score=0;
ScoreText.text=0+"";
}
}
UI管理类也非常少的代码。主要就是 得分 处理 高分处理。失败处理
logo
界面
开始按钮 加上button组件
开始按钮 On click 绑定事件。
排行榜 按钮, 绑定事件
最高记录UI
层级关系
储存分数的Text , 挂载 scorManeger脚本
scoreManeger
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreManger : MonoBehaviour {
// Use this for initialization
Text HighText;
void Start ()
{
HighText=GetComponent<Text>();
HighText.text=PlayerPrefs.GetInt("Score")+"";
}
void OnEnable()
{
HighText=GetComponent<Text>();
HighText.text=PlayerPrefs.GetInt("Score")+"";
}
// Update is called once per frame
void Update () {
}
}
Return 按钮 绑定的事件
提示按钮 层级面板
提示面板的UI
提示按钮 绑定 事件
提示按钮下的排行版,绑定事件。
然后
游戏中的得分
一个空物体 存两个,一个文字 得分的标题,
一个数字, 这个要托给UImanger 里的Score Text;
至此 游戏 就完成了。 安卓版 要开打 playerSetting
Package Name 要改下 ,然后就可以build在手机里 玩了