Comment afficher des cartes dans un jeu de cartes en Java et les rendre cliquables pour la sélection?


Je programme un jeu de cartes en Java, et tout se passe bien, mais j'ai rencontré un problème concernant l'interface utilisateur. Il y a 4 joueurs (1 Humain 3 AI) et 54 cartes. Je veux que les cartes de chaque joueur soient dessinées dans 4 directions à l'écran (DE HAUT en BAS à DROITE) et avoir essentiellement un MouseListener pour les cartes humaines UNIQUEMENT afin de sélectionner une carte avant de jouer move (faites monter la carte un peu pour que vous sachiez qu'elle a été sélectionnée) pendant que les autres joueurs' les cartes ont leur dos dessiné et n'ont besoin d'aucun auditeur. Je fais un positionnement absolu et les cartes devraient apparaître comme ceci: Image

Actuellement, j'étend un JComponent et teste des dessins dans la méthode paintComponent de celui-ci. Comme les cartes sont placées et dessinées au-dessus d'une autre, il m'est impossible d'imaginer travailler avec des coordonnées, et comme j'ai une classe Card pour les cartes, je ne peux pas connecter l'instance Card et l'image dessinée. Une pensée est d'utiliser JLabel pour chaque carte humaine, ajoutez un ImageIcon comme pour la représentation de la carte, et ajoutez un MouseListener pour chaque étiquette afin que le clic de souris soit facilement déterminé. Mais le problème est de savoir comment les faire dessiner et mettre à jour à chaque déplacement de joueur car cela se ferait automatiquement pour les autres cartes de joueurs qui sont juste dessinées sous forme d'images à l'intérieur paintComponent. J'ai essayé de créer et d'ajouter des JLabels au composant à l'intérieur de la méthode paintComponent mais j'ai vu qu'il n'était absolument pas recommandé de le faire. Avez vous une suggestion sur quelle idée devrais je suivre?

Merci et cordialement

Author: Luis, 2018-02-13

1 answers

Impossible de connecter l'instance de la carte et l'image dessinée

Pourquoi pas? Fondamentalement, un Card est un état de données. Un Hand est juste une collection de Cards. Fondamentalement, construire un concept de modèle. Où les données elles-mêmes sont séparées de la vue qui les affiche.

Cela signifie, pour la plupart, la vue devient stupide. Il faut tout simplement le modèle et le rend, d'une certaine façon, permettant à l'utilisateur d'interagir avec elle.

Dans votre cas, vous pouvez utiliser a Rectangle pour représenter les limites de la carte physique. Lorsque vous cliquez dessus, vous itérez sur la main et testez pour voir si la souris a été cliquée dans les limites de chaque Rectangle (mappé à un Card) en utilisant Rectangle#contains

Maintenant, l'exemple suivant est" très " basique. Il se concentre uniquement sur la main d'un seul joueur, mais il devrait vous donner l'idée à partir de laquelle vous pouvez conduire le concept.

Carte

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SimpleCards {

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

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

                Deck.INSTANCE.shuffle();
                List<Hand> players = new ArrayList<>(5);
                for (int index = 0; index < 5; index++) {
                    players.add(new Hand());
                }

                for (int index = 0; index < 5; index++) {
                    for (Hand hand : players) {
                        hand.add(Deck.INSTANCE.pop());
                    }
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new GamePane(players));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Hand {

        private List<Card> cards;

        public Hand() {
            cards = new ArrayList<>(25);
        }

        public void add(Card card) {
            cards.add(card);
        }

        public int size() {
            return cards.size();
        }

        public Iterable<Card> cards() {
            return cards;
        }

        public Iterable<Card> reveresed() {
            List<Card> reversed = new ArrayList<>(cards);
            Collections.reverse(reversed);
            return reversed;
        }
    }

    public static class Card {

        private Suit suit;
        private Face face;

        public Card(Suit suit, Face face) {
            this.suit = suit;
            this.face = face;
        }

        public Suit getSuit() {
            return suit;
        }

        public Face getFace() {
            return face;
        }

    }

    public enum Deck {

        INSTANCE;

        private List<Card> cards;
        private List<Card> playDeck;

        private Deck() {
            cards = new ArrayList<>(52);
            for (Suit suit : Suit.items) {
                for (Face face : Face.items) {
                    cards.add(new Card(suit, face));
                }
            }
            playDeck = new ArrayList<>(cards);
        }

        public void shuffle() {
            playDeck.clear();
            playDeck.addAll(cards);
            Collections.shuffle(playDeck);
        }

        public Card pop() {
            if (playDeck.isEmpty()) {
                return null;
            }
            return playDeck.remove(0);
        }

        public void push(Card card) {
            playDeck.add(card);
        }

    }

    enum Face {
        ONE("1"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"), SEVEN("7"), EIGHT("8"), NINE("9"), JACK("J"), QUEEN("Q"), KING("K");

        private String value;

        private Face(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        private static Face[] items = new Face[]{
            ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, JACK, QUEEN, KING
        };
    }

    enum Suit {
        CLUBS("♣"), DIAMONDS("♦"), HEARTS("♥"), SPADES("♠");

        private String value;

        private Suit(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public static Suit[] items = new Suit[]{
            CLUBS, DIAMONDS, HEARTS, SPADES
        };
    }

    public class GamePane extends JPanel {

        private List<Hand> players;

        private Map<Card, Rectangle> mapCards;

        private Card selected;

        public GamePane(List<Hand> players) {
            this.players = players;
            mapCards = new HashMap<>(players.size() * 5);

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (selected != null) {
                        Rectangle bounds = mapCards.get(selected);
                        bounds.y += 20;
                        repaint();
                    }
                    selected = null;
                    // This is done backwards, as the last card is on
                    // top.  Of course you could render the cards
                    // in reverse order, but you get the idea
                    for (Card card : players.get(0).reveresed()) {
                        Rectangle bounds = mapCards.get(card);
                        if (bounds.contains(e.getPoint())) {
                            selected = card;
                            bounds.y -= 20;
                            repaint();
                            break;
                        }
                    }
                }
            });
        }

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

        @Override
        public void invalidate() {
            super.invalidate();
            mapCards.clear();
            Hand hand = players.get(0);
            int cardHeight = (getHeight() - 20) / 3;
            int cardWidth = (int) (cardHeight * 0.6);
            int xDelta = cardWidth / 2;
            int xPos = (int) ((getWidth() / 2) - (cardWidth * (hand.size() / 4.0)));
            int yPos = (getHeight() - 20) - cardHeight;
            for (Card card : hand.cards()) {
                Rectangle bounds = new Rectangle(xPos, yPos, cardWidth, cardHeight);
                mapCards.put(card, bounds);
                xPos += xDelta;
            }
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Hand hand = players.get(0);
            for (Card card : hand.cards) {
                Rectangle bounds = mapCards.get(card);
                System.out.println(bounds);
                if (bounds != null) {
                    g2d.setColor(Color.WHITE);
                    g2d.fill(bounds);
                    g2d.setColor(Color.BLACK);
                    g2d.draw(bounds);
                    Graphics2D copy = (Graphics2D) g2d.create();
                    paintCard(copy, card, bounds);
                    copy.dispose();
                }
            }
            g2d.dispose();
        }

        protected void paintCard(Graphics2D g2d, Card card, Rectangle bounds) {
            g2d.translate(bounds.x + 5, bounds.y + 5);
            g2d.setClip(0, 0, bounds.width - 5, bounds.height - 5);

            String text = card.getFace().getValue() + card.getSuit().getValue();
            FontMetrics fm = g2d.getFontMetrics();

            g2d.drawString(text, 0, fm.getAscent());
        }
    }

}

