JAVA - Teste de sroll estilo RPG classico

Ver o tópico anterior Ver o tópico seguinte Ir em baixo

JAVA - Teste de sroll estilo RPG classico

Mensagem por Da Galáxia em Sab 29 Jun 2013, 17:59

Descrição - Um teste de scroll no estilo RPG classico como Phantasy Star do master system.
Controles - 'R' Reinicia e 'Setas' movimentam o veículo.

Conclusão:
No ótimo Game Maker, temos uma room, na qual nos é possibilitado a facil criação de tiles e também de podermos dar scroll na tela, mas claro que ao programarmos fora de um ambiente pré-fabricado, algo assim, tão fácil, se torna um desavio tremendo.

Vou postar aqui minha classe, lembrando que isto não é um tutorial, ou seja, os codigos vão funcionar apenas para quem sabe programar em java, ou seja, tera que instanciar a classe na classe que contenha o método principal, e chamar os métodos renderizar, pressionarTecla e soltarTecla da forma que estão acostumados a fazer (pois existe muitas maneiras).
Vou fazer 3 observações:
1° - Apesar de ser em estilo RPG classico, o codigo é adaptavel para qualquer tipo de jogo, como por ex. Shooter ou plataforma.
2° - Meu mapa esta em uma matriz como vocês podem observar no codigo, contudo, isto é viavel apenas porque possuo um unico mapa. Se for para criar um jogo mesmo, é melhor criar uma matriz 3D [][][], e um dos [] ser encarregado de carregar os mapas, que diferentemente daqui, serão criados cada um em um bloco de notas.
3° - Os comentarios do codigo, são o suficiente pra mim, não vão ser de muita ajuda para quem pegar o codigo. Façam um programação reversa, experimentando tudo e vão entender. Um grande desafio é que como cada tile do mapa é de 40x40 e eu tenho que possuir um movimento fluente (pixel a pixel) do veiculo (quando termina o scroll) e do scrolling,  alguns calculos de checagem para os tiles tiveram que ser feitos. Se não houvesse a preocupação com o movimento fluente, o codigo seria extremamente mais simples.

Abaixo do codigo, estou disponibilizando um link do proto jogo.

Código:

import java.awt.*;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;

// Classe.
public class Scroll {
    
    // Constantes do mapa.
    private final int TELA_LARG = 840; // Em pixels.
    private final int TELA_ALT = 600;  // Em pixels.
    private final int MAPA_LARG = 42;  // 42 * TILE(40) = 1680 pixels.
    private final int MAPA_ALT = 30;   // 30 * TILE(40) = 1200 pixels.
    private final int TILE = 40;       // Tamanho do TILE (40 pixels).
    private final int QTD_SPRITES = 10;// Quantidade de sprites do jogo.
    
    // Variáveis do mapa.
    private int[][] mapaMundo;// Matriz de 42 x 30 para o mapa do jogo.
    private int scrollX;      // Posição x.            
    private int scrollY;      // Posição y.
 
   // Variáveis do player.
    private int playerX;// Posição x.
    private int playerY;// Posição y.
    private int frame;  // Troca a imagem do player em: para baixo, cima, esquerda e direita (valores de 6 a 9).
    private enum Direcao {
        nenhuma, direita, esquerda,
        acima, abaixo
    }
    private Direcao move, soltouTecla;
    
    // Variáveis do jogo.
    private int velocidade;                              // Velocidade do scroll e do player.
    private int moeda;
    private Image[] meusSprites = new Image[QTD_SPRITES];// Vetor para imagens.
    
    // Construtor.
    public Scroll() {
        // Chamar métodos.
        carregar();
        iniciar();
        
    }// Fim construtor.

    private void iniciar() {
        // Chamar método.
        setMapa();

        // Iniciar variáveis.
        scrollX = 15 * TILE;// Iniciar com deslocamento de 15 tiles em x (15 * 40 = 600 pixls).
        scrollY = 7 * TILE; // Iniciar com deslocamento de 7  tiles em y (7  * 40 = 280 pixls).
        velocidade = 2;      // Tem que ser multiplos de TILE (TILE é = 40), ou seja 1,2,4,5,8,10 ou 20.
        playerX = 10 * TILE;// 10 vezes 40 (tamanho do TILE).
        playerY = 7 * TILE; // 7 vezes 40 (tamanho do TILE).
        move = Direcao.nenhuma;
        soltouTecla = Direcao.nenhuma;
        frame = 7;// Player virado para a direita.
        moeda = 0;
        
    }// Fim construtor.
    
