BatalhaNaval GUI

Bom, tentar ajustar o seu código pode ser complicado, mas pode ver se o Ofidomundo te ajuda, se você enviar.

O que posso fazer é sugerir uma alternativa, de como eu faria isso.

O tabuleiro eu faria criando uma classe que estendesse JPanel, sobrescrevendo ao menos o método paintComponent pra fazer o desenho do tabuleiro, ao invés de JLabels. Com dois loops for (um pra linhas e outro pra colunas), é possível desenhar um tabuleiro facilmente.

Esse JPanel poderia receber o evento mouseCliked ou mousePressed, pra saber que posição foi clicada e o que fazer: inserir o um navio, por exemplo. Internamente, esse tabuleiro pode ser armazenado numa matriz de inteiros por exemplo, em que 1 indica posição ocupada e zero uma posição livre (água).

Exemplo:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class ExemploTabuleiro extends JFrame{
    public static int TAMANHO = 20;

    private int[][] matrizTabuleiro = new int[10][10];

    public ExemploTabuleiro(){
        super("Exemplo");
        setExtendedState(JFrame.MAXIMIZED_BOTH);
        setLayout(new BorderLayout());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setPreferredSize(new Dimension(500, 500));
        setSize(getPreferredSize());

        PainelTabuleiro tb = new PainelTabuleiro();

        getContentPane().add(tb, BorderLayout.CENTER);

        for (int i = 0; i < matrizTabuleiro.length; i++){
            for (int j = 0; j < matrizTabuleiro[i].length; j++){
                matrizTabuleiro[i][j] = 0;
            }
        }
    }

    public static void main(String[] args){
        java.awt.EventQueue.invokeLater(() -> {
            ExemploTabuleiro e = new ExemploTabuleiro();
            e.setVisible(true);
        });
    }

    class PainelTabuleiro extends JPanel{

        public PainelTabuleiro(){
            // adiciona o mouselistener
            addMouseListener(new MouseAdapter() { 
                public void mouseClicked(MouseEvent me) { 
                    tratarClique(me.getX(), me.getY()); 
                } 
            }); 
        }

        private void tratarClique(int x, int y){
            if (x < 100 || y < 100){
                return;
            }

            // verifica se o clique foi na área de desenho
            int cx = (x - 100) / ExemploTabuleiro.TAMANHO;
            int cy = (y - 100) / ExemploTabuleiro.TAMANHO;

            // verifica se foi numa posição válida
            if (cx >= 0 && cx < matrizTabuleiro.length && 
                cy >= 0 && cy < matrizTabuleiro[1].length){
                
                // troca o valor na matriz do tabuleiro
                if (matrizTabuleiro[cx][cy] == 0){
                    matrizTabuleiro[cx][cy] = 1;
                }else{
                    matrizTabuleiro[cx][cy] = 0;
                }
            }

            // força o painel a redesenhar/atualizar
            this.repaint();
        }

        @Override
        protected void paintComponent(Graphics g){
            Graphics2D g2 = (Graphics2D) g.create();

            // desenha o tabuleiro
            for (int i = 0; i < matrizTabuleiro.length; i++){
                for (int j = 0; j < matrizTabuleiro[i].length; j++){
                    // muda a cor pra desenhar se é agua ou não
                    if(matrizTabuleiro[i][j] == 0){
                        g2.setColor(Color.BLUE); 
                    }else{
                        g2.setColor(Color.YELLOW);
                    }
                    // desenha retangulos de 40x40, a 100 pixels da borda
                    g2.fillRect(
                            i * ExemploTabuleiro.TAMANHO + 100, 
                            j * ExemploTabuleiro.TAMANHO + 100, 
                            ExemploTabuleiro.TAMANHO, 
                            ExemploTabuleiro.TAMANHO);
                    
                    // linhas divisorias
                    g2.setColor(Color.WHITE); 
                    g2.drawRect(
                            i * ExemploTabuleiro.TAMANHO + 100, 
                            j * ExemploTabuleiro.TAMANHO + 100, 
                            ExemploTabuleiro.TAMANHO, 
                            ExemploTabuleiro.TAMANHO);
                }
            }

            g2.dispose();
        }
    }
}

Obs: pode haver alguns problemas, foi um código rápido.

Durante a partida, você pode armazenar qual o turno atual (do jogador ou do adversário) e reagir de acordo. Ex: na vez do adversário, os cliques do jogador são ignorados.

O bom é que com Java2D você consegue desenhar o que quiser: imagens, linhas retângulos, elipses, adicionar alguns efeitos, etc. E não depende de muitos componentes visuais do Swing. Veja mais aqui: http://pontov.com.br/site/java/48-java2d/93-uma-visao-rapida-sobre-o-java-2d

