我正在尝试让图像跟随路径。这条路径的点存储在ArrayList中。现在图像每两秒跳转到下一个点,所以我必须使用线性插值来使运动平滑。但是我如何在update()方法中使用线性插值呢?我在网上搜索了这个问题,但是在update方法中找不到太多关于线性插值的信息,结合带点的ArrayList。
更新方法
public void update(){
repaint();
if(counter < Lane.firstLane.size()){
startPoint = new Point(carPosition.x, carPosition.y);
endPoint = new Point(Lane.firstLane.get(counter).x, Lane.firstLane.get(counter).y);
pointOnTimeLine = new Point(startPoint);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long diff = now - startTime;
if (diff >= playTime) {
diff = playTime;
((Timer) e.getSource()).stop();
}
double i = (double) diff / (double) playTime;
pointInTime = i;
//pointOnTimeLine.x = (int) (startPoint.x + ((endPoint.x - startPoint.x) * i));
//pointOnTimeLine.y = (int) (startPoint.y + ((endPoint.y - startPoint.y) * i));
//carPosition.setLocation(pointOnTimeLine);
carPosition.x=(int) lerp(startPoint.x,endPoint.x,i);
carPosition.y=(int)lerp(startPoint.y,endPoint.y,i);
System.out.println("Car position: x"+carPosition.x+": y"+carPosition.y );
//System.out.println("Point"+pointOnTimeLine);
repaint();
counter++;
}
});
timer.start();
}
else{
//System.out.println("Destination reached");
}
//carPosition.x+=1;
//repaint();
}
double lerp(double a, double b, double t) {
return a + (b - a) * t;
}
线程移动汽车
public void moveCar() {
Runnable helloRunnable = new Runnable() {
public void run() {
car.update();
repaint();
}
};
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(helloRunnable, 0, 40, TimeUnit.MILLISECONDS);
}
Lane. cs
public class Lane {
public static List<Point> firstLane = new ArrayList<>(Arrays.asList(new Point(10,135),new Point(124,190),new Point(363,190),new Point(469,210)));
}
编辑:我已经根据MadProgrammers的建议对我的代码进行了更改。动画现在起作用了,这是动画的电影http://gyazo.com/e6a28b87cb905c0ff5eb023d68955321.我的OP用我当前的代码更新。下一步是转弯部分,但我认为有一种更优雅的方式来调用汽车更新方法并在moveCar中重新绘制()。我已经将此线程中的时间指定为与计时器中相同的长度(40ms)。有没有更好的方法来调用car. update()并在moveCar()中重新绘制?
让我们把这个分解…
基本上,您希望在一段时间内从一个点(A
)移动到另一个点(B
)(t
)。在给定时间点,A
和B
之间的给定点是两者差异的百分比(其中t
被规范化为0和1之间的分数)
因此,如果A
是10
,B
是20
,t
是2秒,则在1秒时p
应该是15
(((B-A)*i)A
)其中i
是0.5
(2秒的50%=1秒)的标准化时间差
因此,给定任何时间点,您可以计算两点之间的差异并计算它应该在的位置。
如果你想知道我为什么规范化时间,考虑一下,如果你把t
改为4秒,计算不会改变,我们只需要计算规范化的时间点(1/4=0.25
)并通过计算返回给我们所需的结果。
所以,你需要知道从点A
到点B
需要多长时间。然后你需要一些机制来定期检查已经过去的时间量,并计算两点之间对象的当前位置。为此,你可以使用SwingTimer
以规则的间隔(例如40毫秒)打勾,直到2秒过去。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
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.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Long startTime;
private long playTime = 2000;
private Point startPoint, endPoint;
private Point pointOnTimeLine;
private double pointInTime; // For rendering...
public TestPane() {
startPoint = new Point(0, 95);
endPoint = new Point(190, 95);
pointOnTimeLine = new Point(startPoint);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long diff = now - startTime;
if (diff >= playTime) {
diff = playTime;
((Timer) e.getSource()).stop();
}
double i = (double) diff / (double) playTime;
pointInTime = i;
pointOnTimeLine.x = (int) (startPoint.x + ((endPoint.x - startPoint.x) * i));
pointOnTimeLine.y = (int) (startPoint.y + ((endPoint.y - startPoint.y) * i));
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(new Ellipse2D.Double(startPoint.x, startPoint.y, 10, 10));
g2d.fill(new Ellipse2D.Double(endPoint.x, endPoint.y, 10, 10));
g2d.setColor(Color.GREEN);
g2d.fill(new Ellipse2D.Double(pointOnTimeLine.x, pointOnTimeLine.y, 10, 10));
g2d.dispose();
}
}
}
好吧,“但是这对我有什么帮助?”我听到你在问。事实上,这是在多个点之间移动的基础。
在您的代码中,您有4个均匀分布的关键点,对象必须在这些关键点上移动,这意味着每个点在时间线上的间隔大约为33%。
当你沿着时间线计算当前位置时,你需要找到它之间的两个点(0-33是第一个和第二个点,34-66是第二个和第三个和67
这有点复杂,因为你需要考虑两点之间的时间量是所有时间的一小部分,但是由于点之间的时间距离大部分是均匀的,它不应该起很大的作用。
这在这里和这里进行了演示,因此您必须原谅我没有再次重新发布代码。
现在,为了我的钱,我也会投入一些时间去了解一个动画库,比如定时框架和/或三叉戟和/或通用吐温引擎