Buscar en Unity

Utilizando prefabs de luces bakeadas en dispositivos móviles

y otros trucos sencillos para llegar a 60 fps en teléfonos de gama baja

Last updated: January 2019

What you will get from this page: Michelle Martin, software engineer at MetalPop Games explains how they optimized their new mobile strategy game, Galactic Colonies, for a range of mobile devices, so they could reach as many potential players as possible.

MetalPop Games faced the challenge of making a game in which players could build huge cities on their low-end devices, without their framerate dropping or their device overheating. See how they found a balance between good-looking visuals and solid performance.

Construcción de ciudades en dispositivos de gama baja

As powerful as mobile devices are today, it is still very difficult to run large and good looking game environments at a solid frame rate. Achieving solid 60fps in a large scale 3d environment on an older mobile device can be quite a challenge.

As developers we could just target high-end phones and assume that most players will have good enough hardware to run our game smoothly. But this will result in locking out a huge amount of potential players, as there are still many older devices in use. Those are all potential customers you don’t want to exclude if it can be avoided.

In our game Galactic Colonies, the players colonize alien planets and can build huge colonies made up from a large number of individual buildings. While smaller colonies might only have a dozen buildings, larger ones can easily have hundreds of them.

This is what our goal list looked like when we started building our pipeline:

  • We want huge maps with a high amount of buildings
  • We want to fast run on cheaper and/or older mobile devices
  • We want nice looking lights and shadows
  • We want an easy and maintainable production pipeline
Iluminación en juegos para dispositivos móviles: los desafíos habituales

Tener una buena iluminación en tu juego es clave para que los modelos 3d se vean increíbles. En Unity esto es, evidentemente, fácil: configura tu nivel, coloca tus luces dinámicas y ya estarás listo. Y si necesitas estar pendiente de la performance, simplemente bakea todas tus luces y añadie algo de SSAO y otros trocitos atractivos a la vista a través de la pila de posprocesamiento. Y listo, ¡ya podrás despacharlo!

Configurar la iluminación en los juegos móviles con frecuencia requiere una buena dosis de trucos y soluciones alternativas. Por ejemplo, a menos que te dirijas a dispositivos de alta gama, es mejor que no uses para nada los efectos de posprocesamiento. De igual manera, tener una escena grande llena de luces dinámicas reducirá también tu framerate de manera considerable.

La iluminación en tiempo real puede ser muy costosa, incluso en una computadora de escritorio. En el caso de los recursos de los dispositivos móviles, las limitaciones son incluso mayores, y no puedes costear todas esas lindas prestaciones que te gustaría tener.

Bakeado de luces o light-baking al rescate

En el caso de los dispositivos móviles, los recursos son bastante limitados y no quieres agotar las baterías de tus usuarios más de lo necesario con demasiadas luces elegantes en tu escena.

Si continuamente fuerzas a un dispositivo al límite de su hardware, el teléfono se calentará, y como resultado, automáticamente se ralentizará, para protegerse. Es aquí donde normalmente comenzarías a bakear cada luz que no necesita emitir sombras en tiempo real.

Unity light baked prefabs for mobile MetalPopGames Galactic Colonies

(Un nivel típico en Galatic Colonies con un gran número de edificios)

Para nosotros, esta no era una opción, ya que el mundo de nuestro juego fue creado en tiempo real por el jugador. El hecho de que continuamente se descubran regiones nuevas, se construyan edificios adicionales o que se mejoren los que ya existen impiden que pueda efectuarse un bakeado de luz eficiente.

Simúlalo hasta que lo consigas

El proceso de bakeado de luz es el precálculo de luces y sombras para una escena (estática) y guardar la información en un lightmap.

El proceso consiste en indicarle al renderer dónde hacer un modelo más luminoso o más oscuro, creando la ilusión de luz.

Renderizar cosas de esta manera es realmente rápido, ya que todos los cálculos de luz costosos y lentos ya se han hecho sin conexión, y durante el runtime, el renderizador (shader) ahora necesita buscar el resultado en una textura.

