Resumen de la experiencia de desarrollo de Unity: base 1.C#

1. Conceptos básicos del lenguaje C#

1. Tipos de valor y tipos de referencia de C#

Los tipos de valor se asignan en la pila, tienen un alto rendimiento y se heredan de ValueType. Una estructura es un tipo de valor, struct.

El tipo de referencia se asigna en el montón y la nueva del tipo de referencia generará GCAlloc. Heredado de Objeto.

2, ArrayList, Lista

El tipo ArrayList es objeto. Se puede colocar cualquier objeto, y habrá operaciones de boxing y unboxing. Boxear es convertir un tipo de valor en un tipo de referencia (objeto). Unboxing es convertir un tipo de referencia en un tipo de valor.

La lista es genérica y tiene un mejor rendimiento. Uso recomendado.

La memoria de ArrayList y List son continuas y ambas son estructuras de matriz. Debe distinguirse de LinkedList.

3. Recorra la lista para eliminar elementos La forma común es atravesar en orden inverso para eliminar el elemento especificado. Si se trata de un recorrido de orden positivo, después de eliminar el elemento, el elemento detrás de la Lista se mueve hacia adelante y luego el iterador salta al siguiente elemento, luego se omitirá el siguiente elemento del elemento eliminado. El recorrido de orden inverso no tendrá este problema.

4. Hacer buen uso de las estructuras de datos. Por ejemplo, nuestra lista de jugadores guarda dos estructuras de datos, una estructura de lista para recorrido y una estructura de diccionario para consulta.

Cuando consultamos con frecuencia, hacer juicios o consultas a través de HashSet o Dictionary mejorará mucho el rendimiento.

En términos del orden de magnitud de los juegos normales, LinkedList es casi innecesario de usar. Incluso si es necesario insertar y eliminar elementos, List generalmente es más rápido. A menos que haya cientos de objetos en la lista.

2. Hilos, corrutinas y procesos

(1) Hilo

1. Multi-threading puede hacer un buen uso de multi-core y mejorar la eficiencia de ejecución.

2. El cálculo de subprocesos no bloqueará el subproceso principal, por lo que algunos cálculos complejos se pueden colocar en subprocesos para reducir el retraso del juego.

3. Hay algunos códigos de bloqueo que deben colocarse en subprocesos, como código de descarga, comunicación de socket, etc.

4. Existen muchas restricciones en los subprocesos y no se puede llamar a la mayoría de las API de Unity.

5. Escribir código de subprocesos múltiples requiere atención a la sincronización de subprocesos. Un poco de descuido puede provocar interbloqueos o errores lógicos o incluso flashbacks debido a la inseguridad del subproceso.

6. Si se crea una gran cantidad de subprocesos, el conjunto de subprocesos se puede utilizar para la optimización. Los servidores son relativamente comunes. Los clientes usan menos.

7. Demasiados subprocesos pueden causar cambios frecuentes de contexto. Guarda el estado de un subproceso, luego procesa las cosas de otro subproceso y luego restaura el estado del primer subproceso para continuar procesando ese subproceso. Esta operación es un proceso intensivo en recursos y, por lo tanto, debe evitarse en la medida de lo posible.

(2) rutina

1. La ventaja de las corrutinas es que la lógica asíncrona se puede procesar en forma de código síncrono, evitando el código de devolución de llamada complejo.

2. Aunque la rutina también se ejecuta de forma asíncrona, se ejecuta en el subproceso principal. Por lo tanto, aún es imposible realizar operaciones que puedan causar que el subproceso principal se congele, como instanciar una gran cantidad de objetos. La operación correcta debe ejecutarse en cuadros.

3. Direct StartCoroutine todavía tiene una cierta sobrecarga, y las corrutinas deben fusionarse tanto como sea posible. Por ejemplo, use CoroutineManager.

(3) Proceso

1. Mutex es un proceso mutex. Cuando una parte de la lógica solo puede ser ejecutada por un proceso, puede usar Mutex para controlarla.

(4) bloquear

1, bloqueo (objeto) {xxxxx}

