Eu tenho trabalhado no desenvolvimento de aplicativos com muitos sistemas GUI "retidos" (abaixo, mais sobre o que quero dizer com isso), como MFC, QT, Forms, SWING e várias estruturas de GUI da Web há alguns anos. Eu sempre achei os conceitos da maioria dos sistemas GUI excessivamente complicados e desajeitados. A quantidade de eventos de retorno de chamada, ouvintes, cópias de dados, algo a ser entendido - conversões (e assim por diante) sempre foram uma fonte de erros e dores de cabeça em comparação com outras partes do aplicativo. (Mesmo com o uso "adequado" de modelos / associações de dados).
Agora estou escrevendo jogos de computador :). Eu trabalhei com uma GUI até agora: Miyagi (não conhecida, mas basicamente a mesma ideia que todos os outros sistemas).
Foi horrível.
Para ambientes de renderização em tempo real como Games, sinto que os sistemas GUI "retidos" são ainda mais obsoletos. As interfaces do usuário geralmente não precisam ter layout automático ou ter janelas redimensionáveis dinamicamente. Em vez disso, eles precisam interagir de maneira muito eficiente com dados sempre em mudança (como posições 3D de modelos no mundo)
Há alguns anos, deparei-me com "IMGUI", que é basicamente como um modo de gráficos imediatos, mas para interfaces de usuário. Não dei muita atenção, pois ainda estava no desenvolvimento de aplicativos e o próprio cenário da IMGUI parecia não ser muito amplo nem bem-sucedido. Ainda assim, a abordagem adotada parece ser tão sexy e elegante que me fez querer escrever algo para o próximo projeto usando esse modo de interface do usuário (não consegui convencer ninguém no trabalho: (...)
deixe-me resumir o que quero dizer com "retido" e "imediato":
GUI Retida: Em uma fase de inicialização separada, você cria "controles da GUI", como Rótulos, Botões, Caixas de Texto, etc., e usa alguma maneira descritiva (ou programática) de colocá-los na tela - tudo antes de qualquer coisa ser renderizada. Os controles mantêm a maior parte de seu próprio estado na memória, como localização X, Y, tamanho, bordas, controles filho, texto da etiqueta, imagens e assim por diante. Você pode adicionar retornos de chamada e ouvintes para se informar sobre eventos e atualizar dados no controle da GUI.
GUI imediata: A biblioteca da GUI consiste nas funções "RenderButton", "RenderLabel", "RenderTextBox" de uma só vez ... (editar: não se confunda com o Renderprefixo. Essas funções também executam a lógica por trás dos controles, como pesquisar entradas do usuário, inserir caracteres, manipular a velocidade de repetição de caracteres quando o usuário mantém pressionada uma tecla e assim por diante ...) que você pode chamar para renderizar "imediatamente" um controle (não tem que ser imediatamente gravado na GPU. Geralmente é lembrado para o quadro atual e classificado em lotes apropriados posteriormente). A biblioteca não possui nenhum "estado" para eles. Se você deseja ocultar um botão ... simplesmente não chame a função RenderButton. Todas as funções do RenderXXX que possuem interação do usuário como botões ou caixa de seleção têm valores retornados que indicam se, por exemplo, o usuário clicou no botão. Então o seu "RenderGUI" A função se parece com uma grande função if / else, na qual você chama ou não as funções RenderXXX, dependendo do estado do jogo e toda a lógica de atualização de dados (quando um botão é pressionado) é entrelaçada no fluxo. Todo o armazenamento de dados está "fora" da GUI e passado sob demanda para as funções Renderizar. (Obviamente, você dividiria as grandes funções em várias ou utilizaria algumas abstrações de classe para agrupar partes da GUI. Nós não escrevemos código como em 1980, não é?
Agora eu descobri que o Unity3D realmente usa a mesma abordagem básica para seus sistemas GUI embutidos. Provavelmente existem algumas GUIs com essa abordagem também?
Ainda .. ao olhar em volta, parece haver um forte viés em relação aos sistemas GUI retidos? Pelo menos eu não encontrei essa abordagem, exceto no Unity3D, e a comunidade IMGUI original parece bastante ... silenciosa.
Então, alguém trabalhou com as duas idéias e tem uma opinião forte?
Edit: Estou mais interessado em opiniões que resultam da experiência do mundo real. Eu acho que há muitas discussões acaloradas no fórum IMGUI sobre qualquer "fraqueza teórica" da abordagem imediata da GUI, mas sempre acho mais esclarecedor saber sobre as fraquezas do mundo real .
Respostas:
Não. Eu fiz um trabalho pago com gamedev em uma horrível GUI de 'modo retido' e em uma terrível GUI de 'modo imediato' e, embora ambos tenham me feito querer arrancar meus olhos, a abordagem do modo retido ainda é claramente a melhor.
As desvantagens do modo imediato são muitas:
As GUIs de modo imediato são tentadoras para programadores solitários que desejam um sistema rápido de HUD e, para esse efeito, são ótimos. Para qualquer outra coisa ... basta dizer não.
fonte
Como alguém com cinco ou seis anos de experiência real com IMGUI na área de trabalho, sinto-me obrigado a defendê-lo. Todas as respostas (e a própria pergunta) são baseadas em uma definição muito restrita de IMGUI, e parece haver muitas reivindicações questionáveis sendo feitas.
Muito disso se origina da suposição de que uma biblioteca IMGUI não pode reter nenhum dado oculto. Isso não é verdade. De fato, uma sofisticada biblioteca IMGUI reterá tantos dados quanto uma biblioteca RMGUI equivalente. A diferença é que uma biblioteca IMGUI armazena em cache os resultados, enquanto uma biblioteca RMGUI mantém o estado autoritativo. No IMGUI, o aplicativo fornece uma função que pega o estado atual do modelo e produz uma GUI para esse estado. Esta é a definição oficial da GUI. A biblioteca é responsável por garantir que a GUI na tela corresponda ao resultado dessa função. Isso não significa que ele precise avaliar totalmente essa função em cada quadro e reconstruir completamente a GUI. Esse é o ponto do cache.
Com essa visão do IMGUI, é possível fazer um layout avançado e o desempenho é bastante razoável. Você também pode manter o estado autoritativo real se facilitar a interface. O objetivo da IMGUI não é fazer com que o aplicativo retenha tudo. O aplicativo provavelmente não deseja armazenar a posição de todas as barras de rolagem e o estado expandido / recolhido de cada nó da árvore. O objetivo é ser capaz de especificar procedimentalmente o conteúdo desses nós da árvore e sua própria existência.
Gostaria de responder e responder a todas as críticas da IMGUI, ponto por ponto, mas não as entendo muito. A maioria deles parece resultar de alguma crença fundamental de que o IMGUI é hackish e o RMGUI é bem estruturado, o que eu acho que deriva dos mal-entendidos acima. Não há nenhuma razão inerente para que as bibliotecas IMGUI tenham problemas com a complexidade ou tenham que estar repletas de globais ou misturar lógica com apresentação. Eu direi que as vantagens do IMGUI se tornam menos importantes quanto mais o conteúdo é direcionado ao artista, mas não sei por que seria realmente pior para o conteúdo direcionado ao artista. (Eu não uso pessoalmente conteúdo direcionado a artistas, além de folhas de estilo para coisas como cores, tamanhos de fonte / borda, etc., mas não vejo por que seria mais difícil de implementar do que para RMGUI.)
Na minha opinião, IMGUI é inquestionavelmente uma coisa boa. Ele fornece um modelo muito melhor para o raciocínio sobre sua GUI. Torna-se muito mais funcional e reativo, e você minimiza a quantidade de estado mutável sobre o qual precisa raciocinar. Por outro lado, implementar uma sofisticada biblioteca IMGUI é um grande desafio e definitivamente mais difícil do que implementar uma biblioteca RMGUI equivalente. É uma troca entre a complexidade da biblioteca e a complexidade do aplicativo. Se você planeja criar aplicativos complexos (ou mesmo muitos aplicativos simples), a troca é muito boa. Se você estiver fazendo isso para um único aplicativo, e for bastante simples, provavelmente alcançará seu objetivo mais rapidamente com o RMGUI.
De qualquer forma, boa sorte!
fonte
Eu chegaria ao ponto de dizer que isso não constitui uma biblioteca de GUI. É apenas um monte de funções de renderização.
Renderizar a GUI é a parte mais fácil de criar uma GUI. Veja uma caixa de texto, por exemplo. Vou assumir o caso mais fácil possível: uma caixa de texto simples de entrada de linha única.
RenderTextBox
apenas desenhará uma caixa de texto. Ele fará uma medição simples do texto e desenhará os glifos na tela. Ele irá nãoEu posso continuar, mas acho que meu argumento é claro. Se você deseja usar
RenderTextBox
para desenhar uma caixa de texto, tudo o que obterá é uma caixa de texto estática e não funcional. Qualquer funcionalidade real deve ser implementada por você.Fazendo medição de texto e desenhando glifos? Essa é a parte mais fácil do trabalho da GUI. GUI significa "Interface Gráfica do Usuário". As duas últimas palavras, nas quais você recebe informações do usuário e faz algo com ele, são a parte mais difícil.
Os jogadores esperam que os controles da GUI funcionem razoavelmente. Em um ambiente de estilo PC (por exemplo: mouse e teclado), se os jogadores virem uma caixa de texto, eles esperam que ela funcione como uma caixa de texto comum. Eles esperam poder se movimentar nele com as teclas de seta, pular para frente e terminar com home e excluir, selecionar letras nele, etc.
Isso significa que você precisará implementar todo esse código da interface do usuário. Você precisa testar qual controle foi clicado. Você precisará fazer algo com base em qual controle foi clicado. Você precisa ter o conceito de um controle "ativo", aquele que obtém entrada do teclado. Controles diferentes devem responder de maneira diferente a diferentes tipos de interação do usuário.
Basicamente, você precisará de uma
TextBoxWindow
aula.Digamos que você esteja implementando uma tela de opções de jogos. Então você tem seus diferentes grupos de opções: jogabilidade, gráficos, som, controles, etc. Então você tem uma lista de diferentes grupos no lado esquerdo da tela. Cada grupo apresenta uma série de controles que o usuário pode manipular. O que acontece quando o usuário pressiona a tecla Tab?
Bem, depende de qual controle está ativo. Se um dos controles do grupo estiver ativo (foi clicado mais recentemente), ele será movido para o próximo grupo. Se, em vez disso, um dos controles dentro do grupo estiver ativo, ele passará para o próximo controle nesse grupo. É assim que o sistema se destina a funcionar.
Portanto, não apenas uma entrada de teclado do processo de controle ativo deve agora cultivar qualquer entrada não processada no controle que a possui . Ou isso, ou os controles devem estar em algum tipo de lista vinculada, para que possa ir e voltar. Isso significa que precisa haver um comportamento padrão em cada controle.
Isso soa cada vez mais como uma GUI mantida, não é? A única diferença é que ... é você quem faz o trabalho duro. O uso de outra pessoa na GUI deve ser uma maneira de fazer menos trabalho. Mas o esforço necessário para fazer uma GUI responder como o usuário espera não é trivial.
Lembre-se: a interface do usuário não é para você, é para seus usuários . É para o jogador. Deve se comportar como eles esperam. E se não, então você falhou.
Esse é um pensamento limitado e limitador.
Eu poderia dar a lengalenga sobre jogos diferentes com diferentes necessidades de interface do usuário, que alguns jogos fazer janelas redimensionáveis precisa e assim por diante. Mas há um problema muito maior.
Seu tipo de pensamento leva ao problema das Batalhas Espaciais Gratuitas.
Se você nunca jogou esse jogo, é um jogo barato, independente e com muita GUI. A jogabilidade real é toda feita na GUI (é um tipo de jogo "configure as unidades e observe-as lutar"). O problema?
A GUI NÃO PODE SER RESCALADA!
Ah, tudo bem se você estiver usando um monitor normal ou tiver uma visão normal. Mas se você é eu, por exemplo, que administra um monitor ridiculamente grande (um tão grande que o Windows amplia o tamanho de todo o texto para poder lê-lo), isso é terrível . O texto é microscópico. Não há como alterar o tamanho da fonte.
É verdade que o GSB é um jogo baseado em GUI, portanto é absolutamente indesculpável para eles. Um jogo GUI-lite provavelmente pode se safar dele.
Nunca assuma que seu usuário está vendo o jogo exatamente como você. Fazer isso é loucura.
Ter um sistema GUI que possa se redimensionar para a resolução do usuário, uma GUI verdadeiramente independente da resolução, exige que o layout faça parte do próprio sistema GUI. Se você não fornecer isso, seu texto aparecerá minúsculo no monitor gigante de alguém. Isso não é uma coisa boa.
Então, eu diria que seu pensamento sobre esse assunto é míope. Você não precisa dessas coisas. Você precisa do que as GUIs mantidas fornecem. Ah, você pode não precisar de tudo que qualquer sistema GUI específico fornece. Mas você precisa disso.
O trabalho padronizado que você precisa fazer para obter dados para o sistema é um preço pequeno a pagar, além de ter uma garantia razoável de que o usuário pode interagir com os controles corretamente e de que ele se redimensionará para atender às necessidades do jogador.
Talvez você consiga se livrar de uma GUI de modo imediato se não receber muita entrada do usuário do player. Mas isso seria tudo.
fonte
RenderTextBox
era uma função , não uma classe. Portanto, sua divisão entre "modo imediato" e "modo retido" parece estar em torno de onde os dados são armazenados, e não de quem os está gerenciando. Nesse caso, você precisa deixar essa distinção mais clara na sua pergunta, pois sugere que uma "GUI de modo imediato" simplesmente desenha coisas na tela. Que ele não pode receber entrada (porque isso exigiria estado persistente) e tal. Então qual é?Há um tempo atrás, as IMGUIs também despertaram meu interesse, como teste, escrevi alguns tutoriais para me familiarizar com as técnicas (comece aqui se estiver interessado, mas não é muito mais do que C # / XNA + o 'tutorial' original aqui ) .
Gostei muito das IMGUIs por um curto período de tempo, porque as informações exibidas são sempre atualizadas e corretas (já que não existe um estado que você sempre alimenta com dados reais). Mas no final, perdi o interesse, então normalmente agora uso as GUIs retidas novamente.
No final, pensei que a proximidade da GUI tornava mais difícil separar a GUI dos dados e, às vezes, tentava desacoplar as coisas introduzindo algum estado, quebrando a elegância das IMGUIs. Também não acho que escrever código para GUIs retidas seja mais trabalho do que escrever código para IMGUIs e gosto que as GUIs retidas possam ser disparadas e esquecidas, e gosto muito de eventos :).
No entanto, toda vez que alguém menciona IMGUIs, algo dentro de mim quer tentar de novo e se esforçar mais para acertar, porque realmente há alguma elegância lá dentro, não tenho 100% de certeza se isso sempre facilitará seu trabalho. Eu diria que experimente, talvez tente como eu fiz e escreva alguns tutoriais (acho que a melhor maneira de aprender novas técnicas é explicando-as para outras pessoas).
fonte
Eu trabalhei muito com o sistema Unity3D GUI, que funciona exatamente como você descreveu. E devo dizer, odeio isso com paixão.
A abordagem "imediata" funciona muito bem em alguns casos. Primeiro, quando você precisa apenas de alguns botões e etiquetas - pense no atirador HUD, por exemplo. Criá-los com a abordagem "retida" não é muito difícil, mas é super fácil com o UnityGUI. O segundo caso é quando você precisa colocar muitos controles na tela, mas realmente não se importa com o layout. Especialmente quando os controles exatos são conhecidos apenas em tempo de execução. Isso não é realmente útil em nenhum jogo, mas é realmente útil ao criar ferramentas em execução no editor do Unity.
No entanto, qualquer GUI semi-complexa - para um RPG (MMO) ou um jogo de estratégia, por exemplo - inevitavelmente se transforma em uma bagunça horrível de código indecifrável, cheia de casos especiais e quebrando de várias maneiras. Ao criar uma GUI, geralmente você tem um layout bastante específico, que deve funcionar para qualquer resolução e ser capaz de mostrar quaisquer dados que você enviar. Você precisa de coisas como, por exemplo, mover alguns botões para baixo para abrir caminho para textos mais longos. Com a GUI "retida", você pode ter um controle que faz isso automaticamente. Com o modo "imediato", não há controles, então você precisa fazer tudo explicitamente.
Outro problema com o UnityGUI (não tenho certeza se isso acontece com todas as GUIs de modo imediato) é que, por exemplo, uma
Button()
chamada funciona sem considerar outras chamadas da GUI. Portanto, se um botão estiver em cima de outro, um clique pressionará os dois botões ao mesmo tempo. Definitivamente, não é o que o usuário espera; portanto, isso adiciona outro caso especial a ser tratado.Para viver com tudo isso, sempre escrevi meu próprio invólucro em torno do UnityGUI que o transforma em um sistema de modo "retido": controles que armazenam seu próprio estado e apenas chamam as
GUI
funções necessárias a cada quadro para mim.Não posso dizer que o modo "imediato" seja definitivamente pior do que o "retido", mas certamente me sinto muito melhor trabalhando no modo "retido".
fonte
Vou defender o IMGUI aqui porque alguns dos problemas listados anteriormente podem ser resolvidos se você estiver disposto a escrever o código para ele. Além disso, se você escrever o código para ele, terá uma biblioteca robusta que é reutilizável e raramente requer modificações.
talvez a princípio pareça que os artistas não tenham muita flexibilidade com o IMGUI, isso foi resolvido ou aprimorado com o carregamento do layout da GUI a partir de um esquema xml ou JSON.
Se esse problema for resolvido com a classificação, você deverá criar a classe UI Manager que classifique a ordem do sorteio. Resolvi esse problema no C # XNA com painéis móveis que podem ser clicados e desenhados um sobre o outro. Posso postar pseudo-código, se solicitado.
você pode desenhar strings de uma matriz? você pode definir áreas retangulares a partir da altura e largura das fontes dessas strings? você pode criar uma proporção do tamanho do botão de rolagem pela altura de todos os retângulos adicionados juntos? Então você está no meio do caminho para criar uma caixa de listagem com um botão de rolagem para mover para cima ou para baixo. Eu pensei que seria difícil, mas acabou sendo muito mais simples do que eu pensava. sem buffers, sem estados, sem retornos de chamada envolvidos.
não deixe os painéis, botões e caixas individuais conterem os dados. Eu sei que na minha primeira tentativa de IMGUI foi imediata, mas apenas no sentido gráfico. Cometi o erro de reter dados na interface do usuário. Foi quase uma mudança filosófica pensar de maneira diferente sobre onde colocar e modificar dados por meio das alterações na interface do usuário.
esse é o limite e a funcionalidade do código do SDK, não o seu. Acho que a interface do SDK (Hammer, Unity, UDK) é quase um novo idioma para aprender de qualquer maneira. Na verdade, eles são semelhantes a mim como Windows.Forms, ambos são configurados para a mais ampla possibilidade e, portanto, têm um peso extra em complexidade.
e, finalmente, assista a isso> http://mollyrocket.com/861
fonte
Depende do gênero e do jogo em questão. Um bom sistema de gerenciamento de inventário é vital para quase todos os RPGs. Você deseja algo que lide com layouts para você, suporte barras de rolagem, arraste e solte, dicas de ferramentas e outros recursos muito mais fáceis de implementar usando a GUI retida.
Não consigo pensar em uma maneira de arrastar e soltar com uma GUI imediata que não exige muito malabarismo ou economia de estado do usuário, como alterar temporariamente o buffer Z e armazenar informações de tempo para descartar um clique que moveu um pouco.
Mas, da perspectiva do design, tenho problemas para imaginar um mundo sem os meus eventos GUI. Uma GUI imediata também não é compatível com o OOP, forçando-o a recorrer à programação procedural.
A simplicidade proporcionada pelas GUIs imediatas tem o custo de complexidade adicional no seu código, que cresce rapidamente à medida que a complexidade exigida pela interface do usuário aumenta; onde, como uma boa GUI retida, é mais complexo originalmente, mas dimensiona melhor. Portanto, abaixo de um certo ponto de complexidade, uma GUI imediata é adequada, mas para a maioria das situações, prefiro uma GUI retida.
fonte