Wczytywanie dużych bitmap oraz stworzenie własnego systemu okienek

1

Cześć,
naszło mnie ostatnio żeby zrobić własną mini gierkę. Wygląda to już całkiem fajnie. Piszę grę wyścigową coś w stylu F1 (screen w załączeniu).
Mam jednak 2 dylematy:

  1. Cała mapa jest w postaci jednej sporej bitmapy. Podczas jej wczytywania otrzymuję błąd "out off memory". Robię to w ten sposób:
track := TBitmap.Create;
track.PixelFormat := pf24bit;
track.LoadFromFile('trasa.bmp');
track.width := 13000;
track.height:= 7000;

Czy możecie podpowiedzieć mi co jeszcze zrobić żeby móc wczytywać "dowolnie" duże obrazy?

  1. Sam wyścig mam praktycznie ogarnięty. Dotarłem do momentu gdzie muszę stworzyć całe GUI. Wszystko rysuję na komponencie stworzonym w taki sposób:
type
  TMyDrawingControl = class(TCustomControl)
  public
    procedure EraseBackground(DC: HDC); override;
    procedure Paint; override;
  end;

Czy jedynym rozwiązaniem żeby rysować samodzielnie okna jest stworzenie od zera całego managera okien ich budowania, ogarniania eventów itp.? Czy istnieje może już jakieś gotowe rozwiązanie do takich zabaw?
A może wygodniej będzie stworzyć zwykłe formy, kolorować je i wyświetlać użytkownikowi?

Proszę o rady :)racingScreen.png

5
karpov napisał(a):
  1. Cała mapa jest w postaci jednej sporej bitmapy. Podczas jej wczytywania otrzymuję błąd "out off memory". Robię to w ten sposób:

Zacznijmy od samego początku — co to znaczy „sporej”? Bo o ile nie masz 128MB RAM, to nie powinieneś mieć żadnego problemu z samym jej załadowaniem do pamięci. Podaj konkretne informacje, żeby było dokładnie wiadomo na czym stoimy.

Czy możecie podpowiedzieć mi co jeszcze zrobić żeby móc wczytywać "dowolnie" duże obrazy?

Dowolnie dużych nie da rady — zawsze limitem będzie ilość dostępnej pamięci. Co najwyżej możesz pójść w stronę retro-sposobów, czyli zamiast jednej bitmapy tła, użyć dziesiątek/setek małych kafli, z których cała mapa będzie budowana. Wtedy mapa to będzie po prostu dwuwymiarowa macierz indeksów kafli (tekstur), co jest bardzo proste do zaprogramowania oraz drastycznie zmniejsza zapotrzebowanie na pamięć. Im bardziej niepowtarzalne elementy mapy, tym więcej kafli.

Nie wiem jakich klas używasz do obsługi bitmap, dlatego napisz dokładnie co jest Twoim obecnym API.

  1. Sam wyścig mam praktycznie ogarnięty. Dotarłem do momentu gdzie muszę stworzyć całe GUI. Wszystko rysuję na komponencie stworzonym w taki sposób:

Czyli Twoim API jest LCL — no nie za dobrze. Obstawiam, że będziesz miał spore problemy z wydajnością (szczególnie na uniksach, bo nie mają GDI+, a LCL używa go domyślnie pod Windows i daje to niemałe przyspieszenie), albowiem renderowanie grafiki przez CPU jest cholernie powolne i nijak nie umywa się do zdolności GPU. Tak więc zanim zaczniesz robić kolejne kroki, zmień samo podejście (czytaj niżej).

Czy jedynym rozwiązaniem żeby rysować samodzielnie okna jest stworzenie od zera całego managera okien ich budowania, ogarniania eventów itp.?

Nie, ale możesz tak robić, jeśli lubisz klepać tysiące linijek kodu w nieskończoność. Tzn. jeśli masz konkretny powód do własnej implementacji całego ekosystemu to go sobie stwórz, ale jeśli nie, to lepiej tego nie rób, bo to pochłonie mnóstwo czasu.

Czy istnieje może już jakieś gotowe rozwiązanie do takich zabaw?

