一、前言
2048休闲小游戏算是以前都接触过得小游戏,刚开始学完Unity做的第一个小Demo就是它,奈何有些判断算法没搞懂就一直落空了。最近也是看见自己电脑素材库里有2048的素材然后就打算重拾2048自己做。
二、主要UI界面和对象树
三、核心代码
1.声明部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Random = UnityEngine.Random;
using System.Threading;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
using Unity.VisualScripting;
using static Unity.Burst.Intrinsics.X86.Avx;
using UnityEngine.SocialPlatforms.Impl;
public class GameManager : MonoBehaviour
{
private SpriteRenderer[] Square_list; //存格子
private Sprite[] sprite2048 = new Sprite[12]; //存图片2,4,8
private int move_state=0; //1:上移 -1:下移 2:右移 -2:左移
private int[] array16 = new int[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //存格子信息
private bool gameover = false;
public GameObject gameOver;
public GameObject Win;
int Score = 0; //得分
int BestScore; //最高分
public Text ScoreText;
public Text BestScoreText;
public AudioSource[] Musiclist; //声音组件
private void Awake()
{
//加载sprite资源
sprite2048 = Resources.LoadAll<Sprite>("2048");
// 获取所有方格对象
Square_list = GetComponentsInChildren<SpriteRenderer>();
}
void Start()
{
array16[0] = 1; //手动初始化
Square_list[0].sprite = sprite2048[1];
array16[10] = 1;
Square_list[10].sprite = sprite2048[1];
array16[11] = 1;
Square_list[11].sprite = sprite2048[1];
array16[12] = 1;
Square_list[12].sprite = sprite2048[1];
//获取本地存储的最大分数
BestScoreText.text = PlayerPrefs.GetInt("BestScore").ToString();
}
主要变量:
sprite2048(2,4,8图片,用 Resources获取资源)
Square_list(方格,获取本身子对象就行了)
array16[16](方格信息,其实就是字符数组存储16格子的信息)
2.移动判断(Press)
void Press()
{
//手机端
if (Application.platform == RuntimePlatform.Android && Input.touchCount == 1 && Input.touches[0].phase == TouchPhase.Moved)
{
float s01 = Input.GetAxis("Mouse X");
float s02 = Input.GetAxis("Mouse Y");
float distence = 5f;
if (s01 < -1 * distence && s02 > -1 * distence && s02 < distence)
{
move_state = -2;
}
else if (s01 > distence && s02 > -1 * distence && s02 < distence)
{
move_state = 2;
}
else if (s02 > distence && s01 > -1 * distence && s01 < distence)
{
move_state = 1;
}
else if (s02 < -1 * distence && s01 > -1 * distence && s02 < distence)
{
move_state = -1;
}
}
//电脑端
else if (Application.platform == RuntimePlatform.WindowsEditor)
{
if (Input.GetKeyDown(KeyCode.UpArrow))
move_state = 1;
else if (Input.GetKeyDown(KeyCode.DownArrow))
move_state = -1;
else if (Input.GetKeyDown(KeyCode.LeftArrow))
move_state = -2;
else if (Input.GetKeyDown(KeyCode.RightArrow))
move_state = 2;
}
if (move_state != 0)
{
Merge_Grid(); //移动
}
}
该函数主要通过变量move_state来接收用户操作的方向(1上,-1下,-2左,2右),还用Application对操作平台进行了判断,在安卓端则通过鼠标Mouse X/Y的返回来判断方向,在Windous端则是通过键盘输入来判断方向(之后我也会想鼠标滑动方面优化的,偷懒--)。在接收完方向数据后再通过Merge_Grid来移动。
3.移动合并(Merge_Grid)
void Merge_Grid()
{
// 1=up, -1=down, 2=right, -2=left
switch (move_state)
{
case 1:
Debug.Log("up"); Musiclist[0].Play(); //播放声音
for (int i = 0; i < 12; i++)
{
//向上紧凑
int k = i;
for (int j = 1; i + j * 4 < 16; j++)
{
//这段用来将空白格和数字格互换
if (array16[i + j * 4] == 0 && array16[k] != 0) k = i + j * 4;
if (array16[i + j * 4] != 0 && array16[k] == 0)
{
array16[k] = array16[i + j * 4];
array16[i + j * 4] = 0;
k += 4;
}
}
if (array16[i] == 0) continue; //为0不合并
//合并
if (array16[i] == array16[i + 4]) //如果上下两数相同则合并
{
Score += (int)array16[i] + (int)array16[i+4]; //算得分
RecordScore(Score); //记录得分
array16[i]++; //合并操作,实际就是提高一个档次
array16[i + 4] = 0; //合并者归0
}
}
break;
case -1:
Debug.Log("down"); Musiclist[0].Play();
for (int i = 15; i > 3; i--)
{
//向下紧凑
int k = i;
for (int j = 1; i - j * 4 >= 0; j++)
{
if (array16[i - j * 4] == 0 && array16[k] != 0) k = i - j * 4;
if (array16[i - j * 4] != 0 && array16[k] == 0)
{
array16[k] = array16[i - j * 4];
array16[i - j * 4] = 0;
k -= 4;
}
}
if (array16[i] == 0) continue;
//合并
if (array16[i] == array16[i - 4])
{
Score += (int)array16[i] + (int)array16[i - 4];
RecordScore(Score);
array16[i]++;
array16[i - 4] = 0;
}
}
break;
case 2:
Debug.Log("right"); Musiclist[0].Play();
for (int i = 15; i > 0; i -= 4)
{
for (int x = 0; x < 3; x++)
{
//向right紧凑
int k = i - x;
for (int j = 1; x + j < 4; j++)
{
if (array16[i - j - x] == 0 && array16[k] != 0) k = i - j - x;
if (array16[i - j - x] != 0 && array16[k] == 0)
{
array16[k] = array16[i - j - x];
array16[i - j - x] = 0;
k--;
}
}
if (array16[i - x] == 0) continue;
//合并
if (array16[i - x] == array16[i - x - 1])
{
Score += (int)array16[i-x] + (int)array16[i-x-1];
RecordScore(Score);
array16[i - x]++;
array16[i - x - 1] = 0;
}
}
}
break;
case -2:
Debug.Log("left"); Musiclist[0].Play();
for (int i = 0; i < 13; i += 4)
{
for (int x = 0; x < 3; x++)
{
//向left紧凑
int k = i + x;
for (int j = 1; x + j < 4; j++)
{
if (array16[i + j + x] == 0 && array16[k] != 0) k = i + j + x;
if (array16[i + j + x] != 0 && array16[k] == 0)
{
array16[k] = array16[i + j + x];
array16[i + j + x] = 0;
k++;
}
}
if (array16[i + x] == 0) continue;
//合并
if (array16[i + x] == array16[i + x + 1])
{
Score += (int)array16[i+x] + (int)array16[i + x+1];
RecordScore(Score);
array16[i + x]++;
array16[i + x + 1] = 0;
}
}
}
break;
}
move_state = 0;
Set_Cell();
Creat_Num();
gameover = Game_Over();
}
void Set_Cell()
{
//遍历
Debug.Log("set cell");
for (int i = 0; i < 16; i++)
{
Square_list[i].sprite = sprite2048[array16[i]];
}
}
主要通过move_state的方向来决定操作内容,移动核心就是套的两层循环,外循环控制行列数,内循环遍历两组数做操作(靠边和合并)详细看第一段向上操作的注释就懂了。然后遍历换图合成的数据array16就实现了移动合并的步骤了。
4.创建新数据(Creat_Num)
void Creat_Num()
{
//出现的数字为几,2、4、8
int tmp = 1;
float random = Random.value;
if (random < 0.33)
tmp = 2;
else if (random > 0.66)
tmp = 3;
//获取空格
while (true)
{
int change = (int)Random.Range(0,16);
Debug.Log("随机数"+change);
if (array16[change] == 0) //该位置为空
{
array16[change] = tmp; //给数
Square_list[change].sprite = sprite2048[tmp]; //换图
return;
}
else continue; //不然重新给随机数
}
这段主要通过概率随机数来决定tmp的值(1-2,2-4,3-8),然后再给到array16中的空闲位置。
5.输赢判断(Game_Over)
bool Game_Over()
{
for (int i = 0; i < 16; i++) //判断有没有2048,有则Win
{
if (array16[i] > 10)
{
Win.SetActive(true);
Musiclist[2].Play();
Time.timeScale = 0f;
return true;
}
}
for (int i = 0; i < 16; i++)
{
if (array16[i] < 1) //判断有没有空白,有则继续游戏
{
return false;
}
if (i < 12 && array16[i] == array16[i + 4]) //上下有可合并的,继续游戏
{
return false;
}
if ((i + 1) % 4 != 0 && array16[i] == array16[i + 1])//左右有可合并的,继续游戏
{
return false;
}
}
gameOver.SetActive(true);
Musiclist[1].Play();
Time.timeScale = 0f;
return true;
}
6.重新游戏和记录数据
public void RestartGame()
{
ScoreText.text = "0"; //得分归0
Score = 0;
//重新读取场景
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
//将最高分存本地
PlayerPrefs.SetInt("BestScore", BestScore);
//最高分本地读取
BestScoreText.text = PlayerPrefs.GetInt("BestScore").ToString();
Time.timeScale = 1;
}
public void RecordScore(int score)
{
ScoreText.text = score.ToString();
if(PlayerPrefs.GetInt("BestScore")!=0)
{
if(PlayerPrefs.GetInt("BestScore") > score)
BestScore = PlayerPrefs.GetInt("BestScore");
else
BestScore = score;
}
else { BestScore = 0; }
BestScoreText.text=BestScore.ToString();
}
该段主要通过PlayrePrefs.SetInt/GetInt来保持游戏最高分和读取游戏最低分。
四、总结
难点:
1.项目的主要部分和难点在于对数据移动合并的判断最难,也是想了好久,查了好久资料才明白。
2.其次是后面加了个数据最高分存储问题,这个通过PlayerPrefs解决了。
3.判断输赢难点,什么时候赢(有2048这个图就赢),什么时候输(并排不能有相同的,字符数组存储的数据不能为空)
感悟:写了两天才写完,后面加最高分和声音耗了一下午。虽然游戏简单,这个游戏考虑的算法方面的比较多。逻辑结构方面也就那样。之后下面准备着手做一下3D方面的游戏。