La disyuntiva aquí es que tendrás que despachar algunas texturas de lightmap extra que incrementarán tu tamaño de compalación y requerirán algo de memoria extra para las texturas en el runtime.

Perderás también algo de espacio porque tus mallas necesitarán UV de lightmap y ser un poquito más grandes.

Pero, en líneas generales, ganarás un tremendo aumento de velocidad.

Unity light baked prefabs for mobile MetalPopGames

(Un edificio con y sin lightmap)

Sin embargo, no bastará por pulsar el botón Bake cuando tienes un mundo dinámico que puede ser cambiado por el jugador en forma continua.

Hacemos frente a una serie de problemas que surgen al bakear luces para escenas altamente modulares.

El bakeado de luces en prefabs no es tan sencillo

En primer lugar, los datos de bakeado de luces en Unity se almacenan y se asocian directamente con los datos de la escena. Este no es un problema si tienes niveles individuales y escena prefabricadas y solo tienes un puñado de objetos dinámicos. Puedes prebakear la iluminación y estarás listo.

Esto evidentemente no funciona si creas tus niveles dinámicamente. En un juego de construcción de ciudades, el mundo no está creado previamente. En cambo, se ensambla en gran medida dinámicamente y sobre la marcha por decisión del jugador respecto de qué construir y dónde hacerlo. Esto normalmente se hace instanciando prefabs cada vez que el jugador decide construir algo.

La única solución a este problema es guardar todos los datos de bakeado de luces de interés dentro del prefab en lugar de hacerlo en la escena.

Lamentablemente, no hay una forma fácil de copiar los datos del lightmap que se utilizará, sus coordenadas y escalarlos en un prefab.

Construyendo un pipeline para prefabs de luces bakeadas

El mejor método es conseguir una pipeline sólida que maneje los prefabs en los que se han bakeado las luces es crear los prefabs en una escena distinta y aparte (varias escenas, en realidad) y subirlos después al juego principal cuando se necesite.

Cada pieza modular se somete a bakeado de luces y después se sube al juego cuando se necesita.

Cuando echas un vistazo más de cerca respecto de cómo funciona el bakeado de luces en Unity, puedes ver que renderizar una malla que se ha sometido al bakeado de luces es justo como aplicar otra textura a esta, y dar luz, oscurecer (o a veces dar color) a la malla apenas un poquito. Todo lo que necesitas es la textura del lightmap y las coordenadas UV, y ambas pueden ser creadas por Unity durante el proceso de bakeado de luces.

Durante el proceso de bakeado de luces, Unit crea un conjunto nuevo de coordenadas UV (que apuntan a la textura del lightmap) y un desfase y escala para la malla individual. Cada vez que se vuelven a bakear las luces, estas coordenadas cambian también.

Cómo utilizar canales UV

Para desarrollar una solución para este problema, es útil comprender cómo funcionan los canales UV y cómo utilizarlos de la mejor manera.

Cada malla puede tener varios juegos de coordenadas UV (a las que se llama canales UV en Unity). En la mayoría de los casos, basta con un conjunto de UV, ya que diferentes texturas (Diffuse, Spec, Bump, etc.) guardan la información en el mismo lugar en la imagen.

Sin embargo, cuando los objetos comparten una textura, como es el caso de un lightmap, y necesitan buscar la información de un lugar muy específico de una textura grande, con frecuencia es evidente que no se puede añadir otro conjunto de UV para que pueda ser utilizado con esta textura compartida.

El inconveniente de usar varias coordenadas UV es que consumen memoria adicional. Si estás usando dos conjuntos de UV en lugar de solo uno, CADA uno de los vértices de la malla ahora tiene dos veces la cantidad de coordenadas UV. Por cada vértice, estás guardando dos números de punto flotante extra, y también estás subiéndolos al GPU al renderizar.

Cómo crear prefabs

Unity se utiliza para generar las coordenadas y el lightmap, con la funcionalidad de light baking normal. El motor escribirá las coordenadas UV para el lightmap en el segundo canal UV del modelo. Es importante señalar que el conjunto principal de coordenadas UV no puede utilizarse para esto, debido a que el modelo tiene que desempaquetarse.

