Como escrever seu primeiro jogo Android em Java

Autor: John Stephens
Data De Criação: 1 Janeiro 2021
Data De Atualização: 19 Poderia 2024
Anonim
How to write your first Android game in Java
Vídeo: How to write your first Android game in Java

Contente


Existem várias maneiras de criar um jogo para Android e uma maneira importante é fazê-lo do zero no Android Studio com Java. Isso dá a você o controle máximo sobre como você deseja que seu jogo pareça e se comporte, e o processo também ensinará habilidades que você também pode usar em vários outros cenários - esteja criando uma tela inicial para um aplicativo ou apenas deseja adicione algumas animações. Com isso em mente, este tutorial mostrará como criar um jogo 2D simples usando o Android Studio e o Java. Você pode encontrar todo o código e recursos no Github se quiser acompanhar.

Configurando

Para criar nosso jogo, precisaremos lidar com alguns conceitos específicos: loops, tópicos e telas de jogos. Para começar, inicie o Android Studio. Se você não o tiver instalado, confira nossa introdução completa ao Android Studio, que aborda o processo de instalação. Agora, inicie um novo projeto e escolha o modelo "Atividade vazia". Como é um jogo, é claro que você não precisa de elementos como o botão FAB para complicar as coisas.


A primeira coisa que você quer fazer é mudar AppCompatActivity para Atividade. Isso significa que não usaremos os recursos da barra de ação.

Da mesma forma, também queremos tornar nosso jogo em tela cheia. Adicione o seguinte código a onCreate () antes da chamada para setContentView ():

getWindow (). setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature (Window.FEATURE_NO_TITLE);

Observe que, se você escrever algum código e ele ficar sublinhado em vermelho, isso provavelmente significa que você precisará importar uma classe. Em outras palavras, você precisa informar ao Android Studio que deseja usar determinadas instruções e disponibilizá-las. Se você apenas clicar em qualquer lugar da palavra sublinhada e pressionar Alt + Enter, isso será feito automaticamente!


Criando sua visualização do jogo

Você pode estar acostumado a aplicativos que usam um script XML para definir o layout de visualizações, como botões, imagens e rótulos. Isto é o que a linha setContentView está fazendo por nós.

Mas, novamente, este é um jogo, o que significa que não é necessário ter janelas do navegador ou rolagem de visualizações de recicladores. Em vez disso, queremos mostrar uma tela. No Android Studio, uma tela é igual à arte: é um meio no qual podemos desenhar.

Então mude essa linha para ler da seguinte maneira:

setContentView (novo GameView (isso))

Você descobrirá que isso está novamente sublinhado em vermelho. Mas agora se você pressionar Alt + Enter, não terá a opção de importar a turma. Em vez disso, você tem a opção de crio uma aula. Em outras palavras, estamos prestes a criar nossa própria classe que definirá o que será exibido na tela. Isso é o que nos permitirá atrair para a tela, em vez de apenas mostrar visualizações prontas.

Então, clique com o botão direito do mouse no nome do pacote na sua hierarquia, à esquerda, e escolha Novo> Classe. Agora você será presenteado com uma janela para criar sua turma e vai chamá-la GameView. Em SuperClass, escreva: android.view.SurfaceView o que significa que a classe herdará métodos - seus recursos - do SurfaceView.

Na caixa Interface (s), você escreverá android.view.SurfaceHolder.Callback. Como em qualquer classe, agora precisamos criar nosso construtor. Use este código:

segmento MainThread privado; GameView público (contexto de contexto) {super (contexto); getHolder (). addCallback (this); }

Cada vez que nossa classe é chamada para criar um novo objeto (neste caso, nossa superfície), ela executa o construtor e cria uma nova superfície. A linha 'super' chama a superclasse e, no nosso caso, é o SurfaceView.

Ao adicionar o retorno de chamada, podemos interceptar eventos.

Agora substitua alguns métodos:

@ Substituir o espaço público vazio surfaceChanged (suporte do SurfaceHolder, formato int, largura int, altura) {} @Override espaço público vazio surfaceCreated (suporte do SurfaceHolder) {} @Observar espaço público vazioDestruído (SurfaceHolder) {}

Isso basicamente nos permite substituir (daí o nome) os métodos na superclasse (SurfaceView). Agora você não deve ter mais sublinhados vermelhos em seu código. Agradável.

