Moving circle on the screen with thread and jFrame’s Paint method

Asked

Viewed 2,623 times

0

Studying the design method of JFrame in case the paint and wanted to make a circle move on the screen using threads, but the circle does not move right. Perhaps you would need a method movimenta to set where I want to place a particular object on the screen.

public class PackMan extends JFrame implements KeyListener, Runnable{
    JLabel label = null;
    int contador = 0;
    int autura = -1;
    int direita = -2;
    int esquerda = -3;
    int baixo = -4;


    public PackMan(){
        super("PackMan");
        label = new JLabel();
        setLayout(new BorderLayout());
        add(label, BorderLayout.CENTER);
        //setLocation(100,100);
        setSize(600, 600);


        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        addKeyListener(this);
    }

    public void paint(Graphics g){
        if (g!=null) {
            g.setColor(Color.RED);
            g.setColor(Color.YELLOW);
            g.fillArc(contador, contador, 70, 70, 135, -270);
            g.setPaintMode();

            //repaint();
        }

    }
    public void movimenta(int parametro){

    }
    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent evento) {
        Thread t = new Thread(this);
        if(evento.getKeyCode() == KeyEvent.VK_UP){
            repaint();
            movimenta(-1);
            t.start();

        }
        if(evento.getKeyCode() == KeyEvent.VK_LEFT){
            t.start();
            repaint();
        }
        if(evento.getKeyCode() == KeyEvent.VK_RIGHT){
            t.start();
            repaint();
        }
        if(evento.getKeyCode() == KeyEvent.VK_DOWN){
            t.start();
            repaint();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }

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

    @Override
    public void run() {
        while(true)
        {
            while (contador < 600) {
                contador += 1;
                pause();
            }
            contador = 600;
            while (contador > 0) {
                contador -= 1;
                pause();
            }
        }

    }
    public void pause() {
        try {
            Thread.sleep(30);
        } 
        catch (Exception e) {}
    }

}
  • Bs the move method is not included in the code...

  • Your question is still not very clear. What does "not move right" mean? Something else, that my answer in another similar question may be useful to you.

  • then Luiz in the class has a circle designed what I wanted to be able to do would be moved to where I want.

1 answer

1

As I had already mentioned in the comment, that other answer will be quite useful to you, mainly because there I describe a number of needs (including mainly the issue of painting with double buffering).

To make the move you want, key capture must change the direction of the character’s movement. In your current code, your main game loop (game loop) totally disregard any code you can build, as it continuously increases and then decreases the variable contador, that you use as your character’s position at the time of painting.

One simple way to solve the problem is to have two variables, one to store the value of X and the other to store the value of Y, and use them at the time of painting. You will need a third variable to store the drive direction, so within your loop you must continuously increment or decrement only one of the variables X and Y according to that direction. Something like:

while(true) {
    if(direcao == 0) // para cima
        Y = Y - 1
    else if(direcao == 1) // para baixo
        Y = Y + 1
    else if(direcao == 2) // para direita
        X = X + 1
    else if(direcao == 3) // para esquerda
        X = X - 1
    Thread.sleep(100);
}

In the case of Packman (as you called it, although the name of the character in the original game is Pacman) this approach may be sufficient, but as I mentioned in the other quoted answer there are more details needed (mostly because usually you don’t use separate threads for different mobile objects - and I imagine eventually you’ll include the ghosts in the game, right?).

I changed my code from the original answer to make an example with your Packman. This code uses vectors to indicate the character’s movement, and the game scene does not use a Jframe, but a Jcomponent (because of the double buffering already implemented - if you try to move the character by forcing calls from repaint in a Jframe you will see that the canvas is not painted correctly). Despite using vectors, the movement is very square (up, down, left and right, as in the original Pacman game). But the use of vectors allows to make smoother transitions in motion (as in the example of the ball of the original response). If your character doesn’t just move across, it’s easy to change the code to instead of simply changing the speed, add the new speed to the current speed to make it also move on diagonals, for example.

Here is the example:

Game class

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;

/**
 * Classe principal, de implementação do jogo.
 * Exemplo para ilustração no SOPT.
 * @author Luiz C. Vieira
 */
@SuppressWarnings("serial")
public class Game extends JFrame implements Runnable {

    /** Thread de execução da applet. */
    private Thread m_oMainThread;

    /** Indicador de que o jogo está em execução. */
    private boolean m_bRunning;

    /** Taxa de quadros por segundo (framerate) ideal do jogo. */
    private static float FPS = 1000f / 60f; 

