「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!」前端
摘要:玩遊戲也能學習知識?還記得高中時的化學元素常見金屬活動性屬性表嗎?一塊兒來看看化學元素和遊戲之間發生的碰撞吧~「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!」算法
玩遊戲也能學習知識?還記得高中時的化學元素常見金屬活動性屬性表嗎?一塊兒來複習一下:鉀K,鈣Ca,鈉Na,鎂Mg,鋁Al,鋅Zn,鐵Fe, 錫Ni,鉛Sn,氫(H),銅Cu,汞Hg,銀Ag,鉑Pt,金Au。 一股很熟悉的味道有沒有?一塊兒來看看化學元素和遊戲之間發生的碰撞吧~後端
2048 一款益智小遊戲,遊戲的規則十分簡單,簡單易上手的數字小遊戲,閒來無事,本身製做一個,卻怎麼也到沒有成功到2048。數組
老規矩,先看下按照此博文一步步操做完成的效果吧~markdown
打開Unity Hub 點擊「新建」,在彈窗中輸入 --> "項目名稱" --> 選擇"項目位置" --> "項目版本控制系統" 選不選都行,不須要在雲端備份的話就不要選(我通常不選) ,選了沒用過的話,會自動幫你安裝PlasticSCM到本地。最後點擊建立便可。dom
將提早準備好的素材(圖片和聲音)導入工程。(文末會提供下載地址)oop
導入項目後工程目錄以下: post
設置分辨率:點擊Game視圖分辨率選框,設置爲(1080x1920),沒有的話點擊加號自行添加一個,或者使用一個豎屏的就能夠。學習
建立背景:右鍵UI --> Image 建立後,重命名爲BG,將其錨點設置爲鋪滿,源文件指定爲素材"play_bg_forest_dark":測試
而後在「MapBg」下再次建立一個Image,重命名爲「gezi」,將其寬高設置爲(400,388),調整其顏色爲(22,0,192,30),仍是同樣調整到本身喜歡的顏色便可。而後「Ctrl + D」 複製15個格子出來:
最後在「MapBg」上添加"Grid Layout Group",屬性設置以下圖,將格子鋪滿到地圖背景上:
場景終於搭建完了,下面開始編寫腳本吧,完整代碼在四中給出,這裏只講解實現思路,查看核心邏輯。
在Project下建立Scripts文件夾,而後建立"Manager.cs" 和 "Number.cs" 腳本。
基礎邏輯就是:默認生成兩個數字,接收用戶輸入,管理器觸發移動邏輯,數字移動並校驗合併,移動後校驗是否遊戲結束,若結束處理遊戲結束摞,若未結束在自動生成一個數字:
graph TD 默認生成數字 --> 管理器接收用戶輸入 --> 管理器觸發數字移動邏輯 --> 數字移動並校驗合併 --> 移動後校驗遊戲結束 --結束--> 處理遊戲結束邏輯 移動後校驗遊戲結束 --繼續--> 默認生成數字
此時全部的遊戲邏輯就都完成,運行遊戲就能夠玩耍了~
音效是這一個遊戲的很重要的部分,一個好的音效可讓用戶獲得更好的反饋,因此再簡單的遊戲也要有背景音和音效給用戶一個好的交互體驗。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Manager : MonoBehaviour
{
public static Manager _isnstance; //單例模式的引用
public Transform poolManager; //生成數字的池子
private GameObject numPrefab; //數字的預製體
public Number[,] numbers = new Number[4, 4]; //保存方格中的數組
//正在移動中的Num
public List<Number> isMovingNum = new List<Number>();
public bool hasMove = false; //是否有數字發生了移動
public GameObject UIFinsh; //遊戲結束頁面
void Awake()
{
_isnstance = this;
}
void Start()
{
numPrefab = Resources.Load<GameObject>("Num");
// 開始遊戲
ReStartBtn();
// 遊戲結束面板按鈕監聽,從新開始
UIFinsh.GetComponentInChildren<Button>().onClick.AddListener(ReStartBtn);
}
// 從新開始
void ReStartBtn()
{
isMovingNum.Clear();
numbers = new Number[4, 4];
for (int i = poolManager.childCount - 1; i >= 0; i--)
{
Destroy(poolManager.GetChild(i).gameObject);
}
hasMove = false;
//遊戲開始生成兩個數字
CreateNun();
CreateNun();
UIFinsh.SetActive(false);
}
void CreateNun()
{
GameObject go = Instantiate(numPrefab);
go.transform.parent = poolManager;
go.transform.localScale = Vector3.one;
}
#region 檢測鍵盤和觸摸輸入
void Update()
{
//--------- 移動端檢測邏輯 ---------
//有觸摸點,且滑動
if (isMovingNum.Count == 0)
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
int dieX = 0;
int dieY = 0;
//獲取滑動的距離
Vector2 touchDelPos = Input.GetTouch(0).deltaPosition;
if (Mathf.Abs(touchDelPos.x) > Mathf.Abs(touchDelPos.y))
{
//滑動距離
if (touchDelPos.x > 10)
{
dieX = 1;
}
else
if (touchDelPos.x < -10)
{
dieX = -1;
}
}
else
{
if (touchDelPos.y > 10)
{
dieY = 1;
}
else if (touchDelPos.y < -10)
{
dieY = -1;
}
}
MoveNum(dieX, dieY);
}
}
//--------- PC端檢測邏輯 ---------
if (isMovingNum.Count == 0)
{
int dieX = 0;
int dieY = 0;
if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.LeftArrow))
{
dieX = -1;
}
else
if (Input.GetKeyDown(KeyCode.D) || Input.GetKeyDown(KeyCode.RightArrow))
{
dieX = 1;
}
else
if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow))
{
dieY = 1;
}
else
if (Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow))
{
dieY = -1;
}
MoveNum(dieX, dieY);
}
if (hasMove && isMovingNum.Count == 0) //生成新的數字
{
CreateNun();
hasMove = false;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (numbers[i, j] != null)
{
numbers[i, j].OneMove = false;
}
}
}
}
}
#endregion
#region 遊戲邏輯
/// <summary>
/// 數字移動方法
/// </summary>
/// <param name="directionX"></param>
/// <param name="directionY"></param>
public void MoveNum(int directionX, int directionY)
{
if (directionX == 1) //向右移動
{
//首先將空格填滿 最右側列不需作判斷
for (int j = 0; j < 4; j++)
{
for (int i = 2; i >= 0; i--)
{
if (numbers[i, j] != null) //格子中有物體(數字),,調用移動方法
{
numbers[i, j].Move(directionX, directionY);
}
}
}
}
else
//===========向左移動==================
if (directionX == -1)
{
for (int j = 0; j < 4; j++)
{
for (int i = 1; i < 4; i++)
{ //最左側的一列 [0,0] [0,1] [0,2] [0,3]
if (numbers[i, j] != null)
{
numbers[i, j].Move(directionX, directionY);
}
}
}
}
else
//===========向上移動==================
if (directionY == 1)
{
for (int i = 0; i < 4; i++)
{
for (int j = 2; j >= 0; j--)
{
if (numbers[i, j] != null)
{
numbers[i, j].Move(directionX, directionY);
}
}
}
}
else
//===========向下移動==================
if (directionY == -1)
{
for (int i = 3; i >= 0; i--)
{
for (int j = 0; j < 4; j++)
{
if (numbers[i, j] != null) //有物體(數字)就移動
{
numbers[i, j].Move(directionX, directionY);
}
}
}
}
}
/// <summary>
/// 判斷是不是空格的方法
/// </summary>
/// <param name="x">數組索引X</param>
/// <param name="y">數組索引Y</param>
/// <returns></returns>
public bool isEmpty(int x, int y)
{
if (x < 0 || x > 3 || y < 0 || y > 3)
{
return false;
}
else if (numbers[x, y] != null)
{
return false;
}
return true;
}
/// <summary>
/// 判斷遊戲是否結束
/// </summary>
/// <returns>返回true則遊戲結束</returns>
public bool isDead()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (numbers[i, j] == null)
{
return false;
}
}
}
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 3; i++)
{
if (numbers[i, j].value == numbers[i + 1, j].value)
{
return false;
}
}
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
if (numbers[i, j].value == numbers[i, j + 1].value)
{
return false;
}
}
}
return true;
}
#endregion
/// <summary>
/// 遊戲結束
/// </summary>
/// <param name="isSuccess">false:輸,true:贏</param>
public void ShowUIFinsh(bool isSuccess)
{
UIFinsh.SetActive(true);
if (isSuccess)
{
UIFinsh.GetComponentInChildren<Text>().text = "遊戲成功";
}
else
{
UIFinsh.GetComponentInChildren<Text>().text = "遊戲失敗";
}
}
}
複製代碼
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using UnityEngine.UI;
using Vector3 = UnityEngine.Vector3;
public class Number : MonoBehaviour
{
//在二維數組中的位置X,Y
public int posX;
public int posY;
private int offsetX = -620; //顯示偏移,Y,,,
private int offsetY = -620;
private int space = 420; // 間距
private bool isMoving = false; //動畫是否播放過的計數
public int value; //產生數字是幾
private bool toDestroy; //判斷數字是否銷燬
public bool OneMove = false; //標識數字是否合併過一次
// Use this for initialization
void Start()
{
// 80%成2的機率,更改自己的Sprite名字,以更換圖片
value = Random.value > 0.2f ? 2 : 4;
this.GetComponent<Image>().sprite = LoadSprite();
do
{
posX = Random.Range(0, 4);
posY = Random.Range(0, 4);
} while (Manager._isnstance.numbers[posX, posY] != null);
transform.localPosition = GetLocalPos();
// 存放數字自己到數組中,表示此位置有數字不能生成新的數字
Manager._isnstance.numbers[posX, posY] = this;
if (Manager._isnstance.isDead())
{
// 遊戲失敗
Manager._isnstance.ShowUIFinsh(false);
}
}
// Update is called once per frame
void Update()
{
//播放一次動畫
if (!isMoving)
{
if (transform.localPosition != GetLocalPos())
{
isMoving = true;
StartCoroutine(MoveAni());
}
}
}
// 移動動畫
IEnumerator MoveAni()
{
Debug.Log("移動動畫...");
float t = 0;
for (int i = 0; i < 10; i++)
{
transform.localPosition = Vector3.Lerp(transform.localPosition, GetLocalPos(), t);
t += 0.1f;
yield return new WaitForEndOfFrame();
}
// 移動結束的回調
MoveOver();
}
#region 遊戲核心移動算法
/// <summary>
/// 核心,移動方法(有空格,有物體是否同樣)
/// </summary>
public void Move(int directionX, int directionY)
{
//Debug.Log("測試");
//==========向右移動==================
if (directionX == 1)
{
int index = 1; // 空格標誌
while (Manager._isnstance.isEmpty(posX+index,posY))
{
index++;
}
// 有空格的移動
if (index>1)
{
if (!Manager._isnstance.isMovingNum.Contains(this))
{ // 保證不會重複添加物體(數字)到列表,
Manager._isnstance.isMovingNum.Add(this);
}
//移動一次,就生成兩個數字的標誌符
Manager._isnstance.hasMove = true;
//向空格位置移動
Manager._isnstance.numbers[posX, posY] = null;
posX = posX + index - 1;
Manager._isnstance.numbers[posX, posY] = this;
}
//有相同數字的移動
if (posX < 3 && value == Manager._isnstance.numbers[posX+1,posY].value &&
!Manager._isnstance.numbers[posX+1,posY].OneMove)
{
// 只合並一次的標誌
Manager._isnstance.numbers[posX + 1, posY].OneMove = true;
// 移動的標誌,(生成新的物體(數字))
Manager._isnstance.hasMove = true;
// 動畫播放的限定(有數字在列表中就不會重複播放第二次動畫)
// 不會重複添加物體(數字)到列表,
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
// 碰到同樣的數字,講位置設爲空 並銷燬自己標識(true),
// 再將其位置上的值變爲2倍,(更換成新的數字)
toDestroy = true;
Manager._isnstance.numbers[posX, posY] = null;
Manager._isnstance.numbers[posX + 1, posY].value *= 2;
posX += 1;
}
}else
//===========向左移動==================
if (directionX == -1)
{
int index = 1;
while (Manager._isnstance.isEmpty(posX - index, posY))
{
index++;
}
//有空格的移動
if (index > 1)
{
Manager._isnstance.hasMove = true;
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
Manager._isnstance.numbers[posX, posY] = null;
posX = posX - index + 1;
Manager._isnstance.numbers[posX, posY] = this;
}
//碰到相同數字的移動
if (posX > 0 && value == Manager._isnstance.numbers[posX - 1, posY].value &&
!Manager._isnstance.numbers[posX - 1, posY].OneMove)
{
Manager._isnstance.numbers[posX - 1, posY].OneMove = true;
Manager._isnstance.hasMove = true;
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
toDestroy = true;
Manager._isnstance.numbers[posX, posY] = null;
Manager._isnstance.numbers[posX - 1, posY].value *= 2;
posX -= 1;
}
}else
//===========向上移動==================
if (directionY == 1)
{
int index = 1; //空格標誌
while (Manager._isnstance.isEmpty(posX , posY + index))
{
index++;
}
//有空格的移動
if (index > 1)
{
Manager._isnstance.hasMove = true;
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
Manager._isnstance.numbers[posX, posY] = null;
posY = posY + index - 1;
Manager._isnstance.numbers[posX, posY] = this;
}
//有相同位置的移動
if (posY < 3 && value == Manager._isnstance.numbers[posX , posY + 1].value && !Manager._isnstance.numbers[posX, posY + 1].OneMove)
{
Manager._isnstance.numbers[posX , posY + 1].OneMove = true;
Manager._isnstance.hasMove = true;
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
toDestroy = true;
Manager._isnstance.numbers[posX, posY] = null;
Manager._isnstance.numbers[posX , posY + 1].value *= 2;
posY += 1;
}
}else
//===========向下移動==================
if (directionY == -1)
{
int index = 1; //空格標誌位
while (Manager._isnstance.isEmpty(posX, posY - index))
{
index++;
}
//有空格的移動
if (index > 1)
{
Manager._isnstance.hasMove = true;
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
Manager._isnstance.numbers[posX, posY] = null;
posY = posY - index + 1;
Manager._isnstance.numbers[posX, posY] = this;
}
//有相同數字的移動
if (posY > 0 && value == Manager._isnstance.numbers[posX, posY - 1].value && !Manager._isnstance.numbers[posX, posY - 1].OneMove)
{
Manager._isnstance.numbers[posX, posY -1].OneMove = true;
Manager._isnstance.hasMove = true;
if (!Manager._isnstance.isMovingNum.Contains(this))
{
Manager._isnstance.isMovingNum.Add(this);
}
toDestroy = true;
Manager._isnstance.numbers[posX, posY] = null;
Manager._isnstance.numbers[posX, posY - 1].value *= 2;
posY -= 1;
}
}
}
#endregion
/// <summary>
/// 動畫結束,標誌改成false
/// </summary>
public void MoveOver()
{
isMoving = false;
//若碰到了相同的數字 銷燬本身,和改變另外一個圖片(數字)
if (toDestroy)
{
Destroy(this.gameObject);
value = Manager._isnstance.numbers[posX, posY].value;
Manager._isnstance.numbers[posX, posY].GetComponent<Image>().sprite = LoadSprite();
//遊戲成功
if (value == 4096)
{
Manager._isnstance.ShowUIFinsh(true);
}
}
Manager._isnstance.isMovingNum.Remove(this);
}
Vector3 GetLocalPos()
{
return new Vector3(offsetX + posX * space, offsetY + posY * space, 0);
}
/// <summary>
/// 根據數字加載對應圖片
/// </summary>
/// <returns></returns>
Sprite LoadSprite()
{
return Resources.Load<Sprite>(value.ToString());
}
}
複製代碼
本文重新建項目開始一步一步帶你完成全部步驟,還在等什麼呢?三連支持一下吧~
須要素材或者源碼的小夥伴能夠評論區留言哦~