. Login Cadastro

Comunidade para programadores


Você não está conectado. Conecte-se ou registre-se

[Tutorial] Iniciando - Jogos com Java AWT

Ver o tópico anterior Ver o tópico seguinte Ir em baixo Mensagem [Página 1 de 1]

Post: #1em Sab Set 16, 2017 10:50 pm

avatar

Morzan

Administrador

Introdução:


Este é o começo de uma série de guias para você entender o desenvolvimento de jogos utilizando Java e a API padrão AWT (Abstract Window ToolKit).

Para este primeiro guia, quero introduzilo ao Java AWT e dar ideias de como funcionam os gameloops de jogos. Com isso, demonstrar diferenças entre passive e active drawing do Java.
A ideia é construir futuramente um Jogo simples e, num futuro guia (quem sabe) a construção de uma engine em Java.

Gameloops:

Jogos são programas que precisam constantemente fazer muitas coisas ao mesmo tempo. Precisam pegar o input do jogador (se houver) > aplicar a logica de jogo (mover inimigos, atualizar IA etc.) > desenhar na tela > retorna ao input do jogador.
Este loop é chamado de Gameloop. Todos jogos o tem.
Código:

while(running) {
   processInput();
   processGame();
   render();
}

Este seria um gameloop ideal.

Neste guia, vamos focar no metodo "render()".


Para renderizar(desenhar), precisamos de uma janela, e algum lugar em que podemos desenhar livremente.

Janela.java

Código:

import java.awt.Dimension;

import javax.swing.JFrame;

public class Frame extends JFrame {

    public Frame() {
        setVisible(true);
        setSize(new Dimension(550, 450));
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        add(new GamePanel());
    }

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




E precisamos do nosso Painel de jogo, onde iremos de fato desenhar nossas coisas do game.



GamePanel.java

Código:

import java.awt.Canvas;

public class GamePanel extends Canvas {
    public GamePanel() {

    }
}



Métodos de renderização:

Existem "duas formas"* de renderização no Java:
disclaimer*: O Java "per-se" nao separa dessa forma. Fica a critério do programador de as fazer assim.



  • Desenho passivo - A JVM gerencia quando haverá o desenho


  • Desenho ativo - O desenho fica sob total responsabilidade do programador


Desenho passivo:

Para voce compreender o desenho passivo olhe o exemplo:
Código:


import java.awt.Canvas;
import java.awt.Graphics;

public class GamePanel extends Canvas {

    public GamePanel() {
        repaint();
    }

    @Override
    public void paint(Graphics g) {
        draw(g);
    }

    public void draw(Graphics g) {
        g.drawLine(0, 0, 550, 450);
    }

}

Note que o que esta acontecendo é : A classe é inicializada e ela chama repaint() que então chama paint, que por fim chama draw. Você pode estar se perguntando: como repaint chamou paint()? E como paint() recebeu Graphics g?
Para a primeira pergunta:

Veja o diagrama:



O AWT funciona com uma pilha de eventos de desenho. Quando ele vai desenhar na tela, o metodo de desenho paint() tem que ser acessavel não só pelo programa, mas pelo sistema operacional ou qualquer framework/programa que vá interagir com janelas. Pois, imagine que se voce minimize ou coloque alguma janela por cima do programa em execução, em algum momento voce vai precisar "redesenhar" a area coberta. O sistema operacional manda um sinal para a JVM sinalizando que o programa tem que redesenhar.
O metodo repaint() coloca outro pedido de desenho na pilha.
No método de desenho passivo, quem controla "quando" desenhar é a propria JVM.
(o metodo paintComponent() é para JComponents, como JPanels, geralmente é mais usados para GUI)
Isso é interessante para o desenvolvimento de GUI, pois tira da mão do programador esse tipo de preocupação. Mas no caso de um jogo, não podemos ser restringidos por isso.


Imagine que voce quer desenhar o maior numero de vezes por segundo o possível. No modo passivo, o algoritmo que gerencia quem será desenhado tenta, por questoes de eficiencia, fazer o seguinte: se houver muitos requests ao mesmo tempo, irei "mescla-los". Se voce tentar pedir muitos requests a mesmo tempo com repaint() verá que ele não desenhará nada! Isto porque ele tentará mesclar ou, pior: Dependendo da frequencia de requests, o AWT pode simplesmente ignorar o request.


Logo, fica evidente que para um jogo, o metodo de desenho passívo não é muito atraente. Como podemos contornar isso?


Para a segunda pergunta: O paint(Graphics g) recebe este parametro de uma forma chamada "callback". O AWT passa o Graphics g  para o programa, o AWT recebe este Graphics por callback do sistema operacional.



Desenho ativo:
No desenho ativo, nós iremos controlar quando será desenhado na tela.


Frame:

Código:


import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.JFrame;

public class Frame extends JFrame {

