0

I am currently working on a simple top down shooter. The object is a ball that slides around the screen, and I am trying to make a sort of wet-dragging affect.

I am using Java Swing and just the default Graphics2d lib inside.

This is what I have:

Original

and this is my goal:

enter image description here

I need to know how I can make a curved line that has the ability to change alpha at the trailing end. I have searched online but I can only find non-dynamic solutions. (The tail needs to update as the player moves across the screen.)

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Matt Hirdler
  • 95
  • 1
  • 10
  • Basically, record the last position of the ball before it moves, place this point within a `List` of `Point`s. When you update the screen, walk through the `List` and generate a line between each point...You might even be able to generate some kind of polygon or other `Shape` to achieve this... – MadProgrammer Apr 08 '14 at 05:04
  • Hm, simple but effective... if you want, make this an answer maybe with a code snippet or something and I will accept it. – Matt Hirdler Apr 08 '14 at 05:13

2 Answers2

2

A simple solution might be to simple add each point to a List of Points which before the player is moved.

You would simply then need to iterate this list and either simple use something like Graphics#drawLine or even GeneralPath to render the "drag" line, for example...

Drag

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.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Drag {

    public static void main(String[] args) {
        new Drag();
    }

    public Drag() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Point> points;
        private Point pos;

        private int diametere = 10;

        public TestPane() {
            points = new ArrayList<>(25);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");

            ActionMap am = getActionMap();
            am.put("left", new MoveAction(-5, 0));
            am.put("right", new MoveAction(5, 0));
            am.put("up", new MoveAction(0, -5));
            am.put("down", new MoveAction(0, 5));

            pos = new Point(100 - (diametere / 2), 100 - (diametere / 2));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (points.size() > 1) {
                g2d.setColor(Color.RED);
                GeneralPath path = new GeneralPath();
                boolean started = false;
                System.out.println("----");
                for (Point p : points) {
                    if (started) {
                        System.out.println(p);
                        path.lineTo(p.x, p.y);
                    } else {
                        path.moveTo(p.x, p.y);
                        started = true;
                    }
                }
                g2d.draw(path);
            }
            int radius = (int) (diametere / 2d);
            g2d.setColor(Color.GREEN);
            g2d.draw(new Ellipse2D.Double(pos.x - radius, pos.y - radius, diametere, diametere));
            g2d.dispose();
        }

        protected void moveBy(int xDelta, int yDelta) {

            if (pos.x + xDelta < 0) {

                xDelta = 0;
                pos.x = 0;

            } else if (pos.x + xDelta + diametere > getWidth()) {

                xDelta = 0;
                pos.x = getWidth() - diametere;

            }
            if (pos.y + yDelta < 0) {

                yDelta = 0;
                pos.y = 0;

            } else if (pos.y + yDelta + diametere > getHeight()) {

                yDelta = 0;
                pos.y = getWidth() - diametere;

            }

            points.add(new Point(pos));

            pos.x += xDelta;
            pos.y += yDelta;

            repaint();

        }

        public class MoveAction extends AbstractAction {

            private int xDelta;
            private int yDelta;

            public MoveAction(int xDelta, int yDelta) {
                this.xDelta = xDelta;
                this.yDelta = yDelta;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                moveBy(xDelta, yDelta);
            }

        }
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • @MattHirdler It's reasonably simple, but gives the jist of the idea ;) – MadProgrammer Apr 08 '14 at 07:14
  • Out of curiosity, what is the benefit of using the GeneralPath? could I not just make an array of points and then use drawLine(X1, Y1, X2, Y2); ? – Matt Hirdler Apr 08 '14 at 07:30
  • The way I've implemented, none really, but what you could do is instead of using a `List` of `Point`s, is literally just keep using `lineTo` in the `GeneralPath` each time the player is moved. It will depend in you want to know how many points there are and how much access you want to them... – MadProgrammer Apr 08 '14 at 07:45
-1

Hmm.. maybe you need something like this:

public class BallArea extends JComponent {
static final int MAX_SIZE = 63;
static final BasicStroke stroke = new BasicStroke(5);
final Queue<Point> points = new LinkedList();

public BallArea() {
    setSize(400, 400);
    setBackground(Color.BLACK);
    addMouseMotionListener(new MouseAdapter() {
        @Override
        public void mouseMoved(MouseEvent e) {
            if (points.size() >= MAX_SIZE) {
                points.poll();
            }
            points.add(e.getPoint());
            repaint();
        }
    });
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.setStroke(stroke);
    int i = 1;
    Point prev = null;
    for (Point p : points) {
        if (prev == null) {
            prev = p;
            continue;
        }
        g2.setColor(new Color(255, 0, 0, i*4));
        g.drawLine(prev.x, prev.y, p.x, p.y);
        i++;
        prev = p;
    }
}
}
PKopachevsky
  • 262
  • 1
  • 6
  • 1
    1) `setSize(400, 400);` A layout manager is more likely to honor the preferred size than the size. Even then it is better to override than set. 2) `public void paintComponent(Graphics g) {` should be `public void paintComponent(Graphics g) { super.paintComponent(g);`. Always call the `super` method first. – Andrew Thompson Apr 08 '14 at 05:28
  • It's just a demonstration sample – PKopachevsky Apr 08 '14 at 05:29
  • *"a demonstration sample"* A demonstration sample should use best practices, not repeat bad advice. -1 – Andrew Thompson Apr 08 '14 at 05:31
  • See [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/q/7229226/418556) (Yes.) – Andrew Thompson Apr 08 '14 at 05:31
  • `paintComponent` should be `protected`, there should never be any cause for anybody to ever call it beyond inherited classes. – MadProgrammer Apr 08 '14 at 05:34