1

I am currently working on a 2D game which will be a puzzle. I got everything set up, got all my pieces added to my board (called View) and they're all aligned properly.

My next step is to select a piece by MouseEvent#mousePressed to be able to move it with the arrow keys with KeyEvent#keyPressed. The issue I'm currently running into is that my pieces will repaint themself whenever I move or resize the window. If I click on one piece and want to move it, the other pieces move too (in a way, they should not since one step equals about 100 pixel).

If anyone could tell me where my issue is and give me some advice, I would appreciate it.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Puzzle {

    public static void main(String[] args) {
    SwingUtilities.invokeLater(Puzzle::new);
    }

    public Puzzle() {
    JFrame frame = new JFrame("Puzzle");
    frame.setSize(400, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    View view = new View();
    view.createPieces();
    frame.setContentPane(view);
    view.setFocusable(true);

    MouseAdapterMod listener = new MouseAdapterMod(view);
    view.addMouseListener(listener);
    view.addKeyListener(listener);

    frame.setVisible(true);
    }
}

class View extends JPanel {

    final List<Piece> pieces = new ArrayList<>();

    public View() {

    }

    void createPieces() {
    Piece topLeft = new Piece(100, 100, 0, Color.YELLOW);
    pieces.add(topLeft);
    }

    @Override
    protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D gc = (Graphics2D) g;
    for (Piece needle : pieces) {
        needle.translate(needle.getLocation().x, needle.getLocation().y);
        gc.setColor(needle.color);
        gc.fill(needle);
        gc.draw(needle);
    }
    }

}

class Piece extends Polygon {

    int x;
    int y;
    final Color color;
    Point location;

    Piece(int x, int y, int type, Color color) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.location = new Point(x, y);

    int[] arrX = new int[] { 0, 100, 100, -100, -100, 0 };;
    int[] arrY = new int[] { 0, 0, -100, -100, 100, 100 };

    for (int drawIndex = 0; drawIndex < arrX.length; drawIndex++) {
        addPoint(arrX[drawIndex], arrY[drawIndex]);
    }
    }

    Point getLocation() {
    return location;
    }

    void setLocation(Point location) {
    this.location = location;
    }
}

class MouseAdapterMod implements MouseListener, KeyListener {

    final View view;
    Polygon current;

    public MouseAdapterMod(View view) {
    this.view = view;
    }

    @Override
    public void mousePressed(MouseEvent e) {
    for (Piece piece : view.pieces) {
        if (piece.contains(e.getX(), e.getY())) {
        current = piece;
        System.out.println(current);
        }
    }
    }

    @Override
    public void keyPressed(KeyEvent e) {
    switch (e.getKeyCode()) {
    case KeyEvent.VK_UP:
        current.translate(0, -100);
        view.repaint();
        break;
    case KeyEvent.VK_DOWN:
        current.translate(0, +100);
        view.repaint();
        break;
    case KeyEvent.VK_LEFT:
        current.translate(-100, 0);
        view.repaint();
        break;
    case KeyEvent.VK_RIGHT:
        current.translate(+100, 0);
        view.repaint();
        break;
    }
    }

    @Override
    public void keyTyped(KeyEvent e) {
    // TODO Auto-generated method stub
    }

    @Override
    public void keyReleased(KeyEvent e) {
    // TODO Auto-generated method stub

    }

    @Override
    public void mouseClicked(MouseEvent e) {
    // TODO Auto-generated method stub

    }

    @Override
    public void mouseReleased(MouseEvent e) {
    // TODO Auto-generated method stub

    }

    @Override
    public void mouseEntered(MouseEvent e) {
    // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent e) {
    // TODO Auto-generated method stub

    }
}

(Sorry for the indent, stackoverflow is messing it up)

EDIT: But the problem is, eventually I want to move my pieces using Piece#setLocation to always keep track of their current x/y coordinates. I figured, I need to invoke Piece#getLocation in my painting to draw them based on the location but yet I do not know how. Using Piece#setLocation literally does nothing.

