Mi historia de crear una aplicación motivadora (iOS y Android) para una hija con una hija en Unity y C #

La historia de la creación de una aplicación que permite a los niños ganar dinero pensando


Esta es la primera parte de la historia (mezclada con la historia de mis errores y sus soluciones) sobre cómo yo (aproximadamente dos años en mi tiempo libre) desarrollé una aplicación móvil para iOS y Android que motivaría a mi hija a resolver ejemplos matemáticos para que ella logrado automatismo en los conceptos básicos de aritmética (almacén número 10 o placa de multiplicación). Como resultado, resultó una aplicación que le permite a un niño ganar dinero con su mente.


Utilicé el motor de Unity y el lenguaje C #, así como un conjunto adicional de software como Photohsop o Audacity (para crear sonidos).


El bosquejo de la historia (primera parte)


  • Antecedentes
  • Sobre monetización
  • ¿Por qué la unidad?
  • Acerca de los objetos programables
  • Acerca del complemento Anima2D
  • Acerca de la localización magra
  • Sobre iTween
  • Sobre Unity Analitics
  • Sobre Visual Studio
  • Referencias

Fondo y escarabajos de Colorado


Mi experiencia previa ha sido de varios años en 3D y, más recientemente, en el desarrollo de varios juegos independientes junto con un programador, donde actué principalmente como diseñador y artista (muy raramente escribiendo algunos guiones elementales en C #). Aunque estoy familiarizado con la programación de primera mano (solía golpear Basic en la escuela y me metí en C / C ++ en la universidad).


Toda esta épica comenzó con un problema. Vi que al resolver problemas escolares, mi hija "se atasca" y se equivoca, no por algo complicado, sino por la base. Decidí que sería divertido si le escribía una aplicación divertida en la que resolvería ejemplos en forma de juego (ganando experiencia de esta manera y logrando automatismo). Y para motivarla aún más, me aseguré de que recibiera dinero por la decisión correcta de los ejemplos (la aplicación calculó la cantidad de dinero en función de la cantidad de respuestas correctas, y luego deduje la cantidad requerida, pagando en efectivo a mi hija).


Aunque ... esta historia comenzó incluso antes. Primero, le hice a mi hija una aplicación que pagaba dinero por aprender palabras en inglés. Pero esta aplicación resultó ser mucho más difícil de implementar, por lo que se volvió conveniente no solo para mí (el desarrollador) y mi hija, sino también para otros padres. Por lo tanto, la aplicación en inglés sigue siendo un desarrollo interno.


Elegí el dinero como motivador, porque era el más fácil de implementar. Y también por los recuerdos personales de mi infancia: me encantaba hacer todo por dinero. Incluso si era un trabajo muy tedioso, como recolectar escarabajos de la patata de Colorado. Recuerdo haber recogido su frasco de medio litro (mis padres me pagaron 1 kopeck por cada error). Así que pensé que desde mi hija (no lo comprobé, pero estoy bastante segura de eso), entonces debería gustarle. Bueno, al final no perdí.


Mi esposa está en contra de alentar a los niños a hacer algo por dinero. Pero mi argumento la convenció un poco: en el caso de esta solicitud, el niño recibe dinero no por lo que debe hacer (como la tarea), sino por el hecho de que además practica las matemáticas en su tiempo libre.


La segunda razón por la que comencé a desarrollar fue porque quería practicar la programación. "Convertirse en programador" ha sido mi sueño preciado desde los primeros grados de la escuela (justo después del sueño de "convertirse en un científico", pero antes del sueño de "hacer dibujos animados").


Monetización y placer


Inicialmente, hice esta aplicación (el nombre de trabajo era Math4Ami) exclusivamente para iPod touch 5 de mi hija. Ni siquiera pensé en hacer que la aplicación estuviera disponible en todos los dispositivos iOS o publicarla para todos y, especialmente, no pensé en lanzarla para la plataforma Android (escuché muchas historias de miedo de los desarrolladores de iOS, además, no tengo nada para probar la versión de Android).


Hace algún tiempo, tuve la tentación de publicarlo en la AppStore (me atrajo la idea de que tendría mi propia aplicación en la tienda de Apple y todos podrían verla).


Pensé que en un mes me las arreglaré. Después de todo, toda la funcionalidad estaba lista, lo único que quedaba era hacerlo funcionar para todas las relaciones de aspecto de la pantalla y comprensible no solo para mí, sino también para otros padres. Y ahora, seis meses después, lo publiqué en AppStore y Google Play.


Inicialmente decidí hacer Math4Ami completamente gratis sin una pizca de monetización. Hay varias razones para esta decisión.


El primero Como ya entendió, inicialmente lo hice gratuito para mi hija y no quería arruinar nada al "final" del desarrollo.


El segundo Decidí que sería un desarrollo para mi propio placer. Ya tengo una experiencia similar: hago un blog por placer (que inicialmente solo comía dinero: dinero para alojamiento y un nombre de dominio, tiempo para escribir artículos y su promoción). Mi lógica era esta: si pago dinero para visitar el parque acuático, compro un libro o un helado para divertirme, ¿por qué no puedo pagar dinero para hospedar, un nombre de dominio o membresía en el Programa de Desarrolladores de Apple, si también me da placer?


El tercero, en aras de una audiencia más amplia, que se reduciría significativamente, haciendo que la aplicación sea paga (como hacen muchos desarrolladores de aplicaciones para niños). Descarté otros tipos de monetización por los motivos que se describen a continuación.


No soporto la publicidad en el juego: no me gusta cuando el diseño de la aplicación está desfigurado por mensajes publicitarios (a excepción de la publicidad en forma de visualizaciones de video a voluntad , y no cuando el video aparece a la vuelta de la esquina). Además, para participar en los programas "Hecho para niños" en Apple y "Diseñado para familias" en Google, debe filtrar estrictamente los anuncios que se muestran a los niños.


Las compras en el juego, yo mismo, como padre, bloqueo en todos los dispositivos y niños, cuando descargan aplicaciones por su cuenta, simplemente físicamente no puedo comprar nada dentro de la aplicación. Otra cosa es cuando el padre mismo compra inicialmente la aplicación para el niño (pero ya dije sobre esto anteriormente).


Por qué unidad y cómo


Elegí Unity porque trabajé antes y me gustó. También tenía un buen amigo como programador de C # y esperaba que él me ayudara con la programación, si es así. Unity también tiene una comunidad magnífica y es muy fácil encontrar respuestas en Google para casi todas las preguntas relacionadas con la implementación de algo en C # + Unity.


También trabajé con Unreal (como artista 3D), pero no entendí la funcionalidad C ++ o 2D.


Inicialmente, Math4Ami estaba "nublado", aunque se dice en voz alta. Todos los datos se almacenaron en mi eVPS (Elastic Virtual Private Server) y utilicé FTP para transferir archivos TXT con datos y configuraciones de aplicación (mis manos no llegaron a la base de datos, aunque escribí los primeros pasos para escribir mi servidor en node.js emprendió). Para trabajar con ftp, fijé a Unity la clase FTP simple de C # fácil de usar.


Luego, cuando decidí hacer pública la aplicación, abandoné el lado del servidor.


Por un lado, sería demasiado confuso: hacer la autenticación (a los usuarios no les gusta esto) o guardar el identificador de sesión en iCloud usando NSUbiquitousKeyValueStore (esto identificaría automáticamente al usuario entre la desinstalación de la aplicación y la reinstalación), pero aún no lo descubrí con esto (tal vez el artículo en el que escribí el complemento de Unity correctamente me ayudaría . Parte 1: iOS , pero aún no era así).


Por otro lado, los datos en esta aplicación no son tan importantes que deben almacenarse en el servidor.


Por otro lado, no había necesidad de sincronización del servidor. Aquí para mi solicitud de enseñanza de inglés, sí, se necesitaba sincronización. Dado que el padre agrega nuevas palabras en la aplicación principal, y el niño las enseña en la aplicación de los niños (aunque tal vez soy un aficionado para complicar las cosas).


Como resultado, me aseguré de que todo estuviera almacenado localmente (en el dispositivo), pero no en txt, sino en formato JSON.


Objeto Scriptable y respuestas correctas


El formato JSON junto con ScriptableObject resultó ser un hallazgo magnífico. Usé los métodos nativos de UnityEngine para serializar objetos en json - JsonUtility (y luego guardé archivos de texto json localmente en el dispositivo en la carpeta Application.persistentDataPath ).


ScriptableObject (SO) es un tema separado de conversación, pero aún lo abordaré. Ni siquiera puedo imaginar cómo solía vivir sin SO.


Todo lo que uso en mi trabajo, lo obtuve de estos dos videos mega útiles sobre los principios de trabajar con SO (y el código que lo acompaña en GitHub y Bitbucket):



Personalmente, utilicé SO para tales fines:


  • Para almacenar datos (para que no tenga que ingresar al código cada vez que agregue nuevas funciones o datos):
    • variedad de ejemplos
    • tipo de moneda
    • estilo de botón (tengo los mismos botones en muchos lugares y solo creo máscaras para ellos basados ​​en SO),
    • valor de recompensa, etc.

  • Como variables globales (que son visibles en todas las escenas):
    • cantidad de respuestas correctas
    • cantidad de dinero ganado
    • ajustes actualmente activos
    • tipo actual de ejemplo
    • temporizador, puntuaciones más altas, etc.

  • Para almacenar la lógica (por ejemplo, suscribirse al evento de recibir la respuesta correcta).

El único inconveniente de SO para trabajar con datos es que no puede almacenar datos entre las sesiones de la aplicación: los activos de SO (después de un inicio en frío de la aplicación) siempre contendrán los datos que escribió allí en el editor. Por lo tanto, la lógica de mi trabajo es la siguiente:


  1. Después de iniciar la aplicación, leo los archivos json del disco y los cargo en los activos SO (método FromJsonOverwrite).
  2. Mientras la aplicación se está ejecutando y necesito el máximo rendimiento, solo trabajo con activos de Objetos programables. Estos activos almacenan datos todo el tiempo mientras la aplicación se está ejecutando o en segundo plano.
  3. Cuando necesita guardar datos (por ejemplo, al finalizar la aplicación o mientras trabaja), serializo SO a json (método ToJson) y lo guardo en el disco.

Hay un inconveniente (obvio) de este enfoque: no puede guardar solo un parámetro modificado en el disco (si hay varios de ellos en SO), debe guardar todo el archivo de texto json todo el tiempo.


Pero no es necesario guardar muchos datos en el disco (por ejemplo, el número actual de respuestas correctas) y luego SO es una herramienta poderosa que me permite simplificar enormemente el trabajo.


En el video a continuación, muestro un ejemplo de mi implementación de la contabilidad de respuestas correctas e incorrectas usando UnityEvent (evento - cambió el número de respuestas correctas) + Oyente (los oyentes hacen algo de trabajo si escuchan que se recibe la respuesta correcta, y la lógica de los oyentes que se suscriben al evento también es implementado en SO) + SO (realiza un seguimiento del número de respuestas correctas):



Por lo tanto, no solo puedo ingresar las respuestas correctas y erróneas con mis manos, sino simplemente moviendo el control deslizante, generar nuevos ejemplos y probar la lógica de la aplicación.


Anima2D, personajes y sonrisa nerviosa


El video de arriba muestra que cuando cae un nuevo centavo, los otros centavos comienzan a sonreír ampliamente, y cuando cae una caca, los centavos se horrorizan.


Durante mucho tiempo no pude vencer la falla, cuando al cambiar de un tipo de sonrisa a otra, el cambio no ocurrió instantáneamente, sino que parpadeó (de un estado a otro) por un tiempo. Además, te diré cómo me di cuenta de esto y cómo derroté este problema técnico.


El cambio de expresiones faciales se implementa utilizando el script Sprite Mesh Animation, que es parte del poderoso complemento Anima2D (que Unity compró recientemente y lo hizo gratis). Este script esencialmente solo cambia los sprites de la boca (sonrisa, sonrisa abierta, boca asustada) usando el control deslizante Marco :


animación de malla de sprites que cambia expresiones faciales con sprites


Toda la emboscada es que el valor del control deslizante Frame no se puede cambiar directamente desde los scripts, sino solo a través de un sistema de animación. Por lo tanto, creé una nueva capa de animación OpenSmile (flecha 1 en la figura a continuación) en modo de fusión Aditivo con un peso de Peso = 1 y agregué animación de horror ( Coin_scared ) y una amplia sonrisa ( SmillingWide ) allí.


Por cierto, ¿notaste el mal ejemplo que estoy dando, con los nombres de las animaciones? Todavía estoy en el proceso de poner nombres a un estilo único. Sería correcto cambiar Coin_scared a A_CoinScared (¿por qué solo leer eso en la sección "Lo que lamento").


Creé una nueva capa y no utilicé la anterior porque no quería sobrescribir la animación de la boca. Todo lo que tenía que hacer era cambiar el sprite de la boca (de sonrisa a sonrisa amplia o de sonrisa a horror) y para que la animación de la boca permaneciera desde la capa base. Es por eso que elegí el modo de fusión Aditivo , agregando una nueva animación a una existente (sin sobrescribirla).


En esencia, las animaciones SmillingWide y Coin_scared son solo animaciones del control deslizante Frame en las posiciones 1 y 2, respectivamente.


Establecer la transición entre animaciones de expresión facial


Todo el problema era que la transición de cualquier estado al estado de horror (cuando hace clic en una transición (flecha 2 en la figura de arriba), el inspector abre las propiedades de esta transición (flecha 3 en la figura de arriba)) no sucedió instantáneamente, pero sin problemas durante la duración de la transición (flecha 4 en la figura anterior), que no era nula por defecto. Por lo tanto, el valor del control deslizante Frame no se pudo cambiar correctamente, porque solo había números enteros, lo que significa que no hay un valor intermedio entre 0 y 1. Por lo tanto, para deshacerse de la falla intermitente, solo fue necesario restablecer el valor de Duración de transición .


Bueno, trigger isScared (flecha 5 en la figura anterior) sirve como condición para entrar en un estado de horror. Activo este disparador en el código usando la siguiente llamada al objeto en el que se cuelga el componente Animator (con el controlador, cuyas capas mostré arriba):


...GetComponent<Animator>().SetTrigger("isScared"); 

Cómo traduje la aplicación a diferentes idiomas


En algún lugar aquí, en Habré, leí que debes pensar en la localización al comienzo de la creación de la aplicación y seguí este consejo ... inmediatamente ... después de un año y medio de desarrollo (tan pronto como decidí que Math4Ami sería público).


No recuerdo por qué elegí Lean Localization (excepto porque el complemento es gratuito), pero recuerdo que elegí durante mucho tiempo y mucho.


Usarlo resultó ser muy simple. Puede configurar manualmente el idioma o utilizar la detección automática de idioma. Me decidí por la detección automática de idioma (siguiendo el ejemplo de las aplicaciones de otros niños).


El complemento traduce todo (desde texto a sonidos e imágenes).


Pero aún cometí un error con la localización (aunque lo hice intencionalmente, porque quería probar diferentes enfoques). El error es que no puse todas las frases en un archivo de texto (a la izquierda en la figura a continuación). Algunas frases permanecieron dentro del componente Lean Localization (a la derecha en la figura a continuación). Entonces, cuando le doy este archivo a un traductor japonés, tengo que trabajar manualmente (para transferir TODO a un archivo de texto).



Aunque algunas cosas no se pueden traducir con un archivo de texto (como el espacio "", que utilicé como separador entre miles), aún debe usar el componente.


ITween jugoso


Érase una vez que vi un magnífico video de Juice it o lo perdí acerca de cómo todo tipo de pequeñas micromovimientos y matices de animación ayudan a hacer una acción impresionante de un juego aburrido. E incluso antes de eso, otro video se hundió en mi alma: el arte del movimiento de la pantalla , que en realidad no es solo y no tanto sobre el movimiento de la pantalla.


Todo el tiempo que estuve creando Math4Ami, tuve en cuenta los conceptos de los videos anteriores, así como la idea de que toda esta animación adicional debería ser lo más breve posible y actuar más en el subconsciente que en el consciente. A veces, pasé más tiempo agregando "jugosidad" que agregando funcionalidades útiles.


Solo un lugar me molesta mucho: el recuento final del dinero ganado (puede ver este momento al final de mi video de demostración anterior). Lo acorté tan pronto como pude, pero todavía toma un poco más de 4 segundos (el teclado desaparece, aparece la victoria de la inscripción, se cuentan los copecks, se sale de la tabla de registros, se muestra la placa de identificación "Nuevo registro", se muestra el botón "Más").


La mejor "fuente de jugo" para mí es el complemento gratuito iTween . Ni siquiera puedo imaginar cómo sin Unity puedes hacer algo. Lo uso donde sea que se necesite al menos algún tipo de animación (ya sea una animación de un botón o la aparición de un elemento de menú o una animación de contar centavos).


Traté de implementar algo similar por mi cuenta basado en corutinas y Mathf.Lerp o Mathf.MoveTowards, pero no era flexible ni universal (y a veces funcionaba de manera diferente en el editor y en el dispositivo). Así que ahora no estoy tratando de reinventar la rueda, solo disfruto iTween.


Hay trampas en este sistema de animación, con el que inicialmente luché incorrectamente:


  • Si durante el trabajo de iTween para ocultar un objeto (a través de SetActive (falso) , por ejemplo), y luego mostrarlo de nuevo, iTween continuará ejecutándose desde el lugar interrumpido.
  • Si durante la operación de un iTween inicia otro (que afecta los mismos valores), al final de la ejecución de ambos, el objeto puede no volver a su posición original.
  • Debe realizar un seguimiento de qué GameObject inicia iTween y en qué animación funciona.

Por ejemplo (en el último punto), el objeto A inicia iTween para que funcione en el objeto B. Para detener la animación iTween, no puede simplemente iniciar iTween.Stop () en el objeto A. Debe iniciar iTween.Stop (objeto B).


La fortaleza de iTween es su capacidad para usar diferentes tipos de flexibilización. Ising es un parámetro que suaviza el movimiento (para que no comience bruscamente y no termine estúpidamente).


Impresionante encontrar para mí son los tipos de ising:


  • la primavera
  • easeOutBounce
  • easeInBack
  • easeOutElastic

Para encontrar el diseño correcto, utilizo la demostración visual de aceleración (necesito un flash). Y aquí tomo la documentación para todo tipo de animaciones iTween .


Las estadísticas de Apple y Google son buenas, pero Unity Analytics es mejor


Incluso por la experiencia de juegos pasados, sabía que tener tus propias estadísticas es muy bueno. Al principio quería crear algún tipo de sistema de registro, pero luego recordé Unity Analitics . Y cuál fue mi sorpresa cuando resultó que la versión gratuita de la funcionalidad para mi caso no está limitada por nada. Sería peor si tuviera algún tipo de monetización, entonces las herramientas de análisis están disponibles solo para suscriptores Pro.


Simplemente integrando Analytics.CustomEvent en el lugar correcto en el código, puedo rastrear qué ejemplos son más populares, cuántos niños resuelven ejemplos en los primeros días o después de un tiempo, etc.


Puedo comparar datos de diferentes plataformas (iOS y Android) en un solo lugar.


Y cuántas cosas son interesantes allí, lo que me gustaría probar, pero no alcanzan todas las manos. Escriba Configuración remota (cambiar el contenido de la aplicación sin actualizar) o Pruebas A / B o Tutorial Manager .


Visual Studio como Sublime


En el pasado, cuando necesitaba editar algún código (ya sea python, html o node.js), usaba Notepad ++ (completamente gratis, pero solo en Windows) y Sublime Text (pago para todos los SO, pero puedes probarlo gratis) )


