Editor de lógica visual para Unity3d. Parte 1

Introduccion


Hola queridos lectores, en el artículo de hoy me gustaría detenerme en un fenómeno de este tipo en el desarrollo de aplicaciones en Unity3d como desarrollo visual o, más precisamente, desarrollo usando representación visual de código y lógica. Y, antes de continuar, quiero aclarar de inmediato, no se trata de programación visual, de la palabra "absolutamente", no hay variaciones de Blueprint en el mundo de Unity y no hay generaciones de código C #. Entonces, ¿qué se entiende por un editor de lógica visual? Si está interesado en la respuesta a esta pregunta, bienvenido a cat.

Artículos de la serie:
Editor de lógica visual, parte 2

¿Qué es un editor de lógica visual?


Muy a menudo, y algunos sostienen que siempre, durante el desarrollo, los programadores escriben muchos códigos diferentes que hacen muchas cosas diferentes, desde los del sistema hasta la mecánica del juego. Este código, si el programador es "real", generalmente se unifica y aísla para que pueda reutilizarse (dentro de Unity, este código son componentes, también son herederos de MonoBehavior ). No es difícil imaginar que puede haber mucho código de este tipo, especialmente si este no es el primer proyecto. Ahora imagine que estamos comenzando un nuevo proyecto y necesitamos hacer muchos prototipos rápidos y diferentes, y el equipo de programadores es limitado y el conjunto está ocupado con el proyecto o proyectos principales. Los diseñadores de juegos están indignados, necesitan probar, verificar, los productores corren alrededor de los gerentes tratando de sacar un programador por sí mismos, el dinero es limitado, el tiempo se acaba, etc.

La otra cara de la moneda, nosotros (programadores) escribimos mucho código en forma de componentes, que cuelgan en una gran lista de diferentes objetos en la escena. Y entonces expandimos el equipo, contratamos a un nuevo programador, él abre el escenario para resolverlo y se ahoga en un lío de preguntas: quién causa quién, en qué orden, qué componente está conectado con qué y cómo, etc. Usted razonablemente dice: - “Y documentación? Existe documentación (aunque no es un hecho en absoluto), pero aquí se trata del umbral para que las nuevas personas que se unen al equipo sean lo más bajas posible, y el tiempo para este proceso lo más corto posible.

¿Cómo resolver las situaciones descritas anteriormente? La respuesta en el título del artículo es Visual Logic Editor. Que es esto Este es un entorno que le permite operar visualmente en varios componentes de la lógica y configurar sus relaciones (en la versión "suave"), así como manipular objetos de la escena indirectamente desde la escena. Si lo describe en una forma fabulosamente simple, es como en la infancia ensamblar diferentes diseños de cubos (solo en nuestro caso, los cubos no están bien conectados, quitando la parte inferior, nuestro diseño no se caerá).

Entonces, descubrimos la definición, pero ¿qué nos da esto al final?

  • Puede ensamblar diseños universales que se pueden reutilizar en proyectos, lo que reduce la rutina posterior. Imagine un tipo de campo puro, que es nuestro proyecto, simplemente tomamos la construcción ensamblada de otro juego, la colocamos en el campo y eso es todo.
  • Puede crear una base de datos de "cubos" aislados (mecánicos, lógicos, funcionales) a partir de los cuales las personas que no son programadores pueden construir construcciones por su cuenta.
  • Es posible reemplazar diseños sobre la marcha con otros, cambiando así el comportamiento de la lógica.
  • Puede usar construcciones en modo diferido, por ejemplo, si el NPC no está presente en el mundo ahora, entonces no existirá ninguna lógica asociada con él en nuestro "campo".
  • Dado que nuestros cubos no están conectados por relaciones rígidas, podemos apagarlos y encenderlos como lo deseamos e implementar ramificaciones condicionales e incondicionales arbitrariamente complejas.