    private boolean m_bUpPressed = false;
    private boolean m_bDownPressed = false;
    private boolean m_bLeftPressed = false;
    private boolean m_bRightPressed = false;

    /**
     * Construtor do jogo.
     */
    public Game() {
        // Define tamanho da janela de jogo
        setSize(800, 600);

        // Adiciona a cena ao jogo
        setLayout(new BorderLayout());
        add(GameScene.instance);

        // O jogo não se inicia automaticamente
        m_bRunning = false;

        // Captura do pressionamento e liberação de teclas
        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "onUpPressed");
        getRootPane().getActionMap().put("onUpPressed", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bUpPressed = true;
            }
        });        

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "onUpReleased");
        getRootPane().getActionMap().put("onUpReleased", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bUpPressed = false;
            }
        });

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "onDownPressed");
        getRootPane().getActionMap().put("onDownPressed", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bDownPressed = true;
            }
        });        

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "onDownReleased");
        getRootPane().getActionMap().put("onDownReleased", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bDownPressed = false;
            }
        });        

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "onLeftPressed");
        getRootPane().getActionMap().put("onLeftPressed", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bLeftPressed = true;
            }
        });        

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "onLeftReleased");
        getRootPane().getActionMap().put("onLeftReleased", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bLeftPressed = false;
            }
        });

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "onRightPressed");
        getRootPane().getActionMap().put("onRightPressed", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bRightPressed = true;
            }
        });        

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "onRightReleased");
        getRootPane().getActionMap().put("onRightReleased", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                m_bRightPressed = false;
            }
        });

        // Captura o fechamento da janela para encerrar o jogo
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                stop();
            }
        });        
    }

    /**
     * Método de início do jogo.
     */
    public void start() {
        m_bRunning = true;
        m_oMainThread = new Thread(this);
        m_oMainThread.start();
        setVisible(true);
    }

    /**
     * Método de interrupção do jogo.
     */
    public void stop() {
        m_bRunning = false;
    }

    /**
     * Método getter do atributo de execução do jogo.
     * @return Valor lógico indicando se o jogo está (true) ou não (false) em execução.
     */
    public boolean isRunning() {
        return m_bRunning;
    }

    /**
     * Método de execução da thread da applet do jogo.
     * Basicamente implementa o game loop, atualizando na cena
     * todos os componentes a cada quadro (cada iteração).
     */
    @Override
    public void run() {

        long lPrevious = System.nanoTime() / 1000000;
        long lLag = 0;

        // Gameloop do jogo
        while(m_bRunning) {

            // Contabilização de tempo
            long lCurrent = System.nanoTime() / 1000000;
            long lElapsed = lCurrent - lPrevious;
            lPrevious = lCurrent;
            lLag += lElapsed;

            // Processamento de entrada (teclado, mouse, etc)!
            // Muda a direção da velocidade conforme a tecla pressionada
            Vector2D vVelocity = GameScene.instance.getPackMan().getVelocity();
            if(m_bUpPressed)
                vVelocity = new Vector2D(0, -8);
            else if(m_bDownPressed)
                vVelocity = new Vector2D(0, 8);
            else if(m_bLeftPressed)
                vVelocity = new Vector2D(-8, 0);
            else if(m_bRightPressed)
                vVelocity = new Vector2D(8, 0);
            GameScene.instance.getPackMan().setVelocity(vVelocity);             

            // Atualização do mundo!
            // Faz chamadas de update enquanto o número desejado de quadros
            // por segundo não tiver sido atingido
            while(lLag >= Game.FPS)
            {
                GameScene.instance.update();
                lLag -= Game.FPS;
            }

            // Renderização do jogo!
            GameScene.instance.repaint();
        }

        System.exit(0);
    }

    public static void main(String[] args) {
        Game oGame = new Game();
        oGame.start();
        while(oGame.isRunning())
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                oGame.stop();
                e.printStackTrace();
            }
    }
}

Gamescene class

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JComponent;

/**
 * Classe de implementação de cenas de jogo.
 * @author Luiz C. Vieira
 */
@SuppressWarnings("serial")
public class GameScene extends JComponent {

    /** Referência estática dos limites do mundo. */
    public static Rectangle BOUNDS = new Rectangle(0, 0, 800, 600);

    /** Referência ao objeto PackMan. */
    private PackMan m_oPackMan;

    /** Instância singleton da cena. */
    public static GameScene instance = new GameScene();

    /**
     * Construtor protegido.
     */
    protected GameScene() {
        // Cria o personagem do jogo
        m_oPackMan = new PackMan();
    }