Nordic88
  • 129
  • 1
  • 12
  • The issue is that in your paintComponent method, you are translating the current x and y as a delta each time you draw. If you remove the needle.translate line you will see that the move will work as intended, but you will need to rework the starting setup – Kudu2 Mar 05 '19 at 22:41
  • @Kudu2 the problem with removing `translate(...)` is that they won't be placed in the right position. I solved this translating it, before painting. But the problem is, eventually I want to move my pieces using `Piece#setLocation` to always keep track of their current x/y coordinates. I figured, I need to invoke `Piece#getLocation` in my painting to draw them based on the location but yet I do not know how. – Nordic88 Mar 05 '19 at 22:50
  • Well you basically ignored my suggestions from your last question. I simply stated you create a unique Polygon for each piece and then you translated the Polygon to its starting position on the board. So now when you move the Piece you just translate the Polygon again. You have made you code way too complicated by trying to extend Polygon and track the location. Just use the translate(...) method to change its location and the Graphics.draw(...) will do the rest. – camickr Mar 05 '19 at 22:57
  • Also, when you ask a question post a proper [mcve]. Your question is about moving a Piece. You don't need 5 pieces to test this and you don't need 5 different types of pieces. All that code is irrelevant to the question and confuses the logic. Keep the code simple and directly related to the question. – camickr Mar 05 '19 at 22:57
  • @camickr I did not ignore your suggestion. The problem was, that I need to invoke a method (which I still need to implement) when the location of a piece changes therefore I created a class inheriting from `Polygon` to actually track the location of each piece. I did not figure any other way to invoke a method once the position changes. Will update my code to MCV any minute. – Nordic88 Mar 05 '19 at 23:06
  • @Nordic88, `I did not ignore your suggestion.` - then why is your painting code doing a translate? I gave you the two lines of code need to paint the Polygon. No need for the translate. `to actually track the location of each piece` - the Polygon tells you the location of itself. You just use the getBounds() method. You don't use translate(...) when you paint the piece because then the contains() method won't work because it doesn't know you are painting the piece at a different location. Your previous design where the Piece contained the Polygon and the Color is all you need. – camickr Mar 06 '19 at 01:59

2 Answers2

1

You could, instead of translating the Polygon, translate the location, and use it for painting:

In keyPressed, replace current.translate to translate the location instead of the whole Polygon (you could override Piece#translate to call current.getLocation().translate for example).

In View#paintComponent, replace:

needle.translate(needle.getLocation().x, needle.getLocation().y);

with

gc.translate(needle.getLocation().x, needle.getLocation().y);

and add

gc.translate(-needle.getLocation().x, -needle.getLocation().y);

at the end of your for-loop. This will translate the whole Graphics2D to paint a polygon, and revert it after.

Snowy_1803
  • 656
  • 2
  • 8
  • 24
  • Well this solutions places all pieces at the correct position but using `MouseEvent#mousePressed` to select one of the pieces will not work then since it will act like all of them are at the same position (so clicking on the top-left piece will get every piece not just the desired top-left piece). – Nordic88 Mar 05 '19 at 23:40
  • Translate the mouse position by the opposite of the location of each piece to resolve this – Snowy_1803 Mar 05 '19 at 23:56
  • 1
    `Point mouse = e.getPoint(); mouse.translate(-needle.getLocation().x, -needle.getLocation().y); if (piece.contains(mouse)) {...` in the for-loop – Snowy_1803 Mar 06 '19 at 00:10
  • 1
    Doing a translate(...) when painting a Piece and then attempting to adjust the mouse point with the opposite of the location so the `contains(...) method works is extra work and makes the whole process more confusing. – camickr Mar 06 '19 at 02:02
1

If you revert back to your Piece code from this question, you can move pieces by:

@Override
public void keyPressed(KeyEvent e) {
    switch (e.getKeyCode()) {
    case KeyEvent.VK_UP:
        view.translatePiece(0, -100);
        break;
    case KeyEvent.VK_DOWN:
        view.translatePiece(0, +100);
        break;
    case KeyEvent.VK_LEFT:
        view.translatePiece(-100, 0);
        break;
    case KeyEvent.VK_RIGHT:
        view.translatePiece(+100, 0);
        break;
    }
}

And adding to your view:

void translatePiece(int dx, int dy) {
    if (current != null) {
        current.x += dx;
        current.y += dy;
        repaint();
    }
}

The if (current != null) { ... } test will prevent your application from crashing if you press an arrow key before clicking on a piece, which it currently does.

AJNeufeld
  • 8,526
  • 1
  • 25
  • 44