java - 我试图让球逐渐移动

标签 java swing keylistener paintcomponent graphic

我试图让一个球在您按下其中一个箭头键时逐渐移动,现在它有点像瞬移。我想要它,这样你就可以看到它在移动。基于此example ,我正在使用键绑定(bind),并且有一个名为 delta 的变量会导致球移动 50 像素,但就像我说的那样,无论你按下箭头键的哪个方向,球都会出现 50 像素,我希望它就像你要踢一个球,你可以看到它从 a 点到 b 点。转到我认为问题所在的第 89 行。

package game;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/**
 * @see https://stackoverflow.com/questions/6991648
 * @see https://stackoverflow.com/questions/6887296
 * @see https://stackoverflow.com/questions/5797965
 */
public class LinePanel extends JPanel {
myObject ball;

private Point b1 = new Point(0,0);


private MouseHandler mouseHandler = new MouseHandler();
private Point p1 = new Point(100, 100);
private Point p2 = new Point(540, 380);
private boolean drawing;

public LinePanel() {
    this.setPreferredSize(new Dimension(640, 480));
    this.addMouseListener(mouseHandler);
    this.addMouseMotionListener(mouseHandler);
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setColor(Color.blue);
    g2d.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setStroke(new BasicStroke(8,
        BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
    g.drawLine(p1.x, p1.y, p2.x, p2.y);
    ball = new myObject(b1.x,b1.y,"Stuff/ball.png",50,50);
    g.drawImage(ball.getImage(),ball.getX(),ball.getY(), ball.getWidth(),         ball.getHeight(), null);
    repaint();
}

private class MouseHandler extends MouseAdapter {

    @Override
    public void mousePressed(MouseEvent e) {
        drawing = true;
        p1 = e.getPoint();
        p2 = p1;
        repaint();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        drawing = false;
        p2 = e.getPoint();
        repaint();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (drawing) {
            p2 = e.getPoint();
            repaint();
        }
    }
}

private class ControlPanel extends JPanel {

    private static final int DELTA = 50;
// above is telling the ball to move by 50 pixels
    // I want it to move by 50 pixels but gradually I dont want it to teleport
    public ControlPanel() {
        this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
        this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
        this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
        this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));


    }


    private class MoveButton extends JButton {

        KeyStroke k;
        int myX, myY;

        public MoveButton(String name, int code, final int myX, final int myY) {
            super(name);
            this.k = KeyStroke.getKeyStroke(code, 0);
            this.myX = myX;
            this.myY = myY;
            this.setAction(new AbstractAction(this.getText()) {

                @Override
                public void actionPerformed(ActionEvent e) {
                    LinePanel.this.b1.translate(myX, myY);

                    LinePanel.this.repaint();
                }
            });
            ControlPanel.this.getInputMap(
                WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
            ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    MoveButton.this.doClick();
                }
            });
        }
    }
}



private void display() {
    JFrame f = new JFrame("LinePanel");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.add(this);
    f.add(new ControlPanel(), BorderLayout.SOUTH);
    f.pack();
    f.setLocationRelativeTo(null);
    f.setVisible(true);
}

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            new LinePanel().display();
        }
    });
}


}

最佳答案

任何动画的基本前提是随时间变化。

您需要能够在给定时间段内将球从位置 A 移动到位置 B。为此,您需要某种可用于更新该时间段内球位置的“自动收报机”。一般来说,25fps(或大约 40 毫秒)绰绰有余。

要在 Swing 中安全地实现这一点,最简单的解决方案是使用 Swing Timer。您可以使用 Thread,但随后您需要负责将更新同步回 UI,并且在这个阶段确实需要更复杂。

此示例使用 1 秒的持续时间将球从 A 点移动到 B 点。这是一个线性动画,您需要研究适当的动画框架以获得更复杂的解决方案。

