制作2D小游戏 “别踩白块儿”(2最终版)
通过上一篇的步骤与代码,可以初步的建立一个简单的别踩白块儿,但是我们会发现,这个别踩白块儿的功能与我们市面上玩的有点不一样,竟然不能自动下落,而且没有退出,也没有没点击的方块落过可点击范围却不扣分,在这里小小的补充一下游戏的功能,分别贴出新的三段代码,只要把这三段代码分别替换上次的代码即可。
第一段代码是控制游戏进程的GameController.cs:
public class GameController : MonoBehaviour
{
public float downTime = 0.7f; //用于控制下落的速度
public static int score = 0; //表示游戏中的分数
public static bool showScore = false; //是否显示分数
public GameObject startUI; //开始界面
public GameObject gameUI; //游戏界面
public GameObject container; //方块的父物体
public GameObject whiteBlock;
public GameObject[] normalBlock;
public bool startGame = false; //用于判断是否游戏开始,用来处理返回按钮的两种操作
public bool gameOver = false; //用于判断游戏是否结束
public ArrayList blocks; //用于储存已实例化的方块
private float gameTime = 0f;
private bool isTouch = false; //用于判断用户是否进行了游戏的第一次点击事件
private void Update()
{
if (isTouch && gameTime >= downTime && score > float.Epsilon) //当玩家点击方块且游戏时间大于下落速度、分数大于0,则使方块下落且
{
MoveDown();
gameTime -= downTime;
}
gameTime += Time.deltaTime;
if (!isTouch) //如果玩家没有触碰方块,那么一直让游戏的时间保持在0防止出现,当十秒后再开始游戏,而游戏时间已经10+秒,从而MoveDown运行十次而是游戏出现问题
gameTime = 0;
if (score <= 0 && startGame && isTouch) //当玩家进入游戏界面并且开始点击方块后分数小于0,说明游戏结束
{
gameOver = true;
isTouch = false;
GameEnd();
}
}
//进入游戏界面
public void EnterGameUI()
{
startGame = true;
//游戏开始,绘制游戏需要的GUI
showScore = true;
startUI.SetActive(false);
gameUI.SetActive(true);
//开始游戏
StartGame();
}
//返回主界面
public void ExitStartUI()
{
startGame = false;
gameTime = 0;
isTouch = false;
startUI.SetActive(true);
gameUI.SetActive(false);
//不绘制GUI
showScore = false;
Clean();
//重置分数
score = 0;
}
private void StartGame()
{
blocks = new ArrayList();
for (int rowIndex = 0; rowIndex < 4; rowIndex++)
{
AddBlock(rowIndex);
}
}
//在指定的行里添加一个方块儿
void AddBlock(int rowIndex)
{
float randomNum = Random.Range(0f, 1f);
GameObject prefab = randomNum > 0.8f ? whiteBlock : normalBlock[4];
GameObject o = Instantiate(prefab) as GameObject;
o.transform.parent = container.transform;
Block b = o.GetComponent<Block>();
int columnIndex = Random.Range(0, 4);
b.SetPosition(columnIndex, rowIndex);
//添加到blocks数组中
blocks.Add(b);
}
/*这个方法不断地重新创建与删除十分的消耗内存,对于这种大量的重复使用相同对象的操作,我们可以使用更好的办法,如:对象池*/
public void Select(Block block)
{
if (!isTouch)
isTouch = true;
GetComponent<AudioSource>().Play(); //播放点击音效,只能在有且只有一个Audio时有效,如果有多个音效,则需要加入音效名添加判断才行
score += block.cantTouch ? -5 : 1; //判断点击的是否为可触摸的方块儿,如果是不可触摸的,分数减五,否则加一
if (score < float.Epsilon && startGame)
{
gameOver = true;
GameEnd();
}
//MoveDown(); //该句可以实现点击方块所有方块往下移动的效果
}
//使方块向下移动
private void MoveDown()
{
//我们常用的i原意是index “索引”的意思
for (int i = 0; i < blocks.Count; i++)
{
Block b = (Block)blocks[i];
//下移之后删除物体
b.MoveDown();
//删掉移除屏幕的色块
if (b.rowIndex <= -1 && b.transform.position.y < -6.3f)
{
if (b.GetComponent<BoxCollider2D>().isActiveAndEnabled && b.name != "block(Clone)" && b.name != "block1(Clone)")
{
score--;
}
blocks.RemoveAt(i);
//print(b.name + ":" + b.rowIndex);
Destroy(b.gameObject);
i--;
}
}
//在最上面添加一个方块儿
AddBlock(3);
}
//清理游戏数据
public void Clean()
{
for (int i = 0; i < blocks.Count; i++)
{
Block b = (Block)blocks[i];
blocks.RemoveAt(i);
Destroy(b.gameObject);
i--;
}
}
//游戏结束
private void GameEnd()
{
GameObject.Find("GameUI").transform.GetChild(2).gameObject.SetActive(false);
blocks.Clear();
GameObject.Find("GameUI").transform.GetChild(3).gameObject.SetActive(true);
score = 0;
}
}
第二段代码,是控制方块的Block.cs:
public class Block : MonoBehaviour
{
public bool cantTouch = false; //默认所有的方块都是可以触摸的,然后在预设体的白方块儿中勾选这个bool值
//行列索引
public int rowIndex;
public int columnIndex;
//x,y轴的坐标偏移量
private float x0ff = -2.25f;
private float y0ff = -3.68f;
//游戏控制器
private GameController gameController;
void Start()
{
gameController = GameObject.FindObjectOfType<GameController>();
}
private void OnMouseDown()
{
gameController.Select(this);
GameObject o = Instantiate(gameController.normalBlock[0].gameObject, transform.position, Quaternion.identity);
Block b = o.GetComponent<Block>();
b.rowIndex = this.rowIndex;
b.columnIndex = this.columnIndex;
gameController.blocks.Add(b);
GetComponent<BoxCollider2D>().enabled = false; //当方块被点击后,该方块无法被再次点击
}
public void SetPosition(int columnIndex, int rowIndex)
{
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
gameObject.transform.position = new Vector3(x0ff + columnIndex * 1.5f, y0ff + rowIndex * 2.68f, 0);
}
//让方块向下移动
public void MoveDown()
{
SetPosition(columnIndex, rowIndex - 1);
}
}
第三段为控制开始按钮的StartButton.cs:
public class StartButton : MonoBehaviour
{
private GameController gameController;
public static AsyncOperation gameOverToStart; //用于判断场景加载是否完毕
void Start()
{
gameController = GameObject.FindObjectOfType<GameController>();
}
void OnMouseDown()
{
GameController.showScore = true;
gameController.EnterGameUI();
GameObject.Find("GameUI").transform.GetChild(3).gameObject.SetActive(false);
if (gameController.gameOver)
{
GameController.showScore = false;
gameOverToStart = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(0);
}
}
}
期间我复制了一个开始按钮并把它拖成了Main场景的子物体,同时设置开始按钮的显示层为5级,灰色方块的显示层为2级,并且我把灰色方块的预设体禁用了碰撞体,这样可以使我在点击方块后实例化一个灰色无法点击的方块,并遮挡住被点击的彩色方块,同时彩色方块被点击的同时我也移除它自身的碰撞体,这样它将会随着实例化遮挡住它自身的灰色方块慢慢向下移动,直到落到视野范围外销毁自身。
我还通过时间的记录与增减来控制什么时候开始自动下落小方块,这样可以使玩家及时进入游戏界面也不用担心因为反应的不及时而使游戏失败。我建了一个isTouch来控制方块什么时候开始自动下落,当我第一次点击方块的时候,使isTouch变为真,当游戏结束则假,这样我就实现了当我第一次点击方块后,游戏正式开始,方块开始自动下落。
当所有的可玩功能都实现之后,我添加了一个游戏结束,通过实时的分数检测,当检测到方块已经被点击了而且分数小于0,这个时候我就判定游戏状态为结束状态,则清空游戏状态,销毁实例化的所有物件并显示开始按钮,当玩家点击游戏的开始按钮就可以重新加载场景,开始新的一局游戏啦。
经过将近两周的时间,我从网上视频的教学到现在新功能添加的完毕,感觉自己还需努力,明明一周就可以做完的工作却因为能力的不足导致拖到现在,接下来会开始学习制作更多新的小游戏并贴出自己的制作代码与小心得,希望自己能够进步的越来越大。