Bueno, ¿eso suena bastante bien? ¿Pero qué en realidad? Si abre el Asset Store y ve la sección Visual Scripting , puede ver, en principio, una gran cantidad de complementos diferentes. La mayoría de ellos son variaciones del tema Blueprint de Unreal Engine, es decir, en esencia, la generación de código. Prácticamente no hay sistemas que se ajusten a los conceptos de un editor de lógica visual. Los más cercanos en significado son:

  1. Creador de juegos Sí, es un complemento FSM, pero no obstante, le permite escribir sus propias acciones. No es tan conveniente desde el punto de vista de la interfaz, pero para ciertas cosas es muy bueno. Blizzard no fue en vano usado en Hearthstone.
  2. Diseñador de comportamiento / MoonBehavior , etc. complementos de árbol de estado. Está más cerca de lo que se describió anteriormente, pero hay muchas limitaciones, después de todo, el árbol de estado no es una lógica completa en los componentes.
  3. ICode Este es un análogo de playmaker, que es, de hecho, también una máquina de estados.

¿Hay alguna salida que pregunten, queridos lectores? Encontré solo uno, escribí mi sistema, lo cual hice, pero el camino hacia él fue bastante largo y espinoso.

El camino


La idea de desarrollar un complemento para el editor de lógica visual para Unity3D surgió hace mucho tiempo. Al principio solo eran pensamientos que, de ser así, sería genial. Estos pensamientos aparecieron en el proceso de trabajar en un proyecto en el que había muchos juegos similares, más de 20 piezas que debían hacerse muy, muy rápidamente. La primera implementación fue terrible en términos de interfaz, aunque, por supuesto, nos permitió desarrollar con éxito todo el conjunto de juegos a una velocidad determinada.

Para el próximo proyecto, se decidió hacer un editor visual completo, pero como resultado, debido a la poca experiencia, la implementación no fue exitosa, todo fue extremadamente lento, el número de conexiones, etc., se redujo tanto que fue imposible averiguar qué y dónde (ver captura de pantalla y no tengas miedo).

imagen

Después de eso, la idea fue pospuesta por algún tiempo. Los siguientes proyectos ya los hice en código puro, pero la idea aún estaba en mi cabeza. Poco a poco, teniendo en cuenta los errores del pasado, se formó la visión final (como me pareció) y la lista de requisitos. Y en 2017, después de la finalización del próximo proyecto independiente, decidí que puedo permitirme pasar de 6 a 7 meses trabajando en este complemento e intentar ponerlo en la tienda de activos (todavía se encuentra y se llama Panthea VS ). Desde el punto de vista de la experiencia trabajando en un proyecto tan complejo, todo fue muy bueno, la parte financiera es lamentable, todavía poder programar y poder vender son dos cosas diferentes. Era noviembre de 2017, después de lo cual perdí un poco mi motivación, me divorcié, cambié mi ciudad, cambié mi vida por completo y, para no caer en el samoyedismo, decidí mirar un ángulo diferente sobre el tema del editor de lógica visual. El resultado fue uViLEd , que decidí publicar de forma gratuita. Desde que firmé un contrato a tiempo completo, tuve que trabajar en él los fines de semana y días festivos y me llevó todo 2018 y principios de 2019. uViLEd es un gran replanteamiento de Panthea VS , una refactorización completa del código para el compilador Roslyn (C # 7+), por lo que todo funciona solo desde la versión Unity3d 2018.3.

Nota : Panthea VS lanzó varios proyectos (Android e iOS, en particular, Lev’s Truck y autos), en principio, la experiencia de usarlo fue exitosa, pero surgió el momento de que una cosa es escribir un editor, otra es aprender a usarlo correctamente (no importa cuán extraño suene) )

uViLEd y cómo usarlo


Introduccion


Entonces, lo que sucedió al final, primero miramos la imagen y luego continuamos (habrá más fotos).

imagen

¿En qué se basa el editor de lógica visual?

imagen

Aquí:

  • Componentes : este es nuestro código que implementa uno u otro funcional, de hecho, un análogo de MonoBehaviour , solo en nuestro caso todos los componentes se heredan de la clase LogicComponent , que a su vez es un ScriptableObject .
  • Las variables son ScriptableObjects especiales que pueden almacenar datos (cualquiera, incluidas estructuras y clases personalizadas, referencias a objetos de escena, prefabricados y activos). Se necesitan variables si es necesario hacer que los datos se compartan entre los componentes, es decir, cada componente puede hacer referencia a una variable del tipo deseado, y será una.
  • Las relaciones son una descripción en forma visual de qué componentes, cómo y en qué orden se llaman los métodos de cada uno. Las relaciones entre componentes se determinan utilizando dos campos especializados de los tipos INPUT_POINT y OUTPUT_POINT . Un enlace siempre se forma como el punto de salida de un componente al punto de entrada de otro componente. Estas conexiones no son rígidas, es decir, no son visibles para el programador y tampoco están en el código. Están presentes, solo en el editor, y luego, cuando comienza la escena, el controlador lógico comprende el código. Cómo sucede esto, hablaremos en un artículo separado.

