8. 暂停按钮,方块落下停止与游戏结束
Hi I'm Shendi
暂停按钮
在游戏右上角制作暂停按钮,PageGame下的Canvas右键 - UI - 按钮,创建一个按钮
将按钮的宽高改为 60,按钮内文本内容为 | |
,将按钮的源图像改为圆圈,调整以下透明度,字体大小改为26
将其拖到右上角,Rect Transform同样到右上角,如下
这样暂停按钮的样式就做好了
当按钮点击后,就暂停游戏,并弹出继续和返回的面板
暂停面板制作
暂停面板包含继续和返回按钮
在 Canvas 处右键 UI - 面板,创建面板,在面板下再创建一个面板,调整第二个面板大小和颜色
在第二个面板下创建两个按钮,分别为继续和返回按钮,将按钮文字更改,文字大小设置为26,按钮宽高设置为150x150,按钮源图像改为 Input 那个,结构与效果如下
当点击暂停按钮时出现这个面板,所以默认取消活动状态,点击面板,取消右上角的勾选
UI方面懒得使用PS绘图了,这样也挺好,精简风...
暂停处理
当按下暂停按钮时游戏暂停,并且弹出暂停面板
让游戏暂停,可以使用 Time.timeScale = 0;
Time.timeScale 是Unity的一个全局时间缩放属性
它可以控制游戏世界里的游戏时间流逝的快慢度。它的范围一般在0.0-1.0之间,0.0表示时间完全停止,1.0表示正常时间流逝,大于1.0表示快进时间,小于1.0表示慢动作。
并且之前在 State 中的定义了一个变量作为判断是否为暂停状态
那么只需要在 Main 加入以下代码即可实现暂停游戏了
public GameObject panelPause;
/** 暂停游戏点击 */
public void PauseGame()
{
State.isGamePause = true;
Time.timeScale = 0;
// 显示暂停面板
panelPause.SetActive(true);
}
在暂停按钮点击事件处增加事件(之前做过的增加按钮点击事件操作),选择摄像机,右边调用的函数选择 PauseGame
点击层级面板的摄像头,在 Main 脚本处,将暂停面板拖入 panelPause
将 PageGame 取消活动状态(取消勾选),启动游戏看下效果
在跳跃到空中时,点击暂停按钮,可以看到角色停在了空中(暂停了)
继续按钮与返回按钮
当点击继续按钮时就将暂停所做的操作复原即可
/** 继续按钮点击 */
public void ContinueGame()
{
State.isGamePause = false;
Time.timeScale = 1;
// 隐藏暂停面板
panelPause.SetActive(false);
}
同样在继续按钮点击事件处增加事件,对象选择摄像机,选择 ContinueGame 函数
这样继续按钮就完成了
而返回按钮,点击后首先需要将游戏结束,然后返回到主界面
可以编写一个 StopGame 函数用来专门处理游戏结束,包括游戏结束的结算等。
一般返回按钮是不结算直接返回到主页这样,但这里直接走正常游戏结束的流程了
至于游戏结束具体操作,等到后面再去做,现在这里定义一个壳子并绑定给返回按钮即可
/** 游戏结束 */
public void StopGame()
{
}
同上面那些按钮一样,给返回按钮增加点击事件...函数指定为 StopGame
方块的落下
根据最开始的策划,开始游戏后方块落下,并随着时间,落下的速度将越来越快
因为涉及到具体数值,这里不使用刚体来让方块落下,而是使用直接移动方块的方式
这里还需要策划一下方块的生成时间,方块每5秒生成一个,随着时间推移将生成的越来越快,10秒减少0.5秒,最快1秒生成一个
默认五秒,一秒是50次,也就是50*5次
落下速度初始每秒一个方块高度,方块高度为0.5,也就是每次下落 0.5/50,每十秒增加0.1,最大为4
每个方块血量值默认为1,每十秒增加0.2(无上限)
在State中定义这些初始值
/** 当前方块生成的速度 */
public static float boxCurSpawnSpeed = boxSpawnSpeed;
/** 方块的生成速度默认值 */
public static float boxSpawnSpeed = 50 * 5;
/** 方块的生成速度最大值 */
public static float boxSpawnSpeedMax = 50;
/** 方块的生成速度调整间隔(50为1秒) */
public static float boxSpawnSpeedTime = 50*10;
/** 方块的生成速度每次调整的大小 */
public static float boxSpawnSpeedTimeNum = 25;
/** 当前方块落下速度 */
public static float boxCurSpeed = boxSpeed;
/** 方块落下初始速度 */
public static float boxSpeed = boxSize / 50;
/** 方块落下最大速度 */
public static float boxSpeedMax = 4 / 50;
/** 方块落下初始速度调整间隔 */
public static float boxSpeedTime = 50*10;
/** 方块落下初始速度每次调整的大小 */
public static float boxSpeedTimeNum = 0.1f / 50;
/** 当前方块血量值 */
public static float boxCurHealth = boxHealth;
/** 方块血量值 */
public static float boxHealth = 1;
/** 方块血量值调整的间隔 */
public static float boxHealthTime = 50*10;
/** 方块血量值每次调整的大小 */
public static float boxHealthTimeNum = 0.2f;
在Main的开始游戏部分将上面定义的三个当前值初始化
public void StartGame()
{
// 初始化值
State.boxCurSpawnSpeed = State.boxSpawnSpeed;
State.boxCurSpeed = State.boxSpeed;
State.boxCurHealth = State.boxHealth;
}
方块的生成
首先实现生成和大体部分,在 Main 中的 FixedUpdate 来实现这个功能
之前在State编写了生成方块的函数,这里直接使用即可生成方块了
/** 方块生成冷却的计时器,50一秒, */
private float boxSpawnTotol = 0;
/** 方块生成速度调整计时器,到达指定大小后调整生成速度 */
private float boxSpawnTimeTotol = 0;
/** 方块下落速度调整计时器,到达指定大小后调整下落速度 */
private float boxSpeedTotol = 0;
void FixedUpdate()
{
if (State.isGameStart && !State.isGamePause)
{
boxSpawnTotol--;
boxSpawnTimeTotol++;
boxSpeedTotol++;
// 方块生成
if (boxSpawnTotol <= 0)
{
State.CreateRanBox();
boxSpawnTotol = State.boxCurSpawnSpeed;
}
// 调整生成间隔
if (boxSpawnTimeTotol >= State.boxSpawnSpeedTime)
{
if (State.boxCurSpawnSpeed > State.boxSpawnSpeedMax)
{
State.boxCurSpawnSpeed -= State.boxSpawnSpeedTimeNum;
}
boxSpawnTimeTotol = 0;
}
// 调整下落速度
if (boxSpeedTotol >= State.boxSpawnSpeedTime)
{
if (State.boxCurSpeed > State.boxSpeedMax)
{
State.boxCurSpeed += State.boxSpeedTimeNum;
}
boxSpeedTotol = 0;
}
}
}
这样,运行游戏,就可以看到方块生成了,并随着时间增加,生成的越来越快
在开始游戏中,需要将上述定义的参数重新初始化
public void StartGame()
{
boxSpawnTotol = 0;
boxSpawnTimeTotol = 0;
boxSpeedTotol = 0;
}
创建一个空对象作为生成方块的父对象,方便管理
在 PageGame 右键新建一个空对象,同背景一样,拖到最上方,在Main中定义如下
public GameObject boxs;
点击摄像机,将创建的对象拖到boxs处就可以了
后续方块可以被一个个消灭,以及方块的下落和停止,所以需要新建一个脚本,绑定在生成的小方块上
新建一个脚本,命名为Box
在Main中给小正方形加Box组件,以及碰撞体组件,代码如下
// 方块生成
if (boxSpawnTotol <= 0)
{
var obj = State.CreateRanBox();
obj.transform.parent = boxs.transform;
obj.transform.localPosition = Vector2.zero;
// 遍历所有子物体给其加上Box脚本组件
for (var i = 0; i < obj.transform.childCount; i++)
{
var tmp = obj.transform.GetChild(i);
tmp.AddComponent<Box>();
tmp.AddComponent<BoxCollider2D>();
}
boxSpawnTotol = State.boxCurSpawnSpeed;
}
会发现有一个问题,就是方块生成了但是看不见,原因是 Z 轴太靠近了,调整一下PageGame的Z轴为0即可
运行后查看小方块,发现绑定上了Box和碰撞体组件了
下落
速度什么的都在 Main 中处理了,下落在 Box 中处理,直接设置即可
代码如下
void FixedUpdate()
{
if (State.isGameStart && !State.isGamePause)
{
transform.position -= new Vector3(0, State.boxCurSpeed);
}
}
运行后可以发现源源不断的方块往下落了,并且角色可以踩在方块上
停留在地面
当方块到达地面时将停下来,并作为地面的一份子
可以通过碰撞检测来实现
- void OnCollisionEnter(Collision collision) {} 当物体发生碰撞时调用
- void OnCollisionExit(Collision collision) {} 当物体碰撞后离开碰撞体时调用
- void OnCollisionStay(Collision collision) {} 当物体粘在另一个物体上时每帧调用
我们使用的是 2D,所以需要在函数和参数后加上 2D
例如void OnCollisionEnter2D(Collision2D collision)
Unity中的碰撞/触发需要两个对象都有碰撞器(Collider),以及一方拥有刚体(Rigidbody)
而给正方形加上刚体则会一直往下落,我们是使用代码来控制方块下落,所以需要给刚体的重力设置为0
在创建正方形的地方(Main)给正方形增加刚体组件
// 遍历所有子物体给其加上Box脚本组件
for (var i = 0; i < obj.transform.childCount; i++)
{
var tmp = obj.transform.GetChild(i);
tmp.AddComponent<Box>();
tmp.AddComponent<BoxCollider2D>();
var rigidbody = tmp.AddComponent<Rigidbody2D>();
rigidbody.gravityScale = 0;
}
运行后会发现四分五裂的方块
这是因为小方块之间互相碰撞了,如何解决这个问题?那就将碰撞体大小改小一点就好了,使其相互之间碰撞距离够不着
改小0.05即可
var boxColidder = tmp.AddComponent<BoxCollider2D>();
boxColidder.size -= new Vector2(0.05f, 0.05f);
将最上面一排的尖刺的Collider改为触发器,否则有可能方块卡在最上方
现在就只用处理碰撞操作了,在 Box 脚本里
在此之前,我们需要先给物体设置标签,以区分碰撞的是什么物体
给角色预制体设置标签 Player
方块预制体添加标签,Box,并选择
游戏内限制范围的三个墙设置为Wall标签,没有就添加
我们还需要新建一个标签叫做BoxWall,当方块到达地面时就改为这个标签
标签处理完后处理碰撞检测,当碰撞的是Wall或者BoxWall时,将方块停止移动并标签改为BoxWall
当碰撞到的是Player时,游戏结束
需要让方块停止先要增加一个标志位,Box 更改如下
// 方块是否执行完成
public bool isOk = false;
void FixedUpdate()
{
if (State.isGameStart && !State.isGamePause && !isOk)
{
transform.position -= new Vector3(0, State.boxCurSpeed);
}
}
游戏结束的处理在 Main 内,将 Main 改为单例模式,bing改为单例模式,并将唯一对象提供出来,Main增加如下代码
/** 单例 */
public static Main _instance;
private void Awake()
{
_instance = this;
}
Box 中碰撞体处理代码如下
void OnCollisionEnter2D(Collision2D collision)
{
switch (collision.gameObject.tag)
{
case "Wall":
case "BoxWall":
// 将父对象的所有子对象都执行完成
for (var i = 0; i < transform.parent.childCount; i++)
{
var obj = transform.parent.GetChild(i);
obj.tag = "BoxWall";
obj.GetComponent<Box>().isOk = true;
}
break;
case "Player":
Main._instance.StopGame();
break;
}
}
运行游戏,发现方块落在地面后就停留了,并且可以堆叠(前提是角色不去干扰,撞方块,因为游戏结束部分还没做)
游戏结束处理
之前说过游戏结束的条件
- 角色碰到落下的方块(标签为Box)
- 角色碰到顶部尖刺
- 暂停面板的返回
游戏结束自然就需要涉及到结算部分,于是在 State 中定义结算部分
/** 结算 */
public static void settle() {}
至于结算部分,后面再去做
当游戏结束,弹出结算面板,其中有一个确定按钮,点击就返回主页
于是在 Main 的 StopGame 中进行以下操作
- 显示结算面板
- 调用结算函数
- 暂停游戏
- 隐藏暂停面板
- ...
先把结算面板做出来,与暂停面板一样,直接复制暂停面板,调整一下大小,位置,按钮文字,效果如下即可
在 Main 中,增加一个按钮点击的函数,游戏结束代码与结算点击代码如下
结算点击将一切状态恢复到最开始的时候
public GameObject panelSettle;
/** 游戏结束 */
public void StopGame()
{
State.isGameStart = false;
State.isGamePause = false;
// 显示结算面板,隐藏暂停面板
panelSettle.SetActive(true);
panelPause.SetActive(false);
Time.timeScale = 0;
}
/** 结算完成按钮点击 */
public void SettleOkClick()
{
// 隐藏game显示main
pageGame.SetActive(false);
pageMain.SetActive(true);
// 隐藏结算面板
panelSettle.SetActive(false);
// 销毁角色
State.PlayerDestory();
// 销毁游戏内生成的方块
foreach (Transform child in boxs.transform)
{
GameObject.Destroy(child.gameObject);
}
// 显示背景
State.isBackgroundPlay = true;
Background._instance.StartCoroutine(Background._instance.CreateRanBox());
Time.timeScale = 1;
}
其中启动背景的 CreateRanBox 函数是没有加 public 的,需要加上,在Background中
public IEnumerator CreateRanBox()
绑定 SettleOkClick 到确认按钮的点击事件, 并把结算面板拖到摄像机的Main脚本中
这样,游戏就可以正常结束并重新开始游戏了
尖刺处理
碰到尖刺也会让游戏结束,之前将 Spikes 的碰撞体的是触发器勾选了,于是编写一个脚本放到Spikes上,脚本就命名为Spikes吧
同方块一样,不过这里是触发检测
使用 OnTriggerEnter2D,代码如下
void OnTriggerEnter2D(Collider2D collision)
{
// 是角色直接游戏结束
if (collision.gameObject.tag == "Player")
{
Main._instance.StopGame();
}
}
效果如下
本文链接:https://sdpro.top/blog/html/article/1068.html
♥ 赞助 ♥
尽管去做,或许最终的结果不尽人意,但你不付出,他不付出,那怎会进步呢?