Cómo escribir tu primer juego de Android en Java

Autor: John Stephens
Fecha De Creación: 1 Enero 2021
Fecha De Actualización: 19 Mayo 2024
Anonim
Cómo escribir tu primer juego de Android en Java - Aplicaciones
Cómo escribir tu primer juego de Android en Java - Aplicaciones

Contenido


Hay muchas formas de crear un juego para Android y una forma importante es hacerlo desde cero en Android Studio con Java. Esto le brinda el máximo control sobre cómo desea que se vea y se comporte su juego y el proceso le enseñará habilidades que puede usar en una variedad de otros escenarios también, ya sea que esté creando una pantalla de presentación para una aplicación o simplemente desee agrega algunas animaciones. Con eso en mente, este tutorial te mostrará cómo crear un juego 2D simple usando Android Studio y Java. Puede encontrar todo el código y los recursos en Github si desea seguirlo.

Configuración

Para crear nuestro juego, necesitaremos tratar algunos conceptos específicos: bucles, hilos y lienzos del juego. Para comenzar, inicie Android Studio. Si no lo tiene instalado, consulte nuestra introducción completa a Android Studio, que repasa el proceso de instalación. Ahora comience un nuevo proyecto y asegúrese de elegir la plantilla "Actividad vacía". Este es un juego, así que, por supuesto, no necesita elementos como el botón FAB que complica las cosas.


Lo primero que quieres hacer es cambiar AppCompatActivity a Actividad. Esto significa que no utilizaremos las funciones de la barra de acción.

Del mismo modo, también queremos que nuestro juego sea de pantalla completa. Agregue el siguiente código a onCreate () antes de la llamada a setContentView ():

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

Tenga en cuenta que si escribe algún código y se subraya en rojo, eso probablemente significa que necesita importar una clase. En otras palabras, debe decirle a Android Studio que desea usar ciertas declaraciones y ponerlas a disposición. Si hace clic en cualquier parte de la palabra subrayada y luego presiona Alt + Intro, ¡eso se hará automáticamente!


Creando tu vista de juego

Puede estar acostumbrado a aplicaciones que usan un script XML para definir el diseño de vistas como botones, imágenes y etiquetas. Esto es lo que la linea setContentView está haciendo por nosotros

Pero, de nuevo, este es un juego, lo que significa que no necesita tener ventanas de navegador o vistas de reciclaje de desplazamiento. En lugar de eso, queremos mostrar un lienzo en su lugar. En Android Studio, un lienzo es igual que en el arte: es un medio en el que podemos dibujar.

Así que cambie esa línea para leer así:

setContentView (nuevo GameView (este))

Verás que esto está una vez más subrayado en rojo. Pero ahora Si presiona Alt + Intro, no tiene la opción de importar la clase. En cambio, tienes la opción de crear una clase. En otras palabras, estamos a punto de crear nuestra propia clase que definirá lo que se incluirá en el lienzo. Esto es lo que nos permitirá dibujar en la pantalla, en lugar de solo mostrar vistas ya hechas.

Así que haga clic derecho en el nombre del paquete en su jerarquía a la izquierda y elija Nuevo> Clase. Ahora se te presentará una ventana para crear tu clase y la llamarás GameView. En SuperClass, escriba: android.view.SurfaceView lo que significa que la clase heredará métodos, sus capacidades, de SurfaceView.

En el cuadro Interfaz (s), escribirás android.view.SurfaceHolder.Callback. Como con cualquier clase, ahora necesitamos crear nuestro constructor. Usa este código:

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

Cada vez que se llama a nuestra clase para hacer un nuevo objeto (en este caso, nuestra superficie), ejecutará el constructor y creará una nueva superficie. La línea "super" llama a la superclase y, en nuestro caso, es SurfaceView.

Al agregar la devolución de llamada, podemos interceptar eventos.

Ahora anule algunos métodos:

@Override public void surfaceChanged (titular de SurfaceHolder, formato int, int width, int height) {} @Override public void surfaceCreated (SurfaceHolder holder) {} @Override public void surfaceDestroyed (SurfaceHolder holder) {}

Básicamente, nos permiten anular (de ahí el nombre) los métodos en la superclase (SurfaceView). Ahora no debería tener más subrayados rojos en su código. Agradable.

