开发者

Java mouse picking on diamond tiled map

开发者 https://www.devze.com 2023-03-20 22:45 出处:网络
Ok, im at my wits end. I am trying to create a small isometric tile map thats bigger then the screen witha viewpoint i can modify with mouse dragging.

Ok, im at my wits end. I am trying to create a small isometric tile map thats bigger then the screen witha viewpoint i can modify with mouse dragging. I got the drawing right (i think), i got the dragging working, just do't seem to be able to get the mouse picking right. I have made it so far that i get the tile nearly right, but its off by about half a tiles size and i can't find a way to make up that offset.

Here is the code:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MapView {

    public static void main(String[] args) {
        JFrame test = new JFrame("IsoView");
        test.setSize(800, 600);
        MapViewPane pane = new MapViewPane();
        test.getContentPane().setLayout(new BorderLayout());
        test.getContentPane().add(pane, BorderLayout.CENTER);
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        test.setVisible(true);
    }

    private static class MapViewPane extends JPanel
            implements MouseMotionListener, MouseListener {

        private BufferedImage BackImage;
        BufferedImage GrassTile, SelectedBorder;
        private Point MousePoint, PrevView, ViewLocation, Selected;
        private boolean Dragging;
        private int mapwidth, mapheight, tilecount;

        public MapViewPane() {
            super();
            this.setOpaque(true);
            createAssets();
            tilecount = 30;
            mapwidth = GrassTile.getWidth() * tilecount;
            mapheight = GrassTile.getHeight() * tilecount;
            ViewLocation = new Point(0, mapheight / 2);
            Selected = new Point(-1, -1);
            addMouseListener(this);
            addMouseMotionListener(this);
        }

        private void createAssets() {
            GraphicsConfiguration gc =
                    GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
            GrassTile = gc.createCompatibleImage(128,
                    64, Transparency.TRANSLUCENT);
            Graphics g = GrassTile.getGraphics();
            Polygon poly = new Polygon();
            poly.addPoint(0, 32);
            poly.addPoint(64, 0);
            poly.addPoint(128, 32);
            poly.addPoint(64, 64);
            g.setColor(Color.GREEN);
            g.fillPolygon(poly);
            g.setColor(Color.BLUE);
            g.drawPolygon(poly);
            g.dispose();
            SelectedBorder = gc.createCompatibleImage(128,
                    64, Transparency.TRANSLUCENT);
            g = SelectedBorder.getGraphics();
            g.setColor(Color.red);
            g.drawPolygon(poly);
            g.dispose();
        }

        @Override
        public void paint(Graphics g) {
            //super.paint(g);
            Rectangle visiblerec = this.getVisibleRect();
            g.setColor(Color.BLACK);
            g.fillRect(visiblerec.x, visiblerec.y,
                    visiblerec.width, visiblerec.height);
            checkBackImage();
            Graphics bg = BackImage.getGraphics();
            drawGrassGrid(bg);
            bg.dispose();
            g.drawImage(BackImage, 0, 0, this);
        }

        private void drawGrassGrid(Graphics g) {
            int dx = 0;
            int dy = 0;
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, BackImage.getWidth(), BackImage.getHeight());
            for (int x = 0; x < tilecount; x++) {
                for (int y = 0; y < tilecount; y++) {
                    dx = x * GrassTile.getWidth() / 2
                            - y * GrassTile.getWidth() / 2;
                    dy = x * GrassTile.getHeight() / 2
                            + y * GrassTile.getHeight() / 2;
                    dx -= ViewLocation.x;
                    dy -= ViewLocation.y;
                    g.drawImage(GrassTile, dx, dy, this);
                    if ((x == Selected.x) && (y == Selected.y)) {
                        g.drawImage(SelectedBorder, dx, dy, this);
                    }
                    g.drawString("(" + x + "," + y + ")", dx, dy
                            + GrassTile.getHeight() / 2);
                }
            }
        }

        private void checkBackImage() {
            if ((BackImage == null) || (BackImage.getWidth() != this.getWidth())
                    || (BackImage.getHeight() != this.getHeight())) {
                GraphicsConfiguration gc =
                        GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
                BackImage = gc.createCompatibleImage(this.getWidth(),
                        this.getHeight(), Transparency.BITMASK);
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (Dragging) {
                ViewLocation.x = PrevView.x + MousePoint.x - e.getX();
                ViewLocation.y = PrevView.y + MousePoint.y - e.getY();
                if (ViewLocation.x < -mapwidth / 2) {
                    ViewLocation.x = -mapwidth / 2;
                }
                if (ViewLocation.y < -mapheight / 2 + this.getHeight()) {
                    ViewLocation.y = -mapheight / 2 + this.getHeight();
                }
                if (ViewLocation.x > mapwidth / 2 - this.getWidth()
                        + GrassTile.getWidth()) {
                    ViewLocation.x = mapwidth / 2 - this.getWidth()
                            + GrassTile.getWidth();
                }
                if (ViewLocation.y > mapheight / 2 + this.getHeight()) {
                    ViewLocation.y = mapheight / 2 + this.getHeight();
                }
                repaint();
            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
        }

        @Override
        public void 开发者_如何学编程mouseClicked(MouseEvent e) {
            if (!Dragging) {
                int x = (GrassTile.getWidth() * (e.getY() + ViewLocation.y)
                        + GrassTile.getHeight() * (e.getX() + ViewLocation.x))
                        / (GrassTile.getWidth() * GrassTile.getHeight());
                int y = (GrassTile.getWidth() * (e.getY() + ViewLocation.y)
                        - GrassTile.getHeight() * (e.getX() + ViewLocation.x))
                        / (GrassTile.getWidth() * GrassTile.getHeight());

//      int x = (int) Math.floor((e.getY() + ViewLocation.y)
//              / (double) GrassTile.getHeight() - (e.getX() + ViewLocation.x)
//              / (double) GrassTile.getWidth());
//      int y = (int) Math.floor((e.getY() + ViewLocation.y)
//              / (double) GrassTile.getHeight() + (e.getX() + ViewLocation.x)
//              / (double) GrassTile.getWidth());

                Selected.setLocation(x, y);
                repaint();
                System.out.println("(" + x + "," + y + ")");
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {
            if ((e.getButton() == MouseEvent.BUTTON1) && !Dragging) {
                MousePoint = e.getPoint();
                PrevView = new Point(ViewLocation);
                Dragging = true;
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            Dragging = false;
            MousePoint = null;
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }
    }
}

I have some formulas in the click method commented out wich i tried, but they dont work (x and y axis are inverted that way, havent tried to figure out why yet). Would realy appreciate if someone can point me to the mistake i'm making.


I managed to fix it for you. Firstly I did a bit of algebra (hopefully explained by the inline comments) to simplify the calculation of which tile is hit. The next bit you did somewhat realise; there's issues casting. You need to convert everything to a double before you use it. If you do int/int then you already made a cast and lost precision.

The trick to making it hit the right tile is 1) casting early and 2) the +/- 0.5 which is used to force the return int cast to go one way or the other. To elaborate, (int)6.9 == 6.

Here's the working answer:

    @Override
    public void mouseClicked(MouseEvent e) {
        if (!Dragging) {
            /*
            // copy of the tile location assignment code as a reminder
            dx = x * GrassTile.getWidth() / 2
                    - y * GrassTile.getWidth() / 2;
            dy = x * GrassTile.getHeight() / 2
                    + y * GrassTile.getHeight() / 2;
            dx -= ViewLocation.x;
            dy -= ViewLocation.y;
            */
            int pickX = e.getX() + ViewLocation.x;
            int pickY = e.getY() + ViewLocation.y;
            int tileW = GrassTile.getWidth();
            int tileH = GrassTile.getHeight();
            /*
            // assignment code refactored
            x - y = 2 * pickX / tileW;
            x + y = 2 * pickY / tileH;

            // x+y= refactored to y=
            y = (2*pickY / tileH) - x;
            // substitute into x-y + refactor
            2x = (2 * pickX / tileW) + (2 * pickY / tileH);

            // x+y= refactored to x=
            x = (2*pickY / tileH) - y;
            // substitute x-y + refactor
            -2y = (2 * pickX / tileW) - (2 * pickY / tileH);
            2y = (2 * pickY / tileH) - (2 * pickX / tileW);
            */
            int hitx = (int)(((double)pickX / (double)tileW) + ((double)pickY / (double)tileH) - 0.5);
            int hity = (int)(((double)pickY / (double)tileH) - ((double)pickX / (double)tileW) + 0.5);
            Selected.setLocation(hitx, hity);
            repaint();
            //System.out.println("(" + x + "," + y + ")");
        }
    }


Polygon implements the Shape interface, so one of the several contain() variations may simplify your calculation. The createTransformedShape() method of AffineTransform may also be helpful, as suggested in this example.

0

精彩评论

暂无评论...
验证码 换一张
取 消