Todo en el compartimento (componentes, variables y relaciones) forma la lógica . En general, nada súper complicado, el programador escribe el código de componentes y variables, y el diseñador del juego u otro (o el mismo) programador / scripter forma la lógica colocando estos componentes en el editor y configurando las conexiones y parámetros.

Características clave de uViLEd


  • Ejecución de la lógica (un conjunto de componentes, variables y relaciones) en modo diferido, incluido el inicio desde una fuente externa (disco duro o servidor)
  • Configuración de conexiones entre componentes (orden de llamada, activación y desactivación)
  • Fácil integración de componentes y cualquier otro código, incluidos los descendientes MonoBehavior
  • Anulación de la apariencia de componentes en el editor (análogo a CustomPropertyDrawer)
  • Configurar ajustes de componentes a través del inspector Unity3d
  • Agregue fácilmente componentes a la lógica a través del archivo de script de arrastrar y soltar oa través de un directorio
  • Agrupando componentes en el editor lógico
  • Configuración de la visualización de componentes en el editor (inversión, minimización, activación y desactivación)
  • Abrir el editor de código de componente directamente desde el editor de lógica
  • Mostrar datos de depuración directamente en el editor lógico durante el inicio en el editor
  • Editor de escala visual visual
  • Si de repente una gran cantidad de componentes están contenidos en la lógica, entonces existe la posibilidad de buscarlos con foco al seleccionar (esto se aplica a las variables)
  • Depuración paso a paso en el modo de inicio de escena en el editor Unity3d con seguimiento de todos los datos transmitidos entre componentes y valores variables
  • Soporte para métodos MonoBehaviour y configuración del orden de su llamada. Nota : aquí queremos decir que, por defecto, en SO no hay métodos como Inicio, Actualización, etc. por lo tanto, su soporte se ha agregado a los componentes mismos. Al mismo tiempo, utilizando el atributo ExecuteOrder, puede configurar el orden en que se llaman los métodos Inicio, Actualización, etc.
  • Soporte para Coroutine, async / await y todos los atributos de Unity3d para el inspector, así como soporte para CustomPropertyDrawer

Trabajar con el editor


Para comenzar a trabajar con el editor, debe abrir la escena y luego iniciar el editor en sí.

imagen

Después de iniciar el editor, inicializamos la escena (el botón de actualización en el editor), después de lo cual será posible crear o agregar lógica existente a la escena.
Después de crear la lógica (un archivo que describirá los componentes, sus parámetros, las relaciones entre componentes, variables y sus valores), puede llenarlo de significado. Para agregar un componente o variable, simplemente arrastre la secuencia de comandos correspondiente al área del editor lógico. Una opción alternativa es usar un directorio que se genera automáticamente usando el atributo ComponentDefinition .

imagen

Después de haber agregado varios componentes a la lógica, se pueden mover, incluso en grupos o combinados en un grupo visual.

imagen

Consideremos con más detalle lo que representamos el componente en sí mismo en un editor visual.

imagen

Aquí:

  • El botón de menú del componente abre un menú desplegable con el que puede:
    • Activar o desactivar un componente
    • Minimice el componente (esto también se puede hacer haciendo doble clic en el encabezado)
    • Invertir componente (intercambiar puntos de entrada y salida)
    • Ocultar o mostrar el área de opciones de componentes
    • Abra el editor de código para el componente.
  • El área de los parámetros del componente es el lugar donde se muestran los valores de los parámetros clave del componente, cuya composición depende del programador

Para configurar los parámetros (campos públicos o campos con el atributo SerializeField), debe seleccionar el componente en el editor lógico y abrir el inspector Unity3d.

imagen

Aquí:

  • En la esquina superior derecha hay un botón que le permite cambiar el color del encabezado, que se muestra en el editor lógico
  • Nombre : campo para configurar el nombre de la instancia del componente
  • Comentario : un campo para establecer un comentario en la instancia del componente; se muestra en el editor lógico cuando pasa el cursor sobre el componente
  • Parámetros del componente : el área donde se muestran los parámetros del componente (campos públicos y campos marcados con SerializeField)
  • Enlaces de variables : un área para establecer referencias a variables (se discutirá más sobre ellas en la sección sobre cómo trabajar con variables).