En Unity, estaba sentado en MonoDevelop, pero estaba tan harto de mí con sus problemas técnicos (como la incapacidad de cambiar entre diseños o pegar algo copiado fuera de Mono) que decidí que era hora de dejar caer el barco que se hundía y subir a Visual Studio Community 2017 (bueno, es gratis para desarrolladores individuales como yo).


Para los desarrolladores de Unity 2018, esto no es relevante en este momento, ya que el código de Visual Studio multiplataforma se incluye en la versión 2018. Pero quería que mi aplicación funcionara con iOS 7 (ya que el iPhone de mi hija está con este iOS), así que tuve que usar cualquier versión de Unity anterior a 2018.


Me ayudó con la transición al video VS Cómo configurar Visual Studio con Unity .


Desde el cuadro, VS no tiene todas las cosas geniales a las que estoy acostumbrado en otros editores, así que simplifiqué mi vida:


  • Encendió el minimapa en lugar de un simple desplazamiento vertical:

Cómo habilitar el minimapa en Visual Studio en lugar de solo desplazarse


  • Se agregó la extensión SemanticColorizer , que permite una personalización más flexible de los colores del código. Específicamente, lo necesitaba para distinguir las variables globales de las locales por color.
  • instaló la extensión Match Margin , que selecciona la palabra debajo del carro y todas sus copias de acuerdo con el texto del código, y también lo hace en el minimapa. Esto es muy conveniente para la navegación rápida de código, para encontrar todos los lugares donde se usa algún método o variable:

diseño de código y minimapa mejorada en visual studio


  • Yo uso Strip'em para corregir automáticamente las terminaciones de línea.

Mis scripts para esta aplicación están en GitHub. Solo están mis scripts , y no todo el proyecto de Unity, lo siento, si esto los hace imposibles de entender. Hasta el último momento, no planeé dar un enlace a las fuentes, porque no considero que mi código sea tan valioso. Pero luego cambió de opinión debido a la posibilidad de que los desarrolladores más experimentados pudieran señalar mis errores.


Este es el final de la primera parte. Continúe leyendo en la segunda parte , donde contaré:


  • Sobre escribir código
  • Sobre el control de versiones
  • Sobre la actuación de voz
  • Sobre el icono
  • Acerca de Android Build
  • Sobre build para iOS
  • Sobre el título y promoción
  • Estadísticas
  • Lo que lamento
  • Lo que entendido

Referencias


Lista de enlaces del cuerpo del artículo en el orden de su mención:


+ Clase simple C # FTP.
+ ID de sesión para iOS.
+ Escribimos el complemento para Unity correctamente. Parte 1: iOS .
+ Métodos para serializar objetos en JSON (ayuda oficial).
+ ScriptableObject (ayuda oficial).
+ Video tutorial Arquitectura del juego con objetos programables ( código ).
+ Taller Derrocando la tiranía MonoBehavior en una Gloriosa Revolución de Objetos Escritables ( código ).
+ Video de demostración de mi aplicación en el editor .
+ Complemento gratuito Anima2D para animación esquelética de personajes 2D .
+ Biblioteca gratuita para localización de aplicaciones - Lean Localization.
+ Video sobre trucos que mejoran la percepción del juego Exprésalo o piérdelo .
+ Video sobre técnicas de animación subliminal de animación El arte del movimiento de pantalla .
+ Sistema de animación iTween gratuito pero potente.
+ Demostración visual de los tipos ising (necesita un flash).
+ iTween (ayuda oficial).
+ Unidad Analitics .
+ Editores de texto Notepad ++ y Sublime Text .
+ Visual Studio Community 2017 y Visual Studio Code .
+ Video tutorial Cómo configurar Visual Studio con Unity .
+ Plugin SemanticColorizer (para la configuración del color del código).
+ Complemento Margen de coincidencia (selecciona la palabra debajo del carro y todas sus copias).
+ Complemento Strip'em (corrección automática de terminaciones de línea).

Source: https://habr.com/ru/post/es414611/


All Articles