Você acabou de criar uma nova classe e cada vez que nos referimos a isso, ele criará a tela para o seu jogo ser pintado. Aulas crio objetos e precisamos de mais um.

Criando Threads

Nossa nova classe será chamada MainThread. E seu trabalho será criar um thread. Um encadeamento é essencialmente como um fork paralelo de código que pode ser executado simultaneamente ao lado do a Principal parte do seu código. Você pode ter vários threads em execução ao mesmo tempo, permitindo que as coisas ocorram simultaneamente, em vez de seguir uma sequência estrita. Isso é importante para um jogo, porque precisamos garantir que ele continue funcionando sem problemas, mesmo quando muita coisa está acontecendo.

Crie sua nova classe exatamente como você fez antes e desta vez ela será estendida Fio. No construtor, vamos chamar super(). Lembre-se, essa é a super classe, que é Thread, e que pode fazer todo o trabalho pesado para nós. É como criar um programa para lavar a louça que apenas chama máquina de lavar().

Quando essa classe é chamada, ela cria um thread separado que é executado como uma ramificação da coisa principal. E é de Aqui que queremos criar nosso GameView. Isso significa que também precisamos fazer referência à classe GameView e também estamos usando o SurfaceHolder que contém a tela. Portanto, se a tela é a superfície, SurfaceHolder é o cavalete. E o GameView é o que une tudo.

A coisa toda deve ficar assim:

classe pública MainThread estende o Thread {private SurfaceHolder surfaceHolder; GameView privado gameView; public MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet. Agora temos um GameView e um tópico!

Criando o loop do jogo

Agora temos as matérias-primas necessárias para fazer o nosso jogo, mas nada está acontecendo. É aqui que entra o loop do jogo. Basicamente, esse é um loop de código que gira e gira e verifica entradas e variáveis ​​antes de desenhar a tela. Nosso objetivo é tornar isso o mais consistente possível, para que não haja gagueiras ou soluços na taxa de quadros, que explorarei um pouco mais tarde.

Por enquanto, ainda estamos no MainThread classe e vamos substituir um método da superclasse. Esta está corre.

E é mais ou menos assim:

@ Substituir public void run () {while (running) {canvas = null; tente {canvas = this.surfaceHolder.lockCanvas (); sincronizado (surfaceHolder) {this.gameView.update (); this.gameView.draw (tela); }} catch (Exceção e) {} finalmente {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exceção e) {e.printStackTrace (); }}}}}

Você verá muitos sublinhados, portanto, precisamos adicionar mais algumas variáveis ​​e referências. Volte ao topo e adicione:

privado SurfaceHolder surfaceHolder; GameView privado gameView; execução booleana privada; tela de lona estática pública;

Lembre-se de importar o Canvas. A tela é a coisa em que realmente iremos desenhar. Quanto ao 'lockCanvas', isso é importante porque é o que essencialmente congela a tela para nos permitir desenhar nela. Isso é importante porque, caso contrário, você pode ter vários threads tentando desenhar nele de uma só vez. Apenas saiba que, para editar a tela, você deve primeiro trava a tela.

Update é um método que vamos criar e é aí que as coisas divertidas acontecerão mais tarde.

o experimentar e pegar Enquanto isso, são simplesmente requisitos de Java que mostram que estamos dispostos a tentar lidar com exceções (erros) que podem ocorrer se a tela não estiver pronta etc.

Por fim, queremos poder iniciar nosso encadeamento quando necessário. Para fazer isso, precisamos de outro método aqui que nos permita colocar as coisas em movimento. Isso é o que corrida variável é para (observe que um booleano é um tipo de variável que é sempre verdadeira ou falsa). Adicione este método ao MainThread classe:

public void setRunning (isRunning booleano) {running = isRunning; }

Mas, neste ponto, uma coisa ainda deve ser destacada e que é atualizar. Isso ocorre porque ainda não criamos o método de atualização. Então volte para GameView e agora adicione método.

atualização de nulidade pública () {}

Nós também precisamos começar o segmento! Nós vamos fazer isso em nossa surfaceCreated método:

@ Substituir public void surfaceCreated (suporte do SurfaceHolder) {thread.setRunning (true); thread.start (); }

