Polygonförmige Components mit MouseListener?

schlseb

Mitglied
Hallo zusammen,

ich bin dabei eine Art Brettspiel zu programmieren, wobei das Spielfeld aus Feldern in Form gleichseitiger Dreiecke besteht. Auf diesem sollen Spielfiguren verschoben werden (Vielecke mit >4 Ecken). Das Spielfeld steht soweit, jetzt überlege ich, wie ich am Besten das Ziehen mit den Spielfiguren umsetze. Im Prinzip sehe ich zwei mögliche Vorgehensweisen:

1) Ich implementiere einen MouseListener für das Panel, auf dem das Spielfeld gezeichnet wird und Frage im Falle von z.B. einem Pressed-Event ab, welche der Figuren gewählt wurde und Verfahre ebenso mit einem Released-Event um das Zielfeld zu ermitteln.

Eine ähnliche Vorgehensweise habe ich in diesem Beitrag gefunden.

2) Ich erstelle die Spielfelder und -figuren als Subklassen von Component (bzw. einer Subklasse von Component), damit ich in der Klasse auf addMouseListener zurückgreifen kann, um somit jedem Objekt seinen eigenen Listener zu spendieren.

Diese Vorgehensweise wird in folgendem Beitrag praktiziert.

Dies wäre meiner Meinung nach die elegantere Lösung, es stellt sich nur ein Problem. Die "bounds", nach denen ein Component einem MouseEvent zugeordnet werden, können nur als Rechteckfläche, nicht aber als Polygon vorgegeben werden:
Java:
setBounds(Rectangle r) 
//bzw. 
setBounds(int x, int y, int width, int height)


Natürlich könnte man jetzt einfach auf Herangehensweise 1 zurückgreifen, mir ist aber ein Problem in den Sinn gekommen, bei dem das nicht möglich ist.

Beispiel:
Wenn eine Spielfigur ausgewählt wurde und per Drag&Drop auf ein anderes Feld gezogen wird, dann soll das Feld beim Darüberziehen der Spielfiguren mit einer farblichen Umrahmung hervorgehoben werden. Dafür müsste ich jedoch ein MouseEntered-Event für ein dreieckiges Polygon mit dem Listener abfangen. OK, theoretisch könnte man das vielleicht auch über mouseDragged im Panel realisieren, aber dann müsste ich mit jedem Aufruf sämtliche Felder mit contains(e.getX(), e.getY()) durchgehen.
Würde mich interessieren, ob dafür jmd eine Idee hat.
 
Da der MouseEvent von der drunterliegenden Platform abhängt und gerade MouseEnteredEvents dazu tendieren, wenig zuverlässig zu sein, würd ich einen Timer machen, der z.B alle halbe Sekunden die Mausposition prüft und dann bestimmen, ob sich der Mauszeiger bewegt hat und wenn ja, ausrechnen in welchem Dreieck er sich gerade befindet... dann erübrigt sich die Abfrage von Mouseentered resp. MouseExit...

nur so ein Gedanke...

Gruss
.bile
 
Hm, OK, für das geschilderte Beispiel wäre das tatsächlich eine Möglichkeit. Auf die Idee bin ich gar nicht gekommen. Wenn ich dich richtig verstehe, dann könnte ich damit auf einen MouseListener verzichten, solange ich nicht noch weitere Events (Clicked o.ä.) abfangen möchte. Nur, wie bekomme ich dann die Maus-Position, denn mit mouseMoved(MouseEvent e) und e.getX() bzw. e.getY(), kann ich das doch nicht auf eine halbe Sekunde einschränken oder?

Eine andere Idee, die mir mittlerweile noch für den Beispielfall gekommen ist, wäre es, nur das letzte Feld der Mausposition und die unmittelbar angrenzenden abzufragen und das letzte Feld ggf. mit dem aktuellen zu überschreiben. Bin mir nur nicht sicher, wie sicher diese Methode ist, bzw. inwiefern man sie durch schnelle Mausbewegungen evtl. austricksen könnte. Es würde aber auf jeden Fall Sinn machen, die betreffenden Felder als erstes abzufragen und erst danach alle anderen.

Trotzdem würde mich noch interessieren, ob vieleicht jmd eine Möglichkeit sieht, die Spielfeldpolygone als eigenständige Components zu realisieren, damit sich die einzelnen Objekte sozusagen selbst organisieren könnten, ohne auf die höhere Panelebene zurückgreifen zu müssen?
 
Hm.. dafür gäbe es ne klasse:

Code:
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();