Para agrupar visualmente los objetos, debe seleccionarlos, y luego presionar el botón derecho y seleccionar el elemento correspondiente en el menú. Los grupos se pueden renombrar, así como cambiar su esquema de color.

imagen

Para escalar la representación visual de los componentes usando la rueda del mouse, todo es bastante simple.

Y lo último a lo que quiero prestar atención es trabajar con las conexiones entre los componentes.
Para establecer una conexión, es necesario conectar el punto de salida de un componente con el punto de entrada de otro.

imagen

imagen

imagen

Las relaciones se establecen en función de la regla de coincidencia de tipos que un punto transmite y recibe. Se hacen excepciones a un punto de entrada que no recibe datos; cualquier punto de salida se puede conectar a él. Cuando se establece una conexión, el sistema verifica automáticamente la coincidencia de tipos y muestra si esta conexión se puede establecer o no. Los puntos de entrada y salida se establecen en el código del componente utilizando las siguientes clases:

INPUT_POINT OUTPUT_POINT INPUT_POINT<T> OUTPUT_POINT<T> 

Las dos primeras clases se utilizan para los puntos de entrada y salida que no aceptan parámetros, la segunda, respectivamente, viceversa. T puede ser de cualquier tipo.

 public INPUT_POINT <float> InputFloatValue = new INPUT_POINT<float>(); public OUTPUT_POINT <float> OutputFloatValue = new OUTPUT_POINT<float>(); 

Para llamar a una cadena de enlaces, debe usar la función Ejecutar.

 OutputFloatValue.Execute(5f); 

Para procesar dicha llamada, es necesario establecer un controlador para el punto de entrada en el código del componente (sobre dónde exactamente hablaremos un poco más adelante).

 InputFloatValue.Handler = value => Debug.Log(value); 

Y finalmente, quiero mencionar un punto importante sobre las conexiones. Si hay varios enlaces desde un punto, entonces en el editor es posible ajustar el orden de su llamada.

Trabajar con variables


Como se mencionó anteriormente, las variables son objetos especiales que le permiten compartir datos entre componentes a través de enlaces a ellos. Las variables, como los componentes, son creadas por programadores.

 [ComponentDefinition(Name = "Float", Path = "uViLEd Components/Base/Variable/Base", Tooltip = "Variable for a floating-point number", Color = VLEColor.Cyan)] public class VariableFloat : Variable<float> { } 

Como puede ver, la clase base para variables es la clase genérica Variable, donde T es el tipo de datos encerrados en la variable, T puede ser cualquier tipo que se pueda serializar.

En el editor, las variables se muestran de la siguiente manera:

imagen

