Flappy bird基于Unity的实现


该游戏火爆于2013年。

游戏 比较简单。

打开Unity 新建项目 把相机调成正交,

再 把 工具栏改成 上 有个2D的按钮,点它


导入素材,制作背景

带有透明背景的,最好是PNG格式

扫描二维码关注公众号,回复: 2047162 查看本文章

然后把 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在手机里 玩了

猜你喜欢

转载自blog.csdn.net/qq2512667/article/details/80951661