如何平稳地每秒增加一定数量的数字?

时间:2015-12-12 13:13:39

标签: java android algorithm timer

我正在尝试开发一种名为Shaking的Cookie Clicker应用程序。

如果您不知道Cookie Clicker是什么:

Cookie Clicker是一款游戏,你点击一个cookie来“烘焙”一个cookie。您尝试获得尽可能多的cookie,您可以使用cookie来购买可以帮助您烘焙饼干的东西。例如,您可以使用50个cookie来购买光标,该光标每10秒自动为您的cookie计数添加一个。您可以购买的每件商品都有Cps金额(每秒Cookie)。所以光标是0.1 Cps。

在我的游戏版本中,您摇动手机而不是点击Cookie。所以Cps变成了赫兹!

现在我想做的是不是每秒都增加抖动次数,而是每隔一段时间增加抖动次数。我真的没有解释清楚,是吗?例如,如果玩家拥有100Hz的东西,我不想每秒将抖动数增加100,我想每0.01秒增加1!我已经使用Android的Handler类编写了一个计时器类,但这没有关联。

以下是我的尝试:

我创建了一个TimerTuple类来存储一个计时器间隔,并且每隔一个间隔就会增加一次抖动量。所以在上面的例子中,定时器间隔为10毫秒,增加的数量为1.顺便说一下,我使用BigDecimal作为Hz,因为它可能会变得非常大。

这是班级,没什么特别的:

// This is an inner class. Don't tell me that I cannot use static in a class definition!
private static class TimerTuple {
    private BigDecimal shakeIncrement;
    private int timerInterval;

    public TimerTuple(BigDecimal shakeIncrement, int timerInterval) {
        this.shakeIncrement = shakeIncrement;
        this.timerInterval = timerInterval;
    }
}

我编写了一个名为getTimerTuple的方法,它返回一个TimerTuple,这样我就可以使用这些信息来设置计时器的间隔,并添加正确的抖动数量。

private TimerTuple getTimerTuple () {
    int timerInterval = 1000;
    BigDecimal shakeIncrement = new BigDecimal(getCurrentHz ());

    if (shakeIncrement.toBigInteger ().compareTo (BigInteger.ZERO) == 0) {
        return new TimerTuple (BigDecimal.ZERO, 1);
    }

    if (shakeIncrement.compareTo (new BigDecimal (Integer.toString (timerInterval))) < 0) {
        timerInterval = timerInterval / shakeIncrement.intValue ();
        shakeIncrement = new BigDecimal ("1");
        return new TimerTuple (shakeIncrement, timerInterval);
    } else {
        shakeIncrement = shakeIncrement.divide (new BigDecimal (Integer.toString (timerInterval)), 8, RoundingMode.DOWN);
        timerInterval = 1;
        return new TimerTuple (shakeIncrement, timerInterval);
    }
}

我认为我在这里所做的是正确的,但实际上,我不确定。

每次计时器滴答时,我都这样做:

TimerTuple tuple = getTimerTuple ();
if (tuple.timerInterval != timer.getInterval())
    timer.setInterval (tuple.timerInterval);
currentShakes = currentShakes.add(tuple.shakeIncrement);
// Code to display the shake amount goes here...

我不知道你是否能理解我所做的所有这些混乱。

我不确定这是否有效,所以我将上述所有内容移到普通的Java应用程序中进行测试,因为它更容易实现。我将计时器更改为javax.swing.Timer。然后我创建了一个JFrame并在其中添加了JLabel。我用这个标签来显示抖动计数。我将Hz设置为1000只是为了发生什么。我的预期结果是摇动计数每秒增加1000.但当然,它没有用。抖动计数非常慢,大致(我没有使用真正的计时器来测量!)每2秒钟震动1000次!

我不知道如何解决它。这是否超过刷新屏幕的最大限制?或者它是计时器的问题?或者我的计算错了?

编辑:

我使用调试器逐步完成代码,发现我的数学是正确的。我尝试将当前Hz设置为20000并运行getTimerTuple,我得到了正确的结果。即定时器间隔为1ms,抖动增量为20。

对于那些不了解TimerTuple做什么的人,它基本上说“每timerInterval毫秒,震动计数应增加shakeIncrement”。然后,我可以使用此信息将timer的间隔设置为tuple.timerInterval

所以我仍然想知道如何让标签每秒显示正确的抖动计数。

编辑:

这次只是一些额外的信息。

我成功计算了震动增加的速度。当currentHz = 1000时,实际Hz为497.然后我尝试将currentHz设置为500并看到实际Hz为325.我得出结论,当前Hz与实际Hz的比率并不总是2:1!

然后我测试了一些其他值,并绘制了一个图表,显示预期的Hz(电流Hz)如何随实际Hz增加:

enter image description here

我绘制了另一张显示比例如何变化的图表

enter image description here

我认为这些表明比率和预期比率实际上是随机的。这让我更加困惑!

1 个答案:

答案 0 :(得分:2)

所以我知道你已经确定你的数学是正确的。但是,我建议您尝试使用BigDecimal个对象来简化代码(正如您将看到我已经完成了您的代码)。

关于显示它,我实现了一个简单的ActionListener,附加到javax.swing.Timer,并让它更新一个非常基本的Swing窗口。

TickerTest

package test;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.math.RoundingMode;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;

public class TickerTest {
    private static final BigDecimal ONE_THOUSAND = BigDecimal.TEN.pow(3);
    private static final BigDecimal CURR_HZ = BigDecimal.TEN.pow(4).multiply(new BigDecimal(2));
    private static final JLabel label = new JLabel("<not running>");

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                TimerTuple timerTuple = getTimerTuple();
                BigDecimal ratio = timerTuple.getTimerInterval().divide(timerTuple.getShakeIncrement(), 8, RoundingMode.DOWN);
                label.setText(ratio.toString());
            }
        };

        frame.getContentPane().add(label);
        frame.pack();
        frame.setVisible(true);

        Timer timer = new Timer(1000, listener);
        timer.start();
    }

    private static TimerTuple getTimerTuple () {
        BigDecimal timerInterval = ONE_THOUSAND;
        BigDecimal shakeIncrement = getCurrentHz();

        if (shakeIncrement.equals(BigDecimal.ZERO)) {
            return new TimerTuple(BigDecimal.ZERO, BigDecimal.ONE);
        }

        if (shakeIncrement.compareTo(timerInterval) < 0) {
            timerInterval = timerInterval.divide(shakeIncrement);
            return new TimerTuple(BigDecimal.ONE, timerInterval);
        } 
        else {
            shakeIncrement = shakeIncrement.divide(timerInterval, 8, RoundingMode.DOWN);
            return new TimerTuple (shakeIncrement, BigDecimal.ONE);
        }
    }

    private static BigDecimal getCurrentHz() {
        return CURR_HZ;
    }
}

TimerTuple

package test;

import java.math.BigDecimal;

public class TimerTuple {
    private BigDecimal shakeIncrement;
    private BigDecimal timerInterval;

    public TimerTuple(BigDecimal shakeIncrement, BigDecimal timerInterval) {
        this.shakeIncrement = shakeIncrement;
        this.timerInterval = timerInterval;
    }

    public BigDecimal getShakeIncrement() {
        return shakeIncrement;
    }

    public BigDecimal getTimerInterval() {
        return timerInterval;
    }
}