[erro] controle sobre desenhos graficos

Oi.
venho treinando sobre componentes graficos… e não estou conseguindo achar aonde estou errando na aplicação de teste que fiz.

Não há erros no codigo… simplismente ele não desenha o retangulo que quero.
Alguem sabe me esplicar o porque!?

public class Janela extends JFrame implements MouseListener, MouseMotionListener{
    private JPanel panTela;
    
    private int imageX = 50, imageY = 50, imageSizeX = 50, imageSizeY = 50;
    private boolean areaImage = false, onImage;
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        Janela J = new Janela();
        J.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        
    }
    
    public Janela(){
        setSize(250, 250);
        setVisible(true);
        
        panTela = new JPanel();
        
        add(panTela);
        //panTela.setBackground(Color.DARK_GRAY);
        
        addMouseListener(this);
        addMouseMotionListener(this);
    }
    
    public void paintComponent(Graphics g){
        //super.paintComponents(g);
        this.setBackground(Color.GRAY);
        
        g.setColor(Color.RED);
        g.fillRect(imageX, imageY, imageSizeX, imageSizeY); // não funciona
        //g.fillRect(50, 50, 50, 50); //não funciona
        
    }
    
    @Override
    public void update(Graphics g){
        paintComponent(g);
    }
    

    public void mouseClicked(MouseEvent e){
        //clicado
        if(onImage = true){
            areaImage = true;
        }else
            areaImage = false;
    }

    public void mousePressed(MouseEvent e) {
        //precionada
        if(areaImage){
            imageX = e.getX();
            imageY = e.getY();
        }
    }

    public void mouseReleased(MouseEvent e) {
        
    }

    public void mouseEntered(MouseEvent e) {
        
    }

    public void mouseExited(MouseEvent e) {
        
    }

    public void mouseDragged(MouseEvent e) {
        //seta arrasta
    }

    public void mouseMoved(MouseEvent e) {
        //seta anda
        int mousePosX = e.getX();
        int mousePosY = e.getY();
        
        if(mousePosX >= imageX && mousePosX <= (imageX+imageSizeX)
                && mousePosY >= imageY && mousePosY <= (imageY+imageSizeY)){
            onImage = true;
        }else
            onImage = false;
        
        repaint();
    }
}

Já os eventos são para eu poder mover o quadrado desenhado na tela com o clique do mouse (segura o clique).
So espero que o codigo do evento pra mover o quadrado funcione… não tive a chance de testa-lo.

Alguem pode me dizer porque o paintComponent() não está desenhando o retangulo (fillReck())

É… consegui arrumar… vi a pequena diferença entre o PaintComponent() e o paint().

vejam bem, eu consegui arrumar mas um topico no forum “interface Gráfica” me chamou a atenção…

eis meu codigo (funciona):

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Janela extends JFrame implements MouseMotionListener{
    private static JPanel panTela;
    
    private static int imageX = 50, imageY = 50, imageSizeX = 50, imageSizeY = 50;
    private static boolean areaImage = false, onImage;
    
    /**
     * @param args the command line arguments
     */
    
    public static void main(String[] args) {
        // TODO code application logic here
        Janela J = new Janela();
        J.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
    }
    
    public Janela(){
        setSize(250, 250);
        setVisible(true);
        
        panTela = new JPanel();
        
        add(panTela);
        //panTela.setBackground(Color.DARK_GRAY);
        addMouseMotionListener(this);
    }
    
    @Override
    public void paint(Graphics g){
        super.paint(g);
        panTela.setBackground(Color.GRAY);
        
        g.setColor(Color.RED);
        g.fillRect(imageX, imageY, imageSizeX, imageSizeY);
        
        
    }
    
    @Override
    public void update(Graphics g){
        paint(g);
    }

    public void mouseDragged(MouseEvent e) {
        //seta arrasta
        if(onImage){
            imageX = e.getX();
            imageY = e.getY();
        }
        repaint();
    }

    public void mouseMoved(MouseEvent e) {
        //seta anda
        int mousePosX = e.getX();
        int mousePosY = e.getY();
        
        if(mousePosX >= imageX && mousePosX <= (imageX+imageSizeX)
                && mousePosY >= imageY && mousePosY <= (imageY+imageSizeY)){
            onImage = true;
        }else
            onImage = false;
        
    }
}

