Portowanie · Unity · Defold · Poradnik

Portowanie gry z Unity do Defolda: praktyczny poradnik

Port gry z Unity do Defolda polega na przepisaniu kodu na Lua i zbudowaniu od nowa scen, interfejsu i integracji. Grafika, dźwięk, dane poziomów i sam projekt gry przechodzą do nowej wersji. Automatycznego konwertera nie ma. Ten poradnik przygotowaliśmy dla programistów i producentów, którzy mają działającą grę w Unity i potrzebują lżejszej wersji na web albo na tańsze telefony. Pokazujemy, czym w Defoldzie zastąpić elementy znane z Unity, co sprawdzić przed napisaniem pierwszej linijki w Lua i według jakiego planu prowadzimy własne porty. Jeśli dopiero wybierasz silnik do nowej gry, zacznij od naszego porównania Defolda i Unity.

· Praktyczny poradnik

Autor:

Read in English →

Portowanie gry z Unity do Defolda

Na czym polega port z Unity do Defolda

Z punktu widzenia gracza gra zostaje ta sama. Zasady, poziomy, balans, grafika i dźwięk przechodzą do nowej wersji. Zmienia się wszystko pod spodem. C# zamieniamy na Lua, prefaby i sceny na obiekty gry i kolekcje Defolda, a każde zewnętrzne SDK na rozszerzenie Defolda albo krótki własny kod.

Najczęściej port zaczyna się od problemu z wersją webową. Build z Unity ładuje się za długo jak na wymagania Poki czy CrazyGames albo gra przycina na tanich telefonach z Androidem. Czasem zespół chce mieć jeden mały projekt na web i mobile. Sam silnik Defold dokłada do buildu kilka megabajtów. Pliki projektu są tekstowe, więc dobrze widać zmiany w Gicie, a po sklonowaniu repozytorium nie trzeba czekać na odbudowę folderu Library.

Port najlepiej traktować jak pisanie gry od nowa według bardzo dokładnej specyfikacji. Tą specyfikacją jest projekt w Unity. W każdej chwili możesz go otworzyć i sprawdzić, jak działa skok albo co robi dany power-up. Dzięki temu port da się zaplanować dużo pewniej niż nową grę.

Unity a Defold: odpowiedniki najważniejszych elementów

Defold ma oficjalny poradnik dla osób przechodzących z Unity. Poniżej opisujemy różnice, które w praktyce zabierają najwięcej czasu.

GameObject, MonoBehaviour i komponenty

W Unity GameObject ma Transform i dowolną liczbę skryptów MonoBehaviour. Obiekt gry w Defoldzie ma wbudowaną pozycję, rotację i skalę oraz komponenty: sprite’y, etykiety, dźwięki, obiekty kolizji, fabryki i skrypty. Komponent skryptu to plik .script z funkcjami cyklu życia.

  • Awake, Start i OnEnable odpowiadają funkcji init(self).
  • Update to update(self, dt), FixedUpdate to fixed_update(self, dt), a LateUpdate to late_update(self, dt) w nowszych wersjach Defolda.
  • OnDisable i OnDestroy odpowiadają funkcji final(self).
  • Wejście trafia do on_input, kiedy skrypt wyśle do siebie acquire_input_focus. Wiadomości od innych skryptów odbiera on_message.

Pola z atrybutem SerializeField mają prosty odpowiednik. Linijka go.property("speed", 200) na początku skryptu pokazuje wartość w edytorze, a każda instancja może ją nadpisać.

Najwcześniej warto porzucić nawyk jednego skryptu na każdy obiekt. Scena z Unity z trzystoma MonoBehaviourami nie powinna zamienić się w trzysta skryptów Defolda, które wysyłają do siebie wiadomości. Projekt w Defoldzie jest czytelniejszy, kiedy skryptów jest mniej. Jeden skrypt sterujący wszystkimi przeciwnikami, z logiką w zwykłych modułach Lua, często zastępuje kilkadziesiąt małych komponentów. Doświadczeni użytkownicy na forum Defolda regularnie radzą to osobom, które przychodzą z Unity.