Bien que je n'arrive pas à comprendre la partie à l'intérieur invalidate () méthode, plus particulièrement les coordonnées. Pourriez-vous expliquer pourquoi vous avez fait de telles formules et comment puis-je trouver la mienne? Merci d'avance

Commencez par obtenir un morceau de papier, dessinez un rectangle représentant l'écran, puis dessinez les cartes comme vous le souhaitez - sérieusement, pour toute interface utilisateur convenablement complexe, je commence toujours par un morceau de papier, vous pouvez gribouiller des idées, effectuer des calculs, effectuer des passages, essentiellement, concentrer votre attention sur votre intention - et jeter beaucoup de choses qui ne fonctionnent pas. J'ai même été connu pour utiliser des découpes

invalidate est appelé lorsque la taille ou la position du composant change. Vous pouvez également utiliser un ComponentListener, je viens de prendre la route la plus facile (attention, je suis aussi paresseux, invalidate peut être appelé plusieurs fois de suite)

Tout d'abord, nous devons savoir quelle sera la hauteur d'une carte, à partir de cela, nous pouvons calculer la largeur comme un rapport de la hauteur

int cardHeight = (getHeight() - 20) / 3;
int cardWidth = (int) (cardHeight * 0.6);

Vous pourriez en dépenser temps de travailler sur un algorithme qui garantit que les cartes sont toujours affichées (c'est - à-dire calculer la hauteur/largeur de la carte en fonction du plus petit des composants hauteur/largeur, mais je suis allé pour facile) - Cela signifie, si la largeur du composant est suffisamment réduite, les cartes déborderont l'espace disponible.

Ensuite, nous devons déterminer par combien chaque carte est décalée par rapport à la précédente...

int xDelta = cardWidth / 2;

Dans ce cas, chaque carte chevauchera la seconde de moitié

Ensuite, nous devons calculer la position de départ de la première carte...

int xPos = (int) ((getWidth() / 2) - (cardWidth * (hand.size() / 4.0)));

(getWidth() / 2) calcule la position horizontale du centre du composant, cela garantira que les cartes sont disposées autour du centre horizontal du composant.

(cardWidth * (hand.size() / 4.0) calcule fondamentalement la largeur totale requise pour disposer toutes les cartes et la divise en deux, car la moitié apparaîtra à gauche du centre horizontal et l'autre moitié à droite. Ok, je réalise que ça n'a peut-être pas de sens, mais souviens-toi, chaque carte chevauche la précédente de moitié sa largeur, donc, nous ne pouvons pas diviser par 2, nous devons la diviser par 4 (2 x 2).

Donc, cardWidth * hand.size() fournira la largeur totale de toutes les cartes disposées côte à côte. (cardWidth * hand.size()) / 2 fournit (évidemment) la moitié de ce montant, qui peut être utilisé comme base pour le point d'origine de la première carte, lorsqu'il est soustrait du centre horizontal. ((cardWidth * hand.size()) / 2) / 2 permettra aux cartes de se chevaucher de la moitié de leur largeur.

Enfin, nous calculons le point d'origine vertical de la cartes de...

int yPos = (getHeight() - 20) - cardHeight;

C'est assez facile et explicite.

Dans la boucle, lorsque nous calculons la position de chaque carte, nous décalons la carte suivante par le xDelta

xPos += xDelta;

Ce qui, évidemment, nous donne la position horizontale de la carte suivante

 2
Author: MadProgrammer, 2018-02-13 19:49:49