2. Por ejemplo, en la comunicación de red, para un búfer, el subproceso principal continúa leyendo y el subproceso continúa escribiendo. Si no lo bloquea al leer, puede entrar en conflicto con el subproceso secundario, lo que puede generar resultados incorrectos o confusión de datos.

3. El estado de Lua no es seguro para subprocesos. El estado lua creado por el subproceso principal, si llamamos a su interfaz en el subproceso, como llamar a una función lua, la pila puede estar desordenada. Es muy probable que provoque que el juego se bloquee. El error que cometimos antes es notificar a lua si la red es anormal en el subproceso de devolución de llamada de socket.BeginReceive. Esta operación es muy peligrosa.

(v) Sistema de trabajo

1. Este es un código de subprocesos múltiples programado por Unity. más eficiente. Y no necesita preocuparse por la seguridad de los subprocesos.

2, trabajo

(6), SEC

1、Entidad–Componente–Sistema

La entidad tiene el componente

El componente es un contenedor de datos.

El sistema maneja el Componente, que puede entenderse como una colección de funciones. Nada que ver con el objeto.

2. Overwatch usa el sistema ECS para implementar convenientemente la reversión de batalla.

3, Sistema de componentes, Matriz nativa

4. El modo ECS de Unity es mejor para la alineación de datos, lo que conduce a aciertos en la memoria caché de la CPU. El rendimiento es mejor.

(7), PUNTOS

1, ECS + sistema de trabajo + ráfaga

3. Otros

1, XML

1.1. Parsing Xml se divide en dos tipos: modelo de objeto de documento (DOM) y modelo de flujo.

1.2. El primero es relativamente simple de escribir y puede leer aleatoriamente el contenido en xml. La desventaja es que consume más rendimiento y necesita cargar todo el archivo xml en la memoria al mismo tiempo.

1.3 El modelo de flujo es de solo lectura y solo puede leer el contenido de xml secuencialmente. La ventaja es un buen rendimiento.

2, Json

2.1 La ventaja es la velocidad rápida. La desventaja es que las anotaciones no son compatibles.

2.2 Cuando analizamos la configuración json, la mayoría de ellos usan directamente la API JsonUtility proporcionada por Unity. Esta interfaz es rápida. La desventaja es que el formato es fijo y no se puede analizar en ninguna estructura como Diccionario.

2.3 Cuando necesite procesar cualquier estructura devuelta por los datos del servidor, use LitJson o MiniJson.

3 、 YAML

3.1 Todos los formatos de texto serializados en Unity son YAML. Como prefabricados, materiales, archivos de escena .unity, etc. Establecer Forzar texto en el editor puede forzar que estas serializaciones estén en formato de texto. De lo contrario, puede estar en formato binario.

4. Cifrado AES

4.1 El estándar de cifrado avanzado (AES, Advanced Encryption Standard) es un algoritmo de cifrado simétrico popular. La clave secreta utilizada para el cifrado y descifrado del algoritmo de cifrado simétrico es la misma. El cifrado DES también es común, pero el cifrado DES es relativamente lento y ya no se recomienda.

4.2 De acuerdo con la clave secreta y el vector, el texto sin formato se cifra en texto cifrado.Después de recibir el texto cifrado, el servidor lo descifra en texto sin formato de acuerdo con la misma clave secreta y vector.

4.3 La velocidad del cifrado AES es muy rápida. Nuestra comunicación de mensajes y el cifrado de archivos Lua utilizan el cifrado AES.

4.4 También hay un algoritmo de cifrado asimétrico. La clave secreta se divide en clave pública y clave privada. Uno común es el algoritmo RSA. La ventaja es que es seguro y extremadamente difícil de descifrar. La desventaja es que es más lento. Por lo tanto, no hay muchos lugares donde se use RSA en juegos normales. Sin embargo, se utilizan certificados de cifrado similares en servidores de notificaciones push, servidores Https y otros lugares. Al mismo tiempo, nuestra autenticación de usuario git de uso común también es este algoritmo. Nuestra clave pública está configurada en el servidor y la clave privada se almacena localmente. La clave privada cifra y la clave pública descifra.

5. Compresión LZ4