Defold nie gwarantuje też kolejności, w jakiej komponenty wywołują init czy update. Kod oparty na ustawieniach Script Execution Order trzeba przebudować. Zwykle robimy to tak, że jeden skrypt zarządzający wywołuje moduły w ustalonej kolejności.

Prefaby a fabryki i fabryki kolekcji

Prefab z jednym obiektem odpowiada plikowi obiektu gry, który tworzy komponent fabryki. Zamiast Instantiate piszesz factory.create("#enemy_factory", position) i dostajesz identyfikator nowego obiektu. Prefab z dziećmi odpowiada kolekcji tworzonej przez fabrykę kolekcji. Ta zwraca tabelę z identyfikatorami całej hierarchii.

Hierarchia działa inaczej niż w Unity. W prefabie możesz zagnieżdżać obiekty dowolnie głęboko, a plik obiektu gry w Defoldzie nie może zawierać innych obiektów. Relacje rodzic i dziecko ustawia się w kolekcji albo w trakcie gry przez go.set_parent. Warianty prefabów nie mają bezpośredniego odpowiednika. My zastępujemy je jednym prototypem i właściwościami skryptu przekazywanymi do factory.create.

Po zaznaczeniu opcji Load Dynamically fabryka wczytuje zasoby dopiero przy pierwszym użyciu. Rzadko używane obiekty nie zajmują wtedy pamięci przez całą grę.

Sceny a kolekcje i proxy kolekcji

Pierwszą sceną jest kolekcja startowa ustawiona w game.project. Kolejne sceny to kolekcje wczytywane przez proxy kolekcji. Wysyłasz do proxy async_load, czekasz na proxy_loaded i wysyłasz enable. Każde proxy wczytuje osobny świat gry z własną fizyką. Dane przekazywane między poziomami idą więc przez moduł Lua albo wiadomość.

Proxy mają jedną rzecz, której brakuje scenom w Unity. Wiadomością set_time_step spowolnisz albo zatrzymasz cały świat, więc pauza poziomu przy animowanym menu pauzy to jedna linijka kodu.

Obiekty z DontDestroyOnLoad zwykle trafiają do głównej kolekcji, która jest wczytana przez cały czas i podmienia poziomy przez proxy. Addytywne wczytywanie scen odpowiada kilku proxy włączonym jednocześnie.

Z C# na Lua

Przepisanie kodu z C# na Lua to największa część pracy, ale idzie szybciej, niż zakłada większość zespołów. Tej części Lua, której potrzebuje kod rozgrywki, można się nauczyć w jedno popołudnie. Większość błędów w świeżym porcie bierze się z kilku różnic:

  • Indeksy w tabelach zaczynają się od 1, a #lista przestaje liczyć na pierwszym nil.
  • Lua 5.1 nie ma dzielenia całkowitego. 7 / 2 daje 3.5, więc kod, który polegał na dzieleniu intów w C#, potrzebuje math.floor.
  • W Lua nie ma klas. Buduje się je z tabel i metatabel albo z małej biblioteki pomocniczej. Interfejsy i typy generyczne znikają i w kodzie rozgrywki rzadko ich brakuje.
  • Wartości vmath.vector3 przekazywane są przez referencję. Jeśli dwie zmienne wskazują ten sam wektor, zmiana v.x zmienia oba. W C# Vector3 jest strukturą, więc kod przepisany linijka po linijce potrafi się tu wyłożyć bez żadnego komunikatu o błędzie.
  • Literówka w nazwie zmiennej tworzy zmienną globalną albo zwraca nil, a kompilator Cię nie zatrzyma. Luacheck albo Lua language server wyłapią większość takich pomyłek. Jeśli zespół chce wrócić do typowania, Teal kompiluje kod z typami do Lua.

