|
Chinar堅持將簡單的生活方式,帶給世人! (擁有更好的閱讀體驗 —— 高分辨率用戶請根據需求調整網頁縮放比例) |
AngryEva游戲效果:

1
Spring Joint 2D —— 彈簧關節
Spring Joint 2D : 是Unity提供的一個彈簧關節組件,可通過AddComponent添加
Unity會自動模擬彈簧的物理效果,來執行函數,使物體具備同樣的彈簧效果
注意:Spring Joint 2D 組件,需要指定鏈接一個的Rigidbody組件:
這個物體是所需固定位置的物體(且物體上必須有Rididbody組件)
在Spring Joint 2D組件下的 Connected Rigid Body 屬性中添加


2
CameraFollow —— 相機跟隨,插值
Mathf.Clamp(posX, 0, 18)
數學函數.范圍(限定目標,0,到 18之間)

/// <summary>
/// 相機在指定范圍跟隨
/// </summary>
private void CameraFollow()
{
//記錄Eva的橫坐標
float posX = transform.position.x;
//相機當前位置 = 插值(當前相機位置,目標位置(Mathf.Clamp-限定范圍:(限定posX,0,18之間))
Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, new Vector3(Mathf.Clamp(posX, 0, 18), Camera.main.transform.position.y, Camera.main.transform.position.z), SmoothFlo * Time.deltaTime);
}

3
RelativeVelocity —— 相對速度(- - 檢測受傷的方式)
collision.relativeVelocity.magnitude > MaxSpeed
碰撞物體的.相對速度.大小 > 最大速度