    // Métodos.
    void setMapa() {
        // Matriz de 42 x 30, para o Mundo.
        mapaMundo = new int[][] {
            // Minha room 42 x 30 multiplicado pelo tamanho do TILE que é 40, room total de 1680 x 1200 pixels.
            // Largura visivel 840, largura total 1680. Deslocamento (offSet) de 840 pixel.
            // Altura visivel  600, altura total  1200. Deslocamento (offSet) de 600 pixel.
            // Tiles: 0 = grama, 1 = montanha esquerda, 2 = agua, 3 = areia, 4 moeda, 5 = montanha direita.
            {1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,3,3,2,2,2,2,2,2,2,2,2,2,2,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,3,3,2,2,2,2,2,2,2,3,2,2,2,2,3,3,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,0,1,5},
            {1,5,1,5,0,0,0,0,3,3,3,2,2,2,2,2,2,3,2,2,2,2,2,3,0,0,0,0,0,1,5,4,1,5,1,5,1,5,0,0,1,5},
            {1,5,1,5,0,0,0,0,0,3,2,2,2,2,3,2,3,3,2,2,2,2,2,3,0,0,0,0,0,1,5,1,5,1,5,1,5,4,0,0,1,5},
            {1,5,1,5,1,5,0,0,0,3,3,3,3,3,3,2,3,3,3,3,3,3,2,3,0,0,0,0,0,0,1,5,1,5,1,5,1,5,0,0,1,5},
            {1,5,1,5,0,0,0,0,0,0,0,0,0,3,2,2,2,2,2,2,2,2,2,3,0,0,0,0,0,0,0,1,5,1,5,0,0,0,0,0,1,5},
            {1,5,1,5,0,0,0,0,0,0,0,0,0,3,2,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,1,5,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,2,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,3,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,3,2,2,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,3,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,3,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,3,3,2,2,3,0,0,0,0,0,0,0,1,5},
            {1,5,0,1,5,0,0,0,0,0,0,3,2,2,2,3,3,0,0,0,0,0,0,0,0,0,0,0,3,3,2,2,3,0,0,0,0,0,0,0,1,5},
            {1,5,1,5,1,5,1,5,1,5,0,3,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,3,3,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,1,5,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,4,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,0,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,4,0,0,0,0,0,1,5,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,0,0,0,0,0,1,5,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,0,1,5,1,5,1,5,0,0,0,3,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5},
            {1,5,1,5,1,5,1,5,1,5,1,5,3,2,3,3,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5}
        };
    
    }// Fim método.
  
   private void carregar() {
        
        ImageIcon[] imgs = new ImageIcon[10];
        
        for (int i = 0; i < QTD_SPRITES; i ++) {
            /*
            Imagens dentro da pasta src >> pasta nome_do_projeto >> pasta (criada) imgs.
            Usando "getResource" basta colocar no caminho abaixo, a ultima pasta (que criei), no caso (imgs).
            Estou carregando 10 imagens de uma vez, cujo nome são img0, img1, img2, img3, img4, img5,
           img6, img7, img8 e img9, os numeos é o "i" (de 6 a 9 são imagens do player e possuem canal alpha.
            Extensão dos arquivos = png.
            */
            imgs[i] = new ImageIcon(this.getClass().getResource("imgs/img" + i + ".png"));
            meusSprites[i] = imgs[i].getImage();  
        
        }// Fim for.
      
     
   }// Fim método.
    
