Skocz do zawartości

Rekomendowane odpowiedzi

Ostatnia aktualizacja: 14.02.2010

Aktualna wersja: 0.6.1

 

gmapi_s.png

 

W skrócie...

GMAPI (jeszcze nie jestem pewny co do tej nazwy ;o) to biblioteka dla Visual C++ pomocna przy pisaniu bibliotek DLL do GM6.1 i GM7.0. Co takiego umożliwia ?:

  • Wywoływanie funkcji GM prosto z biblioteki DLL
  • Bezpośredni dostęp do zasobów gry poprzez przeanalizowane struktury z pamięci runnera (właściwie to to są klasy ;p) w tym min.:

    • do map bitowych (sprite'ów i backgroundów) załadowanych do pamięci przez GM
    • do plików dźwiękowych załadowanych do pamięci przez GM (sounds)
    • do danych i właściwości zasobów (np. wysokość sprite'a, bounding box, zawartość skryptu)
    • do interfejsów Direct3D: IDirect3D8, IDirect3DDevice8, IDirect3DTexture8 (tekstury sprite'ów, surface'ów i backgroundów w pamięci wideo) (GM używa wersji 8)
    • do zmiennych i tablic lokalnych i globalnych
    • do właściwości instancji (x, y, speed etc.)
W aktualnej wersji można się dostać do sprite'ów, background'ów, surface'ów, dźwięków, skryptów i innych danych (min. wskaźniki na interfejsy Direct3D, uchwyty okien, wskaźniki na funkcje GM, wersja GM...). Niedługo postaram się umożliwić Wam dostęp do innych zasobów gry i wprowadzić możliwość np. załadowania sprite'a z pliku/bitmapy w pamięci... w sumie to możecie zaproponować co mógłbym dodać (btw. coś potrzebnego do D3D byłoby mile widziane - nie znam się na tym zbytnio ;p). Niestety z braku czasu nie napisałem dokumentacji... napisałem jedynie na szybko parę przykładów i dodałem opisy klas i metod w pliku nagłówkowym. Tutaj postaram się wyjaśnić najważniejsze rzeczy.

Bibliotekę wydaję na licencji LGPL, więc każdy może sobie zobaczyć jak to jest zrobione czy tam modyfikować :)

Jak projekt będzie już w bardziej zaawansowanym stadium i znajdzie się czas to przeniosę bibliotekę na MinGW C++ i być może na Delphi również.

Korzystanie z biblioteki

Instalacja
Jedyne co musisz zrobić to wypakować zawartość archiwum do wybranego folderu i podać ścieżki do katalogów "include" i "lib" w IDE. Uruchom Visual C++, wybierz z menu "Tools" pozycje "Options... ", następnie przejdź do gałęzi "Projects and Solutions" >> "VC++ Directories" i z comboboxa "Show directories for:" wybierz "Include files" i dodaj ścieżkę do folderu "include" z GMAPI, potem z "Show directories for:" wybierz "Library files" i podaj ścieżkę do "lib". Done.

Inicjalizacja

Zanim zaczniesz pracować z biblioteką musisz włączyć plik nagłówkowy gmapi.h do jednego z plików projektu. Jeśli nie masz zainstalowanego SDK DirectX zdefiniuj GMAPI_NO_D3D przed włączeniem pliku gmapi.h (wtedy typy interfejsów D3D będą zdefiniowane jako void), czyli:

#define GMAPI_NO_D3D
#include <gmapi.h>

Teraz musisz wskazać w opcjach linkowania odpowiednią bibliotekę statyczną - wszystko zależy od ustawień biblioteki uruchomieniowej w projekcie:
gmapi-mt.lib - Multithreaded

gmapi-mt-dll.lib - Multithreaded DLL

gmapi-mt-d.lib - Multithreaded debug

gmapi-mt-d-dll.lib - Multithreaded debug DLL

Następnie musisz utworzyć główny obiekt biblioteki, czyli klasy CGMAPI. Klasa ta jest singletonem, więc tylko jedna jej instancja może być utworzona w twojej bibliotece. Do tego celu służy metoda statyczna CGMAPI::Create, przyjmująca jako argument wskaźnik na zmienną typu unsigned long - w niej zapisany zostanie wynik inicjalizacji klasy (możliwe wartości to stałe: GMAPI_INITIALIZATION_SUCCESS - gdy inicjalizacja przejdzie bezproblemowo; GMAPI_INITIALIZATION_FAILED - gdy inicjalizacja się nie powiedzie; GMAPI_ALREADY_INITIALIZED - gdy jest już utworzony obiekt klasy). Zwraca ona wskaźnik na utworzoną instację klasy który należałoby zapisać. Najlepiej zrobić to w entrypoincie DLL-a, czyli w DllMain np:
gm::CGMAPI* gmapi;

BOOL WINAPI DllMain( HINSTANCE aModuleHandle, int aReason, int aReserved ) {
  switch ( aReason ) {
    case DLL_PROCESS_ATTACH: {
      // Inicjalizacja
      unsigned long result = 0;
      gmapi = gm::CGMAPI::Create( &result );

      // Sprawdzanie wyniku
      if ( result != gm::GMAPI_INITIALIZATION_SUCCESS ) {
        MessageBox( 0, "Inicjalizacja GMAPI nie powiodła się.", 0, MB_SYSTEMMODAL | MB_ICONERROR );
        return FALSE;
      }

      break;
    }

    case DLL_PROCESS_DETACH:
      // Zwalnianie biblioteki GMAPI
      gmapi->Destroy();
      break;
  }

  return TRUE;
}

Metoda CGMAPI::destroy() zwalnia obiekt klasy z pamięci. BTW, wszystkie klasy, funkcje, stałe itd. z GMAPI zadeklarowane są w przestrzeni nazw "gm". Nie należy wywoływać funkcji GM zaraz po inicjalizacji - GMAPI zakłada haka na funkcję external_call z runnera żeby można było przechwycić wskaźnik na instancję obiektu z GM, w którym ta funkcja została wywołana. Tak, więc trzeba powrócić do GM.

Po utworzeniu obiektu klasy można wywoływać funkcje GM i oczywiście korzystać ze składowych CGMAPI.
Wywoływanie funkcji z GM

Gdy GMAPI jest już zainicjalizowane można wywoływać funkcje z GM. Wszystkie funkcje są ładnie opakowane więc wywołuje się je w prosty sposób np.
int list;
gm::CGMVariable value;

list = gm::ds_list_create();

gm::ds_list_add( list, "test" );
gm::ds_list_add( list, 12345.12345 );

value = gm::ds_list_find_value( list, 0 ); // zwroci "test" do zmiennej
value = gm::ds_list_find_value( list, 1 ); // zwroci 12345.12345 do zmiennej

gm::ds_list_destroy( list );

Klasa CGMVariable której instancja jest zdefiniowana w powyższym kodzie naśladuje zmienną z GM, więc jej wartość może być ustawiana zarówno na real (double) jak i na string (char*). Używać jej należy przy funkcjach GM zwracających stringi i funkcjach których typ wyniku jest nieznany (jak w ds_list_find_value - może zwrócić i stringa i real). Trzeba ją przekazywać też w argumentach niektórych funkcji GM z tego samego powodu... poza tym ta klasa symuluje typ string z Delphi - zajmuje się alokacja, konwersją, zwalnianiem itd. stringów wykorzystując do tego celu runnera... typ char* nie jest kompatybilny z typem delphi string, dlatego musiałem to jakoś wykombinować ^^ Aha, zawsze zapisuj wynik funkcji GM mogącej zwrócić stringa do obiektu klasy CGMVariable - ta się zajmie jego dealokacją.

Dostępne są wszystkie funkcje z wyjątkiem matematycznych i operujących na stringach (i takich jak is_real() czy string()). Dodam jeszcze że klasa ma kilka przeładowanych konstruktorów i można utworzyć jej instancję niejawnie przy argumentach funkcji GM (np. ds_list_add w powyższym kodzie w drugim argumencie zdefiniowany ma typ referencyjny CGMVariable i przy tym niejawnie tworzony jest tymczasowy obiekt z typu char* i double)... zaimplementowane są jeszcze przeładowania operatorów takich jak "++", "-=", operatorów rzutowania na double i char*, plus przeładowanie operatora << dla ostream :D

Dostęp do zasobów z runnera

Dostać do nich można się za pomocą trzech metod: za pomocą funkcji GM oczywiście (mało wydajne i mają mniej możliwości), struktur które przeanalizowałem w runnerze i za pomocą "klas dostępowych" do pamięci runnera. Korzystanie za struktur jest najbardziej wydajne i przy tym równie niebezpieczne, jak napiszę konkretną dokumentację to opiszę jak z nich korzystać. Tymczasem można korzystać z klas o których wspomniałem - są bezpieczniejsze i łatwiejsze w użyciu niż "wędrowanie" po strukturach GM-a. Dlaczego nazwałem je dostępowymi (wtf) ? Otóż, operują one całkowicie na pamięci runnera, co jest również powodem nieco pogmatwanego korzystania z nich ;o W klasie CGMAPI zdefiniowanych jest kilka obiektów takich klas: Sprites (klasa ISprites), Backgrounds (IBackgrounds), Sounds (ISounds), Surfaces (ISurfaces) i Scripts (IScripts). Wszystkie te klasy udostępniają kilka metod które przechwytują różne dane związane z poszczególnym typem zasobu GM (np. GetID() zwraca ID zasobu o podanej nazwie, GetCount() zwraca ilość zasobów tego typu) i każda z nich ma przeładowany operator [], który zwraca referencje na kolejną "klasę dostępową" - te klasy dają dostęp do danych zasobu o ID podanym w tym operatorze. Damn, ciężko to załapać... więc przykładowo:
gm::CGMAPI* gmapi;
(...)
// Powiedzmy, że w GM dodaliśmy animacje jako sprite z 5-ma klatkami,
// o nazwie spr_anim i chcemy pobrać jego szerokość
int sprAnim, spriteWidth;

// Najpierw trzeba pobrać jego ID
sprAnim = gmapi->Sprites.GetID( "spr_anim" );

// Teraz pobieramy jego szerokosc:
spriteWidth = gmapi->Sprites[sprAnim].GetWidth();

// A co jeśli sprite nie został znaleziony bo go nie dodaliśmy ? wtedy zostanie
// rzucony wyjątek z obiektem klasy EGMAPISpriteNotExist (klasa wyjątku).
// Łapiemy go oczywiście za pomocą try - catch
try {
  spriteWidth = gmapi->Sprites[sprAnim].GetWidth();
}
catch ( EGMAPISpriteNotExist& exc ) {
  exc.ShowError(); // Pokazuje komunikat z informacjami o błędzie
}

// operator [] w klasie ISprites (obiekt Sprites) zwraca typ referencyjny klasy
// ISprite - ta klasa jako jedyna z tych "wyższego poziomu" ma zdefiniowany
// kolejny obiekt takiej klasy - ISpriteSubimages (obiekt Subimages), ten
// daje dostęp do danej klatki tego sprite'a. Hm, powiedzmy, że chcemy pobrać
// wszystkie identyfikatory tekstury sprite'a (a'la sprite_get_texture z GM) i zapisać w
// vectorze:
for ( int i = 0; i < gmapi->Sprites[sprAnim].Subimages.GetCount(); i++ )
  textures.push_back( gmapi->Sprites[sprAnim].Subimages[i].GetTextureID() );

Hm, to tyle jeśli chodzi o dostęp do zasobów.

Uwagi

- Tak jak już wspomniałem, później zajmę się konkretną dokumentacją a na razie pozostaje wam IntelliSense, dokumentacja komponentów z pliku GmapiInternal.h (opisy powinny być pokazywane przy podpowiedziach funkcji) no i trzy badziewne przykłady które napisałem. W GMAPI znajdują się również stałe z GM. Oczywiście zgłaszajcie wszystkie bugi bo projekt jest w fazie testów ;P i jak coś to pytajcie. Sry za badziewny opis ;p

Download:

Tu znajdziecie wszystkie wersje (najnowsza to 0.6.2)

 

Przykłady:

 

Mój BBCode engine (
) przepisany do C++:

src:

demo:

 

Przeglądarka map bitowych:

src:

demo:

 

Test interfejsów Direct3D przerobiony na przykład (hardcodowy badziew :D):

src:

demo:

 

Przykład dostawania się do instancji i zmiennych z GM:

src:

demo:

 

Przykład dodawania/podmieniania funkcji GML:

src:

 

Test dostępu do particli i wbudowanych zmiennych:

src:

Biblioteki korzystające z GMAPI:

Odnośnik do komentarza
Udostępnij na innych stronach

Czyli to mi pozwoli pobrać mapy pixelowe spritów i backgroundów z resources GMa i wyświetlić we własnym rendererze, tak? No i to samo pytanie tyczy się dźwięków - czy też moge dobrać się tym do danych dźwięków z GMa i odtwarzać je swoim audio? Bo jak tak to mi się to baardzo przyda do PlayGate'a :D

 

EDIT: Odpowiedź już dostałem :P

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Ja tylko jestem ciekaw, kiedy Snake da nam gotowe funkcje Game Makerowe do C++, tak, żebyśmy już nie musieli gier pisać w GM, a w tym drugim, zamiast GMLa korzystając z prawdziwego "kompilowalnego" języka, a nie przerabianego w locie.

Chociaż przy naszych obecnych pomysłach jedynym problemem jest to, że GM mocno zaśmieca pamięć grafikami zużywając RAM, bo z wydajnością gry dawno daliśmy sobie radę. Nie mniej funkcje GM w wersji dla C++ ... mmm ...

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Wiesz, jakby to takie proste było. Próbowałem pisać na PSP PacMana i okazało się, że jest to kilka razy trudniejsze niż w Game Makerze. Samo wyświetlanie grafiki, to pikuś, ale stworzenie odpowiednika obiektów z GM - to już nie taka prosta sprawa. U mnie to były tablice z pozycjami x i y obiektów, ale to nie to samo - GM na tym polu naprawdę ułatwia sprawę, nie trzeba myśleć o tym, że cała gra to jedna wielka pętla while itd.

 

Ale podziwiam Snake'a za jego Dlle - nie wiem czym jeszcze nas zaskoczy, ale wiem, że zaskoczy. Myślę, że trzeba mu nadać jakieś wyróżnienie.

Odnośnik do komentarza
Udostępnij na innych stronach

Teorytycznie jest Darmowy Game Maker, ale nie można w nim tworzyć exe-ców :) ale ogólnie ładniej wygląda

 

