1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package experiments;
26
27
28 import javax.swing.JFrame;
29 import javax.swing.JPanel;
30 import javax.swing.SwingUtilities;
31 import java.awt.Color;
32 import java.awt.Dimension;
33 import java.awt.Graphics;
34 import java.awt.Graphics2D;
35 import java.awt.RenderingHints;
36
37 public class InterpolatedFrame extends JPanel implements Runnable {
38 // Window Constants
39 private final int WIDTH = 800, HEIGHT = 600;
40
41 // Logic Variables
42 private boolean running = false;
43 private Thread gameThread;
44
45 // Physics Variables (Use doubles for precision)
46 private double x = 100, lastX = 100;
47 private double y = 100, lastY = 100;
48 private double xSpeed = 200; // Pixels per second
49 private float renderInterpolation = 0;
50
51 public InterpolatedFrame() {
52 this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
53 this.setBackground(Color.BLACK);
54 }
55
56 public void start() {
57 running = true;
58 gameThread = new Thread(this);
59 gameThread.start();
60 }
61
62 @Override
63 public void run() {
64 double nsPerTick = 1000000000.0 / 60.0; // 60 Fixed Updates per second
65 double delta = 0;
66 long lastTime = System.nanoTime();
67
68 while (running) {
69 long now = System.nanoTime();
70 delta += (now - lastTime) / nsPerTick;
71 lastTime = now;
72
73 // Fixed Update Loop
74 while (delta >= 1) {
75 updatePhysics();
76 delta--;
77 }
78
79 // Calculate Interpolation for rendering
80 renderInterpolation = (float) delta;
81
82 // Schedule Render on EDT
83 SwingUtilities.invokeLater(this::repaint);
84
85 // Cap the loop to save CPU
86 try { Thread.sleep(2); } catch (InterruptedException e) {}
87 }
88 }
89
90 private void updatePhysics() {
91 // Store previous state for interpolation
92 lastX = x;
93 lastY = y;
94
95 // Move (at 60fps, 1 tick = 1/60th of a second)
96 x += xSpeed / 60.0;
97
98 // Wall Bounce
99 if (x > WIDTH - 30 || x < 0) xSpeed *= -1;
100 }
101
102 // @Override
103 protected void paintComponent(Graphics g) {
104 super.paintComponent(g);
105 Graphics2D g2d = (Graphics2D) g;
106 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
107
108 // Interpolated Position: (Current - Last) * Alpha + Last
109 int drawX = (int) ((x - lastX) * renderInterpolation + lastX);
110 int drawY = (int) ((y - lastY) * renderInterpolation + lastY);
111
112 g2d.setColor(Color.CYAN);
113 g2d.fillOval(drawX, drawY, 30, 30);
114 }
115
116 public static void main(String[] args) {
117 JFrame frame = new JFrame("InterpolatedFrame");
118 InterpolatedFrame panel = new InterpolatedFrame();
119 frame.add(panel);
120 frame.pack();
121 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
122 frame.setVisible(true);
123 panel.start();
124 }
125 }