Também precisamos parar o fio quando a superfície é destruída. Como você deve ter adivinhado, lidamos com isso no surfaceDestroyed método. Mas, como pode ser necessário várias tentativas para interromper um segmento, colocaremos isso em loop e usaremos experimentar e pegar novamente. Igual a:

@ Substituir public void surfaceDestroyed (suporte do SurfaceHolder) {nova tentativa booleana = true; while (tente novamente) {try {thread.setRunning (false); thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } nova tentativa = false; }}

E, finalmente, vá até o construtor e crie a nova instância do seu encadeamento, caso contrário, você receberá a temida exceção de ponteiro nulo! E então tornaremos o GameView focável, o que significa que ele pode lidar com eventos.

thread = novo MainThread (getHolder (), este); setFocusable (verdadeiro);

Agora você pode finalmente realmente teste essa coisa! É isso mesmo, clique em executar e devemos realmente executado sem erros. Prepare-se para ser surpreendido!

É ... é ... uma tela em branco! Todo esse código. Para uma tela em branco. Mas, essa é uma tela em branco do oportunidade. Você tem sua superfície pronta e funcionando com um loop de jogo para lidar com eventos. Agora, tudo o que resta é fazer as coisas acontecerem. Não importa se você não seguiu tudo no tutorial até o momento. O ponto é que você pode simplesmente reciclar esse código para começar a fazer jogos gloriosos!

Fazendo gráficos

Certo, agora temos uma tela em branco para desenhar, tudo o que precisamos fazer é desenhar nela. Felizmente, essa é a parte simples. Tudo o que você precisa fazer é substituir o método draw em nosso GameView classe e adicione algumas fotos bonitas:

@Override public void draw (Tela de lona) {super.draw (tela); if (canvas! = null) {canvas.drawColor (Color.WHITE); Pintura pintura = nova pintura (); paint.setColor (Color.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, paint); }}

Execute isso e agora você deve ter um belo quadrado vermelho no canto superior esquerdo de uma tela branca. Isto é certamente uma melhoria.

Teoricamente, você poderia criar praticamente todo o seu jogo colocando-o dentro deste método (e substituindo onTouchEvent para lidar com a entrada), mas isso não seria uma maneira muito boa de fazer as coisas. Colocar o novo Paint dentro de nosso loop atrasará consideravelmente as coisas e, mesmo se colocarmos isso em outro lugar, adicionar muito código ao desenhar seria feio e difícil de seguir.

Em vez disso, faz muito mais sentido manipular objetos de jogo com suas próprias classes. Vamos começar com um que mostre um personagem e essa classe será chamada CharacterSprite. Vá em frente e faça isso.

Esta classe vai desenhar um sprite na tela e parecerá tão

classe pública CharacterSprite {private Bitmap image; public CharacterSprite (Bitmap bmp) {imagem = bmp; } desenho vazio público (tela de lona) {canvas.drawBitmap (imagem, 100, 100, nulo); }}

Agora, para usar isso, você precisará carregar o bitmap primeiro e depois chamar a classe de GameView. Adicione uma referência a private CharacterSprite characterSprite e depois no surfaceCreated método, adicione a linha:

characterSprite = new CharacterSprite (BitmapFactory.decodeResource (getResources (), R.drawable.avdgreen));

Como você pode ver, o bitmap que estamos carregando é armazenado em recursos e é chamado avdgreen (era de um jogo anterior). Agora, tudo que você precisa fazer é passar esse bitmap para a nova classe no desenhar método com:

characterSprite.draw (tela);

Agora clique em executar e você verá o gráfico aparecer na tela! Este é o BeeBoo. Eu costumava desenhá-lo nos meus livros escolares.

E se quiséssemos fazer esse carinha se mexer? Simples: nós apenas criamos variáveis ​​xey para suas posições e depois alteramos esses valores em um atualizar método.

Então adicione as referências ao seu CharacterSprite e depois desenhe seu bitmap em x, y. Crie o método de atualização aqui e por enquanto vamos tentar:

y ++;

Cada vez que o loop do jogo for executado, moveremos o personagem pela tela. Lembrar, y coordenadas são medidas a partir do topo para 0 é o topo da tela. Claro que precisamos chamar o atualizar método em CharacterSprite de atualizar método em GameView.