import java.awt.BasicStroke;
import java.awt.BorderLayout;
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class LinePanel extends JPanel {

//    myObject ball;
    private Point b1 = new Point(0, 0);
    private Point startPoint = new Point(0, 0);
    private Point targetPoint = new Point(0, 0);
    private MouseHandler mouseHandler = new MouseHandler();
    private Point p1 = new Point(100, 100);
    private Point p2 = new Point(540, 380);
    private boolean drawing;

    private Timer animate;
    private long startTime;
    private int duration = 1000;

    public LinePanel() {
        this.setPreferredSize(new Dimension(640, 480));
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);
        animate = new Timer(40, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                long now = System.currentTimeMillis();
                long dif = now - startTime;
                if (dif >= duration) {
                    dif = duration;
                    ((Timer)e.getSource()).stop();
                }
                float progress = (float)dif / (float)duration;
                b1 = calculateProgress(startPoint, targetPoint, progress);
                repaint();
            }
        });
        animate.setRepeats(true);
        animate.setCoalesce(true);
    }

    public void moveBallTo(Point target) {

        if (animate.isRunning()) {
            animate.stop();
        }

        startPoint = b1;
        targetPoint = target;

        startTime = System.currentTimeMillis();
        animate.start();

    }

    public void moveBallBy(int xDelta, int yDelta) {

        animate.stop();

        Point t = new Point(targetPoint == null ? b1 : targetPoint);
        t.x += xDelta;
        t.y += yDelta;

        moveBallTo(t);

    }

    public Point calculateProgress(Point startPoint, Point targetPoint, double progress) {

        Point point = new Point();

        if (startPoint != null && targetPoint != null) {

            point.x = calculateProgress(startPoint.x, targetPoint.x, progress);
            point.y = calculateProgress(startPoint.y, targetPoint.y, progress);

        }

        return point;

    }

    public int calculateProgress(int startValue, int endValue, double fraction) {

        int value = 0;
        int distance = endValue - startValue;
        value = (int)Math.round((double)distance * fraction);
        value += startValue;

        return value;

    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.blue);
        g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(8,
                BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
        g.drawLine(p1.x, p1.y, p2.x, p2.y);

        g.setColor(Color.RED);
        g.drawOval(b1.x - 4, b1.y - 4, 8, 8);
//        ball = new myObject(b1.x, b1.y, "Stuff/ball.png", 50, 50);
//        g.drawImage(ball.getImage(), ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight(), null);
        repaint();
    }

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            drawing = true;
            p1 = e.getPoint();
            p2 = p1;
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            drawing = false;
            p2 = e.getPoint();
            repaint();
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (drawing) {
                p2 = e.getPoint();
                repaint();
            }
        }
    }

    private class ControlPanel extends JPanel {

        private static final int DELTA = 50;
// above is telling the ball to move by 50 pixels
        // I want it to move by 50 pixels but gradually I dont want it to teleport

        public ControlPanel() {
            this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
            this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
            this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
            this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));


        }

        private class MoveButton extends JButton {

            KeyStroke k;
            int myX, myY;

            public MoveButton(String name, int code, final int myX, final int myY) {
                super(name);
                this.k = KeyStroke.getKeyStroke(code, 0);
                this.myX = myX;
                this.myY = myY;
                this.setAction(new AbstractAction(this.getText()) {
                    @Override
                    public void actionPerformed(ActionEvent e) {
//                        LinePanel.this.b1.translate(myX, myY);
                        moveBallBy(myX, myY);
                        LinePanel.this.repaint();
                    }
                });
                ControlPanel.this.getInputMap(
                        WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
                ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        MoveButton.this.doClick();
                    }
                });
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("LinePanel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.add(new ControlPanel(), BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new LinePanel().display();
            }
        });
    }
}

看看...

旁注

在您的 paint 方法中执行此 ball = new myObject(b1.x, b1.y, "Stuff/ball.png", 50, 50); 效率极低。 paint 可以快速连续调用多次。在任何可能的情况下,您都希望尽可能优化 paint 方法,在 paint 方法上花费太多时间会使您的程序看起来很慢

关于java - 我试图让球逐渐移动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15078835/

相关文章:

java - 可以在 Java Web 应用程序中使用 Haml 吗?

java - <?> 在 Java 中代表什么?

java - Java中如何转义转义字符

Java:JTable 内的 JComboBox - setSelectedIndex 对 GUI 没有影响

java - 从 ArrayList 中删除 KeyEvents

java - 如何在Spring Security 4中动态修改登录用户的角色?

java - 在没有 IDE 的情况下在 Java 中导入 javax 包

java - 对一组 jButton 实现 ActionListener 时出现问题

java - keyTyped 使 JTextField 为空

Java使用KeyListener检查括号是否被按下