Oczywiście, że istnieją. Masz do dyspozycji takie biblioteki jak Allegro, PasSFML czy potężny ZenGL, które zawierają wszystko czego potrzebujesz do stworzenia pełnoprawnej gry, a przede wszystkim wsparcie DirectX/OpenGL.

Oczywiście tych API/silników jest znacznie więcej. Wszystkie mają bindingsy dla FPC lub są napisane we Free Pascalu (jak ZenGL). Możesz też skorzystać z SDL — też są bindingsy dla FPC. Tej biblioteki użyto do stworzenia m.in. gry Braid — nie w mąkę pierdnął.

2

Sprawdziłem na szybko u siebie i u mnie bmp o takim rozmiarze jaki masz podane z takim samym pixel formatem (260MB) wczytuje się bez żadnego błędu. Może jakaś inna aplikacja (np. przeglądarka) zjadła Ci cały dostępny RAM? Ja mimo wszystko podzieliłbym mapę na mniejsze części. W ogóle tło bym przechowywał jako jpg, a sprite'y jako png. Wiem, że nikt w tych czasach się już nie przejmuje wielkościami plików, ale jakoś do bmp czuję taki jakiś niesmak ;)
Nie wiem jak bardzo złożone chcesz zrobić GUI, ale oprogramowanie kilku obrazków jako przyciski menu itp. nie powinno zająć dużo czasu nawet robiąc to od podstaw.

3
Arthan napisał(a):

W ogóle tło bym przechowywał jako jpg, a sprite'y jako png. Wiem, że nikt w tych czasach się już nie przejmuje wielkościami plików, ale jakoś do bmp czuję taki jakiś niesmak ;)

Z tym wiąże się pewna właściwość mechaniki klas obrazów, jeśli chodzi o LCL (a to jest obecnie bazowym API).

Nie wiem jak w przypadku JPG (bo tego dziadostwa nie używam nigdzie), ale PNG, po załadowaniu do pamięci, nadal jest skompresowany i zajmuje niewiele pamięci. Aby go dekompresować, należy go po prostu użyć — coś na nim namalować, albo namalować go na innym obrazie. Podczas takiego pierwszego użycia, dane obrazu zostają odkodowane i zostaje zaalokowana pełna ilość pamięci (czyli tyle, ile dla zwykłej bitmapy o takim samym rozmiarze).

W pewnych zastosowaniach jest to istotne, dlatego że dekompresja obrazu trochę trwa, dlatego pierwsze wykorzystanie obrazu PNG powoduje lag, a kolejne już nie. Natknąłem się na to przez przypadek, gdy pracowałem nad projektem CTCT. W nim, tła dla scen to obrazy PNG o rozmiarze 1280×720, po zmianie sceny, odpowiedni obraz jest malowany na płótnie okna. I zawsze pierwsza zmiana sceny odmalowywała okno ze sporym opóźnieniem (rzędu 500ms), przez co nie zgrywało się to z timingiem przejść.

Problem rozwiązałem w ten sposób, że nadpisałem metodę LoadFromFile, w której tuż po załadowaniu obrazu z pliku, malowałem go na tymczasowej bitmapie, co wymuszało dekompresję. Program startował nieco dłużej, jednak później każdorazowa aktualizacja płótna okna trwała dokładnie tyle samo i nie miałem problemu z synchronizacją przejść.

Taka mała ciekawostka.

Nie wiem jak bardzo złożone chcesz zrobić GUI, ale oprogramowanie kilku obrazków jako przyciski menu itp. nie powinno zająć dużo czasu nawet robiąc to od podstaw.

Menu nie jest jakoś szczególnie trudne do zaprogramowania — tym bardziej, jeśli ma być obsługiwane tylko z poziomu klawiatury. To też zależy od tego, jakie elementy ma posiadać, bo czym innym jest oprogramowanie pozycji niezmiennych, a czym innym pozycji pozwalających coś wybrać (gdy np. strzałki góra/dół służą do poruszania się po menu, a strzałki lewo/prawo do zmiany wartości konkretnych opcji), a jeszcze czym innym obsługa menu drzewiastych, widocznych po kilka jednocześnie (w postaci małych okienek).

Jeśli potrzebne jest dość zaawansowane menu, to co nieco można podpatrzeć z tego filmiku:

Natomiast jeśli chodzi o dużo prostsze menu gry, bez scrollowania itd., to polecam poczekać dzień/dwa, bo właśnie takie implementuję w swojej gierce i jeśli ktoś jest chętny, to mogę opublikować źródła do wglądu (pełne źródła i tak będą dostępne na GitHub, ale dopiero jak skończę cały projekt). ;)

screenshot-20210602142811.png

0

Dzięki chłopaki za Wasze odpowiedzi. Na Was zawsze można liczyc :)

Przepraszam za opóźnienie z odpowiedzią ale kupa roboty ostatnio i nie mam czasu usiąść nad swoim projektem.

W pierwszej kolejności przepiszę projekt na jakąś bibliotekę, którą zaproponował @furious programming. Sprawdzę jakiego kopa fpsów wszystko dostanie. Pomyślę potem nad podzieleniem mapy na jakieś mniejsze fragmenty.

Co do okien to zauważyłem, że nawet nowoczesne gry aktualnie rezygnują z myszki. Np. w F1 2020 wszystko obsługuje się klawiaturą, każde okno, zakładkę, ustawienia zmienia się strzałkami na klawiaturze i przyciskami F1, F2 itp.

Moja gierka ma być czymś na kształt managera więc zastanawiam się czy to zdałoby to egzamin i czy nie będzie zbyt uciążliwe w obsłudze. W pierwszej wersji nie chce sobie utrudniać za bardzo i może zrobię obsługę klawiaturą i potem pomyślimy. W końcu jestem zdesperowany żeby to był pierwszy projekt który ukończę ;)

2
karpov napisał(a):

W pierwszej kolejności przepiszę projekt na jakąś bibliotekę, którą zaproponował @furious programming. Sprawdzę jakiego kopa fpsów wszystko dostanie.

Powiem tak — obecnie klepię sobie kolejne narzędzie tetrisowe (w poprzednim poście podałem kilka prototypów). Tylny bufor ma rozmiar 256×240 pikseli, renderuję na nim tło i inne małe pierdy, a następnie wyświetlam w oknie, na pełen ekran. Obecnie w ten sposób jestem w stanie przetworzyć około 120-140 klatek na sekundę, na 8-letnim, biznesowym laptopie. Gdybym renderowanie przerzucił na GPU, dostałbym co najmniej 5.000 klatek na sekundę, a na współczesnym pececie, co najmniej kilkanaście tysięcy fps.

O ile nie zwalisz logiki, to wróżę co najmniej kilkaset fps w gotowym produkcie. ;)

Co do okien to zauważyłem, że nawet nowoczesne gry aktualnie rezygnują z myszki.

Obsługa menu z poziomu muszy jest istotna w trzech przypadkach, czyli gdy:

  • głównym urządzeniem sterującym w rozgrywce jest mysz,
  • menu są na tyle skomplikowane/nietypowe, że obsługa z poziomu klawiatury/kontrolera trwałaby wieki i byłaby niewygodna,
  • menu mają dużo opcji i ich przeznaczenie powinno zapewniać szybką zmianę konfiguracji.

Jeśli rozgrywka nie potrzebuje myszy (bo gra się klawiaturą lub kontrolerem), a menu nie mają miliardów opcji (jak w strategiach czasu rzeczywistego), to obsługa myszy w ogóle nie jest potrzebna i nie ma nawet sensu jej implementować. Tak więc zastanów się nad powyższymi punktami i sam zdecyduj, czy jest Ci to w ogóle potrzebne.

1

@furious programming: tego faceta z filmiku, który załączyłeś to znam. Często oglądam jego filmy. Akurat tego nie widziałem i super, że zrobił tam obsługę okien za pomocą myszki. Kiedyś to zaimplementuje ale chyba na razie ograniczę się do klawiatury dla ułatwienia.

Zacząłem przepisywać program. Wybrałem SDL. Na początku miałem problem z wczytywaniem mapy bo dostawałem błąd "Texture dimensions are limited to 8192x8192". Doczytałem gdzieś, że karta graficzna ma ograniczenia co do wielkości tekstur.

W końcu jednak udało mi się wymusić używanie sprzętowej akceleracji oraz korzystanie z opengl. Dzięki temu mogę wczytać dużą mapę i ją wyświetlić.