Acabas de crear una nueva clase y cada vez que nos referimos a eso, construirá el lienzo para pintar tu juego. Clases crear objetos y necesitamos uno más.

Creando hilos

Nuestra nueva clase se llamará Hilo principal. Y su trabajo será crear un hilo. Un hilo es esencialmente como una bifurcación paralela de código que puede ejecutarse simultáneamente junto con principal parte de tu código. Puede tener muchos subprocesos ejecutándose a la vez, lo que permite que las cosas ocurran simultáneamente en lugar de seguir una secuencia estricta. Esto es importante para un juego, porque debemos asegurarnos de que siga funcionando sin problemas, incluso cuando están sucediendo muchas cosas.

Crea tu nueva clase tal como lo hiciste antes y esta vez se extenderá Hilo. En el constructor solo vamos a llamar súper(). Recuerde, esa es la superclase, que es Thread, y que puede hacer todo el trabajo pesado por nosotros. Esto es como crear un programa para lavar los platos que solo llama lavadora().

Cuando se llama a esta clase, creará un hilo separado que se ejecuta como una rama de lo principal. Y es de aquí que queremos crear nuestro GameView. Eso significa que también necesitamos hacer referencia a la clase GameView y también estamos usando SurfaceHolder, que contiene el lienzo. Entonces, si el lienzo es la superficie, SurfaceHolder es el caballete. Y GameView es lo que lo pone todo junto.

Todo debería verse así:

clase pública MainThread extiende Thread {private SurfaceHolder surfaceHolder; GameView privado gameView; Public MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet ¡Ahora tenemos un GameView y un hilo!

Creando el bucle del juego

Ahora tenemos las materias primas que necesitamos para hacer nuestro juego, pero no pasa nada. Aquí es donde entra el ciclo del juego. Básicamente, este es un ciclo de código que da vueltas y vueltas y comprueba las entradas y variables antes de dibujar la pantalla. Nuestro objetivo es hacer que esto sea lo más coherente posible, para que no haya tartamudeos o hipo en el framerate, que exploraré un poco más adelante.

Por ahora, todavía estamos en el Hilo principal clase y vamos a anular un método de la superclase. Este es correr.

Y va un poco más o menos así:

@Override public void run () {while (running) {canvas = null; pruebe {canvas = this.surfaceHolder.lockCanvas (); sincronizado (surfaceHolder) {this.gameView.update (); this.gameView.draw (lienzo); }} catch (Excepción e) {} finalmente {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Excepción e) {e.printStackTrace (); }}}}}

Verá mucho subrayado, por lo que debemos agregar algunas variables y referencias más. Regrese a la parte superior y agregue:

SurfaceHolder privado surfaceHolder; GameView privado gameView; funcionamiento privado booleano; lienzo público de lona estática;

Recuerde importar Canvas. El lienzo es lo que realmente estaremos dibujando. En cuanto a ‘lockCanvas’, esto es importante porque es lo que esencialmente congela el lienzo para permitirnos dibujar en él. Eso es importante porque de lo contrario, podría tener múltiples hilos intentando dibujar en él a la vez. Solo sepa que para editar el lienzo, primero debe bloquear el lienzo.

La actualización es un método que vamos a crear y aquí es donde las cosas divertidas sucederán más adelante.

los tratar y captura Mientras tanto, son simplemente requisitos de Java que muestran que estamos dispuestos a tratar de manejar las excepciones (errores) que pueden ocurrir si el lienzo no está listo, etc.

Finalmente, queremos poder iniciar nuestro hilo cuando lo necesitemos. Para hacer esto, necesitaremos otro método aquí que nos permita poner las cosas en movimiento. Eso es lo que el corriendo La variable es para (tenga en cuenta que un booleano es un tipo de variable que solo es verdadero o falso). Agregue este método a Hilo principal clase:

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

Pero en este punto, una cosa aún debe destacarse y eso es actualizar. Esto se debe a que todavía no hemos creado el método de actualización. Así que vuelve a entrar GameView y ahora agregue el método.

actualización nula pública () {}

También necesitamos comienzo ¡la amenaza! Vamos a hacer esto en nuestro superficie creada método:

@Override public void surfaceCreated (titular de SurfaceHolder) {thread.setRunning (true); thread.start (); }

