Vistas de página en total

Mostrando entradas con la etiqueta tamaños de pantalla. Mostrar todas las entradas
Mostrando entradas con la etiqueta tamaños de pantalla. Mostrar todas las entradas

viernes, 16 de septiembre de 2016

Modos de escalado en Corona SDK

Para resumir las últimas entradas, vimos que si pintamos objetos en pantalla utilizando los pixels del dispositivo (pixels reales), tenemos un problema por los diversos tamaños de pantalla y resoluciones que tienen los distintos dispositivos. También vimos que Corona permite definir una pantalla virtual con un tamaño determinado y definiendo un modo de escalado. Vimos el modo "letterbox" que lo que hace es centrar la pantalla virtual en la pantalla física aprovechando al máximo el espacio y escalando los objetos proporcionalmente y sin deformarlos. Cuando ponemos los objetos en la pantalla virtual nos olvidamos del tamaño real de la pantalla del móvil, para nosotros sólo existe esa pantalla virtual que tiene un tamaño determinado en pixels virtuales. Obviamente, dependiendo de las proporciones del dispositivo destino, en algunos casos, la pantalla virtual no se adapta 100% a la pantalla física, y pueden quedar zonas (arriba y abajo, o bien, a izquierda y derecha) que no estén cubiertas por la pantalla virtual.
Las constantes:

    display.contentWidth
    display.contentHeight

nos dan el tamaño de la pantalla virtual (en pixels virtuales).

Todo esto en cuanto al modo "letterbox", pero éste no es el único modo de escalado que tiene Corona. En esta entrada vamos a tratar este tema para conocerlos todos y ver cuál se adapta mejor a las necesidades de nuestra aplicación o juego.

Escalado

En Corona, como ya he dicho varias veces, trabajamos con una pantalla virtual de un determinado tamaño fijo, y nuestro programa siempre va a trabajar con pixels en las coordenadas de esa pantalla virtual.
Cuando la aplicación se ejecuta en un dispositivo, los pixels virtuales se expanden (o se contraen) para rellenar la pantalla del dispositivo. A esto se le llama escalado.
Nuestro programa no tiene porqué conocer el tamaño real del dispositivo, simplemente trabaja sobre la pantalla virtual, y Corona se encargará de escalar el contenido a la pantalla física. Corona escalará todos los objetos gráficos, el texto, las imágenes, etc.
Es muy habitual definir una pantalla virtual de 320x480. Se suele utilizar este tamaño porque casi todos los dispositivos iOS y muchos Android tienen la misma relación de aspecto.
Los modos de escalado que ofrece Corona son:
  • letterbox
  • zoomEven
  • zoomStretch
  • none

Modo letterbox

Este es el modo más utilizado y el que se suele utilizar en caso de duda.
El modo letterbox nunca deforma ni corta los objetos. Por tanto, es el modo más fácil de utilizar.
Todo lo que se sitúe dentro de la pantalla virtual se mostrará en la pantalla del dispositivo sin deformaciones y sin que los objetos se salgan de la pantalla.
En modo letterbox hay tres casos a considerar:



En el primer caso, la relación de aspecto de la pantalla virtual coincide con la del dispositivo, con lo cual no tenemos que hacer nada y el escalado será perfecto.
En el segundo caso, el dispositivo es algo alargado, y por tanto, como la pantalla virtual no se puede deformar, quedará un espacio arriba y abajo de la pantalla real que no se pueda rellenar con la pantalla virtual.
En el tercer caso, el dispositivo es más ancho y por tanto, al encajar la pantalla virtual en el dispositivo (sin deformar), quedará espacio a izquierda y derecha sin deformar.

En la práctica, rellenaremos estas zonas fuera de la pantalla virtual con contenido que no sea relevante, por ejemplo con un fondo. No podemos poner objetos que sean importantes porque en algunos dispositivos no se va a visualizar.

Podemos saber el espacio que tenemos fuera de la pantalla virtual con las constantes:

    display.actualContentWidth
    display.actualContentHeight

que nos indican el tamaño total que disponemos, en coordenadas virtuales.
Así por ejemplo, si queremos poner un fondo que ocupe la pantalla entera:

    local bg = display.newRect( 
            display.contentCenterX, display.contentCenterY,
            display.actualContentWidth, display.actualContentHeight)
    bg:setFillColor(0.5, 0.4, 0,7)

Y podemos poner encima otro rectángulo que ocupe la pantalla virtual completamente, que es donde deberíamos situar el contenido importante de nuestra aplicación:

    local content = display.newRect( 
            display.contentCenterX, display.contentCenterY,
            display.contentWidth, display.contentHeight)
    content:setFillColor(0.6, 0.5, 0,8)