/// <summary>
/// 觸發檢測,檢測是否達到受傷條件
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Eva") //需在外部設置標簽,給Eva物體設置Tag為Eva
{
AudioPlay(EvaHurtClip); //播放受傷音效
collision.transform.GetComponent<Eva>().Hurt(); //受傷
}
if (collision.relativeVelocity.magnitude > MaxSpeed) //如果相對速度.大小>最大速度
{
Dead(); //調用死亡消除方法
}
else if (collision.relativeVelocity.magnitude > MinSpeed && collision.relativeVelocity.magnitude < MaxSpeed) //相對速度在4-8之間
{
Render.sprite = HurtSprite; //更換圖片,受傷
AudioPlay(HurtClip);
}
}
4
Eva —— 腳本

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
/// <summary>
/// Eva類腳本
/// </summary>
public class Eva : MonoBehaviour
{
public float MaxDis = 1.8f; //Eva可拖動最遠距離
public float SmoothFlo = 3; //平滑度
[HideInInspector] public SpringJoint2D EvaSP; //彈簧鏈接組件
protected Rigidbody2D EvaRg; //剛體組件
public LineRenderer LeftLineRenderer; //左線組件
public Transform LeftPos; //彈弓左定點
public LineRenderer RightLineRenderer; //右線組件
public Transform RightPos; //彈弓右定點
protected GameObject EvaBoom; //Eva爆炸特效
protected MyTrail myTrail; //定義拖尾腳本對象
public AudioClip SelectEvaClip; //選中Eva音效
public AudioClip FlyEvaClip; //Eva飛出音效
protected SpriteRenderer EvaRender; //Eva渲染組件
public Sprite HurtSprite; //受傷圖
[HideInInspector] public bool isCanTrail; //是否能拖拽
[HideInInspector] public bool isRelease; //是否釋放Eva
private bool isClick; //是否點擊
private bool isFly; //是否正在飛
private void Awake()
{
EvaSP = GetComponent<SpringJoint2D>(); //獲取組件
EvaRg = GetComponent<Rigidbody2D>();
myTrail = GetComponent<MyTrail>();
EvaRender = GetComponent<SpriteRenderer>();
}
void Start()
{
EvaBoom = Resources.Load<GameObject>("Prefabs/EvaMumBoom");
}
// Update is called once per frame
void Update()
{
if (EventSystem.current.IsPointerOverGameObject()) return; //如果點擊了UI界面上的按鈕,圖片。就不向下執行
if (isClick)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition); //屏幕坐標轉世界
transform.position += new Vector3(0, 0, -Camera.main.transform.position.z); //第二種方法:同理,加上攝像機的Z軸偏移量 --得正哦
//transform.position += new Vector3(0,0,10);//第一種方法:既然攝像機在-10方向上,那么Eva就+10
if (Vector3.Distance(transform.position, RightPos.position) > MaxDis) //如果大於設定距離MaxDis
{
Vector3 pos = (transform.position - RightPos.position).normalized; //單位化向量,求得方向
pos *= MaxDis; //賦值最大長度 給向量Pos
transform.position = pos + RightPos.position; //Eva當前位置賦值:最大距離+起點坐標點的位置
}
SlingShort();
}
CameraFollow(); //相機跟隨
if (isFly) //如果在飛出的過程中
{
if (Input.GetMouseButtonDown(0)) //按下鼠標左鍵
{
EvaYellowExpedite(); //啟用黃Eva加速函數
}
}
}
/// <summary>
/// 鼠標按下
/// </summary>
private void OnMouseDown()
{
if (isCanTrail)
{
AudioPlay(SelectEvaClip);
isClick = true; //點擊了
EvaRg.isKinematic = true; //啟動力學
}
}
/// <summary>
/// 鼠標抬起
/// </summary>
private void OnMouseUp()
{
if (isCanTrail)
{
isClick = false; //沒點擊
RightLineRenderer.enabled = false; //關閉右划線
LeftLineRenderer.enabled = false; //關閉左划線
EvaRg.isKinematic = false; //關閉力學
Invoke("Fly", 0.1f); //調用函數,(“函數名”,延遲時間)
isCanTrail = false;
}
}
/// <summary>
/// 飛出后的處理
/// </summary>
private void Fly()
{
isRelease = true; //鼠標抬起
isFly = true; //正在飛,開始
AudioPlay(FlyEvaClip); //播放音效
EvaSP.enabled = false; //禁用彈簧鏈接
Invoke("NextEva", 4); //2秒后調用 下一個Eva函數
myTrail.StartTrail(); //開啟拖尾
}
/// <summary>
/// 彈弓
/// </summary>
private void SlingShort()
{
//給彈弓划線
RightLineRenderer.enabled = true;
LeftLineRenderer.enabled = true;
RightLineRenderer.SetPosition(0, RightPos.position);
RightLineRenderer.SetPosition(1, transform.position);
LeftLineRenderer.SetPosition(0, LeftPos.position);
LeftLineRenderer.SetPosition(1, transform.position);
}
/// <summary>
/// 下一只Eva
/// </summary>
protected virtual void NextEva()
{
GameManager.Instance.EvaList.Remove(this); //從Eva數組中移除當前Eva
Destroy(gameObject);
Instantiate(EvaBoom, transform.position, Quaternion.identity); //實例化特效
GameManager.Instance.NextEva(); //調用總控里的下一個判斷
}
/// <summary>
/// 碰撞檢測
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter2D(Collision2D collision)
{
myTrail.ClearTrail(); //清除拖尾
isFly = false;
Time.timeScale = 1;
}
/// <summary>
/// 相機在指定范圍跟隨
/// </summary>
private void CameraFollow()
{
//記錄Eva的橫坐標
float posX = transform.position.x;
//相機當前位置 = 插值(當前相機位置,目標位置(Mathf.Clamp-限定范圍:(限定posX,0,18之間))
Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, new Vector3(Mathf.Clamp(posX, 0, 18), Camera.main.transform.position.y, Camera.main.transform.position.z), SmoothFlo * Time.deltaTime);
}
/// <summary>
/// 播放音效
/// </summary>
/// <param name="clip"></param>
public void AudioPlay(AudioClip clip)
{
AudioSource.PlayClipAtPoint(clip, transform.position); //靜態方法:播放音效
}
/// <summary>
/// 黃色Eva加速方法
/// </summary>
public virtual void EvaYellowExpedite()
{
isFly = false;
AudioPlay(FlyEvaClip);
}
/// <summary>
/// 受傷函數
/// </summary>
public void Hurt()
{
EvaRender.sprite = HurtSprite;
}
}

5
EvaMum —— Eva媽媽腳本(- -敵人 )
提示:由於該游戲邏輯稍易,可被擊打對象為 EvaMum 與 場景中的可被拆除的建築物
所以此腳本可通用於:被擊打物體
至於是否容易被打死,打碎。取決於碰撞物的相對速度 MinSpeed 與 MaxSpeed 可自己設置