Jak tylko skończę to dam znać jak poprawiła się liczba fpsów :) Sam jestem mega ciekawy :)

1
karpov napisał(a):

Zacząłem przepisywać program. Wybrałem SDL. Na początku miałem problem z wczytywaniem mapy bo dostawałem błąd "Texture dimensions are limited to 8192x8192". Doczytałem gdzieś, że karta graficzna ma ograniczenia co do wielkości tekstur.

I nadal nie napisałeś, jaka jest obecna rozdzielczość tej mapy. Boisz się o tym napisać? Przecież nie ma się czego wstydzić. ;)

W końcu jednak udało mi się wymusić używanie sprzętowej akceleracji oraz korzystanie z opengl.

No i elegancko. SDL i inne API nie są jakoś szczególnie łatwe w użyciu, ale tutoriali (również dla Pascali) nie brakuje.

0

@furious programming: hehe umknelo mi, mapa ma rozdzielczość 13000x7000. W sumie nie jest jakoś nadzwyczajnie wielka ale dostatecznie żeby juz sprawić kłopoty :)
Na szczęście póki co problem rozwiązany. Zrobię z ciekawości test czy uda się załadować np dwa razy wieksza.

1

Update z placu boju. Cały czas przepisuję apkę. W sumie nową obsługę grafiki mam już zrobioną, muszę tylko przystosować kod. Pod spodem nie ma jeszcze logiki, wyświetlane jest tylko tło ale...... wow..... just wow ;)
fps.PNG

Zajmie mi to trochę dłużej bo chcę posprzątać stary kod :)

Wziąłem się też za obsługę okien. Uświadomiłem sobie, że przecież nie trzeba okien przesuwać itp. a jedynie ustalić im konkretne miejsce i "przypiąć".

Napisałem więc prostego managera okien. Wczytuje plik json gdzie jest ustalona cała struktura okien. Każde okno ma własne tło oraz listę itemsów będących buttonami. Każdy button może przyjąć dwa stany, albo zwykłu albo onHover oraz ma przypisaną pozycję względem swojego parenta. Póki co wygląda to świetnie :) Muszę dorobić jeszcze klikanie buttonów i wyzwalanie w związku z tym akcji np. włączenie nowego okna/zakładki.

Każdy item będzie w przyszłości mógł być czymś innym niż buttonem np. polem tekstowym etc. Poniżej przykładowy json i filmik z działaniem.

{
	"numberOfWindows":1,
	"okno0": {
		"ID" : 0,
		"size":{
			"width":400,
			"height":200
		},
		"position":{
			"x":50,
			"y":50
		},
		"bg":"windows/okno1/windowBG.png",
		"canClose":false,
		"itemsCount":1,
		"items":{
			"0":{
				"type":"button",
				"bg":"windows/okno1/button0.png",
				"onHoverBg":"windows/okno1/button0Hover.png",
				"onClickType":"goToWindow",
				"onClickID":2,
				"size":{
					"width":150,
					"height":50
				},
				"position":{
					"x":40,
					"y":40
				}
			}
		}
	}
}

2
furious programming napisał(a):

O ile nie zwalisz logiki, to wróżę co najmniej kilkaset fps w gotowym produkcie. ;)

Miałem rację — framerate wystrzelił w kosmos. :D


karpov napisał(a):

Wziąłem się też za obsługę okien. Uświadomiłem sobie, że przecież nie trzeba okien przesuwać itp. a jedynie ustalić im konkretne miejsce i "przypiąć".

Nie do końca. A co, jeśli rozdzielczość gry się zmieni, np. na mniejszą? Jeśli już robisz menedżer okien, to daj tym okienkom więcej funkcji, a użytkownikowi swobodę — niech może sobie łapać za okienko i przesuwać. Tobie pozostanie jedynie zapis ustalonych współrzędnych okien do pliku konfiguracyjnego, gdzieś pod koniec sesji, tak aby w nowej sesji wszystko wróciło na swoje miejsce.

