Pracowałem nad tworzeniem aplikacji z wieloma „zachowanymi” systemami GUI (poniżej więcej o tym, co mam na myśli), takimi jak MFC, QT, Forms, SWING i kilka frameworków web-GUI kilka lat temu. Zawsze uważałem koncepcje większości systemów GUI za zbyt skomplikowane i niezdarne. Ilość zdarzeń oddzwaniania, detektorów, kopii danych, czegoś do czegoś ciągnąć - konwersje (i tak dalej) zawsze były źródłem błędów i problemów w porównaniu z innymi częściami aplikacji. (Nawet przy „właściwym” wykorzystaniu powiązań / modeli danych).
Teraz piszę gry komputerowe :). Do tej pory pracowałem z jednym GUI: Miyagi (mało znany, ale w zasadzie ten sam Idea, co wszystkie inne systemy).
To było okropne.
W przypadku środowisk renderowania w czasie rzeczywistym, takich jak Gry, mam wrażenie, że „zachowane” systemy GUI są jeszcze bardziej przestarzałe. Interfejsy użytkownika zwykle nie muszą być automatycznie układane ani mieć okien w trakcie pracy, których rozmiar można zmieniać. Zamiast tego muszą bardzo efektywnie współdziałać z ciągle zmieniającymi się danymi (takimi jak pozycje modeli na świecie w 3D)
Kilka lat temu natknąłem się na „IMGUI”, który jest w zasadzie jak tryb Natychmiastowej grafiki, ale dla interfejsów użytkownika. Nie poświęciłem zbyt wiele uwagi, ponieważ wciąż byłem w fazie rozwoju aplikacji, a sama scena IMGUI wydawała się niezbyt szeroka ani udana. Jednak podejście, które przyjęli, wydaje się być tak seksowne i eleganckie, że sprawiło, że chciałem napisać coś do następnego projektu przy użyciu tego interfejsu użytkownika (nie przekonałem nikogo w pracy: (...)
pozwól, że streszczę, co rozumiem przez „zachowane” i „natychmiastowe”:
Zachowane GUI: W osobnej fazie inicjalizacji tworzysz „kontrolki GUI”, takie jak Etykiety, Przyciski, TextBoxy itp., I używasz opisowego (lub programowego) sposobu umieszczania ich na ekranie - wszystko przed renderowaniem czegokolwiek. Elementy sterujące przechowują większość własnego stanu w pamięci, na przykład lokalizację X, Y, rozmiar, obramowania, elementy podrzędne, tekst etykiety, obrazy i tak dalej. Możesz dodać wywołania zwrotne i detektory, aby uzyskać informacje o zdarzeniach i zaktualizować dane w kontrolce GUI.
Natychmiastowe GUI: Biblioteka GUI składa się z jednorazowych funkcji „RenderButton”, „RenderLabel”, „RenderTextBox” ... (edycja: nie daj się pomylić przez Renderujprefiks. Funkcje te wykonują również logikę za elementami sterującymi, takimi jak odpytywanie danych wejściowych użytkownika, wstawianie znaków, obsługa powtarzania znaków, gdy użytkownik przytrzymuje klawisz i tak dalej ...), które można wywołać w celu „natychmiastowego” renderowania formantu (nie t musi być natychmiast zapisany na GPU. Zwykle jest on zapamiętywany dla bieżącej ramki i sortowany na odpowiednie partie później). Biblioteka nie przechowuje dla nich żadnego „stanu”. Jeśli chcesz ukryć przycisk ... po prostu nie wywoływaj funkcji RenderButton. Wszystkie funkcje RenderXXX, które mają interakcję użytkownika, takie jak przyciski lub pole wyboru, mają zwracane wartości wskazujące, czy np. Użytkownik kliknął przycisk. Więc twój „RenderGUI” funkcja wygląda jak duża funkcja if / else, w której wywołujesz lub nie wywołujesz funkcji RenderXXX, w zależności od stanu gry, a cała logika aktualizacji danych (po naciśnięciu przycisku) zostaje wplątana w przepływ. Całe przechowywanie danych odbywa się „poza” interfejsem GUI i przekazywane na żądanie do funkcji renderowania. (Oczywiście podzieliłbyś duże funkcje na kilka funkcji lub używałbyś abstrakcyjnych klas do grupowania części gui. Nie piszemy już kodu jak w 1980 roku, prawda?))
Teraz odkryłem, że Unity3D faktycznie stosuje to samo podstawowe podejście do swoich wbudowanych systemów GUI. Prawdopodobnie jest też kilka GUI z takim podejściem?
Nadal… kiedy się rozglądasz, wydaje się, że silna tendencja do zachowywania systemów GUI? Przynajmniej nie znalazłem takiego podejścia, z wyjątkiem Unity3D, a oryginalna społeczność IMGUI wydaje się raczej .... cicha.
Czy ktoś pracował z obiema pomysłami i miał jakieś mocne zdanie?
Edycja: Najbardziej interesują mnie opinie wynikające z rzeczywistych doświadczeń. Myślę, że na forum IMGUI toczy się wiele gorących dyskusji na temat wszelkich „teoretycznych słabości” bezpośredniego podejścia do GUI, ale zawsze uważam, że lepiej jest wiedzieć o słabościach w świecie rzeczywistym .
Odpowiedzi:
Nie. Zrobiłem płatną pracę dla gamedev nad okropnym GUI w trybie zatrzymanym i okropnym GUI w trybie natychmiastowym i chociaż oba sprawiły, że chciałem oderwać oczy, podejście w trybie zatrzymanym jest nadal zdecydowanie lepsze.
Wady trybu natychmiastowego są liczne:
Interfejsy GUI w trybie natychmiastowym są kuszące dla samotnych programistów, którzy chcą szybkiego systemu HUD, i do tego celu są świetne. Jeśli chodzi o cokolwiek innego ... po prostu powiedz nie.
źródło
Jako osoba z pięcio- lub sześcioletnim doświadczeniem z IMGUI na komputerze czuję się zobowiązany do jej obrony. Wszystkie odpowiedzi (i samo pytanie) oparte są na bardzo ograniczonej definicji IMGUI i wydaje się, że istnieje wiele wątpliwych twierdzeń.
Wiele z nich wynika z założenia, że biblioteka IMGUI nie może przechowywać żadnych danych pod maską. To wcale nie jest prawda. Wyrafinowana biblioteka IMGUI faktycznie zachowa tyle danych, co równoważna biblioteka RMGUI. Różnica polega na tym, że biblioteka IMGUI głównie buforuje wyniki, podczas gdy biblioteka RMGUI utrzymuje autorytatywny stan. W IMGUI aplikacja udostępnia funkcję, która przyjmuje bieżący stan modelu i tworzy interfejs GUI dla tego stanu. To jest autorytatywna definicja GUI. Biblioteka jest odpowiedzialna za upewnienie się, że ekranowe GUI odpowiada wynikowi tej funkcji. Nie oznacza to, że musi w pełni ocenić tę funkcję dla każdej ramki i całkowicie przebudować GUI. Właśnie o to chodzi w buforowaniu.
Z tym widokiem IMGUI można w rzeczywistości wykonać zaawansowany układ, a wydajność jest całkiem rozsądna. Możesz również zachować aktualny stan autorytatywny, jeśli ułatwia to interfejs. Celem IMGUI nie jest sprawienie, aby aplikacja zachowała wszystko. Aplikacja prawdopodobnie nie chce przechowywać pozycji każdego paska przewijania oraz stanu rozwiniętego / zwiniętego każdego węzła drzewa. Chodzi o to, aby móc proceduralnie określać zawartość tych węzłów drzewa i ich istnienie.
Przejrzałbym i odpowiadał na wszystkie krytyki IMGUI punkt po punkcie, ale tak naprawdę nie rozumiem ich wielu. Większość z nich wydaje się wynikać z pewnego fundamentalnego przekonania, że IMGUI jest hackerskie, a RMGUI ma dobrą strukturę, co, jak sądzę, wynika z powyższych nieporozumień. Nie ma nieodłącznego powodu, dla którego biblioteki IMGUI powinny mieć problemy ze złożonością lub być zaśmiecone globalsami lub mieszać logikę z prezentacją. Powiem, że zalety IMGUI stają się mniej ważne, im bardziej twórcy kierują się twoimi treściami, ale nie jestem pewien, dlaczego byłoby tak gorzej w przypadku treści kierowanych przez artystów. (Nie używam osobiście treści kierowanych przez artystów, oprócz arkuszy stylów do takich rzeczy, jak kolory, rozmiary czcionek / ramek itp., Ale nie rozumiem, dlaczego trudniej byłoby je wdrożyć niż w przypadku RMGUI.)
Moim zdaniem IMGUI jest bez wątpienia dobrą rzeczą. Daje ci znacznie lepszy model do rozumowania na temat GUI. Staje się znacznie bardziej funkcjonalny i reaktywny, a ty zminimalizujesz ilość stanu zmiennego, o którym musisz myśleć. Z drugiej strony implementacja wyrafinowanej biblioteki IMGUI jest sporym wyzwaniem i zdecydowanie trudniejszym niż implementacja równoważnej biblioteki RMGUI. Jest to kompromis między złożonością biblioteki a złożonością aplikacji. Jeśli planujesz budować złożone aplikacje (lub nawet wiele prostych aplikacji), kompromis jest bardzo dobry. Jeśli robisz to dla jednej aplikacji i jest to dość proste, prawdopodobnie osiągniesz swój cel szybciej dzięki RMGUI.
W każdym razie powodzenia!
źródło
I tak posunąłbym się do stwierdzenia, że nie stanowi to biblioteki GUI. To tylko kilka funkcji renderowania.
Renderowanie GUI jest najłatwiejszą częścią tworzenia GUI. Weźmy na przykład pole tekstowe. Założę, że jest to najłatwiejszy możliwy przypadek: proste pole tekstowe z jednym wierszem.
RenderTextBox
po prostu narysuje pole tekstowe. Wykona prosty pomiar tekstu i narysuje glify na ekranie. Będzie nieMogę iść dalej, ale myślę, że mój punkt widzenia jest jasny. Jeśli chcesz
RenderTextBox
narysować pole tekstowe, dostaniesz tylko statyczne, niefunkcjonalne pole tekstowe. Wszelkie rzeczywiste funkcje muszą być zaimplementowane przez Ciebie.Robisz pomiary tekstu i rysujesz glify? To łatwa część pracy z GUI. GUI oznacza „graficzny interfejs użytkownika”. Ostatnie dwa słowa, w których bierzesz wkład od użytkownika i robisz z nim coś, są trudną częścią.
Gracze oczekują, że sterowanie GUI będzie działało rozsądnie. W środowisku PC (np. Mysz i klawiatura), jeśli gracze widzą pole tekstowe, oczekują, że będzie działać jak zwykłe pole tekstowe. Oczekują, że będą mogli się w nim poruszać za pomocą klawiszy strzałek, przeskakiwać do przodu i kończyć domem i usuwać, wybierać w nim litery itp.
Oznacza to, że będziesz musiał zaimplementować cały ten kod interfejsu użytkownika. Musisz sprawdzić, która kontrola została kliknięta. Następnie musisz zrobić coś, na podstawie którego kliknięto kontrolkę. Musisz mieć pojęcie „aktywnej” kontroli, takiej, która pobiera dane z klawiatury. Różne elementy sterujące muszą reagować w różny sposób na różne rodzaje interakcji użytkownika.
Zasadniczo będziesz potrzebować
TextBoxWindow
klasy.Załóżmy, że wdrażasz ekran opcji gry. Masz więc różne grupy opcji: rozgrywkę, grafikę, dźwięk, sterowanie itp. Po lewej stronie ekranu masz listę różnych grup. Każda grupa zawiera szereg elementów sterujących, którymi użytkownik może manipulować. Co się stanie, gdy użytkownik naciśnie klawisz Tab?
Zależy to od tego, jaka kontrola jest aktywna. Jeśli jeden z elementów sterujących grupy jest aktywny (został ostatnio kliknięty), wówczas przechodzi do następnej grupy. Jeśli zamiast tego jeden z elementów sterujących w grupie jest aktywny, wówczas przechodzi do następnego elementu sterującego w tej grupie. Tak właśnie ma działać system.
Tak więc nie tylko wejście klawiatury musi być aktywnym procesem sterowania, ale teraz musi farmować wszelkie nieprzetworzone dane wejściowe dla kontrolki, która jest jego właścicielem . Albo to, albo elementy sterujące muszą znajdować się na jakiejś połączonej liście, aby można je było przewijać do przodu i do tyłu. Oznacza to, że w każdej kontrolce musi być domyślne zachowanie.
To brzmi coraz bardziej jak zachowane GUI, prawda? Jedyna różnica polega na tym, że ... to ty wykonujesz ciężką pracę. Używanie cudzego GUI powinno być sposobem na wykonanie mniejszej pracy. Ale wysiłek, jaki należy podjąć, aby GUI zareagował zgodnie z oczekiwaniami użytkownika, nie jest trywialny.
Pamiętaj: interfejs użytkownika nie jest dla ciebie, jest dla twoich użytkowników . To jest dla gracza. Powinno zachowywać się tak, jak tego oczekują. A jeśli nie, to nie powiodło się.
To ograniczona i ograniczająca myśl.
Mógłbym dać gadkę o różnych gier mających różne potrzeby UI, że niektóre gry zrobić potrzebę Resizeable okien i tak dalej. Ale jest o wiele większy problem.
Twój sposób myślenia prowadzi do problemu Gratuitous Space Battles.
Jeśli nigdy nie grałeś w tę grę, jest to tania, niezależna i bardzo obciążająca GUI gra. Właściwa rozgrywka odbywa się w GUI (jest to gra typu „ustaw jednostki i patrz, jak walczą”). Problem?
GUI NIE MOŻNA ODWOŁAĆ!
Och, w porządku, jeśli używasz normalnego monitora lub masz normalny wzrok. Ale jeśli na przykład jesteś mną, który prowadzi absurdalnie ogromny monitor (taki tak duży, że system Windows przeskalowuje rozmiar całego tekstu, aby można go było przeczytać), to jest okropne . Tekst jest mikroskopijny. Nie ma możliwości zmiany rozmiaru czcionki.
To prawda, że GSB to gra oparta na GUI, więc jest dla nich absolutnie niewybaczalna. Gra z graficznym interfejsem użytkownika może prawdopodobnie uciec.
Nigdy nie zakładaj, że użytkownik patrzy na swoją grę dokładnie tak , jak Ty. Takie postępowanie jest głupotą.
Posiadanie systemu GUI, który może przeskalować się do rozdzielczości użytkownika, prawdziwie niezależny od rozdzielczości GUI, wymaga, aby układ był częścią samego systemu GUI. Jeśli tego nie zapewnisz, twój tekst będzie wyglądał na mały na czyimś gigantycznym monitorze. To nie jest dobra rzecz.
Powiedziałbym więc, że twoje myślenie na ten temat jest krótkowzroczne. Ci nie muszą takie rzeczy. Potrzebujesz tego, co zapewniają zachowane GUI. Och, możesz nie potrzebować wszystkiego, co zapewnia dany system GUI. Ale potrzebujesz trochę tego.
Podstawowa praca, którą musisz wykonać, aby uzyskać dane do systemu, to niewielka cena, którą musisz zapłacić, oprócz posiadania wystarczającej pewności, że użytkownik może poprawnie współpracować z elementami sterującymi i że zmieni rozmiar, aby dopasować się do potrzeb gracza.
Być może będziesz w stanie uciec z graficznym interfejsem użytkownika w trybie natychmiastowym, jeśli nie pobierzesz dużo danych od odtwarzacza. Ale to by było na tyle.
źródło
RenderTextBox
była funkcja , a nie klasa. Więc twój podział na „tryb natychmiastowy” i „tryb zatrzymany” wydaje się dotyczyć miejsca przechowywania danych, a nie tego, kto nimi zarządza. Jeśli tak, musisz uwydatnić to rozróżnienie w swoim pytaniu, ponieważ sugeruje, że „GUI trybu natychmiastowego” po prostu rysuje rzeczy na ekranie. Że nie może uzyskać danych wejściowych (ponieważ wymagałoby to trwałego stanu) i tak dalej. Więc co to jest?Jakiś czas temu zainteresowałem się również IMGUI, jako test napisałem kilka samouczków, aby zapoznać się z technikami (zacznij tutaj, jeśli jesteś zainteresowany, ale to niewiele więcej niż C # / XNA + oryginalny „samouczek” tutaj ) .
Naprawdę bardzo mi się podobały IMGUI, ponieważ wyświetlane przez nich informacje są zawsze aktualne i poprawne (ponieważ nie ma stanu, w którym zawsze podajesz rzeczywiste dane). Ale w końcu straciłem zainteresowanie, więc normalnie teraz znów używam zachowanych GUI.
W końcu pomyślałem, że bezpośredniość GUI utrudnia oddzielenie GUI od danych i czasami starałem się rozdzielić rzeczy wprowadzając jakiś stan, łamiąc elegancję IMGUI. Nie sądzę też, że pisanie kodu dla zachowanych GUI to więcej pracy niż pisanie kodu dla IMGUI i podoba mi się, że zachowane GUI można uruchomić i zapomnieć, a ja naprawdę lubię wydarzenia :).
Jednak za każdym razem, gdy ktoś wspomina o IMGUI, gdy coś we mnie chce spróbować jeszcze raz i starać się to naprawić, ponieważ naprawdę jest w tym trochę elegancji, nie jestem w 100% pewien, czy to zawsze ułatwi ci pracę. Powiedziałbym, że wypróbuj, może spróbuj tak samo, jak to zrobiłem i napisz kilka samouczków (znajduję najlepszy sposób, aby nauczyć się nowych technik, wyjaśniając je innym).
źródło
Dużo pracowałem z systemem GUI Unity3D, który działa tak, jak opisano. I muszę powiedzieć, że nienawidzę tego z pasją.
Podejście „natychmiastowe” działa naprawdę dobrze w niektórych przypadkach. Po pierwsze, gdy potrzebujesz tylko kilku przycisków i etykiet - na przykład HUD strzelanki. Tworzenie ich przy użyciu „zatrzymanego” podejścia nie jest bardzo trudne, ale jest bardzo łatwe dzięki UnityGUI. Drugi przypadek ma miejsce, gdy trzeba wypchać wiele elementów sterujących na ekranie, ale tak naprawdę nie obchodzi ich układ. Zwłaszcza, gdy dokładne kontrole są znane tylko w czasie wykonywania. Nie jest to w rzeczywistości przydatne w żadnej grze, ale jest bardzo pomocne, gdy narzędzia działają w edytorze Unity.
Jednak jakikolwiek na wpół złożony GUI - na przykład RPG (MMO) lub gra strategiczna - nieuchronnie zamienia się w okropny bałagan nieczytelnego kodu, pełnego specjalnych przypadków i mnóstwa sposobów. Podczas budowania takiego GUI zwykle masz dość specyficzny układ, który musi działać dla dowolnej rozdzielczości i być w stanie pokazać wszystkie dane, które wysyłasz. Potrzebujesz rzeczy, takich jak na przykład przesunięcie niektórych przycisków niżej, aby zrobić miejsce na dłuższy tekst. Dzięki „zachowanemu” GUI możesz mieć kontrolę, która robi to automatycznie. W trybie „natychmiastowym” nie ma żadnych elementów sterujących, więc musisz zrobić wszystko jawnie.
Innym problemem związanym z UnityGUI (nie jestem pewien, czy dzieje się to ze wszystkimi GUI trybu natychmiastowego) jest to, że na przykład
Button()
połączenie działa bez uwzględnienia innych wywołań GUI. Tak więc, jeśli jeden przycisk znajduje się na drugim, kliknięcie spowoduje naciśnięcie obu przycisków jednocześnie. To zdecydowanie nie jest to, czego oczekuje użytkownik, więc dodaje to kolejny specjalny przypadek do obsługi.Aby żyć z tym wszystkim, zawsze pisałem własne opakowanie wokół UnityGUI, które zamienia go w system trybu „zatrzymanego”: elementy sterujące, które przechowują swój stan i po prostu wywołują
GUI
dla mnie wymagane funkcje dla każdej ramki.Nie mogę powiedzieć, że tryb „natychmiastowy” jest zdecydowanie gorszy niż „zachowany”, ale z pewnością czuję się znacznie lepiej pracując w trybie „zatrzymany”.
źródło
Będę tutaj bronić IMGUI, ponieważ niektóre z wcześniej wymienionych problemów można rozwiązać, jeśli zechcesz napisać dla niego kod. Dodatkowo, jeśli napiszesz dla niego kod, będziesz mieć solidną bibliotekę, którą można ponownie wykorzystać i tylko bardzo rzadko wymaga modyfikacji.
być może początkowo wydaje się, że artyści nie mają dużej elastyczności dzięki IMGUI, jest to rozwiązane lub ulepszane poprzez ładowanie układu GUI ze schematu xml lub JSON.
ten problem został rozwiązany przy sortowaniu, powinieneś utworzyć klasę UI Manager, która sortuje kolejność losowania, rozwiązałem ten problem w C # XNA z ruchomymi, klikalnymi panelami, które rysują jeden na drugim. Mogę opublikować pseudo kod, jeśli zostanie o to poproszony.
czy możesz narysować ciągi z tablicy? czy możesz zdefiniować obszary prostokąta na podstawie wysokości i szerokości czcionek tych ciągów? czy możesz stworzyć stosunek wielkości przycisku przewijania do wysokości wszystkich tych prostokątów zsumowanych? Następnie jesteś w połowie drogi do utworzenia pola listy z przewijalnym przyciskiem, aby poruszać się w górę lub w dół. Myślałem, że to będzie trudne, ale okazało się, że jest o wiele prostsze niż myślałem. bez buforów, bez stanów, bez wywołań zwrotnych.
nie pozwól, aby poszczególne panele, przyciski, pola przechowywały dane. Wiem, że moja pierwsza próba IMGUI była natychmiastowa, ale tylko w sensie graficznym. Popełniłem błąd zatrzymując dane w interfejsie użytkownika. To była niemal filozoficzna zmiana, aby inaczej myśleć o tym, gdzie umieścić i zmodyfikować dane za pomocą zmian interfejsu użytkownika.
to jest granica i funkcjonalność kodu SDK, a nie twój. Uważam, że SDK UI (Hammer, Unity, UDK) to prawie nowy język do nauki. W rzeczywistości są one podobne do mnie jak Windows. Formaty, oba są skonfigurowane dla najszerszej możliwości i dlatego mają dodatkową wagę w złożoności.
i na koniec obejrzyj to> http://mollyrocket.com/861
źródło
To zależy od gatunku i gry. Dobry system zarządzania zapasami jest niezbędny w większości gier RPG. Będziesz chciał czegoś, co obsługuje układy dla Ciebie, obsługuje paski przewijania, przeciągnij i upuść, podpowiedzi i inne funkcje o wiele łatwiejsze do zaimplementowania przy użyciu Zachowanego GUI.
Nie mogę wymyślić sposobu przeciągnięcia i upuszczenia za pomocą natychmiastowego interfejsu GUI, który nie wymaga dużo żonglowania lub oszczędzania stanu użytkownika, takich jak tymczasowa zmiana bufora Z i przechowywanie informacji o czasie w celu wykluczenia kliknięcia, które zdarzyło się przenieść kawałek.
Ale z perspektywy projektowania mam problem z wyobrażeniem sobie świata bez moich wydarzeń z GUI. Natychmiastowe GUI nie jest kompatybilne z OOP, co zmusza Cię do programowania proceduralnego.
Prostota zapewniana przez bezpośrednie interfejsy GUI wiąże się z kosztem dodatkowej złożoności kodu, który szybko rośnie wraz ze wzrostem złożoności wymaganej przez interfejs użytkownika; gdzie jako dobrze zachowane GUI jest początkowo bardziej złożone, ale skaluje się lepiej. Tak więc poniżej pewnego poziomu złożoności wystarcza bezpośredni GUI, ale w większości sytuacji wolę zachować GUI.
źródło