W buildzie HTML5 silnik używa standardowego Lua 5.1 zamiast LuaJIT. Ciężkie pętle profiluj więc także w przeglądarce.

Korutyny i kod asynchroniczny

W Unity korutynami zarządza silnik przez StartCoroutine i yield return. Lua ma korutyny wbudowane w język, ale Defold sam ich nie wznawia. Większość wywołań WaitForSeconds zamienia się w timer.delay(seconds, false, callback). Tweeny z callbackiem na końcu zastępuje go.animate albo gui.animate.

Przy dłuższych sekwencjach, jak tutorial, przerywnik filmowy czy łańcuch zapytań HTTP, kod zostaje czytelny dzięki prostej nakładce na korutyny. Wewnątrz korutyny robisz yield, a wznawiasz ją z timera albo callbacka. Napisz taki pomocnik w pierwszym tygodniu portu i używaj go wszędzie. Inaczej w projekcie szybko pojawią się trzy różne style kodu asynchronicznego.

Eventy a przesyłanie wiadomości

Eventy z C#, UnityEvents i bezpośrednie wywołania metod innych komponentów zamieniają się w wiadomości. msg.post("/player#script", "take_damage", { amount = 10 }) wysyła tabelę Lua do innego skryptu, który obsługuje ją w on_message. Adresy mają postać socket:/ścieżka#komponent. Wiadomość dociera później w tej samej klatce, więc odpowiedzi nie dostaniesz w następnej linijce kodu.

Nie ma GetComponent. Do odczytu i zmiany wartości na innym obiekcie służą go.get i go.set na właściwościach skryptu. Wspólny stan może siedzieć w module Lua, z którego korzysta kilka skryptów. Statyczne eventy z C# najprościej zastąpić małym modułem z funkcjami subscribe i publish. Słuchacze nie muszą wtedy znać swoich adresów.

uGUI a sceny GUI

Interfejs w Defoldzie budujesz w komponentach GUI, oddzielonych od świata gry. Plik .gui zawiera węzły (prostokąty, teksty, węzły pie, szablony, efekty cząsteczkowe), a gui_script steruje nimi przez API gui. GUI rysuje się w przestrzeni ekranu i ma własne warstwy oraz kolejność rysowania.

Ustawieniom Canvas Scaler odpowiada tryb dopasowania (adjust mode) każdego węzła i layouty dla profili ekranu. Pozwalają one ułożyć ekran inaczej w pionie i w poziomie. Layout Group i Content Size Fitter nie mają odpowiedników, więc listy i siatki ustawia się w kodzie. Wiele zespołów korzysta z Druida, biblioteki społeczności z przyciskami, przewijanymi obszarami, siatkami i polami tekstowymi.

Każdy ekran najlepiej odtworzyć ze zrzutów z Unity i oryginalnych sprite’ów. Przenoszenie hierarchii uGUI węzeł po węźle trwa dłużej niż ułożenie ekranu od nowa w edytorze GUI.

Animator a flipbooki, Spine i Rive

Animacje poklatkowe trafiają do atlasu albo tile source jako animacje flipbook i odtwarza się je przez sprite.play_flipbook. Animacje właściwości, czyli to, co zwykle robi się w DOTween albo w prostych klipach Animatora, przechodzą na go.animate z easingiem i trybami odtwarzania. Animację szkieletową 2D obsługują oficjalne rozszerzenia Spine i Rive, a modele 3D ze skinningiem importuje się jako glTF.

Defold nie ma maszyny stanów Animatora. Tę samą pracę wykona krótka maszyna stanów w Lua, która wybiera odpowiedni flipbook albo animację Spine. W większości gier 2D wychodzi krócej niż oryginalny graf w Animatorze.

Fizyka