5.1 Los algoritmos de compresión comúnmente utilizados incluyen Deflate (zlib, zip), lzma (7z) y lz4. También hay algunas características del algoritmo de compresión que se repiten con estos tres, por lo que no se dará ninguna explicación.

5.2.zlib es una biblioteca de algoritmos de compresión muy común. Casi todos los motores de juegos o juegos lo usarán. Implementa el algoritmo Deflate. Los formatos de archivo comprimido comunes, como zip, tar.gz, etc., utilizan principalmente este algoritmo.

Se complementa que png también utiliza este algoritmo de compresión. De hecho, zlib se usó originalmente para la compresión de png. PNG es un formato de compresión sin pérdidas. Finalmente, el tamaño de la imagen es comprimido por zlib.

5.3, lzma, que es el formato 7z, tiene una relación de compresión muy alta. La desventaja es que la velocidad de compresión y descompresión es relativamente lenta. El formato opcional del paquete ab en Unity incluye lzma, y ​​las versiones anteriores a la 5.0 solo tienen dos opciones: lzma y sin compresión. La desventaja de usar lzma en el paquete ab de Unity es que el paquete ab se descomprimirá en la memoria como un todo. Si un paquete ab tiene 100 mb, la memoria descomprimida debe ser de al menos 100 mb.

5.4, ​​lz4. Su ventaja es que es extremadamente rápido. Especialmente la velocidad de descompresión. Puede alcanzar varias veces a docenas de veces la de zlib. Aunque no se usa comúnmente como formato de archivo. Pero es ampliamente utilizado en los juegos. Después de cifrar el archivo lua, volvimos a realizar la compresión lz4. El formato opcional ChunkBaseCompression del paquete ab de Unity es el formato de lz4. Además de ser rápido, la ventaja es que la carga del paquete ab en sí no consume memoria, solo el consumo de memoria del encabezado del archivo. Qué recurso se utiliza y qué parte descomprimir. El diseño de nuestro módulo de gestión de recursos se basa en gran medida en esta función.

Si no hay una función lz4, entonces podemos elegir una solución: el paquete ab elige un formato sin comprimir. Luego use zip para comprimirlo en el apk al empaquetar. Cuando el juego se inicie por primera vez, extráigalo a un directorio de escritura. Muchos juegos solían elegir esta solución. Las principales consideraciones son la velocidad de carga y el uso de la memoria.

6. Codificación de caracteres

6.1, código ASCII. 0-128 representa nuestras letras, números, etc. comunes en inglés.

6.2, estándar ANSI. Solo hay 128 códigos ASCII y solo 256 códigos de extensión. Obviamente es imposible representar todos los textos del mundo.

El código del estándar ANSI formulado por China es GB2312. Contiene más de 7000 caracteres y símbolos chinos.

GBK es totalmente compatible con GB2312 y contiene más de 30 000 caracteres chinos.

GB18030 amplía aún más los caracteres chinos sobre la base de GBK, agregando tibetano, mongol y otros idiomas minoritarios.

6.3, estándar Unicode. Según el estándar ANSI, cada país tiene un significado de codificación diferente. Y Unicode es un estándar unificado en todo el mundo. Contiene todos los textos de todo el mundo.

6.4, MBCS (Sistema de caracteres de varios bytes). Juego de caracteres multibyte. El código ASCII ocupa un byte y la gran mayoría de los caracteres chinos ocupan dos bytes.

6.5, página de códigos. página de código. Este también es un concepto muy común. Al configurar la página de códigos, dígale al sistema operativo qué país o región es el estándar ANSI. Por ejemplo, Windows estipula que la página de códigos de 65001 representa UTF-8 y la página de códigos representada por 936 representa GBK.

6.6, UTF-8, UTF-16, UTF-32

Unicode es un conjunto de caracteres estándar. Existen diferentes implementaciones sobre cómo codificar en una computadora específica.

UTF-8 es un byte variable, compatible con la parte del código ASCII. Los caracteres chinos generalmente ocupan 3 bytes, hasta 4 bytes.

UTF-16 utiliza 2 bytes o 4 bytes para representar un carácter. Los caracteres de uso común, como los códigos ASCII y la mayoría de los caracteres chinos, son de 2 bytes.