    // Chamado na classe principal GameLoop.
    public void renderizar(Graphics2D g) {
        for (int y = 0; y < MAPA_ALT; y++) {
            for (int x = 0; x < MAPA_LARG; x++) {
                
                // Desenhar Mapa.
                // O scroll move contra o player, uso - scroll para ficar no sentido do player (conveniência).
                g.drawImage(meusSprites[mapaMundo[y][x]], (x * TILE) - scrollX, (y * TILE) - scrollY, null);
           }
        }
                    
        // Desenhar Player.
        g.drawImage(meusSprites[frame], playerX, playerY, null);
        
        // HUD (informações na tela).
        AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f);
        g.setComposite(alpha);
       g.fillRect(0, 0, 840, 40);
        g.fillRect(0, 560, 840, 40);
        AlphaComposite alpha2 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f);
        g.setComposite(alpha2);
        Font fonte1 = new Font("arial", Font.BOLD, 12);
        g.setFont(fonte1);
        g.setColor(new Color(150, 150, 150));
        g.drawString("Player X "  + playerX, 60, 10);
        g.drawString("+ Scroll  X " + scrollX, 160, 10);
        g.drawString("= Mapa em X " + (scrollX + playerX), 280, 10);
        g.drawString("Player Y "  + playerY, 60, 22);
        g.drawString("+ Scroll  Y " + scrollY, 160, 22);
        g.drawString("= Mapa em Y " + (scrollY + playerY), 280, 22);
        g.drawString("Tecla 'R' Reinicia", 60, 35);
        g.drawString("'Setas' Movimenta", 160, 35);
        g.drawString("Pivô do player em 0 x 0 (esquerda superior) ", 500, 10);
        g.drawString("Dimensão da tela " + TELA_LARG + " X " + TELA_ALT + " pixels", 500, 22);
        g.drawString("Dimensão do mapa " + MAPA_LARG*TILE + " X " + MAPA_ALT*TILE + " pixels", 500, 35);
        
        Font fonte2 = new Font("arial", Font.BOLD, 20);
        g.setFont(fonte2);
        g.setColor(new Color(255, 100, 0));
        g.drawString("MOEDA: " + moeda, 0, 590);
    
       // Chamar métodos.
        checarAreaDoTile();
        checarItens();
                  
    }// Fim método.
  
   private void checarItens() {

        // Player "dentro" da moeda.
        if (mapaMundo[(scrollY + playerY)/TILE][(scrollX + playerX)/TILE] == 4) {

            moeda += 50;

            // Posição na matriz (mapa) onde esta a moeda. Limpar (voltar a ser grama).
            mapaMundo[(scrollY + playerY)/TILE][(scrollX + playerX)/TILE] = 0;
        }

    }// Fim método.
    
    private void checarSePodeMover(int x, int y) {
        // As condições x e y positivas e negativas, são para diferenciar na hora da chamada do método.
        // x 10 e y 7 é quando o player esta no centro da tela.
        // Quando se tem scrolling o player não se movimenta, término do scrolling, liberdade para o player.

        //  == 0 é a grama e  == 4 é a moeda, neles o player se move.

        // OBS: Existe o movimento do scroll ou do player.
        
        // Esquerda (left).
        if (x == - 1) {
            if (scrollX > 0 && playerX <= 10 * TILE) {

                if (mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX - velocidade) / TILE] == 0
                        || mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX - velocidade) / TILE] == 4) {            
                    // Rola o scroll para a direita, o sinal é -, pois usei - na construção do desenho.
                   scrollX -= velocidade;
                }

            } else {
                if (mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX - velocidade) / TILE] == 0
                        || mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX - velocidade) / TILE] == 4) {
                    // Liberdade de movimento para o player.
                    playerX -= velocidade;
                }
            }

        }// Fim esquerda.

        // Direita (right).
        if (x == 1) {
            if (scrollX < (MAPA_LARG * TILE) - TELA_LARG && playerX >= 10 * TILE) {
                if (mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX) / TILE + 1] == 0
                        || mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX) / TILE + 1] == 4) {
                    // Rola o scroll para a esquerda, o sinal é +, pois usei - na construção do desenho.
                    scrollX += velocidade;
                }

            } else {
                if (mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX) / TILE + 1] == 0
                        || mapaMundo[(scrollY + playerY) / TILE][(scrollX + playerX) / TILE + 1] == 4) {
                    // Liberdade de movimento para o player.
                    playerX += velocidade;
                }
            }

        }// Fim direita.

        // Abaixo (down).
        if (y == 1) {
            if (scrollY < (MAPA_ALT * TILE) - TELA_ALT && playerY >= 7 * TILE) {
                if (mapaMundo[(scrollY + playerY) / TILE + 1][(scrollX + playerX) / TILE] == 0
                        || mapaMundo[(scrollY + playerY) / TILE + 1][(scrollX + playerX) / TILE] == 4) {
                    // Rola o scroll para cima, o sinal é +, pois usei - na construção do desenho.
                    scrollY += velocidade;
                }

            } else {
                if (mapaMundo[(scrollY + playerY) / TILE + 1][(scrollX + playerX) / TILE] == 0
                        || mapaMundo[(scrollY + playerY) / TILE + 1][(scrollX + playerX) / TILE] == 4) {
                    // Liberdade de movimento para o player.
                    playerY += velocidade;
                }
            }

        }// Fim abaixo.

        // Acima (up).
        if (y == - 1) {
            if (scrollY > 0 && playerY <= 7 * TILE) {
                if (mapaMundo[(scrollY + playerY - velocidade) / TILE][(scrollX + playerX) / TILE] == 0
                        || mapaMundo[(scrollY + playerY - velocidade) / TILE][(scrollX + playerX) / TILE] == 4) {
                    // Rola o scroll para baixo, o sinal é -, pois usei - na construção do desenho.
                    scrollY -= velocidade;
                }

            } else if (mapaMundo[(scrollY + playerY - velocidade) / TILE][(scrollX + playerX) / TILE] == 0
                    || mapaMundo[(scrollY + playerY - velocidade) / TILE][(scrollX + playerX) / TILE] == 4) {
                // Liberdade de movimento para o player.
                playerY -= velocidade;
            }

        }// Fim acima.

    }// Fim método.

    private void checarAreaDoTile() {
        // Apenas termina o movimento ao checar se chegou (ou esta) ao fim em x e y de um TILE
        
        // OBS: Existe o movimento do scroll ou do player.
        
        // Relacionado à esquerda
        if (move == Direcao.esquerda) {
            checarSePodeMover(-1, 0);
            frame = 8; // Imagem do player virado para a esquerda.
        }

        if (soltouTecla == Direcao.esquerda
               && (scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
            move = Direcao.nenhuma;
            soltouTecla = Direcao.nenhuma;
        }

        // Relacionado à direita
        if (move == Direcao.direita) {
            checarSePodeMover(1, 0);
            frame = 7; // Imagem do player virado para a direita.
        }

        if (soltouTecla == Direcao.direita
               && (scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
            move = Direcao.nenhuma;
            soltouTecla = Direcao.nenhuma;
        }

        // Relacionado abaixo
        if (move == Direcao.abaixo) {
            checarSePodeMover(0, 1);
            frame = 6; // Imagem do player virado para baixo.
        }

        if (soltouTecla == Direcao.abaixo
               && (scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
            move = Direcao.nenhuma;
            soltouTecla = Direcao.nenhuma;
        }

        // Relacionado acima
        if (move == Direcao.acima) {
            checarSePodeMover(0, -1);
            frame = 9; // Imagem do player virado para cima.
        }

        if (soltouTecla == Direcao.acima
               && (scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
            move = Direcao.nenhuma;
            soltouTecla = Direcao.nenhuma;
        }

    }// Fim método.

    // Chamado na classe principal GameLoop.
    public void pressionarTecla(KeyEvent e) {

        int tecla = e.getKeyCode();

        // As condicionais dentro dos cases, são para checar se chegou (ou esta) ao fim em x e y de um TILE.
        // OBS: Existe o movimento do scroll ou do player.
        switch (tecla) {

            case KeyEvent.VK_LEFT:
                if ((scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
                    move = Direcao.esquerda;
                }
                break;
            
            case KeyEvent.VK_RIGHT:
                if ((scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
                    move = Direcao.direita;
                }
                break;

            case KeyEvent.VK_DOWN:
                if ((scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
                    move = Direcao.abaixo;
                }
                break;

            case KeyEvent.VK_UP:
                if ((scrollX + scrollY) % TILE == 0 && (playerX + playerY) % TILE == 0) {
                    move = Direcao.acima;
                }
                break;

            case 'R':
                iniciar();
                break;

        }// Fim switch/case.

    }// Fim método.

    // Chamado na classe principal GameLoop.
    public void soltarTecla(KeyEvent e) {

        int tecla = e.getKeyCode();

        switch (tecla) {

            case KeyEvent.VK_LEFT:
                soltouTecla = Direcao.esquerda;
                break;
            
            case KeyEvent.VK_RIGHT:
                soltouTecla = Direcao.direita;
                break;
            
            case KeyEvent.VK_DOWN:
                soltouTecla = Direcao.abaixo;
                break;
            
            case KeyEvent.VK_UP:
                soltouTecla = Direcao.acima;
                break;
            
       }// Fim switch/case.

    }// Fim método.

}// Fim classe.






LINK PARA BAIXAR PROTO JOGO.
www.2shared.com/file/velid0aa/Scroll_estilo_RPG.html?

Da Galáxia

Número de Mensagens : 348
Data de inscrição : 14/01/2010
Reputação : 1
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Ver o tópico anterior Ver o tópico seguinte Voltar ao Topo

- Tópicos similares

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