aber der ist relativ zum display, also müsstes du dann einige überprüfungen machen...

oder du lässt das rectangle rectangle sein und reagierst auf den MouseEnteredEvent indem du mit z.B. dein eigenes contains(Point p) ausführst, dass dann die Feinüberprüfung übernimmt, ob sich die Maus nun wirklich im Polygon befindet oder nicht.
 
Also man müsste quasi die relative Panel-Position auf dem Display noch abziehen, um die Mausposition auf dem Panel zu bekommen? -

Ja, stimmt eigentlich, dann würde ich für die Bounds des Component-Objekts praktisch zunächst polygon.getBounds() einsetzen und dann mit dem Event nochmal extra abfragen:

Java:
public void mouseEntered(MouseEvent e) {
        Point p = new Point(e.getX(), e.getY());
        if (polygon.contains(p)){
                markPolygon();
        }
}

Schon komisch, das manmanchmal auf so naheliegende Lösungen nicht kommt. Ich danke jedenfalls für die Unterstützung und schließe damit das Thema.
Abschließend würde mich dennoch interessieren, welche Lösung du favorisieren würdest: objektinterne Abhandlung des Events oder doch eher externe?

 
hi schlseb,
mal eine Frage zum Spielfeld.
Sind die Dreiecke aus denen es besteht alle regelmäßig und zusammenhängend?
Wenn dem so ist, könnte man mittels ein wenig linearer Algebra das Spielfeld herausfinden in dem die Maus steht. Das ganze in zentrale Methoden sowohl zum hin als auch zurückrechenen (am besten noch unterberücksichtigung der Skalierung des angezeigten Brettes) und Du hast an diesem Punkt ausgesorgt.
Ich würde die Spielfiguren nicht von component ableiten, da je nach dem wie viele Spielfiguren Du hast eine gewisse Inflation an recht komplexen Objekten bestehen würde. Ich würde stattdessen in der paintComponent-Routine neben dem Spielbrett auch die gestellten Figuren zeichnen lassen.
Wenn man dann noch eine einfache aber von der Performance sehr zuträgiche "Ziehbewegeng" der Spielfiguren haben möchte, würde ich den Mauszeiger bei einem Drag mit dem Spielfigurenbild beglücken.

Falls Du da noch konkreteres benötigen solltest ... :)

Takidoso
 
Hallo takidoso,

vielen Dank für die Anregung. Das Spielfeld besteht tatsächlich wie du bereits vermutet hast aus gleichseitigen aneinander angrenzenden Spielfeldern. Das gesamte Spielfeld hat aber weder eine dreieckige noch eine sechseckige Form.

Was du mit hinrechnen meinst kann ich noch nachvollziehen, nämlich eben das Spielfeld zu bestimmen, in das geklickt wurde, aber zurück?

Die Idee das Ganze in jeweils eigenen Komponenten zu realisieren, folgt hauptsächlich der Philosophie, dass möglichst viele meiner Objekte im Programm sich selbst verwalten sollen. Dass das zu Lasten der Performance geht ist allerdings ein berechtigter Einwand. Zumal ich später auch die einzelnen Spielfelder von Component bzw. JComponent ableiten wollte, womit ich dann allmählich an die 400 Komponenten insgesamt (mit GUI) kommen würde *g*.
Naja, im Moment funktionierts noch ganz gut und ich werde es denke ich vorerst so beibehalten, melde mich bei Bedarf aber wieder hier.
Eine Frage noch, wie kann ich den Mauszeiger mit der Spielfigur "beglücken"? Das hört sich interessant an.

schlseb

P.S. Falls es jmd mit ähnlichem Problem interessiert: Die in meinem vorherigen Beitrag gemachte zusätzliche Abfrage polygon.contains(p) kann man auch direkt in die contains Methode des Components-Objekt (die dafür überschrieben werden muss) verschieben. Denn dann wird, wie mittlerweile festgestellt, das MouseEvent nur ausgelöst, wenn sich der Mauszeiger innerhalb des angegebenen Polygons befindet und nicht schon, wenn es lediglich innerhalb der Bounds des Component-Objekts (rechteckig) liegt!
 
Hallo takidoso,

vielen Dank für die Anregung. Das Spielfeld besteht tatsächlich wie du bereits vermutet hast aus gleichseitigen aneinander angrenzenden Spielfeldern. Das gesamte Spielfeld hat aber weder eine dreieckige noch eine sechseckige Form.