Imagina una caja que emplea la misma textura para cada uno de sus lados: Cada uno de los lados de la caja tiene las mismas coordenadas UV, debido a que reutilizan la misma textura. Pero esto no funciona para un objeto lightmapped, debido a que cada lado de la caja es alcanzado por las luces y sombras de manera individual. Cada lado necesita su propio espacio en el lightmap con sus datos de iluminación individuales. Por lo tanto, requieren un conjunto nuevo de UV.

Entonces, para configurar un nuevo prefab con bakeado de luces, todo lo que necesitamos hacer es guardar tanto la textura como sus coordenadas de manera que estas no se pierdan y copiarlas en el prefab.

Después de realizar el bakeado de las luces, ejecutamos un script que pasa por todas las mallas de la escena y escribe las coordenadas UV en el canal UV2 real de la malla, aplicando los valores de desfase y modificación de la escala.

El código para modificar las mallas es relativamente sencillo:

Mesh meshToModify = GetComponent().sharedMesh;
Vector4 lightmapOffsetAndScale = GetComponent().lightmapScaleOffset;

Vector2[] modifiedUV2s = meshToModify.uv2;
for (int i = 0; i < meshToModify.uv2.Length; i++)
{
    modifiedUV2s[i] = new Vector2(meshToModify.uv2[i].x * lightmapOffsetAndScale.x +
    lightmapOffsetAndScale.z, meshToModify.uv2[i].y * lightmapOffsetAndScale.y +
    lightmapOffsetAndScale.w);
}
meshToModify.uv2 = modifiedUV2s;

Para ser un poco más específicos: Esto se hace con una copia de las mallas, y no con la original, debido a que vamos a hacer más optimizaciones a tus mallas durante el proceso de bakeado.

Las copias se generan automáticamente, se guardan en un prefab y se les asigna un material nuevo con un shader personalizado y el recientemente creado lightmap. Esto deja intactas nuestras mallas originales, y los prefabs con bakeado de luces se pueden utilizar inmediatamente.

Un shader personalizado con lightmap

Gracias a eso, el flujo de trabajo se vuelve muy sencillo. Para actualizar el estilo y aspecto de los gráficos, simplemente abre la escena adecuada, realiza todas las modificaciones que desees e inicia el proceso automatizado de bakeado y copia. Cuando este proceso haya finalizado, todo estará listo y el juego se iniciará utilizando los prefabs actualizados y las mallas con la iluminación actualizada.

La textura real del lightmap se agrega por acción de un shader personalizado, que aplica el lightmap como una segunda textura de luces al modelo durante el renderizado.

El shader es muy sencillo y corto, y además de aplicar el color y lightmap, calcula un efecto especular/de brillo falso y barato.