Defold używa Box2D do fizyki 2D i Bullet do 3D. Rigidbody i Collider łączą się w jeden obiekt kolizji typu dynamic, kinematic, static albo trigger. Grupy i maski kolizji ustawia się po nazwach. Kolizje przychodzą jako wiadomości, na przykład contact_point_response i trigger_response.

Dwie rzeczy sprawdź na samym początku. Defold liczy w pikselach, więc ustaw skalę fizyki w game.project tak, żeby ciała w Box2D miały rozsądne rozmiary w metrach. Obiekty kinematic nie są same wypychane ze ścian. Kontroler postaci oparty w Unity na CharacterController albo Rigidbody2D.MovePosition potrzebuje własnego kodu, który rozsuwa obiekty na podstawie punktów kontaktu.

Shadery i materiały

Materiał w Defoldzie łączy program wierzchołków i program fragmentów w GLSL ze stałymi, samplerami i tagami. W skrypcie renderowania ustalasz, które tagi rysują się w którym przebiegu, z jaką kamerą i jakim trybem mieszania.

Grafy z Shader Graph i Amplify trzeba przepisać ręcznie na GLSL. Tint, mignięcie po trafieniu, dissolve albo przesuwanie UV to kwestia minut. Postprocessing, na przykład bloom czy korekcja kolorów, buduje się w skrypcie renderowania z użyciem render targetów. Na słabych GPU każdy taki efekt kosztuje czas klatki, więc zostaw te, które naprawdę budują wygląd gry. Duża liczba materiałów z różnymi tagami psuje też batching, co widać w profilerze jako więcej draw calli.

Sorting layers i order in layer zamieniają się w pozycję Z sprite’ów. W domyślnym skrypcie renderowania sprite’y z wyższym Z rysują się na wierzchu, w zakresie od -1 do 1.

ScriptableObject a moduły Lua i JSON

Dane gry ze ScriptableObjectów, takie jak statystyki przedmiotów, listy poziomów czy fale przeciwników, mają w Defoldzie dwa dobre miejsca. Moduł Lua zwracający tabelę sprawdza się przy danych, którymi zajmują się programiści. Pliki JSON dodane jako custom resources w game.project i czytane przez sys.load_resource oraz json.decode pasują do danych, które projektanci edytują w arkuszach.

Dane z Unity wyeksportuj małym skryptem edytora zamiast przepisywać je ręcznie. Będzie szybciej i bez pomyłek w wartościach balansu. W ten sam sposób wyciągniesz poziomy zbudowane z prefabów: jako listę obiektów z pozycjami w JSON, którą w Defoldzie odtwarzają fabryki. Tilemapy z Unity przenosi się do tile map w Defoldzie.

Natywne pluginy a natywne rozszerzenia

Natywnym pluginom z Unity (.aar, .framework, .jslib) odpowiadają natywne rozszerzenia Defolda. Rozszerzenie może zawierać kod w C, C++, Objective-C, Javie albo JavaScripcie i gotowe biblioteki. Serwer buildów Defolda kompiluje je razem z silnikiem, więc w większości projektów nie trzeba lokalnie instalować narzędzi Androida ani iOS.

Zanim zaczniesz pisać nowe rozszerzenie, przejrzyj portal z assetami Defolda. Reklamy, zakupy, analityka, powiadomienia push, obsługa safe area, prośby o ocenę w sklepie i większość SDK portali webowych mają już gotowe rozszerzenia.

Przenoszenie assetów