UTF-32, que es básicamente consistente con la tabla de códigos Unicode, usa 4 bytes para representar un carácter.

6.7 En Windows, wchat_t representa un carácter Unicode, que tiene 2 bytes, lo que significa que el sistema Windows adopta el esquema UTF-16.

Varias API de Windows, como SetWindowTitle y SetWindowTitleW, corresponden a interfaces ANSI e interfaces Unicode respectivamente.

En el entorno chino, el conjunto de caracteres predeterminado del sistema Windows es GBK. Por ejemplo, el nombre del archivo que creamos debe estar codificado en GBK. Si el código está escrito en codificación UTF-8, entonces el archivo creado puede tener caracteres ilegibles. Esta situación es común al convertir archivos en Windows y Mac.

De manera similar, la página de códigos predeterminada de la ventana cmd es 936, el resultado de salida de cmd es la codificación GBK y la codificación de caracteres predeterminada de Jenkins es UTF-8, por lo que cuando Jenkins ejecuta bat en Windows, el resultado de salida puede ser distorsionado. La solución es llamar a chcp 65001 para cambiar la página de códigos a UTF-8 antes de ejecutar el comando bat.

6.8 En Linux, el conjunto de caracteres predeterminado es UTF-8. El wchar_t correspondiente es de 4 bytes. Esto se debe a que la codificación de caracteres elegida por Linux es UTF-8 en lugar de UTF-16. Para UTF-16 seleccionado por Windows, la mayoría de los caracteres pueden representarse con 2 bytes, y los caracteres que deben representarse con 4 bytes son muy raros. Cuando aparece un texto de 4 bytes, en realidad son 2 wchar_t para representar un texto. En este caso, la mayoría de las funciones, como calcular la longitud, son problemáticas, pero debido a que esta situación es extremadamente rara, no hay mayor problema.

La codificación UTF-8 utilizada por Linux, la mayoría de los caracteres chinos son 3 bytes, es imposible usar 2 bytes para representar wchart_t para manejar la mayor parte del texto como Windows, por lo que wchar_t solo puede usar 4 bytes en Linux para representar.

6.9 Es relativamente simple lidiar con la codificación de caracteres en C#, solo use la clase Encoding. Sin embargo, debe tenerse en cuenta que si desea convertir caracteres chinos de UTF-8 a GBK, debe copiar varios archivos DLL al proyecto y configurarlos para que no se recorten en link.xml. Entonces se puede llamar normalmente.

I18N.dll I18N.Oeste.dll I18N.CJK.dll。

Debido a que usamos Encoding.GetEncoding(936) en nuestro código para obtener el objeto de conversión, si no está configurado en link.xml, estos archivos DLL se eliminarán porque no se les hace referencia.

6.10 Finalmente, agregue que la codificación de caracteres de Lua es UTF-8. Cuando C# llama a la interfaz de C++, se puede especificar CharSet para indicar la codificación de caracteres de los parámetros que pasamos. Por ejemplo,

[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]

7. Orden de bytes

7.1 El orden de bytes es un tipo que solo ocupa más de 1 byte en la memoria Cómo almacenar datos en la memoria.

Little Endian, Little Endian. Los datos de byte bajo se almacenan en la dirección baja de la memoria. Nuestras plataformas móviles y de PC comunes son todas little endian.

Big Endian, Big Endian. Los datos de byte alto se almacenan en la dirección baja. Muchas plataformas integradas son big endian.

7.2 Suponga que la dirección de crecimiento de la dirección de memoria es de izquierda a derecha, de menor a mayor.

Por ejemplo, \n en UTF-16, los datos son 0x000D, 00 es el dígito superior y el número es más importante. Imagine que 1 en 102 es el dígito superior, que es más importante.

El diseño de la memoria little endian es 0D00 y el diseño de la memoria big endian es 000D. La correspondencia se puede ver en la descripción anterior.

Este conocimiento es útil cuando observamos la codificación binaria de un archivo. El orden de visualización de izquierda a derecha puede entenderse como el orden de crecimiento de las direcciones de memoria.