Ahora, dependiendo del dispositivo, veremos un caso u otro de los mencionados anteriormente para letterbox.
El fichero config.lau contiene lo siguiente:

application = {
    content = {
        width = 320,
        height = 480,
        scale = "letterbox",
    },
}

Modo zoomEven

Este modo es el más utilizado, después de letterbox. En este modo, la pantalla virtual se expande lo máximo posible, y si es necesario habrá partes de la pantalla virtual que quedarán fuera del dispositivo y no se verán. El escalado es proporcional, sin deformación (escala igual en un eje que en el otro, igual que letterbox). Lo bueno que tiene este modo es que no hay barras laterales sin rellenar. Pero tiene el inconveniente de que pueden quedar zonas de la pantalla que no se muestran.


Para ver el modo zoomEven en acción, escribamos el siguiente fichero de config.lua:

application = {
    content = {
        width = 320,
        height = 480,
        scale = "zoomEven",
    },
}

Y el siguiente main.lua:

local bg = display.newRect( display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight )
bg:setFillColor( 1, 1, 1 )

local circle = display.newCircle( display.contentCenterX, display.contentCenterY, display.contentWidth/2 )
circle:setFillColor( 0.3, 0.6, 0.3 )

En blanco vemos la pantalla virtual y en el centro de ésta un círculo verde, que no se deforma en ningún dispositivo, pero que se puede salir fuera de la pantalla y no dibujarse completamente según el tamaño del dispositivo final.
Si el dispositivo es iPhone 4, entonces veremos el círculo completo, porque la relación de aspecto del iPhone 4 es la misma que la de nuestra pantalla virtual (tamaños distintos pero misma relación de aspecto). Sin embargo en otros dispositivos como el iPhone 5, más alargado, el círculo se cortará.

zoomStretch

Este modo escala, deformando si es necesario, la pantalla virtual a la pantalla física. Es decir, puede escalar de forma distinta en el eje X y en el eje Y.
No se utiliza mucho este modo porque la deformación que produce no suele ser aceptable. No se me ocurre ninguna aplicación en la que pudiera ser útil este modo. Pero es bueno saber que existe, por si acaso es necesario.

none

Este modo es equivalente a no tener ningún modo de escalado en config.lua

Resumen

  • letterbox: no deforma, puede haber barras laterales, no recorta
  • zoomEven: no deforma, no puede haber barras laterales, puede recortar
  • zoomStretch: puede deformar, no puede haber barras laterales, no recorta
¿Cuál es la mejor opción? En mi opinión, yo uso casi siempre letterbox, con una opción de alineamiento que explicaré en la próxima entrada. Pero eso no quita para que en algunos casos sean interesantes los otros modos.

miércoles, 14 de septiembre de 2016

El problema de múltiples pantallas y proporciones

El problema

Este tema de hoy es quizás unos de los más peliagudos y difíciles de entender en la programación de aplicaciones para móviles. En la siguiente entrada veremos cómo resolverlo en Corona SDK.

El problema de la programación para móviles es la (inmensa) diversidad de tamaños de pantalla en los dispositivos. Tenemos móviles desde 4 pulgadas hasta 6 pulgadas, y además tenemos las tablets, que pueden ir desde 7 pulgadas hasta las casi 13 pulgadas del iPad Pro de Apple. Además, para completar el panorama, las proporciones de la pantalla también cambian. Tenemos pantallas 4x3, 5x4, y casi cualquier combinación de parejas números.
Obviamente, como buenos programadores (y como interesados en distribuir/vender muchas unidades de nuestra aplicación), queremos que nuestra aplicación se ejecute igual de bien en el número máximo de dispositivos posible. Este es uno de los problemas más importantes que se dan en la programación móvil. Y hay varias soluciones:
  • layouts: que mueven y redimensionan dinámicamente los elementos en función del tamaño de la pantalla en base a una serie de reglas del estilo: este elemento se ajusta a la izquierda, este elemento va a la izquierda de este otro, etc. Esta programación con layouts es una solución muy interesante y que funciona muy bien "la primera vez", quiero decir, para la primera versión de nuestra aplicación. Pero el gran problema es cuando el cliente o nuestro jefe nos dice: "Tenemos que meter una lista debajo de ese botón", y es entonces cuando aparecen los sudores y temblores en el programador, pensando que va a tener que volver a redefinir todas las reglas de layouts para que todo quede bien por culpa de un simple elemento. Y lo peor llega cuando rematan la frase con: "Esto es fácil, verdad? Es sólo añadir un elemento más". Se puede desprender de estas líneas que aunque es bonita la idea de los layouts, no creo que sean la mejor idea (es mi opinión absolutamente personal). En cualquier caso, Apple utiliza layouts en los desarrollos para iOS y parece que a los programadores les gusta.
  • hacer proporcionales los elementos con respecto al tamaño de la pantalla: esto es más o menos lo que hace Corona, y que veremos en esta entrada.
  • llenar el código de if's y colocar los elementos en función del tamaño y proporciones de la pantalla, asegurándose que todo cuadra en todas las posibilidades. El lector comprenderá que esta opción, un tanto primitiva, no es muy adecuada en cuanto tengamos 10 o más elementos en una misma pantalla. Pero como siempre, para gustos los colores, hay gente muy muy paciente que es capaz de rellenar el código de if's preguntando por miles de posibles valores.