Assety przechodzą najłatwiej, a port to dobry moment, żeby je uporządkować.

  • Atlasy. Każdy sprite, obrazek w GUI i tekstura cząsteczek w Defoldzie pochodzi z atlasu albo tile source. Grupuj grafiki według ekranu albo poziomu, na którym się pojawiają. Pierwszy poziom nie będzie wtedy ciągnął grafiki z całej gry. Eksportuj obrazy w rozmiarze, w jakim gra je pokazuje. Projekty w Unity często trzymają źródła 2048 px i zmniejszają je dopiero w trakcie działania.
  • Kompresja tekstur. Profile tekstur ustawiają kompresję według wzorców ścieżek. Basis Universal jest w trakcie działania gry transkodowany do formatu, który obsługuje GPU, na przykład ASTC, ETC2 albo BC. Jeden build działa więc na wielu telefonach. Profil może też ograniczyć maksymalny rozmiar tekstury. Kompresja wydłuża budowanie, dlatego wiele zespołów włącza ją przy buildach wydaniowych, a w codziennej pracy ją pomija.
  • Dźwięk. Defold odtwarza WAV i Ogg Vorbis. Muzykę przekonwertuj do Ogg, krótkie efekty zapisz w mono, a do osobnej głośności muzyki i efektów użyj grup dźwięków. Dźwięk w Defoldzie domyślnie nie jest przestrzenny. Audio 3D z Unity wymaga prostych obliczeń odległości w Lua albo rozszerzenia FMOD.
  • Fonty. Fonty z TextMeshPro się nie przeniosą. Pliki .font w Defoldzie tworzysz z oryginalnych plików TTF albo OTF. Fonty distance field nadają się do skalowanego tekstu, a fonty bitmapowe do małego tekstu w stylu pixel art. Nowsze wersje Defolda potrafią też generować glify w trakcie gry, co pomaga przy językach z dużą liczbą znaków.
  • Spine i modele 3D. Eksporty ze Spine działają z rozszerzeniem Spine, jeśli zgadzają się wersje runtime’u. Defold czyta glTF i Collada, więc pliki FBX trzeba przekonwertować, najczęściej w Blenderze.

Wymiana SDK: reklamy, analityka, zakupy i portale

Spisz każde SDK z projektu w Unity razem z wersją i funkcjami, które gra naprawdę wywołuje. Potem znajdź odpowiednik po stronie Defolda:

  • Reklamy. Są rozszerzenia do AdMob, AppLovin MAX i Unity LevelPlay. Reklamy z nagrodą i pełnoekranowe przenosi się prawie jeden do jednego. Bannery często wymagają zmian w układzie ekranu.
  • Zakupy w aplikacji. extension-iap obsługuje Google Play i App Store. Weryfikacja paragonów na serwerze może zostać bez zmian.
  • Analityka i crashe. Firebase Analytics i Crashlytics mają rozszerzenia do Defolda, a GameAnalytics udostępnia własne SDK. Zachowaj nazwy eventów z wersji Unity, żeby dashboardy i lejki działały dalej bez przerwy w danych.
  • Portale webowe. Rozszerzenie Poki SDK, rozszerzenie CrazyGames oraz Playgama Bridge albo GamePush, kiedy jedna integracja ma obsłużyć wiele portali. Mobilne SDK reklamowe nie działają w przeglądarce, więc wersja webowa korzysta z funkcji reklamowych portalu.

Niektóre SDK nie mają rozszerzenia do Defolda, na przykład mniejsza sieć reklamowa albo wewnętrzna analityka studia. Dla nich zaplanuj natywne rozszerzenie, a w wersji webowej krótki most w JavaScripcie. Każde takie SDK wydłuża port, dlatego podczas audytu listę SDK czytamy jako pierwszą.

Zapisy graczy i migracja postępów

Jeśli wersja w Defoldzie zastępuje grę z Unity, która już ma graczy, ci gracze oczekują, że ich postęp przetrwa aktualizację. Na urządzeniach mobilnych nowy build ma tę samą nazwę pakietu albo bundle id. Małe natywne rozszerzenie może przy pierwszym uruchomieniu odczytać stare PlayerPrefs (SharedPreferences na Androidzie, NSUserDefaults na iOS) i zapisać je w pliku zapisu Defolda.