Fervi

A może Logomocja? XD tam można nawet exeka zrobić ;D, tylko nie darmowa.

 

Co do GMAPI:

nieźle, nie pomyślałem o takim cyku ;P

eh, ja tego nie użyję, ale patrząc na efekty, daje 5/5.

Odnośnik do komentarza
Udostępnij na innych stronach

Aktualizacja:

- Dodano: możliwość dostępu do zasobów dźwiękowych poprzez klasy ISounds (CGMAPI::Sounds) i ISound

- Poprawki: kilka małych poprawek w klasach dających dostęp do zasobów GM (konkretniej to w metodach Exists())

 

Download v0.2: https://gmclan.org/up541_4_GMAPIv0_2_src.html

 

GM mocno zaśmieca pamięć grafikami zużywając RAM

Hm, GM po załadowaniu do gry grafiki zostawia jej mapę bitową w pamięci, przez co traci się spora ilość wolnego RAM-u (zależnie od wymiarów grafiki)... nie mam 100% pewności ale one pozostawione są chyba tylko po to, żeby szło wczytać taką grafikę przy funkcjach takich jak np. show_message() gdzie można użyć backgrounda jako tła okna, bo tak to GM rysuje wszystko z pamięci wideo... więc może by tak pokombinować ze zwalnianiem bmp z pamięci ? :)

