[AYUDA] Problema con onTouchEvent en surfaceView al continuar la app tras la pausa.

Visto 705 veces
Saltar al primer mensaje no leído

Carlos Barbero

no leída,
20 dic 2012, 7:11:1320/12/12
a desarrollad...@googlegroups.com


Hola soy nuevo en esto de programar para android, he estado siguiendo un videotutorial en youtube sobre programación de juegos en android, el problema es que el juego se quedaba a medias y el que hacia los videotutoriales ya llevaba mucho tiempo sin poner ninguno mas, así que me decidí a añadirle cosas yo mismo que es como se aprende. Poco avance desde que acabe el tutorial pero ya llevo una semana peleándome con un problema que no consigo solucionar.

La cuestión es la parte visual del juego se hace en un surfaceView y también el evento onTouchEvent. El problema es que cuando pauso el juego (se llama al metodo onPause() del activity) el surfaceHolder se destruye (se llama al metodo surfaceDestroyed(SurfaceHolder holder)) para que no de problemas tengo que parar el thread (GameLoopThread) que se dedica a actualizar el juego. Luego cuando se vuelve a iniciar el juego se llama a onResume() y se vuelve a crear el surfaceHolder y a iniciar el thread (GameLoopThread).

El problema que tengo es que después de llamar al metodo onResume() y volver a iniciarlo todo el evento onTouchEvent del surfaceView deja de funcionar, al hacer clic ya no reacciona y tras esperar un rato me salta un mensaje en el emulador que me dice que la aplicación no responde y solo me da las opciones de esperar (sin éxito) o forzar el cierre de la aplicación.

Os dejo el código de la surfaceView (Hay métodos que ya no se usan y algunas cosas comentadas por las pruebas).

package net.balanze.tutorialjuego;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.SoundPool;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
/*
 * Mirar audio con ToneGenerator, MediaPlayer y SoundPool.
 */
public class GameView extends SurfaceView {
   
    private SurfaceHolder holder; // necesario para bloquear el canvas.
    private GameLoopThread gameLoopThread; // gameLoop, actualiza graficos y fisica.
    private List<Sprite> sprites=new ArrayList<Sprite>(); // Lista de Sprites.
    private long lastClick; // guarda el momento de la ultima avez que funciono el evento de touch.
    private Bitmap bmpBlood; // guarda la imagen del charco de sangre.
    private List<TempSprite> temps = new ArrayList<TempSprite>(); // Lista de sprites de sangre.
    private int puntos=0; // Puntuacion de la partida.
    private Paint paint; // Lo usaremos para pintar la puntuacion en el canvas.
    /*
     * iniciamos sndPool con un maximo de 16 flujos simultanios
     * el audio sera del flujo musical, (el ultimo parametro no
     * funciona asi que pongo 0 que es el conversor por defecto).
     */
    private SoundPool sndPool = new SoundPool(16, AudioManager.STREAM_MUSIC, 0);
    private int sonidoMalo;
    /*private int x=0; // posicion del bmp.
    private int xSpeed=1; // velocidad del desplazamiento sobre eje x.*/

