He estado trabajando en el desarrollo de aplicaciones con muchos sistemas GUI "retenidos" (a continuación, más sobre lo que quiero decir con eso) como MFC, QT, Forms, SWING y varios marcos de GUI web hace algunos años. Siempre encontré los conceptos de la mayoría de los sistemas GUI demasiado complicados y torpes. La cantidad de eventos de devolución de llamada, oyentes, copias de datos, algo para encadenar a algo: las conversiones (y así sucesivamente) siempre fueron una fuente de errores y dolores de cabeza en comparación con otras partes de la aplicación. (Incluso con el uso "adecuado" de enlaces / modelos de datos).
Ahora estoy escribiendo juegos de computadora :). Trabajé con una GUI hasta ahora: Miyagi (no conocida, pero básicamente la misma Idea que todos los demás sistemas).
Fue horrible.
Para entornos de renderizado en tiempo real como Juegos, tengo la sensación de que los sistemas GUI "retenidos" son aún más obsoletos. Por lo general, las interfaces de usuario no necesitan tener un diseño automático o tener ventanas redimensionables sobre la marcha. En cambio, necesitan interactuar de manera muy eficiente con datos que cambian constantemente (como las posiciones 3D de modelos en el mundo)
Hace un par de años, me topé con "IMGUI", que es básicamente como un modo de gráficos inmediatos, pero para las interfaces de usuario. No presté demasiada atención, ya que todavía estaba en desarrollo de aplicaciones y la escena IMGUI en sí misma parecía no ser realmente amplia ni exitosa. Aún así, el enfoque que adoptan parece ser tan completamente sexy y elegante, que me hizo querer escribir algo para el próximo proyecto utilizando esta forma de interfaz de usuario (no logré convencer a nadie en el trabajo: (...)
permítanme resumir lo que quiero decir con "retenido" e "inmediato":
GUI retenida: en una fase de inicialización separada, crea "controles GUI" como etiquetas, botones, cuadros de texto, etc. y usa alguna forma descriptiva (o programática) de colocarlos en la pantalla, todo antes de que se muestre algo. Los controles contienen la mayor parte de su propio estado en la memoria, como ubicación X, Y, tamaño, bordes, controles secundarios, texto de etiqueta, imágenes, etc. Puede agregar devoluciones de llamada y escuchas para informarse de los eventos y actualizar los datos en el control de la GUI.
GUI inmediata: la biblioteca GUI consta de funciones "RenderButton", "RenderLabel", "RenderTextBox" ... de un solo disparo (editar: no se confunda con el Renderprefijo. Estas funciones también hacen la lógica detrás de los controles, como sondear la entrada del usuario, insertar caracteres, manejar la velocidad de repetición de caracteres cuando el usuario mantiene presionada una tecla y así sucesivamente ...) que puede llamar para representar "inmediatamente" un control (no no debe escribirse de inmediato en la GPU. Por lo general, se recuerda para el marco actual y se clasifica en lotes apropiados más adelante). La biblioteca no tiene ningún "estado" para estos. Si desea ocultar un botón ... simplemente no llame a la función RenderButton. Todas las funciones de RenderXXX que tienen interacción del usuario como botones o casillas de verificación tienen valores de retorno que indican si, por ejemplo, el usuario hizo clic en el botón. Entonces tu "RenderGUI" La función se ve como una gran función if / else donde llamas o no a tus funciones RenderXXX dependiendo del estado de tu juego y toda la lógica de actualización de datos (cuando se presiona un botón) se intermanga en el flujo. Todo el almacenamiento de datos está "fuera" de la interfaz gráfica de usuario y se pasa a pedido a las funciones de Render. (Por supuesto, dividiría las grandes funciones en varias o usaría algunas abstracciones de clase para agrupar partes de la interfaz gráfica de usuario. Ya no escribimos código como en 1980, ¿verdad?)
Ahora descubrí que Unity3D en realidad usa el mismo enfoque básico para sus sistemas GUI integrados. ¿Probablemente también haya un par de GUI con este enfoque?
Aún así ... al mirar alrededor, ¿parece haber un fuerte sesgo hacia los sistemas GUI retenidos? Al menos no he encontrado este enfoque, excepto en Unity3D y la comunidad IMGUI original parece ser bastante ... tranquila.
Entonces, ¿alguien trabajó con ambas ideas y tiene alguna opinión sólida?
Editar: Estoy más interesado en las opiniones que surgen de la experiencia del mundo real. Creo que hay muchas discusiones acaloradas en el foro IMGUI sobre cualquier "debilidad teórica" del enfoque inmediato de GUI, pero siempre encuentro más esclarecedor saber sobre las debilidades del mundo real .
Respuestas:
No. He realizado un trabajo de gamedev pagado en una horrible GUI de 'modo retenido' y en una horrible GUI de 'modo inmediato' y aunque ambos me hicieron querer arrancarme los ojos, el enfoque del modo retenido sigue siendo claramente el mejor.
Las desventajas del modo inmediato son muchas:
Las GUI de modo inmediato son tentadoras para programadores solitarios que desean un sistema HUD rápido, y para ese propósito son geniales. Para cualquier otra cosa ... solo di que no.
fuente
Como alguien con cinco o seis años de experiencia real con IMGUI en el escritorio, me siento obligado a defenderlo. Todas las respuestas (y la pregunta en sí) se basan en una definición muy restringida de IMGUI, y parece haber muchas afirmaciones cuestionables.
Mucho de esto se origina en el supuesto de que una biblioteca IMGUI no puede retener ningún dato oculto. Esto no es cierto en absoluto. De hecho, una sofisticada biblioteca IMGUI retendrá casi tantos datos como una biblioteca RMGUI equivalente. La diferencia es que una biblioteca IMGUI almacena principalmente resultados en caché , mientras que una biblioteca RMGUI mantiene un estado autorizado. En IMGUI, la aplicación proporciona una función que toma el estado actual del modelo y produce una GUI para ese estado. Esta es la definición autorizada de la GUI. La biblioteca es responsable de asegurarse de que la GUI en pantalla coincida con el resultado de esa función. Esto no significa que deba evaluar completamente esa función en cada marco y reconstruir completamente la GUI. Ese es el punto de almacenamiento en caché.
Con esta vista de IMGUI, de hecho puede hacer un diseño avanzado, y el rendimiento es bastante razonable. También puede conservar el estado autoritario real si facilita la interfaz. El objetivo de IMGUI no es hacer que la aplicación retenga todo. La aplicación probablemente no quiera tener que almacenar la posición de cada barra de desplazamiento y el estado expandido / colapsado de cada nodo del árbol. El punto es poder especificar procesalmente el contenido de esos nodos de árbol y su propia existencia.
Revisaría y respondería a todas las críticas de IMGUI punto por punto, pero realmente no entiendo muchas de ellas. La mayoría de ellos parecen provenir de una creencia fundamental de que IMGUI es hack y RMGUI está bien estructurado, lo que supongo que se deriva de los malentendidos anteriores. No hay una razón inherente para que las bibliotecas IMGUI tengan problemas con la complejidad o tengan que estar llenas de globales o mezclar lógica con presentación. Diré que las ventajas de IMGUI se vuelven menos importantes cuanto más contenido esté dirigido por artistas, pero no estoy seguro de por qué en realidad sería peor para el contenido dirigido por artistas. (Yo personalmente no uso contenido dirigido por artistas, aparte de las hojas de estilo para cosas como colores, tamaños de fuente / borde, etc., pero no veo por qué sería más difícil de implementar que para RMGUI).
En mi opinión, IMGUI es, sin duda, algo bueno. Le da un modelo mucho mejor para razonar sobre su GUI. Se vuelve mucho más funcional y reactivo, y minimiza la cantidad de estado mutable sobre el que tiene que razonar. Por otro lado, implementar una sofisticada biblioteca IMGUI es todo un desafío, y definitivamente más difícil que implementar una biblioteca RMGUI equivalente. Es una compensación entre la complejidad de la biblioteca y la complejidad de la aplicación. Si planea crear aplicaciones complejas (o incluso muchas aplicaciones simples), la compensación es muy buena. Si está haciendo esto para una sola aplicación, y es bastante simple, probablemente alcanzará su objetivo más rápido con RMGUI.
en cualquier caso, buena suerte!
fuente
Me atrevería a decir que esto no constituye una biblioteca GUI. Es solo un montón de funciones de renderizado.
Renderizar la GUI es la parte más fácil de hacer una GUI. Tome un cuadro de texto, por ejemplo. Asumiré el caso más fácil posible: un simple cuadro de texto de entrada de una sola línea.
RenderTextBox
solo dibujará un cuadro de texto. Hará una medición de texto simple y dibujará los glifos en la pantalla. Lo hará sinPuedo seguir, pero creo que mi punto es claro. Si desea utilizar
RenderTextBox
para dibujar un cuadro de texto, todo lo que obtendrá es un cuadro de texto estático, no funcional. Cualquier funcionalidad real debe ser implementada por usted.¿Medición de texto y dibujo de glifos? Esa es la parte fácil del trabajo de la GUI. GUI significa "interfaz gráfica de usuario". Las dos últimas palabras, en las que tomas información del usuario y haces algo con ella, son la parte difícil.
Los jugadores del juego esperan que los controles GUI funcionen razonablemente. En un entorno de estilo PC (es decir, mouse y teclado), si los jugadores ven un cuadro de texto, esperan que funcione como un cuadro de texto normal. Esperan poder moverse con teclas de flecha, saltar al frente y terminar con inicio y borrar, seleccionar letras, etc.
Eso significa que tendrá que implementar todo este código de interfaz de usuario. Tienes que probar en qué control se hizo clic. Luego debe hacer algo en función del control en el que se hizo clic. Debe tener el concepto de un control "activo", el que recibe la entrada del teclado. Los diferentes controles deben responder de manera diferente a los diferentes tipos de interacción del usuario.
Básicamente, vas a necesitar una
TextBoxWindow
clase.Digamos que estás implementando una pantalla de opciones de juego. Entonces tiene sus diferentes grupos de opciones: jugabilidad, gráficos, sonido, controles, etc. Así que tiene una lista de diferentes grupos en el lado izquierdo de la pantalla. Cada grupo muestra una serie de controles que el usuario puede manipular. ¿Qué sucede cuando el usuario presiona la tecla Tab?
Bueno, depende de qué control esté activo. Si uno de los controles de grupo está activo (se hizo clic más recientemente), se mueve al siguiente grupo. Si, en cambio, uno de los controles dentro del grupo está activo, entonces se mueve al siguiente control en ese grupo. Así es como se pretende que funcione el sistema.
Por lo tanto, una entrada de teclado de proceso de control activo no solo debe, sino que también debe almacenar cualquier entrada no procesada en el control que la posee . O eso, o los controles deben estar en algún tipo de lista vinculada, para que pueda ir y venir. Eso significa que debe haber un comportamiento predeterminado en cada control.
Esto suena cada vez más como una GUI retenida, ¿no? La única diferencia es que ... tú eres el que hace el trabajo duro. Se supone que usar la GUI de otra persona es una forma de hacer menos trabajo. Pero el esfuerzo que lleva hacer que una GUI responda como el usuario espera no es trivial.
Recuerde: la interfaz de usuario no es para usted, es para sus usuarios . Es para el jugador. Debería comportarse como lo esperan. Y si no es así, entonces has fallado.
Ese es un pensamiento limitado y limitante.
Podría dar la perorata sobre los diferentes juegos que tienen diferentes necesidades de interfaz de usuario, que algunos juegos hacen necesidad ventanas de tamaño variable y así sucesivamente. Pero hay un problema mucho mayor.
Tu tipo de pensamiento conduce al problema de las batallas espaciales gratuitas.
Si nunca has jugado ese juego, es un juego barato, independiente y con mucha interfaz gráfica de usuario. La jugabilidad real se realiza en la GUI (es un tipo de juego de "configurar las unidades y verlas pelear"). ¿El problema?
¡LA GUI NO PUEDE SER RESCALADA!
Oh, esto está bien si estás usando un monitor normal o tienes una vista normal. Pero si eres yo, por ejemplo, quien ejecuta un monitor ridículamente grande (uno tan grande que tienes a Windows escalando el tamaño de todo el texto para que realmente puedas leerlo), esto es terrible . El texto es microscópico. No hay forma de cambiar el tamaño de fuente.
Por supuesto, GSB es un juego basado en GUI, por lo que es absolutamente inexcusable para ellos. Un juego GUI-lite probablemente puede salirse con la suya.
Nunca asumas que tu usuario está viendo su juego exactamente como tú. Hacer eso es una locura.
Tener un sistema GUI que pueda reescalarse a la resolución del usuario, una GUI verdaderamente independiente de la resolución, requiere que el diseño sea parte del propio sistema GUI. Si no va a proporcionar eso, entonces su texto aparecerá pequeño en el monitor gigante de alguien. Esto no es algo bueno.
Entonces diría que su pensamiento sobre este tema es miope. Usted no necesita esas cosas. Necesita lo que proporcionan las GUI retenidas. Es posible que no necesite todo lo que proporciona un sistema GUI en particular. Pero necesitas algo de eso.
El trabajo repetitivo que necesita hacer para llevar los datos al sistema es un pequeño precio a pagar además de tener una seguridad razonable de que el usuario puede interactuar con los controles correctamente y que se redimensionará para adaptarse a las necesidades del jugador.
Es posible que pueda salirse con una GUI de modo inmediato si no recibe mucha información del usuario del reproductor. Pero eso sería todo.
fuente
RenderTextBox
era una función , no una clase. Por lo tanto, su división entre "modo inmediato" y "modo retenido" parece estar en torno a dónde se almacenan los datos, no quién los administra. Si es así, debe aclarar esa distinción en su pregunta, porque sugiere que una "GUI de modo inmediato" simplemente dibuja cosas en la pantalla. Que no puede obtener información (porque eso requeriría un estado persistente) y tal. Entonces cual es?Hace un tiempo, las IMGUI también me llamaron la atención, como prueba escribí un par de tutoriales para familiarizarme con las técnicas (comience aquí si está interesado, pero no es mucho más que C # / XNA + el 'tutorial' original aquí ) .
Realmente me gustaron las IMGUI por un corto tiempo porque la información que muestran siempre está actualizada y es correcta (ya que no hay un estado que siempre alimente a los datos reales). Pero al final perdí interés, así que normalmente ahora uso GUI retenidas nuevamente.
Al final, pensé que la inmediatez de la GUI hizo que fuera más difícil separar la GUI de los datos y, a veces, traté de desacoplar más las cosas introduciendo algún estado, rompiendo la elegancia de las IMGUI. Tampoco creo que escribir código para GUI retenidas sea más trabajo que escribir código para IMGUI y me gusta que las GUI retenidas se puedan disparar y olvidar, y realmente me gustan los eventos :).
Sin embargo, cada vez que alguien menciona IMGUIs, algo dentro de mí quiere volver a intentarlo, y se esfuerza más para hacerlo bien, porque realmente hay algo de elegancia allí, simplemente no estoy 100% seguro de si siempre hará su trabajo más fácil. Yo diría que lo pruebe, tal vez lo intente como lo hice y escriba algunos tutoriales (creo que la mejor manera de aprender nuevas técnicas es explicándolas a los demás).
fuente
Trabajé mucho con el sistema Unity3D GUI, que funciona tal como lo describiste. Y debo decir que lo odio con pasión.
El enfoque "inmediato" funciona realmente bien en algunos casos. Primero, cuando solo necesita un par de botones y etiquetas, por ejemplo, piense en el tirador HUD. Crearlos con un enfoque "retenido" no es muy difícil, pero es muy fácil con UnityGUI. El segundo caso es cuando necesita introducir muchos controles en la pantalla, pero realmente no le importa el diseño. Especialmente cuando los controles exactos solo se conocen en tiempo de ejecución. Esto no es realmente útil en ningún juego, pero es realmente útil al hacer que las herramientas se ejecuten dentro del editor de Unity.
Sin embargo, cualquier GUI de medio complejo, para un juego de rol (MMO) o un juego de estrategia, por ejemplo, inevitablemente se convierte en un horrible desastre de código indescifrable, lleno de casos especiales y rompiendo de muchas maneras. Al crear una GUI de este tipo, generalmente tiene un diseño bastante específico que debe funcionar para cualquier resolución y poder mostrar los datos que envíe. Necesita cosas como, por ejemplo, mover algunos botones hacia abajo para dejar espacio para un texto más largo. Con la GUI "retenida", puede tener un control que lo hace automáticamente. Con el modo "inmediato", no hay controles, por lo que debe hacer todo explícitamente.
Otro problema con UnityGUI (no estoy seguro si le ocurre a todas las GUI de modo inmediato) es que, por ejemplo, una
Button()
llamada funciona sin considerar ninguna otra llamada de GUI. Entonces, si un botón está encima de otro, un clic presionará ambos botones al mismo tiempo. Esto definitivamente no es lo que el usuario espera, por lo que agrega otro caso especial para manejar.Para vivir con todo esto, siempre he escrito mi propio contenedor alrededor de UnityGUI que lo convierte en un sistema de modo "retenido": controles que almacenan su propio estado y solo llaman a las
GUI
funciones requeridas cada cuadro para mí.No puedo decir que el modo "inmediato" sea definitivamente peor que "retenido", pero ciertamente me siento mucho mejor trabajando en modo "retenido".
fuente
Voy a defender IMGUI aquí porque algunos de los problemas que se enumeraron anteriormente se pueden resolver si está dispuesto a escribir el código. Además, si escribe el código, entonces tiene una biblioteca robusta que es reutilizable y que rara vez requiere modificación.
quizás al principio parece que los artistas no tienen mucha flexibilidad con IMGUI, esto se resuelve o mejora al cargar el diseño de la GUI desde un esquema xml o JSON.
este problema se resuelve con la clasificación, debe crear una clase de Administrador de IU que ordene el orden de dibujo, resolví este problema en C # XNA con paneles movibles y que se pueden hacer clic que se dibujan uno encima del otro. Puedo publicar pseudocódigo si me lo piden.
¿Puedes dibujar cadenas de una matriz? ¿Puedes definir áreas rectangulares a partir del alto y el ancho de las fuentes de esas cadenas? ¿puedes crear una proporción del tamaño de tu botón de desplazamiento por la altura de todos esos rectángulos sumados? Luego está a medio camino de crear un cuadro de lista con un botón desplazable para moverse hacia arriba o hacia abajo. Pensé que iba a ser difícil, pero resultó ser mucho más simple de lo que pensaba. sin buffers, sin estados, sin devoluciones de llamada involucradas.
no permita que los paneles, botones y cuadros individuales contengan los datos. Sé que en mi primer intento de IMGUI fue inmediato pero solo en el sentido gráfico. Cometí el error de retener datos en la interfaz de usuario. Fue casi un cambio filosófico pensar de manera diferente dónde ubicar y modificar los datos a través de los cambios en la interfaz de usuario.
Este es el límite y la funcionalidad del código del SDK, no el suyo. Encuentro que SDK UI (Hammer, Unity, UDK) es casi un nuevo idioma para aprender de todos modos. De hecho, son similares a mí como Windows. Los formularios, ambos están configurados para la posibilidad más amplia y, por lo tanto, tienen un peso adicional en complejidad.
y finalmente mira esto> http://mollyrocket.com/861
fuente
Depende del género y juego en cuestión. Un buen sistema de gestión de inventario es vital para la mayoría de los juegos de rol. Va a querer algo que maneje los diseños por usted, soporte de barras de desplazamiento, arrastrar y soltar, información sobre herramientas y otras características mucho más fáciles de implementar usando GUI retenida.
No se me ocurre una forma de arrastrar y soltar con una GUI inmediata que no requiere mucho malabarismo o ahorro de estado del usuario, como cambiar temporalmente el búfer Z y almacenar información de tiempo para descartar un clic que movió un poco.
Pero desde una perspectiva de diseño, tengo problemas para imaginar un mundo sin mis eventos GUI. Además, una GUI Inmediata no es compatible con OOP, lo que le obliga a recurrir a la programación de procedimientos.
La simplicidad que brindan las GUI inmediatas tiene el costo de una complejidad adicional en su código que crece rápidamente a medida que aumenta la complejidad requerida por su UI; donde, como buena GUI retenida, es originalmente más complejo pero se escala mejor. Entonces, por debajo de cierto punto de complejidad, una GUI inmediata es adecuada, pero para la mayoría de las situaciones prefiero una GUI retenida.
fuente