Agora o meu programa de teste funciona numa boa… mas quando se move o quadrado… ele dá umas falhas.
Então o topico http://www.guj.com.br/posts/list/89597.java me chamou a atenção pois estava querendo algumas informações sobre estes eventos que eu usei.

Um link no topico com uns tutoriais legais… no link: http://www.realapplets.com/tutorial/DoubleBuffering.html
Fala de um buffer para retirar essa falha que o repaint() causa.

Mas infelizmente não consegui entender como funciona esse buffer.
Alguem pode me da uma aulinha explicando como esse buffer funciona!???

Agradeço.

Buffer duplo é uma técnica feita para evitar um efeito chamado flickering, que é quando uma tela parece ficar piscando enquanto você move um objeto em cima dela. Isso acontece por que a velocidade de renderização da tela é insuficiente para enganar os seus olhos.

Bem, a área da memória que representa a tela que você está vendo à sua frente se chama buffer primário.

Na técnica de buffer duplo existe uma segunda área de memória chamada buffer secundário, e ela representa o que vai estar na sua tela(o buffer primário). É nesta tela que é feita a renderização dos objetos, e o mais importante, é por trás dos panos(você não vê, por isso não há flicker). Quando a renderização está pronta, nós mandamos o buffer secundário substituir o primário, e ela se torna a nova imagem que você vê na tela.

Quanto menores os buffers, mais rápida a renderização dos objetos, pois há menos pixels a desenhar na tela. Por isso que um jogo a 1280x1024 é bem mais pesado que um a 800x600. A velocidade para renderizar o buffer secundario e colocá-lo na tela mede-se em frames por segundo, fps.

Existe também o buffer triplo, mas não vou explicá-lo aqui :slight_smile:

Use paintComponent.

Não mude o estado de um componente enquanto ele está sendo desenhado. (this.setBackground(Color.GRAY))

Não sobrescreva o método update. Isso só faz sentido em heavyweights (ou seja, componentes AWT).

renrutal,
Adorei a explicação. ja ta no favoritos para eu não esquecer. :slight_smile:
Agora so falta eu saber como fazer um buffer secundário. Vou ver se acho tutorias no google.
Se alguem puder me exemplificar ou me falar quais classes eu uso para construir o buffer secundário, ficarei grato.

[quote=victorwss]Use paintComponent.

Não mude o estado de um componente enquanto ele está sendo desenhado. (this.setBackground(Color.GRAY))

Não sobrescreva o método update. Isso só faz sentido em heavyweights (ou seja, componentes AWT).[/quote]
paintComponent() foi o primeiro que usei para tentar fazer o desenho do quadrado. A princípio nem funcionou ( nem sei o pq não ), Então substitui o paintComponent pelo paint e passou a desenhar no painel.
No caso do:

this.setBackground(Color.GRAY)

Substitui por via de problemas:

panTela.setBackground(Color.GRAY) :roll:

Só o paint funcionou p/ mim também.

Em teoria só precisaria colocar painel.setDoubleBuffered(true); Mas components com buffer duplo já são padrão, não precisa setar pra true q eles já começam nesse estado.

Eu particularmente não vi muita diferença usando buffer duplo ou não ao fazer um joguinho/simulador.

Vou até postar aqui se alguem quiser dar uma olhada.

Controles p/ jogar:

Clique do mouse: Seta uma célula como viva:
Espaço: Começa/Pausa o jogo
Teclas esquerda e direita: Diminuem e aumentam a velocidade.
C: Limpa a tela
Esc: Fecha o jogo (Alt F4, clicar no X na janela também faz isso)

Como jogar: http://en.wikipedia.org/wiki/Conway's_Game_of_Life

JogoDaVida.java

[code]
package jogodavida;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.Timer;

