-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGameLoop.java
More file actions
75 lines (67 loc) · 2.41 KB
/
Copy pathGameLoop.java
File metadata and controls
75 lines (67 loc) · 2.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package Phase7_Concurrency.Multithreading.SnakeGame;
import javax.swing.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* GameLoop — the simulation thread.
* <p>
*
* Runs on its OWN thread (not the EDT). Sleeps between ticks to control
* speed, then advances the game state and asks the Swing renderer to
* repaint via SwingUtilities.invokeLater. (Swing components must be
* touched on the EDT.)
* <p>
*
* Why a dedicated thread?
* -----------------------
* - The EDT must stay responsive to input and repaint.
* - The simulation should tick at a steady rate independent of how
* long painting takes.
* <p>
*
* Coordination
* ------------
* - state.tick() takes the GameState lock briefly.
* - state.snapshot() (called from the EDT) takes the same lock.
* They never overlap painting and ticking. No torn frames.
* <p>
*
* - A volatile `running` flag controls the loop; setRunning(false)
* stops the loop and the thread exits.
* <p>
*
* Frame-pacing
* ------------
* For simplicity this uses Thread.sleep(tickMs). A production game
* would track wall-clock drift and skip ticks if behind. The pattern
* is the same; the math is more.
*/
public final class GameLoop implements Runnable {
private final GameState state;
private final SnakeBoard board;
private final AtomicBoolean running = new AtomicBoolean(true);
private volatile long tickMs;
public GameLoop(GameState state, SnakeBoard board, long initialTickMs) {
this.state = state;
this.board = board;
this.tickMs = initialTickMs;
}
public void setTickMs(long ms) { this.tickMs = ms; }
public void stop() { running.set(false); }
@Override public void run() {
while (running.get() && !Thread.currentThread().isInterrupted()) {
long t0 = System.currentTimeMillis();
state.tick();
// Request a repaint on the EDT. This does NOT paint on this
// thread; it asks Swing to do it soon.
SwingUtilities.invokeLater(board::repaint);
// Sleep the rest of the frame.
long elapsed = System.currentTimeMillis() - t0;
long sleep = Math.max(0, tickMs - elapsed);
try { Thread.sleep(sleep); }
catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // honour the signal
return;
}
}
}
}