También debemos detener el hilo cuando se destruye la superficie. Como habrás adivinado, manejamos esto en el superficie destruida método. Pero dado que en realidad puede tomar múltiples intentos para detener un hilo, vamos a poner esto en un bucle y usar tratar y captura otra vez. Al igual que:

@Override public void surfaceDestroyed (titular de SurfaceHolder) {boolean retry = true; while (retry) {try {thread.setRunning (false); thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } reintentar = falso; }}

Y finalmente, dirígete al constructor y asegúrate de crear la nueva instancia de tu hilo, de lo contrario obtendrás la temida excepción de puntero nulo. Y luego haremos que GameView sea enfocable, lo que significa que puede manejar eventos.

thread = new MainThread (getHolder (), this); setFocusable (verdadero);

Ahora usted puede finalmente realmente prueba esta cosa! Así es, haz clic en Ejecutar y debería En realidad se ejecuta sin ningún error. ¡Prepárate para quedar impresionado!

¡Es ... es ... una pantalla en blanco! Todo ese código. Para una pantalla en blanco. Pero, esta es una pantalla en blanco de oportunidad. Tienes tu superficie en funcionamiento con un bucle de juego para manejar eventos. Ahora todo lo que queda es hacer que las cosas sucedan. Ni siquiera importa si no seguiste todo en el tutorial hasta este punto. El punto es que puedes simplemente reciclar este código para comenzar a hacer juegos gloriosos.

Haciendo un gráfico

Bien, ahora tenemos una pantalla en blanco para dibujar, todo lo que tenemos que hacer es dibujar en ella. Afortunadamente, esa es la parte simple. Todo lo que necesita hacer es anular el método de extracción en nuestro GameView clase y luego agregue algunas fotos bonitas:

@Override public void draw (Canvas canvas) {super.draw (canvas); if (lienzo! = nulo) {canvas.drawColor (Color.WHITE); Paint paint = nueva pintura (); paint.setColor (Color.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, pintura); }}

Ejecute esto y ahora debería tener un bonito cuadrado rojo en la parte superior izquierda de una pantalla blanca. Esto es ciertamente una mejora.

Teóricamente, podrías crear prácticamente todo tu juego al pegarlo dentro de este método (y anularlo) onTouchEvent para manejar la entrada), pero esa no sería una forma terriblemente buena de hacer las cosas. Colocar una nueva pintura dentro de nuestro bucle ralentizará las cosas considerablemente e incluso si colocamos esto en otro lugar, agregando demasiado código al dibujar El método se pondría feo y difícil de seguir.

En cambio, tiene mucho más sentido manejar objetos de juego con sus propias clases. Vamos a comenzar con uno que muestre un personaje y esta clase se llamará CharacterSprite. Adelante y haz eso.

Esta clase dibujará un sprite en el lienzo y se verá así

CharacterSprite de clase pública {imagen de mapa de bits privada; CharacterSprite público (mapa de bits bmp) {imagen = bmp; } public void draw (Canvas canvas) {canvas.drawBitmap (imagen, 100, 100, nulo); }}

Ahora, para usar esto, primero deberá cargar el mapa de bits y luego llamar a la clase desde GameView. Agregar una referencia a Carácter privado Carácter privado y luego en el superficie creada método, agregue la línea:

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

Como puede ver, el mapa de bits que estamos cargando se almacena en recursos y se llama avdgreen (era de un juego anterior). Ahora todo lo que necesita hacer es pasar ese mapa de bits a la nueva clase en el dibujar método con:

characterSprite.draw (lienzo);

¡Ahora haga clic en ejecutar y debería ver su gráfico aparecer en su pantalla! Este es BeeBoo. Solía ​​dibujarlo en los libros de texto de mi escuela.

¿Y si quisiéramos hacer que este pequeño se moviera? Simple: simplemente creamos variables x e y para sus posiciones y luego cambiamos estos valores en un actualizar método.

Entonces agregue las referencias a su CharacterSprite y luego dibuje su mapa de bits en x, y. Cree el método de actualización aquí y por ahora solo intentaremos:

y ++;

Cada vez que se ejecuta el ciclo del juego, moveremos al personaje hacia abajo de la pantalla. Recuerda, y las coordenadas se miden desde arriba 0 es la parte superior de la pantalla Por supuesto que necesitamos llamar al actualizar método en CharacterSprite desde el actualizar método en GameView.

