Java 鼠标在菱形平铺地图上拾取

发布于 2024-11-24 14:52:34 字数 7725 浏览 1 评论 0原文

好吧,我已经束手无策了。我正在尝试创建一个比屏幕大的小型等距平铺地图,其中的视点我可以通过鼠标拖动进行修改。 我的绘图正确(我认为),我的拖动工作正常,只是似乎无法正确选择鼠标。 到目前为止,我已经做到了,我得到的瓷砖几乎是正确的,但它偏离了大约瓷砖尺寸的一半,我找不到弥补该偏移的方法。

这是代码:

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) {
        }
    }
}

我在 click 方法中有一些公式被注释掉,但它们不起作用(x 和 y 轴以这种方式反转,还没有尝试找出原因)。 如果有人能指出我所犯的错误,我将不胜感激。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浅听莫相离 2024-12-01 14:52:34

我设法为你解决了它。首先,我做了一些代数运算(希望通过内联注释进行解释)来简化计算哪个图块被击中。接下来你确实有所意识到了;选角存在问题。在使用之前,您需要将所有内容转换为双精度值。如果您执行 int/int 那么您已经进行了强制转换并失去了精度。

让它击中正确的图块的技巧是 1) 尽早转换和 2) +/- 0.5 用于强制返回 int 强制转换为单向或另一个。详细来说,(int)6.9 == 6

这是工作答案:

    @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 + ")");
        }
    }

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 + ")");
        }
    }
冧九 2024-12-01 14:52:34

Polygon 实现Shape 接口,因此几个 contain() 变体之一可以简化您的计算。 AffineTransform 也可能会有帮助,如本 示例

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文