Hay otras soluciones, pero realmente la que me interesa explicar con detalle es la que se utiliza en las aplicaciones con Corona.

Ejemplo

Vamos a hacer un sencillo ejemplo donde podemos ver el problema, así de paso, ya vamos viendo el mundo de las apps.
Creamos un directorio en nuestro disco duro y también creamos un fichero nuevo: main.lua, que será el punto de arranque de nuestra aplicación. El contenido del main.lua será:

    local rect = display.newRect( 320, 480, 640, 320 )

Abrimos el simulador de Corona, seleccionamos la carpeta de nuestro proyecto y lo ejecutamos. Pero atención, vamos a ir al menú View > View As > iPhone 4. En la pantalla tendremos algo como lo siguiente:


Reconozco que el programa no es gran cosa, pero no se puede pedir mucho más por una única línea de código.
Aunque creo que se entiende bien, diré que estamos creando un rectángulo en la pantalla, en las coordenadas x=320, y=480, con ancho 640, y alto 320. Como podemos ver, el cuadrado sale centrado verticalmente en la pantalla del móvil y además ocupa todo el ancho de la pantalla. En Corona, como veremos más adelante, las coordenadas x,y de los objetos. sitúan el centro del objeto, por eso el cuadrado aparece centrado en la pantalla del iPhone 4 (que tiene un tamaño de pantalla de 640x960, y el centro es 320x480). En otros entornos, las coordenadas x,y sitúan la esquina superior izquierda del objeto, pero no así en Corona (ya veremos más adelante que este comportamiento se puede cambiar).
Hasta ahora todo muy bien. Pero vamos a ver qué ocurre si visualizamos nuestro programa en otro dispositivo, por ejemplo, en un iPhone 6. Vamos al menú View > View As > iPhone 6 y veremos algo como esto:


El resultado es parecido. Pero hay unas diferencias muy importantes: Sin haber cambiado coordenadas ni tamaño en nuestro programa, resulta que ahora el rectángulo ya no sale centrado verticalmente en la pantalla. ¿Por qué? Esto es fácil de responder: el tamaño del iPhone 6 es distinto, 750x1334, por tanto, la coordenada y=480 ya no es el centro vertical de la pantalla, y por tanto, el objeto no sale centrado verticalmente. Hay otro cambio, el rectángulo no ocupa todo el ancho de la pantalla. La justificación de esto también es obvia: El ancho de pantalla del iPhone 6 es 750, y el ancho de nuestro rectángulo es 640, por tanto no va a ocupar todo el ancho de la pantalla en el iPhone 6 (aunque sí en un iPhone 4).
Vamos a visualizar nuestro programa en un Nexus One (recordemos, View > View As > Nexus One). Aquí la situación todavía es peor, ya que la pantalla de este teléfono tiene 480x800. En este caso, no sólo no tenemos un rectángulo centrado verticalmente en la pantalla, sino que además, no cabe completamente en la pantalla. Esto es todavía peor, porque podemos perder contenido. Creo que no es difícil entender el resultado.

En el caso del Nexus One, estamos perdiendo 160 pixels del rectángulo (640 que es el ancho del rectángulo, menos 480 que es el ancho de pantalla del Nexus).
Si queremos comprobarlo, pongamos un borde alrededor del rectángulo y comprobemos que el borde derecho no se visualiza en el Nexus:

    rect:setStrokeColor( 1,0,0 )
    rect.strokeWidth = 5

En resumen, tenemos un problema. Hay que hacer algo para que cuando coloquemos objetos en la pantalla, no tengamos que preocuparnos del tamaño real del dispositivo en el que se ejecutará nuestra aplicación.