using UnityEngine;
/// <summary>
/// EVA媽媽腳本
/// </summary>
public class EvaMum : MonoBehaviour
{
public float MaxSpeed = 8; //默認最大速度
public float MinSpeed = 3; //默認最小速度
private SpriteRenderer Render; //圖片
public Sprite HurtSprite; //受傷圖片
protected GameObject Boom; //爆炸特效
public GameObject EvaMumScore; //分數圖片
public bool isEvaMum; //是不是Eva媽媽
public AudioClip EvaHurtClip; //Eva受傷音效
public AudioClip DeadClip; //銷毀音效
public AudioClip HurtClip; //受傷音效
private void Awake()
{
Render = GetComponent<SpriteRenderer>(); //獲取圖片渲染組件
}
// Use this for initialization
void Start()
{
Boom = Resources.Load<GameObject>("Prefabs/EvaMumBoom");
}
/// <summary>
/// 觸發檢測,檢測是否達到受傷條件
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Eva") //需在外部設置標簽,給Eva物體設置Tag為Eva
{
AudioPlay(EvaHurtClip); //播放受傷音效
collision.transform.GetComponent<Eva>().Hurt(); //受傷
}
if (collision.relativeVelocity.magnitude > MaxSpeed) //如果相對速度.大小>最大速度
{
Dead(); //調用死亡消除方法
}
else if (collision.relativeVelocity.magnitude > MinSpeed && collision.relativeVelocity.magnitude < MaxSpeed) //相對速度在4-8之間
{
Render.sprite = HurtSprite; //更換圖片,受傷
AudioPlay(HurtClip);
}
}
/// <summary>
/// 死亡消除
/// </summary>
public void Dead()
{
if (isEvaMum)
{
GameManager.Instance.EvaMumList.Remove(this); //移除一個EvaMum
}
Destroy(gameObject); //刪除EvaMum物體
Instantiate(Boom, transform.position, Quaternion.identity); //實例化特效
GameObject scoreobj = Instantiate(EvaMumScore, transform.position + new Vector3(0, 0.5f, 0), Quaternion.identity); //實例化分數
Destroy(scoreobj, 1.5f); //刪除分數
AudioPlay(DeadClip); //播放死亡音效
}
/// <summary>
/// 播放音效
/// </summary>
/// <param name="clip"></param>
public void AudioPlay(AudioClip clip)
{
AudioSource.PlayClipAtPoint(clip, transform.position); //靜態方法:播放音效
}
}
1
EvaYellow —— 黃色Eva腳本
黃色小鳥為:加速小鳥,速度乘以2
注意:由於其他特技類Eva,都屬於Eva。
所以只需繼承自Eva,重寫Eva腳本中的特技方法 EvaYellowExpedite()
這里另建一個 EvaYellow 腳本,來重寫 Eva 中的 EvaYellowExpedite() 方法即可

/// <summary>
/// 黃色Eva
/// </summary>
public class EvaYellow : Eva//繼承自父類Eva
{
public override void EvaYellowExpedite()//重寫特技方法
{
base.EvaYellowExpedite();
EvaRg.velocity *= 2; //速度2倍
}
}

2
EvaBlack —— 黑色Eva腳本
黑色小鳥為:爆炸小鳥 —— 變大,且炸掉周邊敵人/物體
注意:由於其他特技類Eva,都屬於Eva。
所以只需繼承自Eva,重寫Eva腳本中的特技方法 EvaYellowExpedite()
這里另建一個 EvaBlack 腳本,來重寫 Eva 中的 EvaYellowExpedite() 方法即可

using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 黑Eva類腳本
/// </summary>
public class EvaBlack : Eva//繼承自父類Eva
{
public List<EvaMum> EvaMumList = new List<EvaMum>(); //聲明一個敵人數組,用來存放EvaMum
public override void EvaYellowExpedite() //重寫特技方法
{
base.EvaYellowExpedite();
if (EvaMumList.Count > 0 && EvaMumList != null) //判空且有EvaMum存在
{
for (int i = 0; i < EvaMumList.Count; i++) //遍歷
{
EvaMumList[i].Dead(); //調用EvaMumList數組中的EvaMum物體的死亡方法
}
}
ClearAction(); //調用爆炸動作函數
}
/// <summary>
/// 碰撞檢測
/// </summary>
/// <param name="col"></param>
private void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "Enemy")
{
EvaMumList.Add(col.GetComponent<EvaMum>()); //觸發器檢測到范圍內:有敵人,就加入敵人數組EvaMumList
}
}
/// <summary>
/// 退出檢測
/// </summary>
/// <param name="col"></param>
private void OnTriggerExit2D(Collider2D col)
{
if (col.tag == "Enemy")
{
EvaMumList.Remove(col.GetComponent<EvaMum>()); //觸發器檢測到范圍內:無敵人,就移除敵人數組EvaMumList
}
}
/// <summary>
/// 處理爆炸動作
/// </summary>
private void ClearAction()
{
transform.localScale = new Vector3(5, 5, 0); //設置自身比例
EvaRg.velocity = Vector3.zero; //速度歸零
myTrail.ClearTrail(); //清除軌跡
}
}