7.3 UTF-16 significa que 2 bytes representan 1 carácter. También puede ser big endian o little endian. Por lo tanto, se estipula agregar un identificador al encabezado del archivo para explicar el orden de bytes del archivo. Esto es BOM (marca de orden de bytes).

FE FF significa UTF-16BE, que es big endian.

FF FE significa UTF-16LE, que es un archivo little-endian.

Hay otras etiquetas BOM de UTF-32, pero básicamente no se usan. Estas dos etiquetas de UTF-16 son las más utilizadas.

7.4, UTF-8 también tiene un encabezado BOM, EF BB BF. Sin embargo, esto lo determina la propia Microsoft. De hecho, UTF-8 es lo mismo que GBK, y no hay ningún problema de orden de bytes. Todas son codificaciones de varios bytes. Por lo tanto, algunos idiomas, como el idioma Go, no admitían archivos de origen codificados en UTF-8 con encabezados BOM.

7.6 Nuestros archivos Lua están en formato UTF-8 sin BOM. Los archivos fuente de C# y C++ están en formato UTF-8 con BOM.

Se utiliza la codificación UTF-8 porque VSCode o Sublime admiten el formato UTF-8 de forma predeterminada.Si el código fuente está en formato GBK, debe convertirse para mostrarse normalmente, de lo contrario, se distorsionará. Además, si el archivo de origen está codificado en GBK, se mostrará con caracteres ilegibles en Mac, por lo que UTF-8 es más común.

Los archivos Lua no tienen un encabezado BOM porque Luajit admite archivos lua con encabezados BOM, pero Lua5.1 nativo no admite archivos con encabezados BOM. Entonces, si la versión de Lua que usamos es lua nativa, el encabezado BOM debe eliminarse antes de cargar el código. La forma es eliminar estos tres bytes si se determina que los primeros tres bytes del archivo son EF BB BF.

Los archivos C++ tienen encabezados BOM porque nuestras herramientas de visualización y compilación de C# y C++ son propias de Microsoft. Dado que usamos el formato UTF-8 sin BOM, el propio compilador de Microsoft puede compilar e informar errores cuando se encuentre con chino.

8. Salto de línea

Aunque esto es muy simple, es muy común y tiene algunas trampas, así que lo mencionaré por separado.

8.1 El carácter de retorno de carro \r tiene un código ASCII de 13, que es 0D.

El carácter de nueva línea \n tiene un código ASCII de 10, que es 0A.

8.2 El carácter de nueva línea en Windows es \r\n, expresado como CRLF

El carácter de nueva línea en Linux es \n, expresado como LF

El carácter de nueva línea en Mac es \r, expresado como CR

Los saltos de línea son diferentes en diferentes sistemas, por lo que algunos archivos de texto editados en Linux no tendrán saltos de línea cuando se abren con un editor de texto en Windows. VSCode también puede modificar el carácter de nueva línea del archivo actual.

8.3 De forma predeterminada, Git convertirá los caracteres de nueva línea al finalizar la compra. Al enviar para almacenamiento, también se establecerá uniformemente un carácter de nueva línea. Esto a menudo tiene algunos hoyos. Entonces, cuando configuremos el entorno de desarrollo, configuraremos git y desactivaremos la función de autoCrlf. Al verificar y confirmar el código, permanece como está.

A veces, nuestro nuevo almacén no desactiva autoCrlf. Si hay algunos archivos de copia u otras operaciones posteriores, puede parecer que el archivo no se ha modificado, pero indicará que el archivo ha cambiado cuando se envía, y no lo hará. mostrar cualquier contenido modificado cuando se abre. Esto se debe a que, aunque el contenido del archivo no ha cambiado, el carácter de nueva línea sí lo ha hecho.

8.4 Debido a que todos estamos desarrollando bajo Windows, los caracteres de nueva línea de nuestro código y configuración se unifican como CRLF.

8.5 Unity puede establecer saltos de línea en Configuración del proyecto - Editor. El valor predeterminado parece ser LF.

Supongo que te gusta

Origin blog.csdn.net/s10141303/article/details/127305571
Recomendado
Clasificación