    public GameView(Context context) {
        super(context);
        gameLoopThread = new GameLoopThread(this);
        holder = getHolder();
        holder.addCallback(new Callback() {
           
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                gameLoopThread.setRunning(false);
                boolean intento=true;
                // intentamos destruir el thread y no paramos hasta logralo.
                while(intento){
                    try {
                        gameLoopThread.join();
                    } catch (InterruptedException e) {
                        // e.printStackTrace();
                        System.out.println("ERROR --> Problema al destruir la view.");
                    }
                    intento=false;
                }
                //gameLoopThread=null;
            }
           
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                // asignamos los Sprites del arrayList.
                createSprites();
                /*
                 * La asignacion de los Sprites se hace aqui ya que
                 * para calcular las posiciones en las que empieza cada
                 * sprite necesitamos conocer el alto y el ancho de la
                 * pantalla y si no se acabo de crear la view no conocemos
                 * sus dimensiones.
                 */
                // ponemos a funcionar el gameLoopThread
                gameLoopThread=(!gameLoopThread.isAlive())?new GameLoopThread(devolverVista()):gameLoopThread;
                if(!gameLoopThread.isRunning()){
                    gameLoopThread.setRunning(true);
                    gameLoopThread.start();
                }
            }
           
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                    int height) {
                // TODO Auto-generated method stub
               
            }
        });
        // preparamos la imagen del charco de sangre.
        bmpBlood = BitmapFactory.decodeResource(getResources(), R.drawable.blood1);
        // preparamos el sonido de los malos al morir.
        sonidoMalo=sndPool.load(context, R.raw.baddeath, 1);
        // establecemos el color de la puntuacion rojo.
        paint=new Paint();
        paint.setARGB(255, 255, 0, 0);
        paint.setTextSize(30);
        paint.setTypeface(Typeface.SANS_SERIF);
    }
    /**
     * <p><b>private void createSprites()</b></p>
     * <p>Añade a al array list sprites todos los sprites que veremos
     * en pantalla.</p>
     */
    private void createSprites(){
        // establecemos el grafico que queremos pintar.
        sprites.add(createSprite(R.drawable.bad1, false));
        sprites.add(createSprite(R.drawable.bad2, false));
        sprites.add(createSprite(R.drawable.bad3, false));
        sprites.add(createSprite(R.drawable.bad4, false));
        sprites.add(createSprite(R.drawable.bad5, false));
        sprites.add(createSprite(R.drawable.bad6, false));
        sprites.add(createSprite(R.drawable.good1, true));
        sprites.add(createSprite(R.drawable.good2, true));
        sprites.add(createSprite(R.drawable.good3, true));
        sprites.add(createSprite(R.drawable.good4, true));
        sprites.add(createSprite(R.drawable.good5, true));
        sprites.add(createSprite(R.drawable.good6, true));
    }
    /**
     * <p><b>private Sprite createSprite(int Resource)</b></p>
     * <p>Devuelve un sprite con la imagen que se le pasa como recurso.</p>
     * @param Resource Recurso de imagen que queremos pintar en el Sprite.
     * @return Sprite creador con imagen pasada.
     */
    private Sprite createSprite(int Resource, boolean esBueno){
        // preparamos el mapa de bits del Sprite.
        Bitmap bmp = BitmapFactory.decodeResource(getResources(), Resource);
        // devolvemos el sprite creado con el mapa de bits que se nos paso.
        return new Sprite(this, bmp, esBueno);
    }
   
    @Override
    protected void onDraw(Canvas canvas){
        // Establecemos el color de fondo negro.
        canvas.drawColor(Color.BLACK);
        // Colocamos la puntuacion.
        canvas.drawText("Puntuacion: "+puntos, 0, 30, paint);
        // Pintamos los sprites de sangre que necesitemos.
        for(int i=temps.size()-1; i>=0; i--) temps.get(i).onDraw(canvas);
        // Pintamos los sprites.
        for(Sprite sprite:sprites){
            sprite.onDraw(canvas);
        }
    }
   
    @Override
    public boolean onTouchEvent(MotionEvent event){
        /*
         * Solo permitimos que funcione el evento cada 300 milisegundos
         * porque si no hace muchas comprobaciones y mata a mas de un sprite de un toque.
         */
        if(System.currentTimeMillis()-lastClick>300){
            lastClick=System.currentTimeMillis();
            /*
             * Se ejecuta en un thread diferente y de forma sincronizada para
             * evitar problemas con la parte visual.
             */
            synchronized (getHolder()) {
                float x = event.getX();
                float y = event.getY();
                 /*
                  * El bucle tiene que ser al reves ya que arrayList
                  * reordena solo cuando se borra un elemento.
                  */
                for(int i=sprites.size()-1; i>=0; i--){
                    Sprite sprite=sprites.get(i);
                    // Si se toco sobre un sprite que este se borre.
                    if(sprite.isColision(x, y)){
                        // Si es malo hacemos que suene al morir.
                        if(!sprite.esBueno()) sndPool.play(sonidoMalo, 1.0f, 1.0f, 5, 0, 1.0f);
                        // Actualizamos la puntuacion.
                        puntos+=sprite.puntuar();
                        // eliminamos el sprite.
                        sprites.remove(sprite);
                        // pintamos la sangre.
                        temps.add(new TempSprite(temps, this, x, y, bmpBlood));
                        break; // para que con un clic solo puede morir un sprite.
                    }
                }
            }
        }
        //return true;
        return super.onTouchEvent(event);
    }
    /**
     * <p><b>public boolean quedanMalos()</b><P>
     * <p>Devuelve verdadero si quedan sprites de enemigos y falso en caso contrario.</p>
     * @return true si quedan enemigos, false si solo quedan sprites buenos.
     */
    public boolean quedanMalos(){
        boolean soloBuenos=true;
        // recorremos la lista de sprites hasta que aparecca uno malo.
        for(int i=0; i<sprites.size() && soloBuenos; i++) soloBuenos=sprites.get(i).esBueno();
        return !soloBuenos;       
    }
    /**
     * <p><b>public void pause()</b></p>
     * <p>Pausa el juego, porque pausa el gameLoop.</p>
     */
    public void pause(){
        gameLoopThread.setRunning(false);
        boolean intento=true;
        //synchronized (gameLoopThread) {
            while(intento){
                try{
                    gameLoopThread.join();
                }catch (InterruptedException e) {
                    //e.printStackTrace();
                    System.out.println("ERROR --> No se pudo para el gameLoop.");
                }
                intento=false;
            }
        //}
        gameLoopThread=null;
    }
    /**
     * <p><b>public void resume()</b></p>
     * <p>Quita de la pausa al juego. Pone de nuevo en marcha el gameLoop.</p>
     */
    public void resume(){
        // si esta creada la view.
        if(holder.isCreating()){
            gameLoopThread=(!gameLoopThread.isAlive())?new GameLoopThread(this):gameLoopThread;
            if(!gameLoopThread.isRunning()){
                gameLoopThread.setRunning(true);
                gameLoopThread.start();
            }
        }
    }
    /**
     * <p><b>private GameView devolverVista()</b></p>
     * <p>Devuelve una referencia al obgeto actual.</p>
     * @return devuelve una referencia al GameView actual.
     */
    private GameView devolverVista(){
        return this;
    }

}

