I designed a "Ticker" based on how I imagine Minecraft's tick system works. My ticker calls a method tickRate
times a second and provides a Delta Time value (deltaTime
) to the method being called.
The issue I have with my code is that I find the update
method (which handles timing) to be ugly but I'm not sure what I can do.
My ticker class is designed to be instantiated and used whenever, it does not have to be used right away (which is why I don't set any time related information in the constructor). This is why there is a check for nextTick
and lastTick
being 0
inside update
, as to ensure that the delta time does not go crazy on the first tick. The issue with this is that it looks a bit ugly, however I'm not sure what I could do. I could move the reset code into the reset
method, however that would now mean that the reset
method is time dependant, and must be only be called when you intend to use the ticker.
Ticker.java
import java.util.HashSet;
import java.util.Set;
public class Ticker {
private long lastTick;
private long nextTick;
private int tickRate;
private Set<TickListener> tickListeners = new HashSet<>();
public Ticker(int tickRate) {
this.tickRate = tickRate;
}
public void addTickListener(TickListener listener) {
tickListeners.add(listener);
}
public void removeTickListener(TickListener listener) {
tickListeners.remove(listener);
}
public void setTickRate(int tickRate) {
this.tickRate = tickRate;
}
public int getTickRate() {
return tickRate;
}
public void reset() {
lastTick = 0;
nextTick = 0;
}
public boolean update() {
long currentTime = System.currentTimeMillis();
if (currentTime >= nextTick) {
long targetTimeDelta = 1000L / tickRate;
if (lastTick == 0 || nextTick == 0) {
lastTick = currentTime - targetTimeDelta;
nextTick = currentTime;
}
float deltaTime = (float) (currentTime - lastTick) / targetTimeDelta;
for (TickListener listener : tickListeners) {
listener.onTick(deltaTime);
}
lastTick = currentTime;
nextTick = currentTime + targetTimeDelta;
return true;
}
return false;
}
}
TickListener.java
public interface TickListener {
void onTick(float deltaTime);
}
Example usage
Ticker ticker = new Ticker(20); // 20 ticks per second
ticker.addTickListener(new TickListener() {
@Override
public void onTick(float deltaTime) {
System.out.println(String.format("Ticked with deltaTime %f", deltaTime));
}
});
while (true) {
ticker.update();
}
TickListener
a custom class? If so, I'm sure reviewers would appreciate having it so they could more easily understand your code. :) \$\endgroup\$TickListener
is simply an interface with a method calledonTick
. I'll add it to the OP now. \$\endgroup\$System.currentTimeMillis()
to some kind of external interface (for example, aClock
interface) - this would allow you to move backward and forward through time in unit tests without having to actually wait that many milliseconds? \$\endgroup\$