Shader "Custom/LightmappedPrefabWithSpec"
{
  Properties
  {
    _MainTex("Base (RGB)", 2D) = "white" {}
    _Lightmap("Lightmap", 2D) = "white" {}
    _Specmap("Specmap", 2D) = "white" {}
    _SpecularAtt("Glossiness", Range(0.1, 2)) = 0.5
    _SpecularAmt("Specular", Range(0, 1)) = 0.5
  }

  SubShader
  {
    Tags{ "Queue" = "Geometry+1" }
    Pass
    {
      CGPROGRAM

      // Defining the name of the vertex shader
      #pragma vertex vert

      // Defining the name of the fragment shader
      #pragma fragment frag


      // Include some common helper functions,
      // specifically UnityObjectToClipPos and DecodeLightmap.
      #include "UnityCG.cginc"

      // Color Diffuse Map
      sampler2D _MainTex;
      // Tiling/Offset for _MainTex, used by TRANSFORM_TEX in vertex shader
      float4 _MainTex_ST;


      // Lightmap (created via Unity Lightbaking)
      sampler2D _Lightmap;
      // Tiling/Offset for _Lightmap, used by TRANSFORM_TEX in vertex shader
      float4 _Lightmap_ST;

      // Grayscale Map indicating which parts of the models have specular
      // Note: _Specmap_ST is not needed, as this map is using the same
      // UVs as for the _MainTex.
      sampler2D _Specmap;

      // This is the vertex shader input: position, UV0, UV1, normal
      // UV1 (= second UV channel) needed for the lightmap texture coordinates
      struct appdata
      {
        float4 vertex   : POSITION;
        float2 texcoord : TEXCOORD0;
        float2 texcoord1: TEXCOORD1;
        float3 normal: NORMAL;
      };

      // This is the data passed from the vertex to fragment shader
      struct v2f
      {
        float4 pos  : SV_POSITION; // position of the pixel
        float2 txuv : TEXCOORD0; // for accessing the diffuse color map
        float2 lmuv : TEXCOORD1; // for accessing the light map
        float3 normalDir : TEXCOORD2; // for fake specular
      };

      // This is the vertext shader, doing nothing special at all.
      // Most notably it is calculating the surface normal, because that
      // is needed for the fake specular lighting in the fragment shader.
      v2f vert(appdata v)
      {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.txuv = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // using _MainTex_ST
        o.lmuv = TRANSFORM_TEX(v.texcoord1.xy, _Lightmap); // using _Lightmap_ST

        // Calculating the normal of the vertex for the fragment shader
        float4x4 modelMatrixInverse = unity_WorldToObject;
        o.normalDir = normalize(mul(float4(v.normal, 0.0), modelMatrixInverse).xyz);

        return o;
      }

      uniform float _SpecularAtt;
      uniform float _SpecularAmt;

      // Fragment Shader
      half4 frag(v2f i) : COLOR
      {
        // Reading color directly from the diffuse texture, using first UV channel
        half4 col = tex2D(_MainTex, i.txuv.xy);
        // Reading specular (on/off) value from spec map texture
        half4 specVal = tex2D(_Specmap, i.txuv.xy);
        // Reading lightmap value from the lightmap texture
        half4 lm = tex2D(_Lightmap, i.lmuv.xy);

        // Fake specular light angle calculation with a hard-coded light direction
        half3 th = normalize(half3(0, 1, -0.25));
        float spec = max(0, dot(i.normalDir, th));

        // Adjusting by overall specular amount and glossyness (material parameters)
        spec = _SpecularAmt * pow(spec, 40.0 * _SpecularAtt);
        // We're just using red value of the specular texture, like a grayscale map,
        // although technically spec could be colored.
        // Example: float3 specCol = specVal * spec;
        spec = spec * specVal.r;

        // Calculating the final color of the pixel by bringing it all together
        col.rgb = min(half4(1,1,1,1), col.rgb * DecodeLightmap(lm) + col.rgb * spec);
        return col;
      }
      ENDCG
    }
  }
  Fallback "Diffuse"
}

Lo que sigue es una imagen de la configuración de un material con el uso del shader antes descrito:

Unity light baked prefabs for mobile Material setup MetalPopGames

(Configuración del material utilizando un shader personalizado)

Configuración y batching estático

En nuestro caso, tenemos cuatro escenas distintas con todos los prefabs configurados. Nuestro juego presenta distintos biomas, como las zonas tropicales, glaciales o el desierto, y dividimos nuestras escenas atendiendo a esa diversidad. Para tu juego, el número de escenas podría variar, dependiendo de tus necesidades.

Todos los prefabs que se utilizarán en una escena determinada comparten un lightmap único. Esto significa una sola textura extra, además de los prefabs que comparten un solo material.

Como consecuencia de ello, pudimos renderizar todos los modelos como estáticos y renderizar por lotes casi todo nuestro mundo en una sola draw call.

Unity light baked prefabs for mobile bake level MetalPopGames

(El nivel de bakeado en el editor de Unity)

Las escenas con bakeado de luces, en las que todos nuestros mosaicos/compilaciones están configuradas, cuentan con fuentes de luz adicionales para crear aspectos destacados localizados. Puedes colocar en las escenas configuradas todas las luces que necesites, ya que, de todos modos, todas estarán bakeadas.

El proceso de bakeado se maneja en un diálogo UI personalizado que se ocupa de todos los pasos necesarios. Garantiza lo siguiente:

  • The correct material is assigned to all meshes
  • Hides everything that doesn’t need to be baked during the process
  • Combines/bakes the meshes
  • Copies the UVs and creates prefabs
  • Names everything correctly and checks out the necessary files from the version control system