Muchas gracias por la ayuda.

PD: Si necesitáis algo mas del código avisarme que no dudare en ponerlo todo, como os digo casi todo el código viene de la lista de youtube que puse al iniciar el hilo.

PAZ

Juan de Dios Maldonado Sánchez

no leída,
12 mar 2013, 3:17:0912/3/13
a desarrollad...@googlegroups.com

En concreto fíjate en el siguiente código (que es el que hace que se pase a otra actividad) dentro del tutorial que te he puesto arriba:
        Intent i = new Intent(this, AcercaDe.class );
        startActivity(i);

2013/3/12 israel muela <muel...@gmail.com>
A ver tengo un problema, yo cargo el GameView desde el MainActiviy pero ademas tengo otras actividades, como hago para que una vez llego al objetivo del juego o no, cargue la activity de gameOver o la de el menu principal, gracias por adelantado

--
Para participar es necesario que leas detenidamente las normas del grupo: http://goo.gl/20KhL
---
Has recibido este mensaje porque estás suscrito al grupo "desarrolladores-android" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a desarrolladores-a...@googlegroups.com.
Para publicar una entrada en este grupo, envía un correo electrónico a desarrollad...@googlegroups.com.
Visita este grupo en http://groups.google.com/group/desarrolladores-android?hl=es.
Para ver este debate en la Web, visita https://groups.google.com/d/msg/desarrolladores-android/-/p7-EFWY2r08J.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.
 
 

Israel Muela Garcia-Serrano

no leída,
12 mar 2013, 7:32:5212/3/13
a desarrollad...@googlegroups.com
Buenos dias, 
el problema que tengo es que desde dento del GameView<Graphics> no me permite cargar otra activity de esa manera.
en realidad mi clase gameview es esto:
                      public class GameView<Graphics> extends SurfaceView

y lo que quiero saber es como salir de ella y cargar otra activity. por que no se si al ser asi hay otra manera 

Juan de Dios Maldonado Sánchez

no leída,
12 mar 2013, 7:49:5712/3/13
a desarrollad...@googlegroups.com
Claro que sí puedes. Todos los Views mantienen una referencia al Context, así que puedes llamar al startActivity(i) dentro de tu GameView<Graphics> de la forma:

getContext().startActivity(i);


2013/3/12 Israel Muela Garcia-Serrano <muel...@gmail.com>

--
Para participar es necesario que leas detenidamente las normas del grupo: http://goo.gl/20KhL
---
Has recibido este mensaje porque estás suscrito al grupo "desarrolladores-android" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a desarrolladores-a...@googlegroups.com.
Para publicar una entrada en este grupo, envía un correo electrónico a desarrollad...@googlegroups.com.
Visita este grupo en http://groups.google.com/group/desarrolladores-android?hl=es.

Israel Muela Garcia-Serrano

no leída,
12 mar 2013, 7:58:4312/3/13
a desarrollad...@googlegroups.com
mira te paso mi codigo solo de Gameview, los dos metodos que se encuentran comentados que se llaman salir y gameover cargan activity y lo e puesto con getcontext y tampoco, Podrias ayudarme