W przeglądarce Unity trzyma PlayerPrefs w IndexedDB. Most w JavaScripcie odczyta je pod warunkiem, że build z Defolda działa w tej samej domenie. Najprościej jest z zapisami w chmurze przypisanymi do konta gracza, o ile Defold czyta i zapisuje dane w tym samym formacie. O migracji zdecyduj już przy audycie, bo od niej zależy, jak od pierwszego dnia zbudujesz system zapisu.

Audyt: lista kontrolna przed pierwszą linijką Lua

  • Wersja Unity, render pipeline (Built-in, URP albo HDRP) i docelowe platformy.
  • Rozmiar obecnych buildów i czas ładowania na średniej klasy Androidzie oraz w Safari na iPhonie.
  • Każdy pakiet z Asset Store i każdy plugin, razem z funkcjami, z których gra korzysta.
  • Każde SDK i jego wersja.
  • Własne shadery i postprocessing.
  • Sceny, prefaby i skrypty, a wśród nich te, w których siedzi większość logiki.
  • Gdzie są dane gry: w ScriptableObjectach, JSON-ach, arkuszach czy na sztywno w kodzie C#.
  • Lokalizacja: języki, fonty i sposób, w jaki teksty trafiają do gry.
  • System zapisu i to, czy postęp graczy trzeba przenieść.
  • Kto akceptuje „feel” gry i jak porównacie obie wersje.

Plan portu krok po kroku

  1. Audyt i decyzje. Przejdź przez listę kontrolną. Zapisz, co zostaje bez zmian, co upraszczacie, a z czego rezygnujecie.
  2. Szkielet projektu. Przygotuj game.project, kolekcję startową, skrypt renderowania, mapowanie wejścia, moduł zapisu i wczytywanie poziomów przez proxy. Ustal teraz strukturę folderów i nazewnictwo, póki nic to nie kosztuje.
  3. Eksport danych. Napisz w Unity skrypty edytora, które wyeksportują poziomy, przedmioty i balans do JSON albo Lua.
  4. Vertical slice. Przenieś główną pętlę rozgrywki i jeden pełny poziom z docelową grafiką i dźwiękiem. Zmierz rozmiar buildu, czas ładowania i liczbę klatek na najsłabszym docelowym telefonie. Zagraj w niego obok wersji z Unity.
  5. Treść i interfejs. Przenieś pozostałe poziomy, menu, sklep i funkcje meta w kamieniach milowych. Każdy kończy się grywalnym buildem.
  6. SDK. Zintegruj reklamy, zakupy, analitykę i SDK portali. Każde z nich przetestuj na prawdziwych urządzeniach.
  7. Optymalizacja. Dopracuj profile tekstur i podział atlasów, skompresuj dźwięk, przenieś do archiwów Live Update treści, które mogą poczekać, i wyłącz w app manifeście nieużywane moduły silnika.
  8. QA i porównanie z oryginałem. Porównaj grę z wersją z Unity poziom po poziomie i popraw różnice w odczuciu: łuk skoku, opóźnienie sterowania, płynność kamery.
  9. Premiera i migracja. Wydaj grę, obserwuj raporty crashy i analitykę, a projekt w Unity zarchiwizuj jako punkt odniesienia.

Najbardziej pilnuj kroku 4. Jeśli vertical slice osiąga zakładany rozmiar i liczbę klatek na tanim telefonie, reszta portu to głównie kwestia ilości pracy. Jeśli nie osiąga, dowiadujesz się o tym wtedy, kiedy zmiana kierunku jeszcze niewiele kosztuje.

Testy na słabych telefonach i w przeglądarce