Unity light baked prefabs for mobile custom inspector MetalPopGames

(Inspector personalizado para un flujo de trabajo más sencillo)

Que los prefabs correctamente nombrados se creen fuera de las mallas de manera que el código del juego pueda cargarlos y utilizarlos directamente. Los metaarchivos también sufren cambios durante este proceso, lo que permite que las referencias a las mallas de los prefabs no se pierdan.

Este flujo de trabajo nos permite retocar nuestras compilaciones tanto como queramos, darles la iluminación que nos guste y, finalmente, dejar que el script se ocupe de todo.

Cuando volvemos a nuestra escena principal y ejecutamos el juego, simplemente funciona, no es necesaria la intervención manual ni otras actualizaciones.

Simulación de luz y bits dinámicos

Uno de los inconvenientes obvios de una escena en la que el 100 % de la iluminación está prebakeada es que es difícil tener objetos dinámicos o movimiento. Cualquier cosa que proyecte una sombra necesitaría el cálculo de luz y sombra en tiempo real, lo que, sin duda, nos gustaría evitar del todo. Pero sin objetos en movimiento, el entorno 3D tendría un aspecto estático y sin vida.

Por cierto, estábamos dispuestos a vivir con ciertas restricciones, ya que nuestra máxima prioridad era lograr buenos efectos visuales y un renderizado rápido. Para crear la impresión de una colonia o ciudad espacial viva y en movimiento, no era necesario que una gran cantidad de objetos se desplazara realmente, y la mayoría de ellos no requería necesariamente sombras, o por lo menos no se notaría su ausencia.

En nuestro caso, empezamos por dividir todos los elementos básicos de construcción de la ciudad en dos prefabs separados. Una parte estática, que contenía la mayoría de los vértices, todos los bits complejos de nuestras mallas, y una parte dinámica, que contenía la menor cantidad posible de vértices.

Las partes dinámicas de un prefab son bits animados colocados por encima de las partes estáticas. No tienen ningún bakeado de luces, y utilizamos un shader de simulación de iluminación muy rápido y barato para crear la ilusión de que el objeto estaba iluminado dinámicamente.

Shader "Custom/FakeLighting"
{
  Properties
  {
    _Color ("Color", Color) = (1,1,1,1)
    _Brightness ("Brightness", Range(0,1)) = 0.4
    _MainTex ("Albedo (RGB)", 2D) = "white" {}
  }

  SubShader
  {
    Tags { "RenderType" = "Opaque" }
    LOD 200
    Pass
    {

      CGPROGRAM

      // Define name of vertex shader
      #pragma vertex vert
      // Define name of fragment shader
      #pragma fragment frag

      // Include some common helper functions, such as UnityObjectToClipPos
      #include "UnityCG.cginc"

      float4 _Color;
      float _Brightness;

      // Color Diffuse Map
      sampler2D _MainTex;
      // Tiling/Offset for _MainTex, used by TRANSFORM_TEX in vertex shader
      float4 _MainTex_ST;

      // This is the vertex shader input: position, UV0, UV1, normal
      struct appdata
      {
        float4 vertex   : POSITION;
        float2 texcoord : TEXCOORD0;
        float3 normal: NORMAL;
      };

      // This is the data passed from the vertex to fragment shader
      struct v2f
      {
        float4 pos  : SV_POSITION;
        float2 txuv : TEXCOORD0;
        float3 normalDir : TEXCOORD2;
      };

      // This is the vertex shader
      v2f vert(appdata v)
      {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.txuv = TRANSFORM_TEX(v.texcoord.xy,_MainTex);

        // Calculating normal so it can be used for fake lighting
        // in the fragment shader
        float4x4 modelMatrixInverse = unity_WorldToObject;
        o.normalDir = normalize(mul(float4(v.normal, 0.0), modelMatrixInverse).xyz);

        return o;
      }

      // This is the fragment shader
      half4 frag(v2f i) : COLOR
      {
        // Reading color from diffuse texture
        half4 col = tex2D(_MainTex, i.txuv.xy);

        // Using hard-coded light direction for fake lighting
        half3 th = normalize(half3(0.25, 1, -0.25));
        // Using hard-coded light direction for fake specular
        // This matches the value inside the LightmappedPrefabWithSpec shader
        half3 sth = normalize(half3(0, 1, -0.25));

        // Fake lighting
        float lightVal = max(0, dot (i.normalDir, th));
        float lightScale = 0.75;
        lightVal = lightVal * lightScale;

        // Fake spec
        float spec = max(0, dot(i.normalDir, sth));
        float specScale = 0.65;
        float specAtt = 0.65;
        spec = specScale * pow (spec, 40.0 * specAtt);

        // Add in a general brightness (similar to ambient/gamma) and then
        // calculate the final color of the pixel
        col.rgb = min(half4(1,1,1,1), col.rgb * _Brightness +
                      col.rgb * lightVal * _Color + col.rgb * spec);
        return col;
      }

      ENDCG
    }
  }
  FallBack "Diffuse"
}