Samo przesuwanie okien to bardzo prosta matematyka, więc nie ma się czego obawiać. Po wciśnięciu przycisku myszy, zapisujesz do zmiennych odległość kursora od lewego górnego rogu oraz zapalasz flagę przeciągania (trzy zmienne są potrzebne), a podczas przesuwania pobierasz współrzędne kursora, odejmujesz od nich zachowaną różnicę i otrzymujesz nowe współrzędne lewego górnego rogu okna. Przy zwolnieniu przycisku myszy, resetujesz zmienne. I to cała magia.

W powyższy sposób możesz przesuwać okna, łapiąt je w dowolnym miejscu. Jeśli chcesz mieć belkę do przesuwania, to flagę przeciągania zapal tylko wtedy, kiedy kursor znajduje się w obszarze belki.

Napisałem więc prostego managera okien. Wczytuje plik json gdzie jest ustalona cała struktura okien. Każde okno ma własne tło oraz listę itemsów będących buttonami. Każdy button może przyjąć dwa stany, albo zwykłu albo onHover oraz ma przypisaną pozycję względem swojego parenta. Póki co wygląda to świetnie :)

Nie wiem czy świetnie czy nie, w końcu to póki co jednokolorowe prostokąty. Ale to działa — to się liczy. ;)

Każdy item będzie w przyszłości mógł być czymś innym niż buttonem np. polem tekstowym etc. Poniżej przykładowy json i filmik z działaniem.

Robisz coś na kształt VCL? Jakiś obiekt nadrzędny, posiadający listę kontrolek dowolnego typu, wywodzącego się z jednej, bazowej klasy? Jeśli już bawić się w system okien i kontrolek, to IMO właśnie w ten sposób — dzięki temu łatwiej będzie budować okna i je edytować (a także modyfikować w trakcie działania gry, jeśli i taka potrzeba zajdzie).

0
furious programming napisał(a):

Nie do końca. A co, jeśli rozdzielczość gry się zmieni, np. na mniejszą? Jeśli już robisz menedżer okien, to daj tym okienkom więcej funkcji, a użytkownikowi swobodę — niech może sobie łapać za okienko i przesuwać. Tobie pozostanie jedynie zapis ustalonych współrzędnych okien do pliku konfiguracyjnego, gdzieś pod koniec sesji, tak aby w nowej sesji wszystko wróciło na swoje miejsce.

Ogarniam właśnie kwestię automatycznego skalowania elementów przez sdl. Jest tam taka możliwość więc chce z niej skorzystać.
Z tego co póki co zrozumiałem to wygląda to tak, że ustawiasz "oryginalną" rozdzielczość, renderujesz wszystkie elementy a kiedy rozdzielczość się zmienia to na podstawie oryginalnego obrazu sdl wszystko automatycznie skaluje.

furious programming napisał(a):

Samo przesuwanie okien to bardzo prosta matematyka, więc nie ma się czego obawiać. Po wciśnięciu przycisku myszy, zapisujesz do zmiennych odległość od lewego górnego rogu oraz zapalasz flagę przeciągania (trzy zmienne są potrzebne), a podczas przesuwania pobierasz współrzędne kursora, odejmujesz od nich zachowaną różnicę i otrzymujesz nowe współrzędne lewego górnego rogu okna. Przy zwolnieniu przycisku myszy, resetujesz zmienne. I to cała magia.

Nie wiem czy będzie mi to potrzebne bo w sumie w grach nie spotkałem się raczej z możliwością przesuwania okienek. Chyba, że dorobić taką możliwość na potrzeby np animacji rozwijania itp.

furious programming napisał(a):

Nie wiem czy świetnie czy nie, w końcu to póki co jednokolorowe prostokąty. Ale to działa — to się liczy. ;)

Te prostokąty to są grafiki wczytywane z odpowiednich folderów. Zamysł jest taki żeby móc szybko zbudować jakiś elementy menu wyświetlając tło i na nim jakieś buttony np tak jak tutaj:

screenshot-20210610155617.png

Tło i buttony już mam. Tak jak pisałem dorobię jeszcze np możliwość wpisywania tekstu w jakąś kontrolkę, strzałki zmieniające jakąś wartość/okno, czy labelki do wyświetlania wartości.

furious programming napisał(a):

Robisz coś na kształt VCL? Jakiś obiekt nadrzędny, posiadający listę kontrolek dowolnego typu, wywodzącego się z jednej, bazowej klasy? Jeśli już bawić się w system okien i kontrolek, to IMO właśnie w ten sposób — dzięki temu łatwiej będzie budować okna i je edytować (a także modyfikować w trakcie działania gry, jeśli i taka potrzeba zajdzie).

