Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am trying to make a ball gradually move when you press one of the arrow keys, right now it kind of just teleports. I want it so that you can see it move. Based on this example, I am using key bindings, and there is a variable called delta that causes the ball move by 50 pixels, but like I said the ball just appears 50 pixels in whichever direction the arrow key is you pressed, I want it to be like if you were to kick a ball you can see it get from point a to b. Go to line 89 that is where I think the problem is.

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();
        }
    });
}


}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
231 views
Welcome To Ask or Share your Answers For Others

1 Answer

The basic premise for any animation is change over time.

You need to be able to move the ball from position A to position B over a given time period. To do that, you need some kind of "ticker" that can be used to update the position of the ball over that period. Generally speaking, 25fps (or about 40 milliseconds) is more than enough.

To achieve this safely in Swing, the easiest solution is to use a Swing Timer. You could use a Thread, but then you become responsible for syncing the updates back to the UI and it's more complexity then is really required at this stage.

This example uses a duration of 1 second to move the ball from point A to point B. This is a linear animation and you would need to investigate an appropriate animation framework to get a more complex solution.

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();
            }
        });
    }
}

Take a look at ...

Side note

Doing this ball = new myObject(b1.x, b1.y, "Stuff/ball.png", 50, 50); in you paint method is incredibly inefficient. paint may be called a number of times in quick succession. Where ever possible, you want the paint methods to be as optimized as you can make them, to much time spent in your paint method will make your program look slow


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...