Java基本2d游戏动画口吃
所以,我已经在2d rpg上工作了一段时间,但我似乎无法解决这个问题。由于未知原因,图形似乎每隔几秒钟“跳跃”或结巴一次。这是相当恼人,因为我不知道是什么原因造成的 这是我写的一个非常基本的程序,它有一个从屏幕一侧移动到另一侧的红方块。即使是在这个非常基本的程序中,square仍然每隔几次更新就会出现口吃,我真的无法用我的一生来理解它Java基本2d游戏动画口吃,java,animation,graphics,jpanel,2d,Java,Animation,Graphics,Jpanel,2d,所以,我已经在2d rpg上工作了一段时间,但我似乎无法解决这个问题。由于未知原因,图形似乎每隔几秒钟“跳跃”或结巴一次。这是相当恼人,因为我不知道是什么原因造成的 这是我写的一个非常基本的程序,它有一个从屏幕一侧移动到另一侧的红方块。即使是在这个非常基本的程序中,square仍然每隔几次更新就会出现口吃,我真的无法用我的一生来理解它 public class Main extends JPanel { int x=0, y=0; public JFrame window = new JFr
public class Main extends JPanel {
int x=0, y=0;
public JFrame window = new JFrame("Window");
public Main(){
window.setSize(1000, 500);
window.setVisible(true);
window.add(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(x, y, 500, 500);
x+=3;
if(x>900){
x=0;
}
}
public void start(){
while(true){
repaint();
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
Main game = new Main();
game.start();
}
}
如果运行该类,您将以图形方式看到问题所在。显然,我的游戏由更多的类组成,比这复杂得多,但同样的原则适用。如果有人对我的问题有任何见解,我很想听听。提前谢谢
已更新
以下是我的两个主要课程:
主类:包com.ultimatum.Main
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.ultimatum.Mangers.ImageLoader;
import com.ultimatum.Mangers.KeyStates;
import com.ultimatum.Mangers.ScreenUpdater;
import com.ultimatum.Mangers.UserInput;
@SuppressWarnings("serial")
public class Ultimatum extends JPanel {
/**
* @param x This is the start width of the screen and can be adjusted manually, but will not go any lower than this integer
* @param y This is the start height of the screen and can be adjusted manually, but will not go any lower than this integer
* @param contentPlaneX This is how much the width of the content plane is (Frame-Borders)
* @param contentPlaneY This is how much the height of the content plane is (Frame-Borders)
*/
public int x=850, y=610, contentPlaneX, contentPlaneY, middleX, middleY, tileSize=90;
public Dimension minimumSize = new Dimension(x, y);
public JFrame window = new JFrame("Ultimatum");//This makes the JFrame for the game
public KeyStates keyStates = new KeyStates();
public UserInput input = new UserInput(keyStates);
public ImageLoader imageLoader = new ImageLoader();
public static Ultimatum ultimatum;//Makes the object of this class
public static ScreenUpdater su;//This is creating the object that is going to be making changes to the screen. For example, the animation.
private BufferedImage screenImage;
public boolean isWindowInFullscreenMode;
private boolean imagesLoaded;
public void initializeUltimatum(){
toWindowedMode();
addMouseListener(input);
addMouseMotionListener(input);
contentPlaneX=window.getContentPane().getWidth();
contentPlaneY=window.getContentPane().getHeight();
middleX=(int)contentPlaneX/2;
middleY=(int)contentPlaneY/2;
su = new ScreenUpdater(ultimatum, keyStates, imageLoader, "Test", tileSize);
imageLoader.loadImages();
}
public void toFullscreenMode(){
window.dispose();
window.setUndecorated(true);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(0,0,Toolkit.getDefaultToolkit().getScreenSize().width,Toolkit.getDefaultToolkit().getScreenSize().height);
addListenersAndClassToWindow();
isWindowInFullscreenMode=true;
}
public void toWindowedMode(){
window.dispose();
window.setUndecorated(false);
window.setSize(x,y);
window.setMinimumSize(minimumSize);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setLocationRelativeTo(null);
addListenersAndClassToWindow();
isWindowInFullscreenMode=false;
}
public void addListenersAndClassToWindow(){
window.add(ultimatum);//This connects paintComponent and the frame to this class
window.addKeyListener(input);
}
public void paintComponent(Graphics g){
if(imagesLoaded){
super.paintComponent(g);
//su.updateScreen(g);
g.drawImage(screenImage, 0, 0, contentPlaneX, contentPlaneY, null);
}else imagesLoaded = true;
}
public void update(){
screenImage = su.updateScreen();
}
/**
* This main class sets up the program. The while loop that keeps the game running is also contained inside this class. Most of this class is easily
* readable so i'm not going to comment that much.
*/
public static void main(String[] args){
ultimatum = new Ultimatum();
ultimatum.initializeUltimatum();
final int FPS=60, TARGET_TIME=1000/FPS;
long start, elapsed, wait;
while(true){//This loops purpose is to keep the game running smooth on all computers
start = System.nanoTime();
ultimatum.update();
ultimatum.repaint();//This calls the paintComponent method
elapsed = System.nanoTime() - start;
wait = TARGET_TIME-elapsed/1000000;
if(wait<0) wait = TARGET_TIME;
try{//Catches the error in case the tries to give an error (which it won't)
Thread.sleep(wait);//This is how long it waits it till the screen gets repainted
}catch(Exception e){
e.printStackTrace();
}
}
}
}
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.Toolkit;
导入java.awt.image.buffereImage;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入com.ultimatum.Mangers.ImageLoader;
导入com.ultimatum.managers.KeyStates;
导入com.ultimatum.Mangers.ScreenUpdater;
导入com.ultimatum.managers.UserInput;
@抑制警告(“串行”)
公共类最后通牒扩展JPanel{
/**
*@param x这是屏幕的起始宽度,可以手动调整,但不会低于此整数
*@param y这是屏幕的起始高度,可以手动调整,但不会低于此整数
*@param contentPlaneX这是内容平面的宽度(帧边框)
*@param contentPlaneY这是内容平面的高度(帧边框)
*/
公共int x=850,y=610,contentPlaneX,contentPlaneY,middleX,middleY,tileSize=90;
公共尺寸最小尺寸=新尺寸(x,y);
public JFrame window=new JFrame(“最后通牒”);//这将生成游戏的JFrame
public KeyStates KeyStates=新的KeyStates();
公共用户输入=新用户输入(keyStates);
public ImageLoader ImageLoader=新的ImageLoader();
公共静态最后通牒;最后通牒;//生成此类的对象
public static ScreenUpdater su;//这是在创建要对屏幕进行更改的对象。例如,动画。
私有缓冲图像屏幕图像;
公共布尔值isWindowInFullscreenMode;
私有布尔图像着色;
公共无效初始值EULTIMATUM(){
toWindowedMode();
addMouseListener(输入);
addMouseMotionListener(输入);
contentPlaneX=window.getContentPane().getWidth();
contentPlaneY=window.getContentPane().getHeight();
middleX=(int)contentPlaneX/2;
middleY=(int)contentPlaneY/2;
su=新的屏幕更新程序(最后通牒、keyStates、imageLoader、“测试”、tileSize);
loadImages();
}
public void tofullscreensemode(){
window.dispose();
窗口。设置未装饰(真实);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(0,0,Toolkit.getDefaultToolkit().getScreenSize().width,Toolkit.getDefaultToolkit().getScreenSize().height);
AddListener和ClassToWindows();
isWindowInFullscreenMode=true;
}
公共无效模式(){
window.dispose();
窗户。未装饰(假);
设置窗口大小(x,y);
window.setMinimumSize(minimumSize);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setLocationRelativeTo(空);
AddListener和ClassToWindows();
isWindowInFullscreenMode=false;
}
public void addlistener和classtowindow(){
add(最后通牒);//这将paintComponent和框架连接到此类
addKeyListener(输入);
}
公共组件(图形g){
如果(图像已添加){
超级组件(g);
//su.updateScreen(g);
g、 drawImage(屏幕图像,0,0,contentPlaneX,contentPlaneY,null);
}else imagesLoaded=真;
}
公共无效更新(){
screenImage=su.updateScreen();
}
/**
*这个主类设置程序。保持游戏运行的while循环也包含在这个类中。这个类的大部分都很容易
*可读性好,所以我不打算多加评论。
*/
公共静态void main(字符串[]args){
最后通牒=新的最后通牒();
最后通牒。初始化EULTIMATUM();
最终整数FPS=60,目标时间=1000/FPS;
长时间启动、经过、等待;
虽然(正确){//这个循环的目的是保持游戏在所有计算机上顺利运行
start=System.nanoTime();
更新()的最后通牒;
retimatum.repaint();//这将调用paintComponent方法
已用=System.nanoTime()-开始;
等待=目标时间经过/1000000;
如果(wait不在paintComponent
方法中更新状态,则可能会由于多种原因进行绘制,其中许多原因您不会启动或将收到通知。相反,状态只应由“主循环”更新
有关如何在Swing中进行绘制的更多详细信息,请参见
已更新
基于Swing定时器的解决方案
这个例子允许你制作1-10000个精灵的动画,每个精灵独立地移动和旋转
我明白你的意思。我遇到了一个调用repaint的程序,但它没有被告知(至少是我)。你说的“状态应该只由你的“主循环”更新是什么意思?你的意思是我应该只使用while循环来更新状态,而在别处使用paint方法来更新状态吗
package com.ultimatum.Mangers;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import com.ultimatum.Engine.BuildingGenerator;
import com.ultimatum.Engine.TextBoxGenerator;
import com.ultimatum.Entities.Character.Player;
import com.ultimatum.Gamestates.Buildings.HealingCenter;
import com.ultimatum.Gamestates.Menus.EscapeScreen;
import com.ultimatum.Gamestates.Menus.StartScreen;
import com.ultimatum.Gamestates.Menus.TitleScreen;
import com.ultimatum.Gamestates.Routes.RouteSuperClass;
import com.ultimatum.Gamestates.Towns.TownSuperClass;
import com.ultimatum.Main.Ultimatum;
public class ScreenUpdater {
public Ultimatum ul;
public Resizer rs;//This is the object that captures the resize in two integers
public KeyStates ks;
public ImageLoader loader;
public Fader fader;
public TextBoxGenerator textBox;
public Initializer initer;
public TileMap tm;
public Player p;
public BuildingGenerator bg;
//Menus
public TitleScreen titleScreen;
public StartScreen startScreen;
public EscapeScreen escScreen;
//Towns
public TownSuperClass towns;
//Routes
public RouteSuperClass routes;
//Buildings
public HealingCenter healingCenter;
public final int TITLE_SCREEN=0, START_SCREEN=TITLE_SCREEN+1, LOAD=START_SCREEN+1, TOWN_ONE=LOAD+1,
ROUTE_ONE=TOWN_ONE+1, HEALING_CENTER=ROUTE_ONE+1, ESC_MENU=HEALING_CENTER+1;
public int screenNo = TITLE_SCREEN;
public int prevScreen=0;
public boolean prevMenuState, menuState;//These variables are for the checkEsc method
public boolean isMouseVisible=true, prevIsMouseVisible;//Simple boolean for setting the mouse from visible to invisible and vice versa
public ScreenUpdater(Ultimatum ultimatum, KeyStates keyStates, ImageLoader imageloader, String location,
int tileSize){
ul = ultimatum;
ks = keyStates;
loader = imageloader;
fader = new Fader(ul, this);
textBox = new TextBoxGenerator(loader, ks, ul);
initer = new Initializer(fader, textBox);
fader.sendIniterData(initer);
p = new Player(ul, fader, loader, ks, initer, this);
fader.sendPlayerData(p);
tm = new TileMap(tileSize, loader, p);
fader.sendTileMapData(tm);
rs = new Resizer(ul, p);
bg = new BuildingGenerator(ul, p, loader, tm);
//Below are the game states being loaded
//Menus
titleScreen = new TitleScreen(ul, this, loader, ks, fader);
startScreen = new StartScreen(ul, this, fader, loader, ks, textBox);
escScreen = new EscapeScreen(ul, fader, loader, ks);
rs.sendEscapeScreenData(escScreen);
//Towns
towns = new TownSuperClass(p, fader, bg, tm, this);
//Routes
routes = new RouteSuperClass(p, fader, bg, tm, this);
//Buildings
healingCenter = new HealingCenter(ul, fader, loader, ks, textBox);
}
public void clearScreen(Graphics g){
g.setColor(Color.black);
g.fillRect(0, 0, ul.contentPlaneX, ul.contentPlaneY);
}
public void checkEsc(Graphics g){
if(ks.escReleased&&screenNo>LOAD&&!fader.fadingOut&&fader.fadingIn){
if(screenNo<HEALING_CENTER&&!p.isMoving){
menuState=true;
prevScreen=screenNo;
}
else if(screenNo==ESC_MENU) menuState=false;
}
if(prevMenuState!=menuState){
int toScreen;
boolean mouseVisiblity;
if(menuState){
toScreen=ESC_MENU;
mouseVisiblity=true;
}
else{
toScreen=prevScreen;
mouseVisiblity=false;
}
fader.FadeOut(g, 255, toScreen, false, "", 0, 0, false, 0, mouseVisiblity);//The zeros don't matter because the boolean is set to false
if(!fader.fadingOut){
prevMenuState=menuState;
initer.initFader();
}
}
}
public void checkForF11(){
if(ks.isF11PressedThenReleased){
if(ul.isWindowInFullscreenMode) ul.toWindowedMode();
else ul.toFullscreenMode();
}
}
public void setMouseVisible(){
ul.window.setCursor(ul.window.getToolkit().createCustomCursor(loader.cursor, new Point(0, 0),"Visible"));
}
public void setMouseInvisble(){
ul.window.setCursor(ul.window.getToolkit().createCustomCursor(new BufferedImage(3, 3, BufferedImage.TYPE_INT_ARGB), new Point(0, 0),"Clear"));
}
public void checkMouseState(){
if(isMouseVisible!=prevIsMouseVisible){
if(isMouseVisible) setMouseVisible();
else setMouseInvisble();
prevIsMouseVisible=isMouseVisible;
}
}
public BufferedImage updateScreen(){
BufferedImage screenImage = new BufferedImage(ul.contentPlaneX, ul.contentPlaneY, BufferedImage.TYPE_INT_ARGB);
Graphics2D screenGraphics = screenImage.createGraphics();
Color oldColor = screenGraphics.getColor();
screenGraphics.setPaint(Color.white);
screenGraphics.fillRect(0, 0, ul.contentPlaneX, ul.contentPlaneY);
screenGraphics.setColor(oldColor);
checkForF11();
clearScreen(screenGraphics);
switch(screenNo){
case TITLE_SCREEN:
titleScreen.titleScreen(screenGraphics);
break;
case START_SCREEN:
startScreen.startScreen(screenGraphics);
break;
case TOWN_ONE:
towns.townOne(screenGraphics);
break;
case ROUTE_ONE:
routes.routeOne(screenGraphics);
break;
case HEALING_CENTER:
healingCenter.healingCenter(screenGraphics);
break;
case ESC_MENU:
escScreen.escapeScreen(screenGraphics);
break;
}
checkEsc(screenGraphics);
rs.checkForResize();
ks.update();
checkMouseState();
//g.drawImage(screenImage, 0, 0, ul.contentPlaneX, ul.contentPlaneY, null);
//screenGraphics.dispose();
return screenImage;
}
}
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
PaintPane pane = new PaintPane();
JSlider slider = new JSlider(1, 10000);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
try {
pane.setQuantity(slider.getValue());
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
slider.setValue(1);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(pane);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class PaintPane extends JPanel {
private static final int SPOOL_DELTA = 100;
private List<Sprite> pool;
private List<Sprite> sprites;
private int quantity;
public PaintPane() {
try {
BufferedImage img = ImageIO.read(getClass().getResource("/resources/Pony.png"));
pool = new ArrayList<>(128);
sprites = new ArrayList<>(128);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (sprites.size() < quantity) {
List<Sprite> toAdd = new ArrayList<>(SPOOL_DELTA);
int required = quantity - sprites.size();
if (pool.isEmpty()) {
for (int index = 0; index < Math.min(SPOOL_DELTA, required); index++) {
int x = (int)(Math.random() * getWidth());
int y = (int)(Math.random() * getHeight());
toAdd.add(new Sprite(img, new Point(x, y)));
}
} else {
toAdd.addAll(pool.subList(0, Math.min(SPOOL_DELTA, pool.size())));
pool.removeAll(toAdd);
}
sprites.addAll(toAdd);
} else if (sprites.size() > quantity) {
List<Sprite> toRemove = new ArrayList<>(SPOOL_DELTA);
int required = sprites.size() - quantity;
if (sprites.size() > required) {
toRemove.addAll(sprites.subList(0, Math.min(SPOOL_DELTA, required)));
sprites.removeAll(toRemove);
pool.addAll(toRemove);
}
}
for (Sprite sprite : sprites) {
sprite.update(getSize());
}
repaint();
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
setFont(getFont().deriveFont(Font.BOLD, 18f));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Sprite sprite : sprites) {
sprite.draw(g2d, this);
}
String text = NumberFormat.getNumberInstance().format(sprites.size());
FontMetrics fm = g2d.getFontMetrics();
int x = getWidth() - fm.stringWidth(text);
int y = (getHeight() - fm.getHeight()) + fm.getAscent();
g2d.drawString(text, x, y);
g2d.dispose();
}
public void setQuantity(int value) throws IOException {
this.quantity = value;
}
}
public static class Sprite {
private BufferedImage img;
private Point location;
private double angle;
private Point delta;
private double angleDelta;
public Sprite(BufferedImage cache, Point location) {
img = cache;
this.location = new Point(location);
delta = new Point(rnd(), rnd());
while (angleDelta == 0) {
angleDelta = (Math.random() * 5) - 2.5;
}
}
protected int rnd() {
int value = 0;
while (value == 0) {
value = (int) (Math.random() * 9) - 4;
}
return value;
}
public void update(Dimension size) {
location.x += delta.x;
location.y += delta.y;
if (location.x < 0) {
location.x = 0;
delta.x *= -1;
}
if (location.x + img.getWidth() > size.width) {
location.x = size.width - img.getWidth();
delta.x *= -1;
}
if (location.y < 0) {
location.y = 0;
delta.y *= -1;
}
if (location.y + img.getHeight() > size.height) {
location.y = size.height - img.getHeight();
delta.y *= -1;
}
angle += angleDelta;
}
public void draw(Graphics2D g2d, JComponent parent) {
Graphics2D g = (Graphics2D) g2d.create();
AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
at.rotate(Math.toRadians(angle), img.getWidth() / 2, img.getHeight() / 2);
g.transform(at);
g.drawImage(img, 0, 0, parent);
g.dispose();
}
}
}
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main extends JPanel {
double x = 0, y = 0;
private Rectangle2D shape;
private double angel = 0;
private ReentrantLock updateLock = new ReentrantLock();
public JFrame window = new JFrame("Window");
public Main() {
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(1000, 500);
window.add(this);
window.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setColor(Color.red);
updateLock.lock();
try {
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(angel),
shape.getCenterX(),
shape.getCenterY()));
g2d.fill(shape);
} finally {
updateLock.unlock();
}
g2d.dispose();
}
public void start() {
shape = new Rectangle2D.Double(x, y, 50, 50);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
long startTime = System.nanoTime();
long runTime = TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS);
System.out.println(runTime);
double rotateFrom = 0;
double rotateTo = 720;
while (true) {
long now = System.nanoTime();
long diff = now - startTime;
double progress = diff / (double) runTime;
if (progress > 1.0d) {
progress = 0d;
startTime = System.nanoTime();
}
x = (getWidth() * progress);
updateLock.lock();
try {
angel = rotateFrom + ((rotateTo - rotateFrom) * progress);
shape.setRect(x, y, 50, 50);
} finally {
updateLock.unlock();
}
repaint();
try {
Thread.sleep(8);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.setDaemon(true);
t.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Main game = new Main();
game.start();
}
});
}
}