提问者:小点点

Java图像沿着列表中的点移动并使用线性插值


我正在尝试让图像跟随路径。这条路径的点存储在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()中重新绘制?


共1个答案

匿名用户

让我们把这个分解…

基本上,您希望在一段时间内从一个点(A)移动到另一个点(B)(t)。在给定时间点,AB之间的给定点是两者差异的百分比(其中t被规范化为0和1之间的分数)

因此,如果A10B20t是2秒,则在1秒时p应该是15((B-A)*i)A)其中i0.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

这有点复杂,因为你需要考虑两点之间的时间量是所有时间的一小部分,但是由于点之间的时间距离大部分是均匀的,它不应该起很大的作用。

这在这里和这里进行了演示,因此您必须原谅我没有再次重新发布代码。

现在,为了我的钱,我也会投入一些时间去了解一个动画库,比如定时框架和/或三叉戟和/或通用吐温引擎