Abraço.

1 curtida

Fala TerraSkilll,

Mandei pro Ofidomundo o link do repositório, se estiver certo, eu coloco aqui no post depois.

O meu código está praticamente igual ao que você falou.
O tabuleiro eu crio com uma clase (class Botao) que estende JLabel usando um loop que monta os “botões” em um JPanel e armazena cada botao em uma matriz.
Dentro da classe Botao, eu tenho os métodos para pegar linha e coluna de cada botao e todo botao é criado com mouseListener.
Cada botao tem um “Estado” (VAZIO, AGUA, NAVIO).
Eles são inicializados como AGUA, quando posiciono os navios, cada botao que recebe um pedaço do navio, tem seu estado alterado de AGUA para NAVIO, então, quando alguém atira, eu pego o estado do botao selecionado e mostro o resultado.

Não sei se expliquei direito, mas acho que é por aí.

Diego, coloquei os Sysout’s e deu o que eu já imaginava.
A orientação ele ta pagando do JOptionPane e a linha e a coluna vem zerado.
O primeiro navio é posicionado, retornando true. O próximo, retorna false, pq a

linha = btn.getLinha(); // retornando zero
coluna = btn.getColuna(); // retornando zero

Já descobriu um problema, agora vc tem que resolver.

Só uma ideia:

Botao[][] botões = new Botao[LINHAS][COLUNAS];
Botao selecionado = null;

// constrói os botoes
for (int l = 0; l < LINHAS; l++) {
  for (int c = 0; c < COLUNAS; c++) {
    botões[l][c] = new Botao();
    add(botões[l][c]); // adiciona na tela
    botões[l][c].setLinha(l);
    botões[l][c].setColuna(c);
    // adiciona o evento, pode ser o MouseListener se preferir
    botões[l][c].addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e) {
        selecionar((Botao) e.getSource());
      }
    });
  }
}

// seleciona
public void selecionar(Botao btn) {
  selecionado = btn;
}

// no posicionaPlayer
linha = selecionado.getLinha();
coluna = selecionado.getColuna();
1 curtida

@IghorSantiago ele tem razão, essa seria uma boa saída, pois o que você faz lá é criar novos botões sem posicionamento, onde deveria estar na verdade recebendo talvez a tabela toda ou todos os botoes da tela

@IghorSantiago esquece isso, ja sei

adciona um action listener para o tabuleiro player, quando estiverem em estado.Aguardo você manda ele para o btn que esta na classe!?

deu para entender? kkkkk

public void criaTabuleiro(JPanel tela, Botao [][] tabuleiro, String jogador) {
	
	for(int i = 0; i < 10; i++) {

		for(int g = 0; g < 10; g++) {

			tabuleiro[i][g] = new Botao();
			tabuleiro[i][g].setName(jogador + " " + String.valueOf(i * 10 + g));
			tabuleiro[i][g].setEstado(Estado.AGUA);
			tabuleiro[i][g].setId(0);
			tabuleiro[i][g].setLinha(i);
			tabuleiro[i][g].setColuna(g);
			tabuleiro[i][g].setToolTipText("ID: " + String.valueOf(tabuleiro[i][g].getId()));

			tela.add(tabuleiro[i][g]);
		}
	}

	if(jogador.equals("Player")) {

		player.setNome(JOptionPane.showInputDialog(null, "Digite seu nome:"));
		tela.setBorder(BorderFactory.createTitledBorder("Alm. " + player.getNome()));
	}
	else
		tela.setBorder(BorderFactory.createTitledBorder("Alm. Computador"));
}

Na classe Botao():

public int getLinha() { return linha; }
public void setLinha(int linha) { this.linha = linha; }	

public int getColuna() { return coluna; }
public void setColuna(int coluna) { this.coluna = coluna; }