    /**
     * Getter do packman.
     * @return Objeto PackMan na cena.
     */
    public PackMan getPackMan() {
        return m_oPackMan;
    }

    /**
     * Método de atualização da cena. É chamado a cada quadro do jogo.
     */
    public void update() {
        // Atualiza todos os objetos em cena
        m_oPackMan.update();
    }

    /**
     * Método de pintura da cena.
     * @param g Intância com o objeto Graphics da applet para pintura. 
     */
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        // Pinta o fundo da cena (simplesmente todo preto nesse exemplo)
        g.setColor(Color.black);
        g.fillRect(0, 0, GameScene.BOUNDS.width, GameScene.BOUNDS.height);

        // Faz a repintura de cada um dos objetos em cena
        m_oPackMan.paint(g);
    }
}

Packman class

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

/**
 * Classe do PackMan (não seria PacMan? :) ).
 * Ela não implementa thread! Esse controle fica por parte das classes
 * Game e GameScene que executam o método update.
 * @author Luiz C. Vieira
 */
public class PackMan {

    /** Vetor com a posição atual do personagem. */
    private Vector2D m_vPosition;

    /** Vetor velocidade (direção de movimentação + velocidade do movimento) do personagem. */
    private Vector2D m_vVelocity;

    /** Indicação de modo de depuração, para desenho do vetor velocidade. */
    private static boolean DEBUG = true;

    /**
     * Construtor padrão. Inicializa o personagem.
     */
    public PackMan() {
        m_vPosition = new Vector2D(0, 0);
        m_vVelocity = new Vector2D(8, 0);
    }

    /**
     * Getter da posição atual.
     * @return Vetor com a posição atual.
     */
    public Vector2D getPosition() {
        return m_vPosition;
    }

    /**
     * Setter da posição atual.
     * @param vPosition Vetor com a nova posição.
     */
    public void setPosition(Vector2D vPosition) {
        m_vPosition = vPosition;
    }

    /**
     * Getter da velocidade.
     * @return Vetor com a velocidade atual.
     */
    public Vector2D getVelocity() {
        return m_vVelocity;
    }

    /**
     * Setter da velocidade.
     * @param vVelocity Vetor com a nova velocidade.
     */
    public void setVelocity(Vector2D vVelocity) {
        m_vVelocity = vVelocity;
    }

    /**
     * Método de desenho do personagem. É chamado pela cena sempre que for necessário
     * repintar.
     * @param g Instância do objeto Graphics para pintura do personagem.
     */
    public void paint(Graphics g) {
        // Simplesmente desenha no Graphics o PackMan.

        g.setColor(Color.RED);
        g.setColor(Color.YELLOW);
        g.fillArc((int) m_vPosition.x, (int) m_vPosition.y, 70, 70, 135, -270);
        g.setPaintMode();

        // Se a depuração está ligada, desenha o vetor velocidade
        if(PackMan.DEBUG) {
            g.setColor(Color.red);
            Vector2D vVel = m_vVelocity.scalarMult(10); // Escala x10 para facilitar a visualização
            Vector2D vAux = m_vPosition.plus(vVel);
            g.drawLine((int) m_vPosition.x, (int) m_vPosition.y, (int) vAux.x, (int) vAux.y);
        }
    }

    /**
     * Método de atualização do objeto. É chamado a cada quadro do jogo.
     */
    public void update() {
        // Atualiza a posição de acordo com a velocidade (direção e valor)
        m_vPosition = m_vPosition.plus(m_vVelocity);

        // Trata colisões com o mundo
        handleCollisions();
    }

    /**
     * Método de tratamento das colisões. "Teletransporta" o personagem nos eixos x e y quando
     * ele "some" em um dos cantos da tela.
     */
    protected void handleCollisions() {
        Rectangle oBounds = GameScene.BOUNDS;
        int iWidth = 70; // A mesma largura que você usa no desenho
        int iHeight = 70; // Idem para a altura

        if(m_vPosition.x <= oBounds.x - iWidth)
            m_vPosition.x = oBounds.width + iWidth;
        else if(m_vPosition.x >= oBounds.width + iWidth)
            m_vPosition.x = -iWidth;

        if(m_vPosition.y <= oBounds.y - iHeight)
            m_vPosition.y = oBounds.height + iHeight;
        else if(m_vPosition.y >= oBounds.height + iHeight)
            m_vPosition.y = -iHeight;
   }
}

The class Vector2D is the same referenced in my original response. Below a screen of the example in operation (use the arrow keys to change the direction of constant motion):

inserir a descrição da imagem aqui

Browser other questions tagged

You are not signed in. Login or sign up in order to post.