Muszę się zastanowić jak to zbudować. Teraz mam po prostu procedure z klasy manadzera, ktora rysuje tlo i buttony.

1
karpov napisał(a):

Ogarniam właśnie kwestię automatycznego skalowania elementów przez sdl. Jest tam taka możliwość więc chce z niej skorzystać.
Z tego co póki co zrozumiałem to wygląda to tak, że ustawiasz "oryginalną" rozdzielczość, renderujesz wszystkie elementy a kiedy rozdzielczość się zmienia to na podstawie oryginalnego obrazu sdl wszystko automatycznie skaluje.

Tak, skalowanie jest ”w standardzie”, ale problemem nie jest to jak skalować, a czy jest to zasadne. Bo o ile ja pisząc NES-owego klona Tetrisa, mogę sobie wszystko renderować na buforze o stałym rozmiarze (nie tyle mogę, co muszę) i wyrzucić na ekran zachowując proporcje 4:3 (i zostawić czarne pasy po bokach ekranu), o tyle Ty robić tego nie powinieneś.

Powód jest prosty — różne proporcje i rozdzielczości ekranów. Jeśli stworzysz interfejs typowo pod proporcje 16:9, a użytkownik ma monitor pionowy (np. 9:16), to po zeskalowaniu tylnego bufora do rozdzielczości docelowej, obraz będzie tak rozciągnięty, że kompletnie nieczytelny. Ale tu nie trzeba przypadków brzegowych, bo wystarczy odpalić grę na monitorze z proporcjami 4:3 i już dostaniesz takie zniekształcenie obrazu, że obraz będzie wyglądał bardzo źle.

Dlatego dostosowywanie rozmiaru i proporcji tylnego bufora musi istnieć w Twojej grze, tak jak istnieje w każdej innej, poważnej produkcji. Jest masa gier, które mają okienka, i które bez względu na rozdzielczość i proporcje obrazu, zachowują prawidłowe proporcje okien (proporcje są stałe, wymiary są dopasowane do rozdzielczości ekranu lub tylnego bufora, jeśli ten jest mniejszy niż wybrana rozdzielczość ekranu). To pozwala wyświetlać różny obszar mapy bez zniekształceń (im wyższy współczynnik proporcji, tym kamera obejmuje większy obszar mapy), a także dopasować rozmiar okien do rozdzielczości, zachowując prawidłowe proporcje tych okien.

Tak więc masz trzy rozwiązania:

  1. Stałe proporcje tylnego bufora — przy ustalaniu rozmiaru wewnętrznego bufora, respektujesz wymyślone jego proporcje (np. 16:9) i obraz renderujesz z zachowaniem tych proporcji. To powoduje, że docelowy obraz klatki (na ekranie) jest zgodny z 16:9, a pozostały obszar wypełniasz na czarno, tworząc pasy po bokach obrazu lub na górze i dole. Okna mają stałe proporcje, także ustalone z góry, a podczas renderowania skaluje się je zgodnie z rozdzielczością bufora (bez zniekształceń). Dodatkowo, w ustawieniach dajesz możliwość wyboru, czy obraz ma zachowywać proporcje i mają być renderowane na ekranie czarne pasy wypełnienia, czy ma być rozciągnięty na pełen ekran (a więc zniekształcony).

  2. Zmienne proporcje tylnego bufora — tylny bufor zawsze ma proporcje zgodne z proporcjami rozdzielczości ekranu. To powoduje, że na różnych rozdzielczościach kamera obejmuje różny, szerszy lub węższy (oraz wyższy lub niższy) obszar mapy. Okna zachowują z góry ustalone proporcje, ich rozmiar jest dopasowywany do aktualnej rozdzielczości, ale na różnych rozdzielczościach zajmują różną powierzchnię ekranu (zakładając, że okno jest kwadratowe, to na rozdzielczości 16:9 zajmują połowę szerokości ekranu, a na 4:3 zajmują — na oko — 3/4 powierzchni ekranu). W tym przypadku zawsze renderujesz obraz rozciągnięty na pełen ekran, bez wypełniania czarnymi pasami po bokach lub u góry i na dole.

  3. Stały rozmiar bufora i rozciąganie na pełen ekran — to jest to co obecnie masz w planach, czyli rozciąganie bufora na cały ekran przy hardkodowanych jego proporcjach. Jest to opcja najgorsza, bo obraz będzie zniekształcony na wszystkich rozdzielczościach, które współczynnikiem proporcji odbiegają od tych z góry ustalonych.