Pressione play novamente e agora você verá que sua imagem é exibida lentamente na tela. Ainda não estamos ganhando nenhum jogo, mas é um começo!

Ok, para fazer as coisas levemente mais interessante, vou apenas soltar um código de 'bola saltitante' aqui. Isso fará com que nosso gráfico salte ao redor da tela, como os antigos protetores de tela do Windows. Você sabe, os estranhamente hipnóticos.

atualização do void público () {x + = xVelocity; y + = yVelocidade; if ((x & gt; screenWidth - image.getWidth ()) || (x & lt; 0)) {xVelocity = xVelocity * -1; } if ((y & gt; screenHeight - image.getHeight ()) || (y & lt; 0)) {yVelocidade = yVelocidade * -1; }}

Você também precisará definir estas variáveis:

private int xVelocity = 10; private int yVelocity = 5; private int screenWidth = Resources.getSystem (). getDisplayMetrics (). widthPixels; private int screenHeight = Resources.getSystem (). getDisplayMetrics (). heightPixels;

Otimização

Há sim bastante mais a explorar aqui, desde a manipulação da entrada do jogador, a escala de imagens, o gerenciamento de vários caracteres se movendo pela tela ao mesmo tempo. No momento, o personagem está saltando, mas se você olhar de perto, há uma pequena gagueira. Não é terrível, mas o fato de que você pode vê-lo a olho nu é um sinal de alerta. A velocidade também varia muito no emulador em comparação com um dispositivo físico. Agora imagine o que acontece quando você tem toneladas acontecendo na tela de uma vez!

Existem algumas soluções para esse problema. O que eu quero fazer para começar é criar um número inteiro privado no MainThread e chame isso targetFPS. Isso terá o valor de 60.Vou tentar fazer o meu jogo rodar nessa velocidade e, enquanto isso, verificarei se é isso. Para isso, eu também quero um duplo privado chamado averageFPS.

Também vou atualizar o corre para medir quanto tempo cada loop do jogo está levando e depois pausa esse loop do jogo temporariamente, se estiver à frente do targetFPS. Vamos então calcular quanto tempo agora tirou e imprimiu isso para que possamos vê-lo no log.

@Override public void run () {long startTime; long timeMillis; long waitTime; totalTime longo = 0; int frameCount = 0; targetTime longo = 1000 / targetFPS; while (running) {startTime = System.nanoTime (); canvas = nulo; tente {canvas = this.surfaceHolder.lockCanvas (); sincronizado (surfaceHolder) {this.gameView.update (); this.gameView.draw (tela); }} catch (Exceção e) {} finalmente {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exceção e) {e.printStackTrace (); timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; tente {this.sleep (waitTime); } catch (Exceção e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {averageFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; totalTime = 0; System.out.println (averageFPS); }}}

Agora, nosso jogo está tentando travar seu FPS para 60 e você deve achar que geralmente mede 58-62 FPS razoavelmente constantes em um dispositivo moderno. No emulador, você pode obter um resultado diferente.

Tente alterar esses 60 para 30 e veja o que acontece. O jogo fica mais lento e devemos Agora leia 30 no seu logcat.

Pensamentos finais

Existem outras coisas que podemos fazer para otimizar o desempenho também. Há uma ótima postagem no blog sobre o assunto aqui. Tente não criar novas instâncias do Paint ou bitmaps dentro do loop e faça todas as inicializações lado de fora antes do jogo começar.

Se você está planejando criar o próximo jogo Android de sucesso, existem Certamente maneiras mais fáceis e eficientes de fazer isso atualmente. Mas definitivamente ainda existem cenários de casos de uso para atrair para uma tela e é uma habilidade muito útil para adicionar ao seu repertório. Espero que este guia tenha ajudado um pouco e desejo a você boa sorte nos seus próximos empreendimentos de codificação!

PróximoUm guia para iniciantes em Java

Parece que ontem memo vimo o Realme 1 er anunciado, ma o Realme etá de volta com uma nova família de telefone mai uma vez. Deta vez, a emprea lançou o Realme 5 e o Realme 5 Pro na Í...

Dua variante do Realme 3 foram lançada em março. Enquanto a verão global foi equipada com o Helio P60, a variante indiana veio com o Helio P70. O Realme 3i é baicamente o Realme 3 ...

Recomendado Por Nós