Odnośnik do komentarza
Udostępnij na innych stronach

No dzieki wielkie Snake, to teraz moge do PlayGate'a dodac obsluge tekstur i wreszcie dzwiekow z GMa, bez wczytywania z zewnatrz, bomba :D

 

Hm, GM po załadowaniu do gry grafiki zostawia jej mapę bitową w pamięci, przez co traci się spora ilość wolnego RAM-u

Ha, jednak sie nie mylilem co do tego :D Przyda sie wlasnie takie odsmiecanie z RAMu, ale wtedy dodaj jakas mozliwosc szybkiego reload do RAMu gdy jest to konieczne i po uzyciu od razu kasowanie. Da sie tak zrobic? :)

Odnośnik do komentarza
Udostępnij na innych stronach

Update - dodałem możliwość zwalniania bitmap z pamięci za pomocą nowej metody ReleaseBitmap w ISpriteSubimage i IBackground:

https://gmclan.org/up541_4_GMAPIv0_3_src.html

 

A tu taki prosty teścik tego ficzera:

https://gmclan.org/up541_4_BMPDeallocator.html (na spacji dealokacja, na enterze show_message ze zmienionym tłem == crash) ;D

 

Jednak po zwolnieniu mapy bitowej z pamięci nie można użyć sprite'a/backgrounda w tabeli wyników, komunikatach, przekazywać do takich funkcji jak sprite_save() itp... poza tym, sprite/background musi być załadowany do pamięci wideo (opcja preload), bo inaczej crash przy próbie załadowania do vram przez GM.

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy

