Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre dos temas los cuales estan relacionados entre si y como uno deriva en el otro.

Anuncios
Anuncios

Primero hablemos sobre el bucle del juego, este es diferente a los bucles que vimos en este post porque en realidad no es un metodo o funcion sino un ciclo durante la ejecucion de nuestro codigo porque el juego necesita sincronizar la accion del jugador con lo dibujado y la inteligencia artificial mientra respeta las reglas de nuestro sistema operativo, como el juego necesita constantemente actualizar los objetos del juego, ya sea por el movimiento o dibujar todo en su posicion actual mientras espera la entrada del usuario, para entender el concepto veamos el siguiente grafico

Anuncios
Anuncios

En este caso primero vamos a actualizar los objetos del juego, nuestro siguiente paso sera dibujar los objetos en su nueva posicion, para finalmente responder a cualquier accion del jugador, despues de este ciclo volvemos a la prinera accion, y asi hasta la finalizacion del juego, este es un ciclo muy basico pero es para comprender la idea, cuando veamos el segundo proyecto entenderemos mejor este concepto, pero tambien nos surge otro inconveniente como es la respuesta al jugador, por este motivo nuestro juego no sera una actividad porque ahora nuestro juego no solamente debe detectar el toque en la pantalla sino que tambien debe dibujar en la misma y a su vez no descuidar los requerimientos del Sistema Operativo, para esto necesitaremos manejar los tiempos perfectamente para iniciar y detener el loop anterior y no bloquear a nuestro Sistema Operativo por manipular nuestro CPU de manera exagerada, pero como podemos hacer esto? Para esta situacion entra en accion los Threads, hilos o como yo tambien los llamo procesos, veamos la siguiente imagen

Anuncios

Como podemos observar nuestro bucle tendra un thread para controlar el inicio y detencion gracias al Track time que tendremos disponible gracias a este, entrando en escena las acciones del jugador en el bucle, con esto aclarado pasemos a hablar sobre los threads.

Anuncios
Anuncios

Los threads, tambien conocidos como hilos, nos permiten correr distintas tareas de manera independiente, esto nos posibilita que determinadas partes del codigo pueden corrar por diferentes threads lo cual nos facilitara que cada proceso no tome el control absoluto, en general siempre habra un hilo (thread) principal por donde corre la interfaz del usuario y el sistema operativo, si nosotros ejecutamos nuestra aplicacion y esta consume mucho uso del CPU nos bloqueara el hilo principal y por ende bloqueara el dispositivo, usualmente muestra un mensaje de espera o cierre de la aplicacion, para evitar esto usamos a Thread para generar otro hilo que evita el uso del hilo principal, aunque esto no es tan asi porque si bien creamos un nuevo hilo este seguira corriendo sobre el principal, si quieren saber mas sobre Threads les recomiendo este post donde lo explico desde cero.

Tener un programa con multiples hilos puede tener sus problemas porque si no ocurre la sincronizacion apropiada puede hacer que las cosas vayan a mal, vamos a considerar la siguiente situacion: tenemos la siguiente variable, int x, la cual es una pieza clave de tres hilos de nuestro programa en uso, que sucede si uno de los hilos se adelante y convierte en incorrecto a los otros dos? Este es el problema de la correccion causada por multiples hilos corriendo por finalizaciones ajenas debido a que son codigos tontos despues de todo.

Anuncios
Anuncios

Este problema se puede solucionar si supervisamos a los hilos y los bloqueos, cuando hablamos de bloqueo (Locking) significa que temporalmente impedimos la ejecucion en un hilo para asegurarnos de que las cosas corran de una manera sincronizada y correcta, otro inconveniente que podemos tener es el llamado deadlock donde uno o mas hilos se convierte en locked esperando el momento “correcto” para acceder a int x pero ese momento nunca llega y eventualmente el programa entero se detiene.

De lo descripto anteriormente desprendemos dos cosas, la primera es que se soluciono el problema de correccion pero nos puede ocurrir el segundo problema, afortunadamente el problema ha sido resuelto para nosotros asi como usamos a la clase Activity y sobreescribimos a onCreate para saber cuando necesitamos crear nuestro juego tambien podemos usar otras clases para crear y manejar los hilos (threads), al igual que hasta ahora solo necesitamos saber como usarlas no como lo hacen todo esto.

Anuncios

Entonces no tendremos problemas para escribir nuestro código Java para crear y trabajar dentro de nuestros hilos, hay algunas clases diferentes de Android que manejan hilos, diferentes clases de hilos funcionan mejor en diferentes situaciones, todo lo que debemos recordar es que escribiremos partes de nuestro programa que se ejecutarán casi al mismo tiempo.

Anuncios

Para entender mejor este concepto vamos a hacer el siguiente ejemplo, para ello vamos a crear un nuevo proyecto con las siguientes caracteristicas:

  • Dispositivos: Phone and Tablet
  • Actividad: Empty Activity
  • Nombre: Hilos
  • Nombre de paquete: org.example.hilos
  • API Minimo: API 14 (Android 4.0)
Anuncios

Una vez generado nuestro proyecto procederemos a modificar el archivo activity_main.xml con el siguiente codigo:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/entrada"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:inputType="numberDecimal"
            android:text="5">
            <requestFocus />
        </EditText>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="calcularOperacion"
            android:text="Calcular factorial" />
    </LinearLayout>
    <TextView
        android:id="@+id/salida"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text=" "
        android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