6
GameManager ——游戲控制腳本
用來管理關卡場景中的游戲控制相關操作,掛載到空物體之上
注意:由於其他特技類Eva,都屬於Eva。
所以只需繼承自Eva,重寫Eva腳本中的特技方法 EvaYellowExpedite()
這里另建一個 EvaBlack 腳本,來重寫 Eva 中的 EvaYellowExpedite() 方法即可

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
/// <summary>
/// 游戲控制腳本
/// </summary>
public class GameManager : MonoBehaviour
{
public static GameManager Instance
{
get { return instance; }
set { instance = value; }
}
private static GameManager instance; //單例
public List<Eva> EvaList; //Eva數組
public List<EvaMum> EvaMumList; //EvaMum數組
private Vector3 OriginPos; //Eva初始化位置
public GameObject WinPanel; //勝利游戲面板
public GameObject LosePanel; //輸了游戲面板
public GameObject PausePanel; //輸了游戲面板
public GameObject[] Stars; //星星數組
private Button ListenButton; //按鈕
private Animator PauseAnimator; //暫停動畫
private int StarNum; //星星數量
private bool isPause; //是否暫停
public int StarLevelNum = 0; //開始關卡數
public int EndLevelNum = 3; //結束關卡數
public string LevelCount; //關卡標識符
private int IndexCount = 0; //記錄每關星星數量
void Awake()
{
instance = this;
if (EvaList.Count > 0)
{
OriginPos = EvaList[0].transform.position;
} //如果存在Eva,記錄初始化位置
}
void Start()
{
Initialize(); //調用初始化函數
StarNum = 0;
}
/// <summary>
/// 初始化函數
/// </summary>
private void Initialize()
{
for (int i = 0; i < EvaList.Count; i++)
{
if (i == 0)
{
EvaList[i].transform.position = OriginPos; //給第一個小鳥初始化位置
EvaList[i].enabled = true; //激活第一個Eva腳本
EvaList[i].EvaSP.enabled = true; //激活第一個彈簧鏈接組件
EvaList[i].isCanTrail = true;
}
else
{
EvaList[i].enabled = false; //關閉所有Eva腳本
EvaList[i].EvaSP.enabled = false; //關閉所有彈簧鏈接組件
}
}
}
/// <summary>
/// 判斷是否啟用下一個Eva
/// </summary>
public void NextEva()
{
if (EvaMumList.Count <= 0) //如果敵人依舊存在
{
WinPanel.SetActive(true); //勝利游戲面板
AddButtonListen("WinRePlay");
ListenButton.onClick.AddListener(RePlay);
AddButtonListen("WinMainMenu");
ListenButton.onClick.AddListener(Home);
}
else
{
if (EvaList.Count > 0) //如果Eva存在
{
Initialize(); //初始化Eva
}
else
{
LosePanel.SetActive(true); //結束游戲面板
AddButtonListen("LoseRePlay");
ListenButton.onClick.AddListener(RePlay);
AddButtonListen("LoseMainMenu");
ListenButton.onClick.AddListener(Home);
}
}
}
/// <summary>
/// 贏了顯示星星
/// </summary>
public void WinAndShowStar()
{
StartCoroutine(ShowStars()); //開啟協成,一個個顯示
}
/// <summary>
/// 一個個顯示星星協成
/// </summary>
/// <returns></returns>
private IEnumerator ShowStars()
{
for (; StarNum < EvaList.Count + 1; StarNum++)
{
if (StarNum >= Stars.Length) break; //如果小鳥數量大於星星數量,就跳出:防止越界
yield return new WaitForSeconds(0.5f);
Stars[StarNum].SetActive(true); //開啟星星
}
}
/// <summary>
/// 添加按鈕事件
/// </summary>
private void AddButtonListen(string str)
{
ListenButton = GameObject.Find(str).GetComponent<Button>();
}
/// <summary>
/// 重新開始
/// </summary>
public void RePlay()
{
if (isPause)
{
SceneManager.LoadScene(2);
Time.timeScale = 1;
}
else
{
SceneManager.LoadScene(2);
SaveData(); //儲存數據
}
}
/// <summary>
/// 回到主頁
/// </summary>
public void Home()
{
if (isPause)
{
SceneManager.LoadScene(1);
}
else
{
SceneManager.LoadScene(1);
SaveData(); //儲存數據
Time.timeScale = 1;
}
}
/// <summary>
/// 暫停游戲
/// </summary>
public void PauseGame()
{
PausePanel.SetActive(true);
PauseAnimator = PausePanel.GetComponent<Animator>(); //獲取暫停動畫機
PauseAnimator.SetBool("isPause", true);
AddButtonListen("RePlayButton");
ListenButton.onClick.AddListener(RePlay);
AddButtonListen("HomeButton");
ListenButton.onClick.AddListener(Home);
AddButtonListen("ContinueButton");
ListenButton.onClick.AddListener(PauseResume);
isPause = true; //暫停游戲了
if (GameManager.Instance.EvaList.Count > 0) //如果場景里還有Eva
{
if (GameManager.Instance.EvaList[0].isRelease == false) //如果沒有飛出
{
GameManager.Instance.EvaList[0].isCanTrail = false;
}
}
}
/// <summary>
/// 繼續游戲
/// </summary>
public void PauseResume()
{
Time.timeScale = 1;
PauseAnimator.SetBool("isPause", false);
isPause = false; //關閉暫停
if (GameManager.Instance.EvaList.Count > 0)
{
if (GameManager.Instance.EvaList[0].isRelease == false)
{
GameManager.Instance.EvaList[0].isCanTrail = true;
}
}
}
private int num = 0;
private int IndexCount10 = 0;
private int IndexCount20 = 0;
/// <summary>
/// 存儲數據
/// </summary>
private void SaveData()
{
if (StarNum > PlayerPrefs.GetInt(PlayerPrefs.GetString("NowLevel"))) //判斷
{
PlayerPrefs.SetInt(PlayerPrefs.GetString("NowLevel"), StarNum); //分別設置每個關卡的星星個數
}
//所有星星數量相加
if (LevelCount == "0")//通過標識符判斷是哪一大系列關卡,並對數據進行保存
{
for (int i = StarLevelNum; i <= EndLevelNum; i++)
{
num += PlayerPrefs.GetInt("Level (" + i + ")");
}
PlayerPrefs.SetInt("Level_1", num);
}
else if (LevelCount == "10")
{
for (int i = StarLevelNum; i <= EndLevelNum; i++)
{
num += PlayerPrefs.GetInt("Level (" + i + ")");
}
PlayerPrefs.SetInt("Level_2", num);
}
else if (LevelCount == "20")
{
for (int i = StarLevelNum; i <= EndLevelNum; i++)
{
num += PlayerPrefs.GetInt("Level (" + i + ")");
}
PlayerPrefs.SetInt("Level_3", num);
}
PlayerPrefs.SetInt("AllStarNum",
PlayerPrefs.GetInt("Level_1") + PlayerPrefs.GetInt("Level_2") +
PlayerPrefs.GetInt("Level_3")); //在“AllStarNum”中存儲總星星數量//將所有產生數據的關卡星星數量總和
}
}

7
Next Tutorial —— 下一個教程
至此游戲場景相關結束,需要結合關卡場景
請跳轉至另一個教程 —— Unity3D游戲憤怒的小鳥游戲源碼和教程(二)
支持
May Be —— 搞開發,總有一天要做的事!
|
Chinar 提供一站式教程,閉眼式創建! 為新手節省寶貴時間,避免采坑! |
先點擊領取 —— 阿里全產品優惠卷 (享受最低優惠)
1 —— 雲服務器超全購買流程 (新手必備!)
2 —— 阿里ECS雲服務器自定義配置 - 購買教程(新手必備!)
3—— Windows 服務器配置、運行、建站一條龍 !
4 —— Linux 服務器配置、運行、建站一條龍 !
技術交流群:806091680 ! Chinar 歡迎你的加入
本博客為非營利性個人原創,除部分有明確署名的作品外,所刊登的所有作品的著作權均為本人所擁有,本人保留所有法定權利。違者必究
對於需要復制、轉載、鏈接和傳播博客文章或內容的,請及時和本博主進行聯系,留言,Email: ichinar@icloud.com
對於經本博主明確授權和許可使用文章及內容的,使用時請注明文章或內容出處並注明網址