public void init() {
	
	setText("");
	setOpaque(true);
	setBorder(BorderFactory.createLineBorder(Color.BLACK));
	setPreferredSize(new Dimension(30, 30));
	setHorizontalAlignment(SwingConstants.CENTER);
	setBackground(Color.LIGHT_GRAY);
	setIcon(imagem.mar);
	estado = Estado.AGUA;
	
	Botao esteBotao = this;
	
	addMouseListener(new MouseAdapter() {
		
		@Override
		public void mouseClicked(MouseEvent e) {
			
			selecionado = esteBotao;
			JOptionPane.showMessageDialog(null, "Botão selecionado"
												+ "\n[" + selecionado.getLinha()
												+ "][" + selecionado.getColuna()
												+ "] - " + selecionado.getEstado());
		}
	});

Não entendi, foi mal

a função get selecionado retorna o botao que foi clickado, voce precisa de algo parecido, porem na classe player para que funcione com a matriz de botões toda

Evento fora do botão é melhor

tabuleiro[i][g].setLinha(i);
tabuleiro[i][g].setColuna(g);
tabuleiro[i][g].addMouseListener(new MouseAdapter() {
  public void mouseClicked(MouseEvent e) {
    Botao btn = (Botao) e.getSource(); // obtem o btn selecionado
    posicionaPlayer(btn); // passa como parâmetro para a função
  }
}

e

public void posicionaPlayer(Botao btn) {
  navio.posicionaNavio("?", orientacao, btn.getLinha(), btn.getColuna(), navio, tabuleiroPlayer);
}

Código para somente para testar:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;

public class BatalhaNaval extends JFrame {
    int LARGURA = 10;
    int ALTURA = 6;
    JLabel[][] campoBtns = new JLabel[ALTURA][LARGURA];
    
    enum ESTADO {
        JOGADOR_POSICIONANDO,
        COMPUTADOR_POSICIONANDO,
        JOGADOR_JOGANDO,
        COMPUTADOR_JOGANDO;
    }
    
    enum DIRECAO {
        HORIZONTAL, VERTICAL;
    }
    
    int tamanho = 3;// todos serao 3
    
    ESTADO estado = ESTADO.JOGADOR_POSICIONANDO; // inicia com jogador posicionando
    DIRECAO direcao = DIRECAO.HORIZONTAL;
    
    Border BLACK_BORDER = BorderFactory.createLineBorder(Color.BLACK, 3);
    Border GREEN_BORDER = BorderFactory.createLineBorder(Color.GREEN, 3);
    Border RED_BORDER = BorderFactory.createLineBorder(Color.RED, 3);
    
    
    public BatalhaNaval() {
        setLayout(new GridLayout(ALTURA, LARGURA));
        for (int l = 0; l < ALTURA; l++) {
            for (int c = 0; c < LARGURA; c++) {
                add(campoBtns[l][c] = criarCampoBtn(l, c));
            }
        }
        pack();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    
    public JLabel criarCampoBtn(int linha, int coluna) {
        JLabel btn = new JLabel();
        btn.setPreferredSize(new Dimension(36,36));
        btn.setOpaque(true);
        btn.setBorder(BLACK_BORDER);
        btn.setBackground(Color.BLUE);
        MouseAdapter ma = new MouseAdapter(){
            @Override
            public void mouseReleased(MouseEvent e) {
                switch(estado) {
                    case JOGADOR_POSICIONANDO:
                        System.out.println("Inseriu navio: "+posicionarJogador(linha, coluna));
                        return;
                }
            }
            @Override
            public void mouseEntered(MouseEvent e) {
                switch(estado) {
                    case JOGADOR_POSICIONANDO:
                        destacarPosicao(linha, coluna);
                        return;
                }
            }
            
            @Override
            public void mouseExited(MouseEvent e) {
                switch(estado) {
                    case JOGADOR_POSICIONANDO:
                        for (int l = 0; l < ALTURA; l++) {
                            for (int c = 0; c < LARGURA; c++) {
                                // limpa as bordas
                                campoBtns[l][c].setBorder(BLACK_BORDER);
                            }
                        }
                        return;
                }
            }
            
            void destacarPosicao(int linha, int coluna) {
                int tamanhoHorizontal = 1;
                int tamanhoVertical = 1;
                if (direcao == DIRECAO.HORIZONTAL) tamanhoHorizontal = tamanho;
                if (direcao == DIRECAO.VERTICAL) tamanhoVertical = tamanho;
                // se tem espaço para por o navio
                boolean espacoOk = coluna + tamanhoHorizontal <= LARGURA
                        && linha + tamanhoVertical <= ALTURA;
                for (int l = linha; l < Math.min(linha + tamanhoVertical, ALTURA); l++) {
                    for (int c = coluna; c < Math.min(coluna + tamanhoHorizontal, LARGURA); c++) {
                        // não esta ocupado e tem espaco
                        boolean ok = !campoBtns[l][c].getText().equals("x") && espacoOk;
                        campoBtns[linha][c].setBorder(ok ? GREEN_BORDER : RED_BORDER);
                    }
                }
            }
            
            boolean posicionarJogador(int linha, int coluna) {
                int tamanhoHorizontal = 1;
                int tamanhoVertical = 1;
                if (direcao == DIRECAO.HORIZONTAL) tamanhoHorizontal = tamanho;
                if (direcao == DIRECAO.VERTICAL) tamanhoVertical = tamanho;
                // se tem espaço para por o navio E não colide com outros navios
                if (coluna + tamanhoHorizontal <= LARGURA
                        && linha + tamanhoVertical <= ALTURA
                        && !verificarColisoes(linha, coluna, tamanhoHorizontal, tamanhoVertical)) {
                    // coloca o navio
                    for (int l = linha; l < linha + tamanhoVertical; l++) {
                        for (int c = coluna; c < coluna + tamanhoHorizontal; c++) {
                            campoBtns[l][c].setBackground(Color.YELLOW);
                            campoBtns[l][c].setText("x");
                        }
                    }
                    return true;
                } else {
                    return false;
                }
            }
            
            // verifica colisoes com outros navios
            boolean verificarColisoes(int linha, int coluna, int tamanhoHorizontal, int tamanhoVertical) {
                for (int l = linha; l < linha + tamanhoVertical; l++) {
                    for (int c = coluna; c < coluna + tamanhoHorizontal; c++) {
                        if (campoBtns[l][c].getText().equals("x")) {
                            return true;
                        }
                    }
                }
                return false;
            }
        };
        btn.addMouseListener(ma);
        btn.addMouseMotionListener(ma);
        return btn;
    }
}

ai fazendo um tipo de listenner no botão, variando cada ação dependendo de cada “fase” do jogo, seja ela preparação, jogo ou final

Funcionou!
Coloquei esse pedaço no meu código e foi.

Só não entendi uma coisa:
Assim ele vai chamar o evento sempre que eu clicar nos botões. Como farei na hora de atirar?

Obrigado Diego!

Acho que eu vou ter que fazer isso agora pra poder atirar, depois de posicionar os navios…

  int estado;
  final int POSICIONANDO = 0;
  final int ATIRANDO = 1;
  public void mouseClicked(MouseEvent e) {
    if (estado == POSICIONANDO) {
      Botao btn = (Botao) e.getSource(); // obtem o btn selecionado
      posicionaPlayer(btn); // passa como parâmetro para a função
    } else if (estado == ATIRANDO) {
      /***/
    }
  }

Coloquei um if.

public void posicionaPlayer(Botao btn) {

	int orientacao = 0, linha = 0, coluna = 0;
	
	if(navios <= 5) {
		
		navio = navio.constroiNavio(navios);
		
		JOptionPane.showMessageDialog(
				null, "Posicione o " + navio.getNome() + "."
					+ "\nEsse navio ocupa " + navio.getTamanho() + " espaços."
					+ "\nEscolha o primeiro espaço que ele irá ocupar...");
	
		Object[] opcoes = {"Horizontal", "Vertical"};
	
		orientacao = JOptionPane.showOptionDialog(null, 
							"Escolha a orientação do navio",
							"Escolha",
							JOptionPane.YES_NO_CANCEL_OPTION,
							JOptionPane.QUESTION_MESSAGE,
							null,
							opcoes,
							opcoes[1]);
		
		if(btn == null){
			System.out.println("btn nulo");
		}
		if(btn.selecionado == null){
			System.out.println("selecionado nulo");
		}
		
		linha = btn.getLinha();
		coluna = btn.getColuna();
		
		if(navio.getTamanho() != 0) {

			if(orientacao < 2) {
				
				if(linha < 10) {

					if(coluna < 10) {

						if(navio.posicionaNavio("C", orientacao, linha, coluna, navio, tabuleiroPlayer)) {

							navios++;

						}
					}
				}
			}
		}
	}
	else
		jogo.vez();
	
	navio.posicionaNavio("P", orientacao, linha, coluna, navio, tabuleiroPlayer);
}

Se navios (variável que contabiliza os navios) for menor que 5 (quantidade máxima de navios), ele posiciona o próximo aonde eu cliquei. Se já tiver os 5 navios, ele chama o método “vez()”, que vê de quem é o turno e chama o método pra atirar.

Amanhã eu vou ver se tudo funciona e comento aqui…

Obrigado a todos pela força!

1 curtida

Posiciona os navios (Computador e Player)
Atira (Player)
Quando chamo o método pro computador atirar, ele da NPE. Só não entendi porque que ele entra no if e depois dá a exceção. As 2 linhas são praticamente iguais, só que um pega o “estado” e a outra seta.

se vc prestar atenção no que esta escrito, o pocisionamento esta retornando nulo, entao ele da null pointer pois player.tabuleiroplayer[l][c] esta nulo

Eu só não entendi porque que ele entra no if então, era pra dar NPE na linha 53.

1 curtida