public class GameView<Graphics> extends SurfaceView {
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Sprite> sprites=new ArrayList<Sprite>();
private List<TempSprite> temps=new ArrayList<TempSprite>();
private Bitmap bmpRoto;
private Bitmap imagen;
private Bitmap upNivel;
private Bitmap barra;
private SoundPool soundPool;
private int fallos=0;
private int aciertos=0;
private int platos=20;
private int Nivel=1;
private int cuota=15;
private int puntuacion;
private int miSonidoId=-1;
private Paint pincel1;
private Typeface face;
private boolean preparado=true;
public GameView(Context context,SoundPool soundPool,int miSound) {
super(context);
gameLoopThread=new GameLoopThread(this);
pincel1=new Paint();
holder=getHolder();
holder.addCallback(new SurfaceHolder.Callback(){
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry=true;
//cierre ordenado
gameLoopThread.setRunning(false);
while(retry){
try{
gameLoopThread.join();//hace destruccion esperando a que el termine para destruirlo
retry=false;
}catch(InterruptedException e){}
}
}
public void surfaceCreated(SurfaceHolder holder) {
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
bmpRoto=BitmapFactory.decodeResource(getResources(),R.drawable.humo);
imagen = BitmapFactory.decodeResource(getResources(),R.drawable.paisaje);
upNivel = BitmapFactory.decodeResource(getResources(),R.drawable.gameover);
barra=BitmapFactory.decodeResource(getResources(),R.drawable.barrapuntos);
miSonidoId=miSound;
face = Typeface.createFromAsset(context.getAssets(),"face.ttf");
pincel1.setTypeface(face);
pincel1.setColor(Color.BLACK);
pincel1.setTextSize(15);
this.soundPool=soundPool;
}
private void createSprites(){
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
sprites.add(createSprite(R.drawable.plato));
}
private Sprite createSprite(int resource){
Bitmap bmp=BitmapFactory.decodeResource(getResources(), resource);
return new Sprite(this,bmp);
}
protected void onDraw(Canvas canvas){
pincel1.setColor(Color.BLACK);
canvas.drawBitmap(imagen, 0, 0,null);
canvas.drawBitmap(barra, 0,canvas.getHeight()-barra.getHeight(),null);
canvas.drawText(Integer.toString(platos),20, canvas.getHeight()-15, pincel1);
canvas.drawText(Integer.toString(puntuacion),404, canvas.getHeight()-15, pincel1);
canvas.drawText(Integer.toString(Nivel),124, canvas.getHeight()-15, pincel1);
canvas.drawText(Integer.toString(cuota),254, canvas.getHeight()-15, pincel1);
for (int i=temps.size()-1;i>=0;i--){
temps.get(i).onDraw(canvas);
}
if(platos>0){
sprites.get(0).onDraw(canvas);
}
if(platos==0 && aciertos<cuota){
//gameover();
}
if(platos==0 && Nivel==5 && aciertos>=cuota){
//salir();
}
if(platos==0 && Nivel<5 && aciertos>=cuota){
subirNivel();
}
}
public void subirNivel(){
createSprites();
Nivel++;
platos=20;
fallos=0;
aciertos=0;
cuota++;
}
/*public void salir(){
Intent mainIntent = new Intent(GameView.this, menu.class);
GameView.this.startActivity(mainIntent);
GameView.this.finish();
}
public void gameover(View view){
Intent mainIntent = new Intent(GameView.this, GameOver.class);
GameView.this.startActivity(mainIntent);
GameView.this.finish();
}*/
public boolean onTouchEvent(MotionEvent event){
if(event.getAction()==MotionEvent.ACTION_DOWN){
float x=event.getX();
float y=event.getY();
synchronized (getHolder()){
for(int i=sprites.size()-1;i>=0;i--){
Sprite sprite=sprites.get(i);
if(sprite.isCollision(event.getX(),event.getY())){
muertess(sprite,x,y);
break;
}
}
}
}
return true;
}
public void muertess(Sprite sprite,float x, float y){
sprites.remove(sprite);
temps.add(new TempSprite(temps,this,x,y,bmpRoto));
aciertos++;
puntuacion+=100;
platos--;
if(miSonidoId!=-1){
soundPool.play(miSonidoId,1,1,0,0,1);
}
}
public void muertesss(Sprite sprite){
sprites.remove(sprite);
fallos++;
platos--;
}
}

Carlos Barbero

no leída,
12 mar 2013, 8:09:1412/3/13
a desarrollad...@googlegroups.com
En el constructor del GameView fíjate que se le pasa una instancia de Context como te dice Juan de Dios, depende de donde quieres que se lance el activity puede que necesites guardar en una propiedad de la clase GameView la referencia a este objeto Context.

PAZ


Has recibido este mensaje porque estás suscrito a un tema del grupo "desarrolladores-android" de Grupos de Google.
Para anular la suscripción a este tema, visita https://groups.google.com/d/topic/desarrolladores-android/dDn8CLtXYto/unsubscribe?hl=es. Para anular la suscripción a este grupo y todos sus temas, envía un correo electrónico a desarrolladores-a...@googlegroups.com.

Juan de Dios Maldonado Sánchez

no leída,
12 mar 2013, 8:11:0512/3/13
a desarrollad...@googlegroups.com
Es correcto lo que dice Carlos, pero en el método onDraw puedes acceder al Context del View llamando a getContext().

Deberías hacer algo similar a:

public void salir(){
Intent mainIntent = new Intent(GameView.this, menu.class);
getContext().startActivity(mainIntent);
//...
}

Verás que compila perfectamente. Otra cosa es que la actividad no arranque porque no la tengas definida correctamente en el Manifest.xml

2013/3/12 Carlos Barbero <carlosbarb...@gmail.com>

Israel Muela Garcia-Serrano

no leída,
12 mar 2013, 8:39:4812/3/13
a desarrollad...@googlegroups.com
con eso me funciona pero el problema que me carga la activity menu varias veces.
cual podria ser el problema.
ademas no me deja finalizar la activity gameView con el finish()
alguna idea...
muchas gracias Juan y carlos

Juan de Dios Maldonado Sánchez

no leída,
12 mar 2013, 8:48:3712/3/13
a desarrollad...@googlegroups.com
Se llama muchas veces porque la condición que llama a ese método se cumple en cada fotograma.
Tendrás que definir una variable lógica en tu GameView<Graphics> que se llame por ejemplo "gameviewFinalizado" y la inicias a false.

Luego, añades una comprobación en la condición y una vez que se cumpla cambias su valor para que así sólo entre la primera vez:

if(platos==0 && aciertos<cuota && !gameviewFinalizado){
gameviewFinalizado = true;
//gameover();
}

2013/3/12 Israel Muela Garcia-Serrano <muel...@gmail.com>
con eso me funciona pero el problema que me carga la activity menu varias veces.

--

Israel Muela Garcia-Serrano

no leída,
12 mar 2013, 9:03:4412/3/13
a desarrollad...@googlegroups.com
muchas Gracias juan me has ayudado muchisimo

Carlos Barbero

no leída,
16 mar 2013, 16:19:4916/3/13
a desarrollad...@googlegroups.com
Muchas gracias LUICIT URIBE, lo he probado, pero el fallo persiste, empiezo a creer que se puede tratar de un problema con los thread, que se me cargue demasiado el thread principal, haber si algún día consigo resolverla, lo cierto es que esta aplicación la tengo aparcada desde hace tiempo ya que me ha acabado frustrando xD.

De todos modos muchas gracias.

PAZ

El sábado, 16 de marzo de 2013 08:29:46 UTC+1, LUICIT URIBE escribió:
SABES COMO PUDE SOLUCIONAR LA PAUSA DEL JUEGO Y ESO DE TANTO INVESTIGAR Y DEPURAR EL JUEGO PARA PODER ENTENDER COMO SE CREABA Y SALIA SE ME OCURRIO LA GRAN IDEA DE IMPLEMENTAR addCallback A MI GAMEVIEW Y ME QUEDA ESTA LINEA DE CODIGO ASI:

public class GameView extends SurfaceView implements SurfaceHolder.Callback {

Y EN EL CONSTRUCTOR DE LA MISMA CLASE MODIFIQUE LA LINEA DONDE VIENE EL HOLDER Y QUEDARIA ASI:

getHolder().addCallback(this);

Y YA MEDIANTE LOS CLICK DERECHO QUE TE VA A IR DICIENDO PARA IMPORTAR Y AGREGAR LOS METODOS YA DE UNA MANERA QUE PUEDAS ACCEDER A DICHOS METODOS QUE SON LOS surfaceDestroyed(SurfaceHolder holder), surfaceCreated(SurfaceHolder holder), surfaceChanged(SurfaceHolder holder, int format, int width, int height).

Y YA ASI PODRAS PODER PAUSAR Y REANUDAR EL JUEGO CUANTAS VECES QUIERAS Y LA PAUSA SOLAMENTE TIENES QUE DECIR QUE ES EL METODO  surfaceDestroyed(SurfaceHolder holder) Y LA REANUDAR EL JUEGO SERA surfaceCreated(SurfaceHolder holder) Y ASI NO TENDRAS EL PROBLEMA DE QUE SE TE TRUENE EL PROGRAMA
Responder a todos
Responder al autor
Reenviar
0 mensajes nuevos