前言
真的如标題所說肝了好幾天晚上。本來是想着拿之前做的項目直接來寫教程的;寫着寫着發現了很多做的不好的地方,又重新整理修改。其實完全就是重新做了一個項目。文章從建立工程開始一步步帶你完成零基礎也能制作的像素鳥遊戲。還原經典玩法,一起來嘗試下吧~
老規矩,先看下最終實作效果:
怎麼樣?有心動嗎?想體驗下自己制作完成的成就感嗎? 下面開始制作過程吧~
一,建立目錄
1.1 建立工程後建立目錄如下圖:
建立的目錄存放資源分别對應:
- Audios:音頻資源, Materials:材質球, Prefabs:預制體, Scenees:遊戲場景, Scripts:腳本資源, Texture:圖檔資源
1.2 導入提前準備好的音效和圖檔資源并放到對應目錄:【文末提供】
1.3 導入後将UI使用的圖檔資源格式修改為Sprite(2D and UI),按住Ctrl選中gameover,score和start三個圖檔進行個數修改,如下圖:
然後找到點選下面的“Apply”按鈕:
修改完成後是如下效果:
二,制作材質
2.1 制作材質球(背景,管道和像素鳥)
在Materials檔案上右鍵,選擇Create --> Material 建立材質球:
然後修改其名稱為bg,修改其Shader為Unlit/Transparent:
最後為其指派為背景圖檔 --> 點選Select按鈕 --> 在彈窗中輕按兩下bg圖檔即可:
然後選中bg材質球,使用快捷鍵 Ctrl+D 複制三個材質球:
修改其名稱并重複上面修改圖檔的步驟将其挨個修改為back,brid,tipe (别忘了将材質球的貼圖重新指定一下哦) , 全部處理完的效果如下:
三,場景搭建
3.1 修改主錄影機屬性:
- 修改其坐标為(0,0,-2),旋轉為(0,0,0),縮放為(1,1,1)
- 将其修改為正交錄影機 (Projection --> Orthographic)
3.2 修改遊戲尺寸
點選Game視圖下的分辨率選擇面闆,在點選最下面“+”建立新的分辨率,屬性值設定如下:
然後再次點選分辨率選擇面闆,選擇剛剛建立的屬性:
四,建立地圖
4.1 建立背景
在Hierarchy空白處右鍵 --> 選擇3D Object --> 選擇Quad 建立出來作為背景。
若建立出來在Game視圖顯示不出來的話,修改其Position坐标為(0,0,0),修改Scale縮放為(10,12,1),最後将我們之前做好的bg材質球指派給Quad,得到效果如下:
4.2 建立底部
選中上面建立的背景“Quad”,右鍵 選擇3D Object --> 選擇Quad 在建立一個,然後将back的材質球指派給它,得到下圖結果:
4.3 整理位置
将背景“Quad” 重命名為BG,其子物體重命名為Back。然後修改BG的Position坐标為(-4,-1,0),使其在最左端出現,效果如下:
4.4 移除碰撞體
選中BG,在Inspector面闆“Mesh Collider”元件上,右鍵選擇“Remove Component”來移除碰撞體元件:
同理選中Quad,移除“Mesh Collider”元件。
五,制作管道
5.1 複制背景鋪滿螢幕
選中Hierarchy --> BG 使用快捷鍵 Ctrl+D 複制一份,将其坐标調整為(6,-1,0),調整後效果如下:
5.2 制作一個管道
在新複制出來的BG (1) 下面建立一個空物體命名為Pipe1作為上下兩個管子的父物體,設定坐标,旋轉為(0,0,0),縮放為(1,1,1):
然後選中Pipe1,再按照上面的步驟建立Quad命名為Up,材質球指定為tipe;設定其坐标(0,0.7,0),旋轉(0,0,0),縮放(0.07,1,1);
然後選中Up,Ctrl+D複制一份,将其坐标修改為(0,-0.5,0),其他屬性都不動,得到效果如下:
5.3 調整位置
現在可看到現在下面的管子是暴露在地圖外面,調整下下部背景Back的z軸為-1,得到效果如下:
六,建立主角
6.1 建立小鳥
按照上面的步驟再次建立一個Quad命名為Brid,設定Position為(0,0,-1),然後将材質球指定為brid,效果如下:
6.2 設定Brid的Tag标簽為Player:
6.3 修改材質球屬性
修改Tiling(0.3333,1), Offset(0.3333,1) 修改後效果如下:
6.4 讓小鳥飛起來
建立腳本
BridHandler
挂載到Brid上,内容編輯如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BridHandler : MonoBehaviour {
//單例
public static BridHandler intance;
private void Awake()
{
intance = this;
}
private Renderer _renderer; // 材質
private float timer; //計時器,
private int frameNum =10; // 每秒顯示幾幀
private int frameCount; // 幀的計數器
void Start () {
_renderer = this.GetComponent<Renderer>();
}
// Update is called once per frame
void Update () {
timer += Time.deltaTime; //加上一幀的時間
if (timer >= 1.0f / frameNum) //大于1幀所用的時間
{
frameCount++; //幀數增加
timer -= 1.0f / frameNum;
//三幀 (0,1,2顯示每一幀畫面)
int frameIndex = frameCount % 3;
//更新offset x 屬性
_renderer.material.SetTextureOffset("_MainTex", new Vector2(0.33333f * frameIndex, 0));
}
}
}
運作後,即可看到小鳥在飛了,效果如下:
七,小鳥動起來
7.1 在主角Brid上面添加Rigidbody剛體元件:
并将其Constraint --> Freeze Rotation 的XZY全部勾選上,為避免其進行旋轉:
7.2 在BridHandler腳本添加腳本如下:
private Rigidbody _rigidbody; //剛體元件
private float x = 3; //小鳥運作速度
void Start () {
_rigidbody = this.GetComponent<Rigidbody>();
_rigidbody.useGravity = false;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
StartGame();
}
}
public void StartGame()
{
_rigidbody.useGravity = true;
_rigidbody.velocity = new Vector3(x, 0, 0);
}
7.3 然後運作點選空白處,就可以看到小鳥動起來了:
7.4 處理報錯
若你的工程運作後報錯(沒報錯可忽略)
勾選Brid的Mesh Collider的 Convex屬性即可:
添加Sphere Collider 元件
調整碰撞體半徑為0.28,在Scene看下和鳥的顯示輪廓保持一緻即可:
八,遊戲狀态控制
8.1 建立GameManager腳本,并挂載到場景中。
添加腳本内容如下:
using UnityEngine;
public enum GAME_STATUS
{
// 遊戲開啟
GAME_START = 0,
// 遊戲中
GAME_PLAYING = 1,
// 遊戲結束
GAME_END = 2
}
public class GameManager : MonoBehaviour {
//單例
public static GameManager intance;
private void Awake()
{
intance = this;
}
/// <summary>
/// 目前遊戲狀态
/// </summary>
public GAME_STATUS GameState = GAME_STATUS.GAME_START;
// 需要移動的背景
public Transform moveBgTrans;
void Start()
{
}
void Update () {
switch (GameState)
{
case GAME_STATUS.GAME_START:
//點選螢幕 開始遊戲
if (Input.GetMouseButtonDown(0))
{
GameState = GAME_STATUS.GAME_PLAYING;
BridHandler.intance.StartGame();
}
break;
case GAME_STATUS.GAME_PLAYING:
break;
case GAME_STATUS.GAME_END:
break;
default: break;
}
}
}
8.2 在Hierarchy上右鍵,建立一個空物體GameObject,并将其名稱修改為GameManager。
8.3 修改BridHandler.cs腳本Update方法,其中添加遊戲狀态控制和小鳥跳的邏輯,具體代碼如下:
void Update()
{
if (GameManager.intance.GameState == GAME_STATUS.GAME_PLAYING) //可以跳的狀态
{
Vector3 vel = _rigidbody.velocity;
//跳
if (Input.GetMouseButtonDown(0))
{
vel.x += 0.05f; // 每跳一次增加一點移速
_rigidbody.velocity = new Vector3(vel.x, 5, vel.z);
Debug.Log(vel);
}
//通過遊戲狀态控制,是否播放此動畫
timer += Time.deltaTime; //加上一幀的時間
if (timer >= 1.0f / frameNum) //大于1幀所用的時間
{
frameCount++; //幀數增加
timer -= 1.0f / frameNum;
//三幀 (0,1,2顯示每一幀畫面)
int frameIndex = frameCount % 3;
//更新offset x 屬性
_renderer.material.SetTextureOffset("_MainTex", new Vector2(0.33333f * frameIndex, 0));
}
}
}
8.4 修改小鳥的初始位置偏左上角一點,坐标為(-2,1.8,-1):
8.5 此時再次運作,點選螢幕則小鳥就可以跳了:
九,錄影機跟随
9.1 建立腳本
FollowBrid
,并附加到錄影機上:
using UnityEngine;
//攝像頭跟随
public class FollowBrid : MonoBehaviour {
private Transform bridTrans;
// Use this for initialization
void Start () {
bridTrans = GameObject.FindGameObjectWithTag("Player").transform;
}
// Update is called once per frame
void Update () {
Vector3 bridPos = bridTrans.position;
float y = bridPos.y - 4.4f;
//限制錄影機的最大最小位置
if (y > 1.5f)
{
y = 1.5f;
}
if (y < 0 )
{
y = 0f;
}
this.transform.position = new Vector3(bridPos.x + 3.63223f,y, -10);
}
}
9.2 運作效果如下:
十,添加碰撞檢測
10.1 給底部加碰撞體: 選中Back添加"Box Collider"碰撞體(注意兩個Back都要添加哦),添加後将其偏移和大小分别設定為(0,-0.36,0)和(1,0.28,2),如下圖:
10.2 給管子碰撞體: 首先選中Up移除自身的“Mesh Collider”元件,然後添加"Box Collider",修改size為(1,1,2);Up (1)也做如上操作,這樣上下兩個管子就都有碰撞體了,效果如下:
10.3 添加遊戲接觸檢測,建立腳本
PipUporDown
,給上面添加碰撞體的幾個遊戲物體都挂載上,這樣小鳥碰到柱子和底部就可以觸發遊戲結束了,腳本内容如下:
public class PipUporDown : MonoBehaviour {
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "Player")
{
// 遊戲結束
GameManager.intance.GameState = GAME_STATUS.GAME_END;
}
}
}
10.4 給管子父物體添加Box Collider 碰撞體,将其中心調整為(0,0.1,0),大小(0.1,0.2,2),位置設為兩個管子中間即可。勾選Is Trigger 用于觸發穿過得分:
PS:注意這個碰撞體size值為(0.1,0.2,2)哦。
10.5 添加管子高度随機邏輯和得分邏輯: 建立PipeHandler腳本,并挂載BG(1) --> Pipe1 上,用來随機管子的顯示高度,代碼如下:
using UnityEngine;
public class PipeHandler : MonoBehaviour {
private void Start()
{
RandomPos();
}
/// <summary>
/// 産生随機數
/// </summary>
public void RandomPos()
{
float pos_y = Random.Range(-0.1f, -0.1f);
this.transform.localPosition = new Vector3(this.transform.localPosition.x, pos_y, this.transform.localPosition.z);
}
// 穿過觸發得分:
private void OnTriggerExit(Collider other)
{
if(other.tag == "Player")
{
Debug.Log("todo...加分");
}
}
}
10.6 複制一份管子Pipe1,并将其位置調整為(0.5,0,0),這樣一個地圖兩個管道了,這個間距比較符合遊戲難度,效果如下圖:
十一,完善背景
11.1 處理背景連續1: 選擇BG (1) 右鍵建立空物體重命名為“MoveTrrgger”,調整位置為(1.3,0,-1),并為其添加Box Collider 元件大小修改為(0.1,1,1),效果如下:
11.2 處理背景連續2: 建立腳本
MoveTrigger
,将其挂載到10.4建立的“MoveTrrgger”物體上,代碼内容如下:
using UnityEngine;
public class MoveTrigger : MonoBehaviour
{
public Transform currentBg;
public PipeHandler pipe1;
public PipeHandler pipe2;
private void OnTriggerExit(Collider other)
{
// Debug.Log("觸發到背景移動");
if (other.tag == "Player")
{
//前面的背景移動到後面
//擷取第一個背景位置
Transform firstbg = GameManager.intance.moveBgTrans;
//移動
currentBg.position = new Vector3(firstbg.position.x + 10, currentBg.position.y, currentBg.position.z);
//更新
GameManager.intance.moveBgTrans = currentBg;
pipe1.RandomPos();
pipe2.RandomPos();
}
}
}
11.3 給代碼指派,指派情況如下:
11.4 複制兩份BG(1),得到BG(2),BG(3)位置分别設定為(16,0,0),(26,0,0),效果如下:
11.5 給
GameManager
中 moveBgTrans 屬性指派
11.6 此時運作則可以實作背景的無限連接配接了:
至此遊戲主體邏輯已經完成大部分了,還差UI和音效兩部分就完成了哦,加油加油!
十二,UI部分
12.1 分數統計
在Hierarchy空白處右鍵,建立UI --> Text, 并将其錨點調整左上角,文本大小修改為(300,100),字型大小: 42,效果如下:
12.2 遊戲結束
在Canvas下建立Image,作為遊戲結束的背景闆,使其鋪滿螢幕并調整其透明度為100,并将其重命名為"UIBG":
在"UIBG"下建立再建立一個Image作為分數背景闆,将score指派給它:
在"UIBG"下建立兩個文本作為本局得分和最高分數顯示分别命名為"Score"和"Best",調整位置文本如下:
12.3 重新開始
在"UIBG"下建立一個Button按鈕作為重新開始按鈕,隐藏其子物體Text,Image指派為start,位置調整如下圖:
12.4 邏輯處理
GameManager類,完整代碼如下:
public class GameManager : MonoBehaviour {
//單例
public static GameManager intance;
private void Awake()
{
intance = this;
}
/// <summary>
/// 目前遊戲狀态
/// </summary>
public GAME_STATUS GameState = GAME_STATUS.GAME_START;
// 需要移動的背景
public Transform moveBgTrans;
// 分數
public Text ScoreText;
// 遊戲結束UI
public GameObject GameOverUI;
// 遊戲結束得分
public Text GameOverScore;
// 遊戲結束最高得分
public Text GameOverBest;
// 遊戲結束重開按鈕
public Button GameOverButton;
// 本局得分
int score = 0;
void Start()
{
GameOverButton.onClick.AddListener(()=>{
SceneManager.LoadScene(0);
});
}
void Update () {
switch (GameState)
{
case GAME_STATUS.GAME_START:
GameOverUI.SetActive(false);
//點選螢幕 開始遊戲
if (Input.GetMouseButtonDown(0))
{
GameState = GAME_STATUS.GAME_PLAYING;
BridHandler.intance.StartGame();
}
break;
case GAME_STATUS.GAME_PLAYING:
break;
case GAME_STATUS.GAME_END:
// 遊戲結束,顯示面闆 ,維護分數
GameOverUI.SetActive(true);
GameOverScore.text = score.ToString();
GameOverBest.text = PlayerPrefs.GetInt("Best").ToString();
break;
default: break;
}
}
// 分數更新
public void UpdateScore()
{
//分數增加
score++;
ScoreText.text = "分數:" + score;
// 維護最高得分
if (score > PlayerPrefs.GetInt("Best"))
{
PlayerPrefs.SetInt("Best", score);
}
}
}
給GameManager公有變量拖住指派:
再修改
PipeHandler
腳本的
OnTriggerExit()
方法中寫:
private void OnTriggerExit(Collider other)
{
if(other.tag == "Player")
{
GameManager.intance.UpdateScore();
}
}
12.5 添加場景
File --> Build Settings --> Add Open Scenes 添加場景,為打包和重玩邏輯做準備:
至此遊戲主體邏輯已經完成,還差最後一步吧音效添加上就可以了。
十三,添加音效
13.1 添加背景音樂
在Main Camera上添加"Audio Source"元件
将bgm音效指派給AudioClip屬性,勾選Loop屬性一遍循環播放:
13.2 添加跳躍音效
在Brid上添加"Audio Source"元件,将sfx_jump音效指派給AudioClip屬性,取消勾選Play On Awake屬性避免自動播放:
編輯
BridHandler
腳本,添加下面三行代碼到,下圖位置
// 聲明跳躍音效
private AudioSource Jumpaudio;
// 指派
Jumpaudio = this.GetComponent<AudioSource>();
// 播放
Jumpaudio.Play();
13.3 添加得分音效
在Brid上添加"Audio Source"元件,将sfx_point音效指派給AudioClip屬性,取消勾選Play On Awake屬性避免自動播放:
GameManager
腳本,添加下面三行代碼到,下圖位置:
private AudioSource getSecoreAudio;
getSecoreAudio = GetComponent<AudioSource>();
getSecoreAudio.Play();
至此所有遊戲邏輯就都完成了,可以玩耍了~
十四,寫在結尾
說好的:
文末源碼開發環境Unity2019.4.19 (支援5.x - 2021.x所有版本)。
已測試打包平台 --> Window, Mac, Android ,IOS, WebGL。
終于是制作完成了,雖然文章文字不多,但是結合配圖已經講解的很詳細了。文章很長,建議收藏。原創不易,三連支援下再走吧~