Hm, mam pytanie, skoro GM trzyma grafiki w RAM i na GFX, to w takim razie czy na karcie zajmują one tak samo dużo? Chodzi o to, że np. w starej Almorze zużycie ramu to jakieś 900Mb, w takim razie GFX też jest zapchany na maxa, czy tam grafika trzymana jest inaczej ? Bo zastanawiam się właśnie, czy by mi coś GMAPI dało czy nie.

Odnośnik do komentarza
Udostępnij na innych stronach

A ja sie spytam dokladniej o licencje. Bo nie jestem pewien jej warunkow. Czy jesli uzyje biblioteki GMAPI w PlayGate, to bede musial opublikowac kod calego PlayGate'a, lub jego czesci w ktorej uzylem biblioteki, czy moze nie bede musial wcale publikowac kodu?

Odnośnik do komentarza
Udostępnij na innych stronach

Czekaj, bo nie rozumiem jaki zasieg kodu obejmuje licencja, czy dotyczny tylko GMAPI czy tez projektu wykorzystujacego ta biblioteke, bo jesli nie zamierzam modyfikowac GMAPI, a jedynie go dodac do projektu, a projekt korzysta z drugiej biblioteaki jaka jest xenon a w przyszlosci xenon moze byc komercyjny, a wiec czy wtedy licencja obejmuje takze xenona i nie bede mogl wydac komercyjnie czy tylko obejmuje kod ktory wykorzystuje GMAPI, czyli sam playgate ktory sam w sobie nie bedzie komercyjny?

 