Anuncios

Este nuevo codigo utilizara dos LinearLayout, el interno sera para ubicar dos elementos de forma horizontal y el externo sera para ubicarlos de forma vertical, en el interno tendremos un EditText para que ingresemos un valor, a este lo llamaremos entrada y tambien tendremos un boton para llamar a la funcion calcularOperacion, la cual todavia no existe, por fuera tendremos un TextView que usaremos para mostrar el resultado de calcularOperacion y llamaremos salida, pasemos al nuevo codigo para MainActivity:

MainActivity.java

package org.example.hilos;

import android.app.Activity;
import android.os.SystemClock;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {
    private EditText entrada;
    private TextView salida;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        entrada = (EditText) findViewById(R.id.entrada);
        salida = (TextView) findViewById(R.id.salida);
    }

    class MiThread extends Thread {
        private int n, res;

        public MiThread(int n){
            this.n = n;
        }

        @Override
        public void run(){
            res = factorial(n);
            salida.append(res + "\n");
        }
    }

    public void calcularOperacion(View view){
        int n = Integer.parseInt(entrada.getText().toString());
        salida.append(n + " ! = ");
        MiThread hilo = new MiThread(n);
        hilo.start();
    }

    public int factorial(int n){
        int res = 1;
        for(int i=1; i<=n; i++){
            res *=i;
            SystemClock.sleep(1000);
        }
        return res;
    }
}
Anuncios
Anuncios

En este ejemplo primero crearemos los dos objetos que luego usaremos para vincular con los elementos del layout (entrada y salida), en onCreate definiremos la vinculacion de los elementos por medio de findViewById, con esto podremos interactuar sobre nuestros elementos, el siguiente bloque es una clase llamada miThread la cual hereda a Thread, esta se encargara de crear nuestro hilos para el programa, primero definimos dos variables (n y res), nuestro siguiente paso sera definir un nuevo constructor el cual recibira un valor y este valor lo asignara a n, despues redefiniremos al metodo run de la clase Thread y en este caso asignara el resultado de la funcion factorial del valor informado en n, una vez realizado lo asigna a la salida a traves de append, esta funcion solo se ejecuta cuando se le solicite lo cual veremos en un momento, por ahora pasemos a la funcion factorial.

Esta funcion calculara el factoreo del valor informado, una vez calculado lo devolvera a traves del valor almacenado en res, nuestra ultima funcion es calcularOperacion la cual es invocada por el boton del layout, en este caso primero obtendra el valor de entrada y lo asignara a la variable n creada al principio, despues lo asigna a salida, la siguiente linea crea un objeto de tipo MiThread con el nombre de hilo y usamos el nuevo constructor que definimos, y por ultimo iniciamos a hilo por medio de start el cual es el encargado de llamar a run, el metodo que vimos en la clase MiThread, si lo probamos nos sucedera esto

Anuncios

Nos devolvera un error, esto es debido a lo que comente antes, no se puede trabajar sobre el hilo principal sino que solamente puede hacerlo el mismo, para solucionar este inconveniente modifiquemos la siguiente linea de la clase MiThread:

salida.append(res + "\n");
Anuncios

Con el siguiente bloque:

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        salida.append(res + "\n");
    }
});
Anuncios

Este nos permitira decirle al sistema que corra esta parte del codigo sobre el hilo principal, antes de probarlo veamos como quedo nuestro codigo final:

package org.example.hilos;

import android.app.Activity;
import android.os.SystemClock;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {
    private EditText entrada;
    private TextView salida;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        entrada = (EditText) findViewById(R.id.entrada);
        salida = (TextView) findViewById(R.id.salida);
    }

    class MiThread extends Thread {
        private int n, res;

        public MiThread(int n){
            this.n = n;
        }

        @Override
        public void run(){
            res = factorial(n);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    salida.append(res + "\n");
                }
            });
        }
    }

    public void calcularOperacion(View view){
        int n = Integer.parseInt(entrada.getText().toString());
        salida.append(n + " ! = ");
        MiThread hilo = new MiThread(n);
        hilo.start();
    }

    public int factorial(int n){
        int res = 1;
        for(int i=1; i<=n; i++){
            res *=i;
            SystemClock.sleep(1000);
        }
        return res;
    }
}
Anuncios

Con nuestro codigo comentado y verificado procedamos a ejecutar y ver su funcionamiento en el siguiente video

Anuncios

Como pueden ver a pesar de que el programa demora en realizar la operacion nos permite seguir trabajando con nuestro equipo, si ven el video en un momento devuelve un valor negativo pero esto es por haber elegido int, deberiamos haber elegido long y no haber dicho que use unsigned, pero para esto basicamente se usan los threads, si quieren observar como es que llegamos a este codigo les recomiendo este post donde explico como es paso a paso y en este otro post comento sobre AsyncTask para mejorar el otro post.

Anuncios

En resumen, hoy hemos visto como es el ciclo de un juego, para que nos sirve, que no importa el tipo de juego siempre va a ser del mismo tipo de bucle, como esto nos genera la necesidad de un thread (hilo), que son, para que se usan, como nos benefician, que inconvenientes nos pueden generar y como tambien lo solucionamos, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00