public class JogoDaVida extends javax.swing.JFrame implements ActionListener
{
private final Dimension MIN;
private final Mapa mapa;
private Timer timer;
private int speed;
private int PIXEL_SIZE;

public JogoDaVida(Mapa mapa, int pixel)
{
    this.mapa = mapa;
    this.PIXEL_SIZE = pixel;
    this.MIN = new Dimension(mapa.getLargura() * pixel, mapa.getAltura() * pixel);
    this.speed = 500;
    this.timer = new Timer(speed, this);

    initComponents();
}

private void desenhaMapa()
{
    Graphics g = painel.getGraphics();

    int p = PIXEL_SIZE;

    for (int linha = 0; linha < mapa.getAltura(); linha++)
        for (int coluna = 0; coluna < mapa.getLargura(); coluna++)
            if (mapa.getMatriz()[linha][coluna])
                g.fillRect(coluna * p, linha * p, p, p);
}

@Override
public void paint(Graphics g)
{
    super.paint(g);
    desenhaMapa();
}

public void actionPerformed(ActionEvent e)
{
    mapa.avaliaTodos();
    repaint();
    timer.restart();
}

public static void main(String args[])
{
    final Mapa mapa = new Mapa(64, 64);

    java.awt.EventQueue.invokeLater(new Runnable()
    {
        public void run()
        {
            new JogoDaVida(mapa, 10).setVisible(true);
        }
    });
}

/** This method is called from within the constructor to
 * initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is
 * always regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")

//
private void initComponents() {

painel = new javax.swing.JPanel();

setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle(“Jogo da Vida”);
setResizable(false);
addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
formMouseClicked(evt);
}
});
addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
formKeyPressed(evt);
}
});

painel.setBackground(new java.awt.Color(255, 255, 255));
painel.setDoubleBuffered(false);
painel.setName(“painel”); // NOI18N
painel.setPreferredSize(MIN);

javax.swing.GroupLayout painelLayout = new javax.swing.GroupLayout(painel);
painel.setLayout(painelLayout);
painelLayout.setHorizontalGroup(
painelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 10, Short.MAX_VALUE)
);
painelLayout.setVerticalGroup(
painelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 10, Short.MAX_VALUE)
);

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(painel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(painel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);

pack();
}//

private void formMouseClicked(java.awt.event.MouseEvent evt) {
if (painel.getMousePosition() != null)
{
int x = painel.getMousePosition().x;
int y = painel.getMousePosition().y;

    int p = PIXEL_SIZE;
    int linha = y / p;
    int coluna = x / p;

    mapa.mudaCelula(linha, coluna);
    //repaint();
    repaint(coluna * p, linha * p, (coluna + 1) * p, (linha + 1) * p);
}

}

private void formKeyPressed(java.awt.event.KeyEvent evt) {
int key = evt.getKeyCode();

if (key == KeyEvent.VK_SPACE)
    if (timer.isRunning())
        timer.stop();
    else
        timer.restart();
else if (key == KeyEvent.VK_RIGHT)
{
    timer.stop();
    if (speed > 50)
        speed -= 50;

    timer = new Timer(speed, this);
    timer.start();
}
else if (key == KeyEvent.VK_LEFT)
{
    timer.stop();
    if (speed < 2000)
        speed += 50;

    timer = new Timer(speed, this);
    timer.start();

}
else if (key == KeyEvent.VK_C)
{
    timer.stop();
    mapa.limpar();
    repaint();
}
else if (key == KeyEvent.VK_ESCAPE)
    System.exit(0);

}

// Variables declaration - do not modify
private javax.swing.JPanel painel;
// End of variables declaration
}

[/code]Desculpe pela bagunça, a maior parte disso é gerada pelo Netbeans.

Mapa.java

package jogodavida;

public class Mapa
{
    // true = celula viva
    private boolean[][] matriz;
    private int altura;
    private int largura;

    public Mapa(int largura, int altura)
    {
        this.matriz = new boolean[altura][largura];
        this.altura = altura;
        this.largura = largura;
    }

    /**
     * x = matriz[linha][coluna]
     * 
     * Vizinhos de x na matriz:
     * 
     * 123
     * 4x5
     * 678
     * 
     * @param linha
     * @param coluna
     * @return int quantidade de vizinhos vivos da célula
     */
    private int getQtdVizinhos(int linha, int coluna)
    {
        int qtd = 0;

        // Faz um mapa sem fronteiras, onde indo p/ cima acaba saido em baixo
        int linhaCima   = (linha  == 0)           ? altura  - 1 : linha  - 1;
        int linhaBaixo  = (linha  == altura  - 1) ? 0           : linha  + 1;
        int colunaCima  = (coluna == 0)           ? largura - 1 : coluna - 1;
        int colunaBaixo = (coluna == largura - 1) ? 0           : coluna + 1;
        
        //1
        if (matriz[linhaCima][colunaCima])
            qtd++;

        //2
        if (matriz[linhaCima][coluna])
            qtd++;

        //3
        if (matriz[linhaCima][colunaBaixo])
            qtd++;

        //4
        if (matriz[linha][colunaCima])
            qtd++;

        //5
        if (matriz[linha][colunaBaixo])
            qtd++;

        //6
        if (matriz[linhaBaixo][colunaCima])
            qtd++;

        //7
        if (matriz[linhaBaixo][coluna])
            qtd++;

        //8
        if (matriz[linhaBaixo][colunaBaixo])
            qtd++;
        
        return qtd;
    }