Nie wiem czy będzie mi to potrzebne bo w sumie w grach nie spotkałem się raczej z możliwością przesuwania okienek. Chyba, że dorobić taką możliwość na potrzeby np animacji rozwijania itp.

Animacje rozwijania okien możesz zrobić bez wsparcia ich przeciągania za pomocą myszy. Tutaj wystarczy po prostu zmieniać współrzędne i/lub rozmiar okna, mysz nie jest do tego potrzebna. Ale jeśli chcesz na przyszłość zostawić sobie taką możliwość, to każde okno też powinieneś renderować na osobnym, tylnym buforze, a podczas renderowania całej klatki, namalować tylny bufor okna na tylnym buforze klatki ze skalowaniem.

Czyli zakładając, że okno ma natywne wymiary 200×200 pikseli, to renderujesz je na osobnym buforze o rozmiarach 200×200. Następnie z danych o animacji wychodzi, że okno ma być namalowane we współrzędnych 10,10 i rozmiarze 200×50 (spłaszczone), to kopiujesz obszar okna z 0,0,200,200 do obszaru klatki 10,10,210,60 — i dostajesz całe okno ściśnięte pionowo. To tylko przykład, gdy okno ma się pojawiać w sposób animowany, z niewidocznego, poprzez coraz mniej spłaszczone pionowo, aż do kwadratowego (200×200). Mam nadzieję, że rozumiesz tę analogię.

Muszę się zastanowić jak to zbudować. Teraz mam po prostu procedure z klasy manadzera, ktora rysuje tlo i buttony.

To co powinieneś mieć to bazową klasę, która będzie reprezentować okno oraz kontrolki. Klasa ta powinna zawierać listę kontrolek osadzonych, być w stanie odbierać komunikaty myszy i klawiatury, obsługiwać je a także przekazywać stan myszy i klawiatury do wszystkich kontrolek osadzonych. Z takiej klasy powinny dziedziczyć klasy różnych kontrolek i w odpowiedni sposób implementować obsługę myszy i klawiatury — inaczej tekst, inaczej przycisk, a jeszcze inaczej pole edycyjne. Klasa okna powinna dodatkowo obsługiwać możliwość przeciągania siebie po ekranie, czyli zmianę swojego położenia i/lub rozmiaru.

Ogólnie chodzi o to, aby stworzyć sobie drzewko dziedziczenia, tak aby każdy element interfejsu przejmował funkcjonalność swojej bazowej. I o to, aby okno było korzeniem, a wszystko co w nim się znajduje, tworzyło drzewko kontrolek, gdzie komunikaty sterujące i aktualizujące stan kontrolek płynęłyby z góry (okna) do dołu (np. przycisku).

Wydaje się to dość trudne do osiągnięcia, ale wbrew pozorom jest to dosyć proste do zrobienia. Szczególnie, jeśli zależy Ci jedynie na obsłudze odpowiedników zdarzeń OnMouseEnter, OnMouseLeave, OnMouseMove, OnMouseDown, OnMouseUp, OnMouseScroll, OnKeyDown i OnKeyUp, czyli na reagowanie na ruch i wciskanie przycisków myszy, a także na wciskanie klawiszy klawiatury.

2

Bardzo długo mnie nie było ale nie marnowałem czasu. Chciałem się tylko pochwalić, że przerzuciłem się na Unity i walczę z tym samym tematem tylko, że w 3D ;) O wiele łatwiej się pracuje z Unity ponieważ wsparcie jest gigantyczne, mnóstwo skryptów, artykułów i filmów na YT więc jest się z czego uczyć :)
Jeżeli zrobię jakiś znaczący postęp to założe temat w innym dziale:)

1 użytkowników online, w tym zalogowanych: 0, gości: 1