Presiona reproducir de nuevo y ahora verás que tu imagen lentamente recorre la pantalla. Todavía no ganamos ningún premio de juego, ¡pero es un comienzo!

Ok, para hacer cosas ligeramente más interesante, solo voy a soltar un código de "pelota hinchable" aquí. Esto hará que nuestro gráfico rebote alrededor de la pantalla fuera de los bordes, como esos viejos protectores de pantalla de Windows. Ya sabes, los extrañamente hipnóticos.

actualización vacía pública () {x + = xVelocity; y + = yVelocidad; if ((x & gt; screenWidth - image.getWidth ()) || (x & lt; 0)) {xVelocity = xVelocity * -1; } if ((y & gt; screenHeight - image.getHeight ()) || (y & lt; 0)) {yVelocity = yVelocity * -1; }}

También deberá definir estas variables:

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

Mejoramiento

Ahi esta mucho más para profundizar aquí, desde manejar la entrada del jugador, escalar imágenes, hasta manejar tener muchos personajes moviéndose por la pantalla a la vez. En este momento, el personaje está rebotando, pero si miras muy de cerca, hay un ligero tartamudeo. No es terrible, pero el hecho de que puedas verlo a simple vista es una señal de advertencia. La velocidad también varía mucho en el emulador en comparación con un dispositivo físico. Ahora imagina lo que sucede cuando tienes montones pasando en la pantalla a la vez!

Hay algunas soluciones a este problema. Para empezar, lo que quiero hacer es crear un número entero privado en Hilo principal y llama eso targetFPS. Esto tendrá el valor de 60.Voy a intentar que mi juego funcione a esta velocidad y, mientras tanto, lo comprobaré para asegurarme de que sea así. Para eso, también quiero un doble privado llamado promedio FPS.

También voy a actualizar el correr método para medir cuánto tarda cada ciclo del juego y luego pausa ese ciclo de juego temporalmente si está por delante del targetFPS. Luego vamos a calcular cuánto tiempo ahora tomó y luego imprime eso para que podamos verlo en el registro.

@Override public void run () {long startTime; mucho tiempo Milis; larga esperaTiempo; largo totalTime = 0; int frameCount = 0; long targetTime = 1000 / targetFPS; while (running) {startTime = System.nanoTime (); lienzo = nulo; pruebe {canvas = this.surfaceHolder.lockCanvas (); sincronizado (surfaceHolder) {this.gameView.update (); this.gameView.draw (lienzo); }} catch (Excepción e) {} finalmente {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Excepción e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; intente {this.sleep (waitTime); } catch (Excepción e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {averageFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; totalTime = 0; System.out.println (promedio FPS); }}}

Ahora nuestro juego está intentando bloquear su FPS a 60 y deberías descubrir que generalmente mide un 58-62 FPS bastante estable en un dispositivo moderno. En el emulador, aunque podría obtener un resultado diferente.

Intenta cambiar ese 60 a 30 y mira qué sucede. El juego se ralentiza y debería ahora lee 30 en tu logcat.

Pensamientos finales

También podemos hacer otras cosas para optimizar el rendimiento. Aquí hay una gran publicación de blog sobre el tema. Intente abstenerse de crear nuevas instancias de Paint o mapas de bits dentro del bucle e inicie todo fuera de antes de que comience el juego.

Si planeas crear el próximo exitoso juego de Android, entonces hay ciertamente formas más fáciles y más eficientes de hacerlo en estos días. Pero definitivamente todavía hay escenarios de casos de uso para poder dibujar en un lienzo y es una habilidad muy útil para agregar a su repertorio. ¡Espero que esta guía te haya ayudado un poco y te deseo la mejor de las suertes en tus próximas empresas de codificación!

próximoUna guía para principiantes sobre Java

Al ver tanto buque inignia abandonado hata el momento, lo auriculare UB-C e convirtieron en la alternativa predeterminada a lo auriculare convencionale de 3.5 mm. i ere de lo que no etá lito para...

Comprar teléfono uado ​​puede parecer un movimiento arriegado. Depué de todo, ¿quién abe lo que hizo (o no hizo) el dueño anterior cuando lo etaba uando?...

Nuevos Artículos