    /**
     * Regra 1: Celula morre se vizinhos < 2
     * Regra 2: Celula morre se vizinhos > 3
     * Regra 3: Celula nasce se vizinhos == 3
     * Regra 4: Celula continua a mesma se vizinhos == 2
     * 
     * @param linha
     * @param coluna
     * @return boolean Novo estado da célula
     */
    private boolean aplicaRegras(int linha, int coluna)
    {
        int qtd = getQtdVizinhos(linha, coluna);

        if (matriz[linha][coluna] && (qtd < 2 || qtd > 3))
            return false;
        else if (!matriz[linha][coluna] && qtd == 3)
            return true;
        else
            return matriz[linha][coluna];
    }

    /**
     * Calcula o próximo estado do mapa de células
     */
    public void avaliaTodos()
    {
        boolean[][] matrizNova = new boolean[altura][largura];

        for (int linha = 0; linha < altura; linha++)
            for (int coluna = 0; coluna < largura; coluna++)
                matrizNova[linha][coluna] = aplicaRegras(linha, coluna);

        matriz = matrizNova;
    }

    public void mudaCelula(int linha, int coluna)
    {
        if (linha < 0 || linha >= altura || coluna < 0 || coluna >= largura)
            throw new IndexOutOfBoundsException("Índice: (" + linha + "," + coluna + ")");

        matriz[linha][coluna] = !matriz[linha][coluna];
    }

    public void limpar()
    {
        this.matriz = new boolean[altura][largura];
    }

    public boolean[][] getMatriz()
    {
        return matriz;
    }

    public int getAltura()
    {
        return altura;
    }

    public int getLargura()
    {
        return largura;
    }
}

bem interessante o seu jogo.
Vou poder consultar muita coisa pelo seu código. 8) Tanto pra programas em geral quanto pra produção de jogos…
ja ta no favoritos.

Sabe… a opção setDoubleBuffered() parece ser false. Apos ativa-la, diminuiu um pouco o flicker. mas ainda continua muito pouco que aparece.
O engraçado eh que no exemplo em applet. com o setDoubleBuffered() ativado… o meu se encaixa no exemplo do meio.
A minha curiosidade quanto ao terceiro que me intriga. :?

Não há se quer um pingo de flickering.


sei que há a parte do forum “Metodologias de Desenvolvimento e Testes de Software”, mas se bem que podiam criar uma área para projetos. Para poder colocar os projetos prontos com o codigo aberto. Ajudaria muita gente. ( eu por exemplo, aprendo facil se ver um exemplo :smiley: ).