Los objetos o bien no tenían ninguna sombra o creamos una sombra falsa como parte del bit dinámico. La mayoría de nuestras superficies son planas, por lo que, en nuestro caso, eso no fue un gran obstáculo.

Unity light baked prefabs for mobile Galactic Colonies MetalPopGames

(Construcciones que utilizan objetos dinámicos adicionales)

No hay sombras en los bits dinámicos, pero esto apenas se nota, a menos que sepas buscarlo. La iluminación de los prefabs dinámicos es completamente falsa: no hay iluminación en tiempo real.

El primer atajo barato que tomamos fue predefinir en el código la posición de nuestra fuente de luz (el sol) al shader de iluminación falsa. Es una variable menos que el shader debe buscar y llenar dinámicamente del mundo.

Siempre es más rápido trabajar con un valor constante y no con uno dinámico. Esto nos proporcionó una iluminación básica, los lados claro y oscuro de las mallas.

Especular/brillo

Para que las cosas tengan un aspecto algo más brillante, agregamos un cálculo especular/de brillo falso a los shaders, tanto para los objetos dinámicos como para los estáticos. Los reflejos especulares no solo ayudan a crear un aspecto metálico, sino que contribuyen también a trasladar la curvatura de una superficie.

Puesto que los resaltados especulares son una forma de reflejo, se requiere el ángulo de la cámara y la fuente de luz correspondiente para su cálculo correcto. Cuando la cámara se mueve o rota, el efecto especular cambia. Cualquier cálculo del shader requeriría el acceso a la posición de la cámara y a todas las fuentes de luz de la escena.

Sin embargo, en nuestro juego, solo tenemos una fuente de luz que nos interesa utilizar para el efecto especular: el sol. En nuestro caso, el sol no se mueve nunca y puede considerarse una luz direccional. Podemos simplificar mucho el shader utilizando una sola luz, y asumiendo simplemente una posición fija y un ángulo entrante para este.

Aún mejor, nuestra cámara en Galactic Colonies muestra la escena desde una perspectiva aérea, como la mayoría de los juegos de construcción de ciudades. La cámara puede inclinarse un poco, y hacer acercamientos y alejamientos, pero no puede rotar alrededor del eje superior.

En general, siempre mira al entorno desde arriba. Para simular una mirada especular barata, aparentamos que la cámara estaba completamente fija, y que el ángulo entre la cámara y la luz era siempre el mismo.

De esta forma podríamos predefinir en el código un valor constante al shader y lograr así un efecto especular/de brillo barato.

Unity light baked prefabs for mobile Galactic Colonies MetalPopGames

(Efecto especular/de brillo falso)

Utilizar un ángulo fijo para el efecto especular es, por cierto, técnicamente incorrecto, pero es prácticamente imposible reconocer con certeza la diferencia en tanto el ángulo de la cámara no cambie mucho. Para el jugador, la escena seguirá teniendo una apariencia correcta, lo que constituye la razón de ser de la iluminación en tiempo real

Iluminar un entorno en un videojuego en tiempo real tiene y ha tenido que ver siempre más con el hecho de parecer visualmente correcto y no con ser realmente una simulación físicamente correcta.

