Andengine es un sistema muy completo, que esta hecho en mi opinion con una arquitectura casi ideal.
A veces entender de qué va cada cosa no es lineal del todo. Hoy me he encontrado un problema: tengo una Activity que carga muchisimos recursos, y he creado un splash screen, y luego un hilo que carga todo y cuando esta listo quita el splash screen.
El sistema que uso para cargar texturas es bastante bueno, lo entiendo perfectamente, y no suelo encontrar ningun problema. Pero hoy a media carga se me ha quedado toda la pantalla blanca. Problema con las texturas seguro.
Me he puesto a seguir la carga linea a linea, y mas o menos esto pasaba al cargar un ChangeableText. Esto es basicamente un Rectangle, que es algo que tengo controlado (de esto heredan muchisimas Entities) y luego tiene una Font. Ok, primer paso: breakpoint en el constructor del ChangeableText, y cuando llego ahi, un break point en cada funcion, a ver si lo localizo mejor.
Veo que la funcion
public synchronized void update(final GL10 pGL)
es una gran candidata, porque es la que esta tocando el contexto de opengl. especialmente el punto critico es:
if(lettersPendingToBeDrawnToTexture.size() > 0)
vamos, que hay algo que necesita tocar texturas, por lo tanto necesita subirse al hardware, y si no va bien, va a romper tambien lo que hubiese en la memoria grafia.
Como estamos en dos hilos, es muy probable que estemos modificando el array lettersPendingToBeDrawnToTexture en un momento que no debemos, asique buscamos donde (que evidentemente, es facil siguiendo el constructor del ChangeableText). Sigo y sigo, y esta todo bien. Las fuentes se cargan cuando se debe, y la textura que usamos esta ya cargada mediante el loadTexture de TextureManager, (algo que en cualquier caso, sabemos que no significa que esté en hardware, y esta es la primera sospecha) volviendo al update de Font, se nos confirma esto, el this.mTexture.getHardwareTextureID() nos devuelve un rotundo -1.
Esto es facil de entender. OpenGL en android no es tan quisquilloso con los hilos como lo es en windows, pero aun asi tiene sus normas. Podemos hacer muchas cosas en muchos hilos, pero cuando queramos subir algo al hardware, lo normal es hacerlo en el update thread, en el principal. En andengine es asi, basicamente porque es donde tenemos el objeto GL que nos ha creado el SurfaceView. Entonces, aunque hagamos el TextureManager.loadTexture(), lo unico que hacemos es marcarla para que sea subida al hardware, pero no se sube hasta que el update thread llegue al punto anterior a imprimir, que es donde comprueba las texturas que hay que subir. Claro, es facil que estemos tratando de utilizar una textura que realmente no esta en el hardware, y que no lo estará hasta dentro de un tiempo. Hay un millon de cosas que se pueden hacer, pero por suerte con las texturas de fuente tenemos una solucion bastante sencilla.
si vamos a la funcion onDrawFrame del Engine, vemos que antes de llamar al draw de la Scene, nos esta actualizando las fuentes.
public void onDrawFrame(final GL10 pGL) throws InterruptedException {
final State threadLocker = this.mThreadLocker;
threadLocker.waitUntilCanDraw();
this.mTextureManager.updateTextures(pGL);
this.mFontManager.updateFonts(pGL);
if(GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS) {
this.mBufferObjectManager.updateBufferObjects((GL11) pGL);
}
this.onDrawScene(pGL);
threadLocker.notifyCanUpdate();
}
Y uno se pregunta por qué no se encarga él solo de comprobar que la textura esta cargada antes de hacer nada, pero bueno, no pidais tanto! LA solucion es facil:
Como sabemos, el proceso para utilzar una fuente es:
- crear la textura (new Texture),
- añadir la textura al TextureManager.
- Crear la fuente (new Font).
- añadir la fuente al fontManager (mEngine.getFontManager().loadFont(mFont))
- y luego ya se la metemos al componente que queramos,
- y la añadimos a la escena.
Vale, pues la solucion sale sola:
- crear la textura (new Texture),
- añadir la textura al TextureManager.
- Crear la fuente (new Font).
- esperar a que la textura este en el hardware (no pasa nada por redundar, por ejemplo:
- isLoadedToHardware()
- getHardwareTextureID()!=-1
- ! isUpdateOnHardwareNeeded()Todo esto lo podemos comprobar en un bucle, bloqueando, si podemos permitirnoslo, o crear un updatehandler que lo compruebe en cada iteracion, y cuando esté ok
- añadir la fuente al fontManager (mEngine.getFontManager().loadFont(mFont)) (si no hacemos esto, el engine no encontrará la necesidad de buscar esa textura) pero claro, tambien tenemos que tener en cuenta que tampoco podemos usarla aun, asique los demas pasos tambien deben ir detras de estos.
- y luego ya se la metemos al componente que queramos,
- y la añadimos a la escena.
He asumido que se tiene un conocimiento general bastante amplio, pero si alguien necesita mas datos sobre algo, que pregunte.