Para cambiar la visualización de valores variables, simplemente redefina el método ToString en el tipo T.

 public struct CustomData { public readonly int Value01; public readonly int Value02; public CustomData (int value01, int value02) { Value01= value01; Value02= value02; } public override string ToString() { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Value01 = {0}".Fmt(Value01)); stringBuilder.Append("Value02 = {0}".Fmt(Value02)); return stringBuilder.ToString(); } } 

Por lo tanto, una variable de este tipo se verá así:

 public class VariableCustomData : Variable<CustomData> { } 

Para agregar una referencia a una variable en un componente, debe usar una clase especial.

 public VARIABLE_LINK<CustomData> CustomVariableLink = new VARIABLE_LINK<CustomData>(); 

Después de eso, el enlace se puede configurar en el inspector, y en el menú desplegable solo se mostrarán las variables del tipo CustomData , lo que simplifica enormemente el trabajo con ellos.

Para la conveniencia de trabajar con variables, existen métodos especiales que le permiten determinar cuándo una variable ha cambiado su valor o cuándo se le ha establecido algún dato.

 CustomVariableLink.AddSetEventHandler(CustomDataSet); CustomVariableLink.AddChangedEventHandler(CustomDataChanged); 

Debe tenerse en cuenta que Changed funciona con la condición Equals , por lo tanto, si se utilizan estructuras y clases, este método debe redefinirse para garantizar un funcionamiento correcto.

Trabajando con objetos de Unity


Debido a la naturaleza del sistema uViLEd, los enlaces directos a los objetos de Unity no se pueden usar en él, ya que no se pueden restaurar al cargar la lógica. Para resolver este problema, se creó un shell especializado de VLObject , que le permite crear dichos enlaces, así como guardarlos y cargarlos. Entre otras cosas, este shell tiene un editor de propiedades especial que le permite obtener componentes de cualquier objeto en la escena (consulte la figura a continuación) si desea acceder a ellos. Con VLObject, puede almacenar enlaces no solo a objetos de escena y sus componentes, sino también a prefabricados y archivos de recursos, como texturas, sonidos, etc.

imagen

Nota : si la lógica existente se usa en otra escena, se perderán referencias a objetos, incluidas referencias a prefabricados, porque la escena actúa como su almacenamiento. Esto también debe tenerse en cuenta si planea utilizar la lógica como plantilla, en este caso, la mejor opción es transferir los enlaces necesarios desde el exterior (por ejemplo, desde la lógica adjunta a la escena).

También es posible restringir el tipo de objeto de Unity que se instalará en VLObject . Esto afecta solo al inspector de Unity y se usa para la conveniencia de trabajar con ellos.

 [SerializeField] [TypeConstraint(typeof(Button))] private VLObject _button; 

Crear un componente lógico


Para crear un componente lógico, un programador solo necesita agregar un archivo de script C # simple al proyecto (también puede crear un componente o una variable inmediatamente a través de un menú especial en la pestaña Proyecto) y cambiar el código en él a lo siguiente:

 [ComponentDefinition(Name = "MyComponent", Path = "MyFolder/MySubfolder", Tooltip = "this my logic component", Color = VSEColor.Green)] public class MyLogicComponent : LogicComponent { } 

Como se mencionó anteriormente, ComponentDefinition es un atributo que le permite crear automáticamente un catálogo de componentes, aquí debe tenerse en cuenta que Color (el color del encabezado) se establece en la cadena como un formato HEX.

LogicComponent es la clase base de todos los componentes, que a su vez es un descendiente de ScripatableObject .

El siguiente es un ejemplo simple de un componente que se bifurca por un valor entrante de tipo bool:

 public class IfBool : LogicComponent { public INPUT_POINT<bool> ValueToBeChecked = new INPUT_POINT<bool>(); public OUTPUT_POINT True = new OUTPUT_POINT(); public OUTPUT_POINT False = new OUTPUT_POINT(); public override void Constructor() { ValueToBeChecked.Handler = ValueToBeCheckedHandler; } private void ValueToBeCheckedHandler(bool value) { if(value) { True.Execute(); }else { False.Execute(); } } } 

Entonces, como puede ver en el código, creamos el punto de entrada del componente, que toma un valor de tipo bool y dos puntos de salida que se llaman según el valor que obtuvimos.

Quizás ahora tengas una pregunta, ¿qué tipo de constructor es este? Te explico De manera predeterminada, ScriptableObject no admite métodos como Inicio , Actualización , etc., pero también admite los métodos Awake , OnEnable , OnDisable y OnDestroy . Así que aquí está Awake (como OnEnable ), en caso de que ScriptableObject se cree a través del método CreateInstance , siempre se llama, y ​​este, de hecho, es el problema. Debido al hecho de que el objeto se crea en la memoria para la serialización en el modo editor, fue necesario excluir el código del componente del funcionamiento en este momento, por lo tanto, el análogo Despertar se agregó como método Constructor , lo mismo se aplica a los métodos OnDisable y OnDestroy al eliminar un objeto en el editor. Si necesita procesar correctamente la eliminación de un componente (al descargar una escena, por ejemplo), debe usar la interfaz IDisposable .

En general, como puede ver, no hay nada difícil en la creación de componentes. Esta es una clase regular, en la que puede haber cualquier código que desee. En el caso particular, los componentes pueden no contener puntos de entrada y salida, sino comunicarse mediante mensajes globales. Por cierto, para esto, en uViLEd hay una clase GlobalEvent : es un sistema de mensajes basado en tipos de datos (se puede encontrar más sobre esto en mi artículo).

Lo último que me gustaría mencionar es la capacidad de configurar los puntos de entrada y salida del componente dependiendo de los parámetros del componente.

imagen

Para hacer esto, en el código del componente, es suficiente implementar una o ambas interfaces IInputPointParse e IOutputPointParse . A continuación se muestra un ejemplo de código de clase genérico abstracto para componentes de rama Switch ; los puntos de salida se crean automáticamente aquí, según el parámetro SwitchValues .

SwitchAbstract Class Code
 public abstract class SwitchAbstract<T> : LogicComponent, IOutputPointParse { [Tooltip("input point for transmitting value, which should be checked")] public INPUT_POINT<T> ValueToBeChecked = new INPUT_POINT<T>(); [Tooltip("set of values for branching")] public List<T> SwitchValues = new List<T>(); protected Dictionary<string, object> outputPoints = new Dictionary<string, object>(); public override void Constructor() { ValueToBeChecked.Handler = ValueToBeCheckedHandler; } protected virtual bool CompareEqual(T first, T second) { return first.Equals(second); } protected virtual string GetValueString(T value) { var outputPontName = value.ToString(); #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) { if (outputPoints.ContainsKey(outputPontName)) { outputPontName += " ({0})".Fmt(outputPoints.Count); } } #endif return outputPontName; } private void ValueToBeCheckedHandler(T checkedValue) { foreach (var value in SwitchValues) { if (CompareEqual(checkedValue, value)) { ((OUTPUT_POINT)outputPoints[GetValueString(value)]).Execute(); return; } } } public IDictionary<string, object> GetOutputPoints() { #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) { outputPoints.Clear(); } #endif if (outputPoints.Count == 0) { foreach (var value in SwitchValues) { outputPoints.Add(GetValueString(value), new OUTPUT_POINT()); } } return outputPoints; } } 


Depuración lógica


UViLEd proporciona varios mecanismos para la lógica de depuración:

  1. La capacidad de mostrar variables internas y sus valores en el editor lógico en modo de inicio de escena. Para hacer esto, use el atributo ViewInDebugMode
  2. Capacidad para ver valores de variables lógicas en modo de inicio de escena
  3. Posibilidad de depuración paso a paso de llamadas entre componentes y visualización de datos que se transfieren entre ellos

UViLEd tiene un modo especial para el último elemento, que se enciende cuando comienza la escena.

imagen

Este modo, desafortunadamente, tiene ciertas limitaciones asociadas con la transición entre escenas. En este caso, los datos de depuración de la escena anterior y las lógicas se perderán, y en la nueva comenzarán a mostrarse solo desde el momento en que se active la lógica en el editor.

Conclusión


En este artículo traté de presentarles brevemente el enfoque de desarrollo que uso en mis proyectos actuales. A pesar del escepticismo inicial (incluido el mío), la práctica muestra significativamente la conveniencia de su uso, especialmente cuando se realizan prototipos. Entre otras cosas, el trabajo de los diseñadores de juegos se ha simplificado enormemente, no entran en escena, no empujan objetos para configurar el proceso del juego, se les crea una lógica separada con un conjunto de datos de variables y componentes en los que pueden configurar fácilmente todo. También una gran ventaja es el hecho de que en mis proyectos, a menudo, el contenido se descarga desde el exterior. Utilizando el editor de lógica visual, puedo actualizar el equilibrio del proceso del juego sin actualizar la aplicación principal, en algunos casos, la lógica en sí misma se puede cambiar.

Por mi parte, decidí que ese enfoque de desarrollo es el lugar ideal, por supuesto, no es aplicable a grandes proyectos, pero puede usarse allí para algunos guiones de juego para revitalizar el mundo, en el diseño de niveles, etc. proyectos en curso (segmento infantil), hasta ahora, está mostrando excelentes resultados.

Que sigue

Esta fue la primera parte de una serie de artículos sobre el editor visual de logic uViLEd, luego habrá partes sobre:

  1. El núcleo del sistema : cómo se carga la lógica, por qué se selecciona el ScriptableObject, cómo se organiza la API, qué le permite hacer, etc., qué dificultades surgieron y cómo se resolvió todo.
  2. Editor : cómo se desarrolló, cómo se construyó, qué problemas y qué soluciones, etc., que rehacería ahora.

Escriba en los comentarios si tiene alguna pregunta específica que le gustaría que cubra en artículos posteriores.

PD : Traté de hablar sobre los puntos clave de uViLEd , si lo desea, puede familiarizarse con él descargando el complemento de Asset Store, hay documentación completa (aunque en inglés): un manual de usuario, una guía para programadores y API.

Editor de lógica visual, parte 2

Editor de lógica visual UViLEd
Artículo de mensajería global

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


All Articles