Od vertical slice’a testuj na sprzęcie, który mają Twoi gracze. Komputer z mocną kartą graficzną ukrywa prawie każdy problem.

  • Telefon z Androidem z 2 albo 3 GB RAM i starszym GPU oraz iPhone sprzed kilku generacji.
  • Chrome na Androidzie i Safari na iOS dla buildów webowych. Safari ma najostrzejsze limity pamięci i przeładowuje kartę, kiedy gra zużywa jej za dużo.
  • Ograniczenie prędkości sieci w narzędziach deweloperskich przeglądarki, żeby zobaczyć pierwsze ładowanie na danych mobilnych.
  • Profiler Defolda w buildzie debug: czas klatki, draw calle i pamięć.
  • Sesje po 30 minut i dłużej. Wycieki z obiektów, które powstają i nigdy nie są usuwane, wychodzą dopiero po czasie.
  • Dźwięk, który rusza po pierwszym dotknięciu, pauza po przełączeniu karty i poprawny powrót po zablokowaniu telefonu.

Więcej wskazówek dla przeglądarki znajdziesz w naszym poradniku optymalizacji gier HTML5.

Najczęstsze błędy

  • Skrypt na każdym obiekcie. Setki skryptów, które wysyłają do siebie wiadomości, trudno debugować i działają wolniej niż kilka skryptów zarządzających.
  • Tłumaczenie linijka po linijce. Do kodu trafiają wzorce, których Lua nie potrzebuje: gettery, interfejsy, głębokie dziedziczenie. Przenoś zachowanie, sprawdzaj je z buildem z Unity i pisz prosty kod w Lua.
  • Założona kolejność wykonania. Kod, który polegał na Script Execution Order albo na tym, że Awake wykona się przed Start, psuje się w sposób wyglądający na losowy.
  • Fizyka, która „czuje się” inaczej. Ustawienia Box2D, skala fizyki i stały krok czasu zmieniają odczucie skoków i zderzeń. Dostrój je w trakcie vertical slice’a, póki jest na to czas.
  • Jeden ogromny atlas. Cała grafika w jednym atlasie powiększa pierwsze pobranie i zużycie pamięci.
  • Zapominanie o przeglądarce. Mobilne SDK reklamowe, dostęp do plików i wątki w buildzie HTML5 działają inaczej albo nie działają wcale. Sprawdź każde z nich w wersji webowej na wczesnym etapie.
  • Zapisy odłożone na koniec. Przeniesienie postępów graczy po premierze jest dużo trudniejsze niż zaplanowanie go na starcie.

Port, przepisanie od nowa czy zostanie przy Unity

Po audycie zwykle wybiera się jedną z trzech dróg.

  • Port ma sens, kiedy gra jest w 2D albo lekkim 3D, celem jest web albo słabsze telefony, a kod C# jest na tyle czytelny, że może służyć za specyfikację.
  • Przepisanie od nowa wybierz, jeśli projekt gry i tak się zmienia albo kod w Unity jest tak splątany, że jego czytanie kosztuje więcej niż budowa według dokumentacji projektowej. Build z Unity dalej przyda się jako wzorzec odczucia i treści.
  • Zostań przy Unity, jeśli gra opiera się na oświetleniu z URP albo HDRP, terenie, rozbudowanych shaderach z Shader Graph albo dużym zestawie narzędzi z Asset Store. Tak samo jest, gdy build webowy już mieści się w limitach portali i dobrze działa. Wtedy lepiej poświęcić czas na optymalizację obecnej wersji.

Defold nie ma wbudowanego terenu, komponentów światła, line i trail rendererów ani symulacji tkanin. Każdą z tych rzeczy da się zbudować, a dla części z nich społeczność ma gotowe rozwiązania. Jeśli jednak gra opiera się na kilku takich funkcjach naraz, może lepiej jej będzie w Unity.

Jak pracujemy przy portach z Unity

Porty z Unity to stała część naszej pracy w Codeward, najczęściej na web. Wśród nich były mobilne tytuły znanych marek gier mobilnych, zrobione w Unity i przeniesione do Defolda, żeby działały w przeglądarce.