Debido a que casi todas nuestras mallas comparten un material, con muchos detalles que provienen del lightmap y los vértices, agregamos un mapa de texturas especular, para indicarle al shader cuándo y dónde aplicar el valor especificado, y en qué medida. Se accede a la textura utilizando el canal UV primario, por lo que no se necesita un conjunto adicional de coordenadas. Y debido a que no contiene mucho detalle, es de muy baja resolución y apenas consume espacio.

Para algunos de nuestros bits dinámicos más pequeños con un conteo bajo de vértices, podríamos incluso hacer uso del batching dinámico automático de Unity, acelerando más el renderizado.

Prefabs con luces bakeadas encima de los prefabs con luces bakeadas

Todas las sombras bakeadas pueden algunas veces crear problemas nuevos, especialmente cuando se trabaja con construcciones relativamente modulares. En un caso, teníamos un depósito que el jugador podía construir que mostraba el tipo de artículos almacenados allí en el edificio real.

Esto genera problemas ya que tenemos un objeto con bakeado de luces encima de un objeto con bakeado de luces. ¡Bakeado de luces sobre bakeado de luces!

Hicimos frente al problema aplicando otro truco barato:

  • The surface where the additional object was to be added had to be flat and use a specific gray color matching the base building
  • In return for that trade-off, we could bake the objects on a smaller flat surface and place it on top of the area with just a slight offset
  • Lights, highlights, colored glow and shadows were all baked into the tile

Unity light baked prefabs for mobile Galactic Colonies MetalPopGames

(Los objetos bakeados tiene un brillo azul y proyectan una sombra)

Bakeado de luces barato y eficiente, si puedes vivir con las restricciones que esto implica

Al crear y bakear nuestros prefabs podemos tener mapas enormes con cientos de edificios y al mismo tiempo mantener un recuento de draw calls súper bajo. Todo el mundo de nuestro juego se renderiza con solo un material y estamos en un punto donde la UI usa más draw calls que el mundo de nuestro juego.

Esto nos deja mucho lugar para añadir más cosas a nuestro mundo, como partículas, efectos de clima y otros elementos que son atractivos a la vista.

De esta manera inclusive los jugadores que tienen dispositivos antiguos pueden construir ciudades grandes con cientos de edificios y mantener una framerate estable de 60 fps.

Unity light baked prefabs for mobile Galactic Colonies MetalPopGames

(Una colonia creada por un jugador con prefabs a los que se ha aplicado el bakeado de luces)

Sigue con lo que funciona para tu juego

Nuestra solución para renderizar planetas grandes con ciudades de intensa actividad se personaliza de acuerdo con nuestro juego. Nos quedamos con lo que funciona para nosotros. Utilizamos nuestros ángulos de cámara limitados, y una sola fuente de luz en la esena para reducir muchísimo la complejidad del shader. Esto nos permitió también reducir esquinas en lo que respecta a la iluminación de objetos dinámicos y a calcular el especular con un ángulo fijo. Reducimos la cantidad de objetos dinámicos y agrupamos en lotes el resto aplicando la estadística.

Y, por supuesto, nuestro estilo visual basado únicamente en el color y de baja poligonización hace que las cosas sean mucho más sencillas, ya que permite compartir materiales entre casi todos los objetos, lo que hace que el procesamiento por lotes sea tanto estático como dinámico.

Lo más probable es que no podrás transferir todas las soluciones que se describen aquí a tu juego exactamente de la misma manera en que están, y esto está bien, ya que no se supone que puedas hacerlo. Son puntos iniciales sobre los que puedes basarte para encontrar la mejor manera de optimizar tu propio juego. Descubre qué restricciones y limitaciones puede aceptar tu juego, y utilízalas para hace que tu juego se ejecute más rápido.

Mas recursos

¡Debemos saberlo! ¿Te gustó este contenido?

Sí. Que sigan llegando Me da igual. Podría ser mejor
Lo tengo

Usamos cookies para brindarte la mejor experiencia en nuestro sitio web. Visita nuestra página de política de cookies si deseas más información.