Was du mit hinrechnen meinst kann ich noch nachvollziehen, nämlich eben das Spielfeld zu bestimmen, in das geklickt wurde, aber zurück?
Naja damit ist halt berechnung mittels lineareer Algebra gemeint. Könntest Du meine Neugierde vielleicht befriedigen indem Du eine Skizze des Spielfeldes mal hier zeigst?

Bei einem Risiko ähnlichen Spiel hatte ich es mal so gemacht, die Landflächen als Polygone zu beschreiben. Da die Standard-Javabibliotheken leider nicht die Grenzen genau errechnen, in denen ein Polygon liegt, hatte ich glücklicherweise eine Bibliothek eines kalifornischen Dozenten im Netz gefunden, die da ales was man brauchte bot. So konnte man bei völlig unregelmäßigen Vielecken problemlos einen Klick u.ä. nachvollziehen ohne so arg viel selbst mathematische Kenntnisse besitzen zu müssen.

hier der Link

Eine Frage noch, wie kann ich den Mauszeiger mit der Spielfigur "beglücken"? Das hört sich interessant an.
Ich habe hier einen kleinen Auszug aus meinem Q-Schachspiel, welches das ganze ein wenig verdeutlicht:
Code:
public class SchachBrettPanel extends JPanel implements BauernWandelnd
                                                       ,PropertyChangeListener
                                                       ,GameOverListener
                                                       ,MoveListener
                                                       ,BrettListener
{

... 

    private void this_mousePressed(MouseEvent e)
    {
        if (e.getButton() == e.BUTTON1)
        {
            Position pos    =  getPosOfPoint(e.getPoint());
            m_selectedFigur = (PieceLike)m_model.getFigur(pos);
/** @todo hier rausfinden warum Spieler über Netz nicht spielen kann Netzkommunikation Problem May*/
            if (m_selectedFigur!=null &&
                m_selectedFigur.getOwner().getSeite()==m_model.getAnDerReihe() &&
                (!SingletonManager.getNetCom().isOnline() || m_model.isLokalerSpielerDran()) &&
                m_model.sindAlleSpielbereit())
            {
                Image bild = m_selectedFigur.getImageIcon().getImage();
                Toolkit dKit = Toolkit.getDefaultToolkit();
                int k = getQuaKantenLaenge();
                int h = dKit.getBestCursorSize(k,k).height/2;
                int w = dKit.getBestCursorSize(k,k).width/2;
                Point hotSpot = new Point(w,h);
                Cursor fCursor = dKit.createCustomCursor(bild,  hotSpot, "figur");
                setCursor(fCursor);
            }
            else
            {
                m_selectedFigur = null;
            }

            repaint();
        }
    }

    private void this_mouseReleased(MouseEvent e)
    {
        if (e.getButton() == e.BUTTON1 && m_selectedFigur!=null)
        {
            try
            {
                m_selectedFigur.moveTo(getPosOfPoint(e.getPoint()));
                m_selectedFigur = null;
            }
            catch (Exception ex)
            {
                MExceptionHandler.handleException(ex);
            }
            setCursor(Cursor.getDefaultCursor());
            Owner spieler = m_model.getSpielerAnDerReihe();
            if(GlobalConstants._JA.equals(m_mainProps.getProperty(GlobalConstants.PN_BRETT_NACH_ZUG_DREHEN)))
            {
                setOwnerSeiteToSouth(spieler);
            }

            repaint();
        }
    }


    public int getQuaKantenLaenge()
    {
        double kante = getSize().height>getSize().width
                       ? getSize().getWidth()-getSkalarBreite()
                       : getSize().getHeight()-getSkalarBreite();

        int    qKante = (int)kante/m_brettGroesse;
        return qKante;
    }

    protected int getSkalarBreite()
    {
        /** @todo an verwendeten Font angleichen */
        return 0;
    }


....
}

relevant für Dich dürften dabei vor allem die dickgedrukten Zeilen sein.

P.S. Falls es jmd mit ähnlichem Problem interessiert: Die in meinem vorherigen Beitrag gemachte zusätzliche Abfrage polygon.contains(p) kann man auch direkt in die contains Methode des Components-Objekt (die dafür überschrieben werden muss) verschieben. Denn dann wird, wie mittlerweile festgestellt, das MouseEvent nur ausgelöst, wenn sich der Mauszeiger innerhalb des angegebenen Polygons befindet und nicht schon, wenn es lediglich innerhalb der Bounds des Component-Objekts (rechteckig) liegt!

Ja poste mal Deine Lösung dazu. Ist sicher für einige interessant.!
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück