第三十九节 实现关卡功能

作者: 安静的程序员 | 来源:发表于2019-06-25 12:18 被阅读0次

注意:这个卡读成“qiǎ”。

如果至始至终都是一样的速度,那也太无聊了。所以增加关卡就很有必要了。

01.制作关卡对象

直接复制一个场景中的“HighScore”,重命名为Level,调整坐标:(206, 97, 0)。
删除最后一个数字,修改中间的数字:除了“Middle”,全部隐藏。
然后将03重命名为Line,04,05重命名为03,04:



然后移除该对象上的ScoreScreen脚本组件。

02.添加关卡类

添加一个关卡类脚本:Level。该类只需4个数字,前两个数字为主关卡,后两个为次关卡:

using UnityEngine;

public class Level : MonoBehaviour
{
    public GameObject Number01;
    public GameObject Number02;
    public GameObject Number03;
    public GameObject Number04;

    public void SetLevel(int mainLevel, int MinorLevel)
    {
        // 设置主关卡
        Number01.GetComponent<Number>().SetNumber(mainLevel % 100 / 10);
        Number02.GetComponent<Number>().SetNumber(mainLevel % 10);

        // 设置次关卡
        Number03.GetComponent<Number>().SetNumber(MinorLevel % 100 / 10);
        Number04.GetComponent<Number>().SetNumber(MinorLevel % 10);
    }
}
03.添加关卡脚本

将脚本添加至关卡对象上,并绑定场景中的数字对象:


除此之外,还要将关卡对象拖放到导演对象的“Level Obj”上。

04.添加成员和初始化

准备工作都做好了,接下来实现功能。

首先要添加成员,关卡脚本的引用和关卡值:

Level _levelScript;
int _level;                 // 关卡

本游戏没有主次关卡之分,所以只需要一个关卡值就够了。
然后就是初始化:

_level = 0;
// 初始化关卡
_levelScript = LevelObj.GetComponent<Level>();
if (_levelScript == null) return false;
_levelScript.SetLevel(0, _level);
05.如何实现变速

仅仅是实现变速的话,将定时器的时间间隔修改下即可:

InvokeRepeating("GameCore", 0, 1 - (_level - 1) * 0.1f);

按照这个公式,那么关卡1~10的速度分别为:
1 - 1s下落1格
2 - 0.9s下落1格
3 - 0.8s下落1格
4 - 0.7s下落1格
5 - 0.6s下落1格
6 - 0.5s下落1格
7 - 0.4s下落1格
8 - 0.3s下落1格
9 - 0.2s下落1格
10 - 0.1s下落1格,这个是最快的速度,一秒下落10格

计算速度并不难,关键是变速的时机。
首先,当方块处于加速下落时(按下下方向键但不松开),肯定不能变更速度,否则按键还没松开,速度突然变慢了(加速的速度大于所有关卡的速度)算怎么回事?
然后消除方块后如果正好使分数达到了提升关卡的条件,那么就需要变速,也就是说,在消除方块阶段结束后,需要启动一个更快的定时器来驱动GameCore()。但如果在这里启动定时器,就会重现第32节中出现的Bug,不过有一个比较不错的解决方案,就是在Invoke Repeating()之前先调用CancleInvoke():

CancelInvoke("GameCore");
InvokeRepeating("GameCore", 0, 1 - (_level - 1) * 0.1f);

虽然还是会有问题(多线程的问题),不过应该能大大减少触发Bug的概率。之前的Bug是因为我没有调用CanlInvoke():



调用CancleInvoke()之后:



不过还是会有问题,因为有可能会出现这种顺序:

只是概率非常低,因为停止和启动两行代码是写在一起的,要触发Bug需要你松开按键的时机必须刚刚好卡在线程一的这两行代码之间。就算触发了,按一下加速就恢复了。
然后因为不能在加速状态中变速,所以需要加一个标记来判断是否处于加速状态中:

bool _InQuickDown = false;          // 记录是否正在快速下落

在按下下方向键时设置为true,松开后设置为false,顺便修改启动核心逻辑的定时器代码,根据关卡来计算时间间隔:

// 按键 - 下方向键
if (Input.GetKeyDown(KeyCode.DownArrow))
{
    _InQuickDown = true;
    CancelInvoke("GameCore");
    InvokeRepeating("GameCore", 0, 0.03f);
}
if (Input.GetKeyUp(KeyCode.DownArrow))
{
    _InQuickDown = false;
    CancelInvoke("GameCore");
    InvokeRepeating("GameCore", 0, 1 - (_level - 1) * 0.1f);
}

然后需要在消除阶段结束后变更速度前添加一个判断:

// 如果未处于加速中
if (!_InQuickDown)
{
    CancelInvoke("GameCore");
    InvokeRepeating("GameCore", 0, 1 - (_level - 1) * 0.1f);
}

好了,现在变速的几个地方都添加好代码了,最后一件事就是根据分数计算关卡。
我的关卡提升规则是,每增加2500分提升一个Level:

// 更新关卡
int newLevel = _currentScore / 2500 + 1;
if (newLevel > 10) newLevel = 10;
if (newLevel > _level)
{
    _level = newLevel;
    _levelScript.SetLevel(0, _level);
}

为了方便测试提升关卡后是否会提升速度,你可以将2500改成一个很小的值,比如200分。

1秒下落10格真的反应不过来:


代码链接:https://pan.baidu.com/s/1eIYPXNIL6V8i-aJ6ncmcXQ
提取码:at6a

相关文章

网友评论

    本文标题:第三十九节 实现关卡功能

    本文链接:https://www.haomeiwen.com/subject/xzrtcctx.html