Zawsze mialem problem z rozumieniem zasiegu licencji GNU pochodnych :P

Odnośnik do komentarza
Udostępnij na innych stronach

  • 2 tygodnie później...
  • 4 tygodnie później...

Update, łau.

 

Nowe możliwości:

  • Dostęp do zmiennych lokalnych/globalnych i tablic lokalnych/globalnych z GM
  • Możliwość zmiany aktualnej instancji na inną (można wywoływać funkcje GM z inną instancją niż ta, z której została wywołana funkcja z DLL-a)
  • Możliwość wykonywania kodu z daną grupą instancji (a'la "with" z GM)
  • Dostęp do właściwości ("zmiennych wbudowanych") instancji obiektów z GM (x, y, hspeed, path_index, direction etc.)

Zmiany nieco dokładniej:

  • Od teraz żeby korzystać z D3D8 z GMAPI trzeba zdefiniować GMAPI_USE_D3D, poprzednie "GMAPI_NO_D3D" zostało usunięte
  • Struktura GMVARIABLE: zmiana nazwy na GMVALUE, dodane przeładowanie operatora << z std::ostream, zmienione nazwy składowych
  • Nowa struktura GMVARIABLE (okazało się, że zmienne w GM mają nieco inną strukturę, stąd zmiana nazwy w/w struktury na GMVALUE - ta jest wartością zmiennych)
  • CGMVariable: Nowy konstruktor przyjmujący w parametrze strukturę GMVALUE
  • Nowe metody z klasie CGMAPI: GetSymbolID, GetLocalVariablePtr, GetGlobalVariablePtr, GetInstancePtr, GetCurrentInstancePtr, GetCurrentInstanceID, SetCurrentInstance, EnumInstances i With. (+ inne z których się nie korzysta :))
  • Małe poprawki (+ nowa funkcja GMFindSymbolID) w GMAPICore\main.asm
  • Wywołania MessageBox zmienione na MessageBoxA (przez to nie można było skompilować projektu z zestawem znaków UNICODE)
  • Prefixy typów wskaźnikowych z GmapiDefs zmienione z LP na P (np. LPGMSPRITE >>> PGMSPRITE)
  • Poprawki w niektórych komentarzach

 

Download v0.4:

https://gmclan.org/up541_4_GMAPIv0_4_src.html

 

Przykładzik nowych możliwości:

src: https://gmclan.org/up541_4_GmapiVariablesSrc.html

demo: https://gmclan.org/up541_4_GmapiVariablesDemo.html

Odnośnik do komentarza
Udostępnij na innych stronach

to GMAPI naprawdę przyśpiesza gry :o porównajcie sobie 5000 instance GM a GMAPI :o GMAPI nawet się nie ugina x_X aż z tego skorzystam jak tylko wyjdzie GM 8 i 0 dekompilacji, ofc jeżeli update'niecie to ;p

 

gafa: 0 dekompilacji oznacza 0 dostępu do zasobów ;p więc wątpię, żeby wyszedł update;p

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...