    public Frame() {
        setVisible(true);
        setSize(new Dimension(550, 450));
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        GamePanel gp = new GamePanel();
        add(gp);
        gp.run();
        setLayout(new GridLayout(1, 1, 0, 0));
    }

    public static void main(String[] args) {
        Frame frame = new Frame();
    }
}

GamePanel:


Código:

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;

public class GamePanel extends Canvas {

    private boolean running = true;
    public Graphics g;
    private BufferStrategy buffer;

    public GamePanel() {
        /*
         * Ignora os requests de paint/repaint do sistema operacional
         */
        setIgnoreRepaint(true);
        // setSize( 550, 450);
    }

    public void initialize() {
        g = null;
        /*
         * Um buffer é qualquer coisa que voce "salva" coisas
         *
         * Aqui criamos nosso buffer strategy. Como comentado, ele será abordado com
         * mais detalhes em proximos guias e caso em duvida voce deveria procurar a
         * documentação para saber mais sobre. Em suma: Nos queremos mostrar uma tela
         * pronta enquano desenhamos uma "de background" (não visivel) e quando esta
         * estiver pronta, nós substituimos elas. Existem duas formas de fazer isso:
         * bliting (modo janela) e page-flipping(full screen) Logo,
         * createBufferStrategy(2) cria 2 "telas" de desenho. Se voce parar para pensar,
         * depois de um certo numero, elas começam a ser mais prejudicial do que ajudar
         * em si o processamento.
         */
        createBufferStrategy(2);
        /*
         * Aqui, nós pegamos o buffer criado para o componente (no caso, o buffer do
         * Canvas em questao)
         *
         */
        buffer = getBufferStrategy();
    }

    public void render() {
        try {
            /*
             * Nosso metodo render funcionará de forma simples: Como nós usamos graphics
             * para desenhar, nós pegaremos o graphics do nosso buffer e desenharemos
             * livremente.
             *
             * BufferStrategy é especialmente importante para animações. Porque enquanto a
             * superficie "escondida" pode ser perdida a qualquer momento, é importante ter
             * certeza que o conteudo não é perdido antes de mostrar a imagem.
             *
             * Java recomenda fazer chamadas de rendering envoltos em blocos de try/catch
             * para que os graficos possam ser mostrados mesmo se acontecer alguma exceção.
             *
             */
            g = buffer.getDrawGraphics();
            g.drawLine(0, 0, 550, 450);
            g.drawLine(550, 0, 0, 450);
            /*
             * E então verificamos se não houve perda do nosso buffer
             */
            if (!buffer.contentsLost()) {
                // buffer.show simplesmente troca as imagens de fundo com a principal
                buffer.show();
            }

        } finally {
            if (g != null) {
                // dispose exibe a imagem de g.
                g.dispose();
                System.out.println("disposed");
            }
        }
    }

    public void run() {
        initialize();
        // Enquanto o programa estiver rodando, chame a função render
        while (running) {
            render();
        }

    }

}





O que este código irá fazer é desenhar um X na tela e printar disposed para cada vez que ele "atualizar" (repintar) a tela.
Com o metódo ativo, podemos atualizar o maximo que o computador consegue.

Esta diferença é crucial para o desenvolvimento de jogos. O metodo ativo é o que iremos usar para desenhar nossos jogos daqui em diante!

O ideal é dominar java threads para o proximo guia. Irei cobrir levemente o assunto no proximo guia, assim como explicar um pouco melhor page-flipping, e doublebuffering e etc.
Threads são muito importantes para jogos, pois o paralelismo que threads disponibilizam é crúcial para o jogo ser fluido e jogavel.

Até o proximo guia!

Ver perfil do usuário http://doublefree.forumeiros.com

Ver o tópico anterior Ver o tópico seguinte Voltar ao Topo Mensagem [Página 1 de 1]

Permissão deste fórum:
Você não pode responder aos tópicos neste fórum