Kilka osób w naszym zespole pracowało w Unity przez lata i bardzo dobrze zna C#. Zanim napiszemy pierwszą linijkę w Lua, czytamy oryginalny kod i szukamy miejsc, z których bierze się charakter rozgrywki. Często to kilka linijek w skrypcie kontrolera albo jedna krzywa animacji, i właśnie one muszą przetrwać port w niezmienionej formie.

Jeśli masz grę w Unity i zastanawiasz się, czy port do Defolda ma sens, wyślij nam build i krótki opis przez formularz kontaktowy albo na biuro@paweljarosz.pl. Napiszemy, co byśmy przenieśli, co zmienili i gdzie widzimy ryzyko. O produktowej stronie przenoszenia gry mobilnej do przeglądarki piszemy w poradniku o portowaniu gry mobilnej na web, a o innych silnikach w tekście o portowaniu gier z Unity, Godota i Construct na przeglądarkę.

FAQ

Pytania o portowanie z Unity do Defolda

Czy projekt z Unity da się automatycznie przekonwertować do Defolda?

Nie. Nie ma konwertera kodu, scen ani prefabów. Kod C# przepisuje się na Lua, a sceny buduje od nowa jako kolekcje Defolda. Sprite’y, tekstury, dźwięki, fonty, pliki Spine i dane poziomów można wykorzystać ponownie, często po eksporcie skryptem albo ponownym wyeksportowaniu w lepszym rozmiarze.

Czy programista Unity musi najpierw nauczyć się Lua?

Programista Unity zaczyna pisać przydatny kod w Lua po dniu albo dwóch. Sam język jest mały. Większą zmianą jest sposób budowania gry w Defoldzie: mniej skryptów, przesyłanie wiadomości, fabryki i proxy kolekcji.

Co w Defoldzie zastępuje MonoBehaviour, prefaby i sceny?

MonoBehaviour zastępują komponenty skryptów z funkcjami cyklu życia. Prefab z jednym obiektem to plik obiektu gry tworzony przez fabrykę, a prefab z dziećmi to kolekcja tworzona przez fabrykę kolekcji. Rolę scen pełnią kolekcje wczytywane przez proxy kolekcji.

Czy gracze zachowają postęp po porcie z Unity do Defolda?

Zwykle tak, jeśli zaplanujesz to wcześnie. Na urządzeniach mobilnych natywne rozszerzenie może przy pierwszym uruchomieniu odczytać stare PlayerPrefs. W przeglądarce build z Defolda musi działać w tej samej domenie, żeby odczytać dane Unity z IndexedDB. Zapisy w chmurze działają, jeśli format danych się nie zmieni.

Czy w Defoldzie są reklamy, zakupy w aplikacji i SDK portali?

Tak. Defold ma rozszerzenia do AdMob, AppLovin MAX, Unity LevelPlay, zakupów w aplikacji, Firebase i GameAnalytics oraz do portali webowych, takich jak Poki i CrazyGames. SDK bez gotowego rozszerzenia wymagają natywnego rozszerzenia albo mostu w JavaScripcie.

O autorze

Paweł Jarosz jest programistą gier z Krakowa z ponad 13-letnim doświadczeniem, założycielem studia Codeward oraz Community & Content Managerem w Fundacji Defold. Tworzy gry i dzieli się praktyczną wiedzą na kanale Unfolding Gamedev na YouTube.

Myślisz o przeniesieniu gry z Unity?

Codeward to studio produkcji gier z Krakowa. Przenosimy gry z Unity do Defolda na web i urządzenia mobilne, tworzymy gry na zamówienie i robimy playable ads. Wyślij link do gry albo aktualny build, a odpiszemy najszybciej, jak to możliwe.

Co warto nam wysłać?

  • Link do gry albo aktualny build.
  • Wersję Unity, render pipeline oraz wtyczki i SDK, z których korzysta gra.
  • Platformy albo portale, na które chcesz trafić.
  • Termin.