Jazz Jackrabbit 2 na Androidu za odpoledne

Jazz Jackrabbit 2 běžící na telefonu s Androidem, vlevo je směrový kříž a vpravo kulatá tlačítka

Po dvou dílech Might and Magic přišla hra, kde šlo skoro všechno samo. Engine Jazz² Resurrection Android podporuje včetně dotykového ovládání, takže zbylo přibalit data do aplikace a doladit drobnosti. Tady je postup i prompt ke zkopírování.

Jazz Jackrabbit 2 je z roku 1998 a existuje k němu Jazz² Resurrection, moderní engine, který si s původními daty rozumí a Android podporuje oficiálně, i s dotykovým ovládáním. Proti dvěma dílům Might and Magic je to procházka: nebylo potřeba dopisovat pravidla ani vymýšlet, kam s tlačítky.

Práce zbyla hlavně na tom, aby výsledkem byla jediná aplikace, která po instalaci rovnou funguje. Data se proto balí dovnitř a při prvním spuštění se rozbalí, stejně jako u Might and Magic.

Hlavní menu Jazz² Resurrection na telefonu s Androidem
Hlavní menu Jazz² Resurrection na telefonu s Androidem

Co musíte mít připravené

  • Legálně koupenou hru Jazz Jackrabbit 2 z GOG.com, nainstalovanou na počítači. Data zaberou jen asi 57 MB.
  • Windows 10 nebo 11, zhruba 10 GB volného místa a nainstalovaný Docker Desktop.
  • Git a Python 3.
  • Telefon s Androidem 5 a novějším, 64bitovým procesorem ARM a podporou OpenGL ES 3.0. V telefonu asi 300 MB volného místa.
  • Půl hodiny při prvním sestavení, další pak zaberou pár minut.

Engine je pod licencí GPL-3, herní data jsou vaše z GOGu. Hotová aplikace je tedy zase jen pro vás a nikam se nenahrává.

Prompt

Postup je stejný jako u předchozích dílů: otevřete Claude Code v pracovní složce, přepíšete si odstavec MOJE ÚDAJE a necháte ho pracovat. Prompt počítá s ověřenou verzí enginu a říká, ať se u novějšího ověří verze nástrojů v jeho vlastním nastavení sestavení.

Prompt pro Claude Code

Chci si pro vlastní potřebu postavit Android port hry Jazz Jackrabbit 2 jako JEDNO samostatné APK,
které po instalaci rovnou funguje. Hru vlastním legálně z GOG.com a mám ji NAINSTALOVANOU.
APK bude obsahovat moje herní data, takže je jen pro mě a nesmí se šířit. Na to mě na konci upozorni.

MOJE ÚDAJE (doplň / uprav):
- Cesta k nainstalované hře: C:\GOG Games\Jazz Jackrabbit 2
- Pracovní složka projektu: D:\Projekty\Jazz2
- Název aplikace v telefonu: Jazz 2
- ID balíčku: cz.jj2.game
- Mám Docker Desktop: ano. Mám Android Studio (SDK + emulátor): ano/ne.

ZÁSADNÍ PRAVIDLA:
1. Do složky s originální hrou NIKDY nezapisuj, jen z ní čti. Pracuj na kopii dat ve složce projektu.
2. Než začneš něco velkého stahovat nebo stavět, napiš mi krátce plán a počkej na moje „ano".
   Potom pracuj samostatně.
3. Nic netvrď bez ověření. U každé věci řekni, jestli je ověřená (a jak), nebo jen předpokládaná.
4. Emulátor mi NEMAŽ (wipe-data), mám v něm vlastní aplikace. Docker nikdy neresetuj do továrního nastavení.
5. Komunikuj česky.

KROK 0: OVĚŘ PŘEDPOKLADY (nic přitom neměň):
- Hra je nainstalovaná: ve složce je Jazz2.exe, Anims.j2a a desítky *.j2l a *.j2t.
- Docker běží (docker info), je git a Python 3 s Pillow. Zjisti, jestli mám Android SDK
  a emulátor (%LOCALAPPDATA%\Android\Sdk, emulator -list-avds).

TECHNICKÝ POSTUP (ověřená cesta, drž se jí):

A) Engine
- Jazz² Resurrection: github.com/deathkiller/jazz2-native (GPL-3). Naklonuj ho do <projekt>\engine.
  Ověřený commit je 221950ee4b29d903a1c57e1c204c6f5b5a9c9664 (verze 3.8.0). Novější zkus, ale když
  něco nesedí, vrať se k němu. Commit si zapiš do engine-upstream.txt.
- Android build jde přes CMake (NCINE_BUILD_ANDROID=ON), který vygeneruje Gradle projekt
  do _build/Android. Postup kopíruj z CI workflow repa (.github/workflows, Android).

B) Build jen v Dockeru
- Image z ubuntu:24.04: openjdk-17-jdk-headless, git, curl, g++, make, ninja-build, unzip, wget,
  python3, rsync. Android cmdline-tools a sdkmanager: platform-tools, platforms;android-35,
  build-tools;36.0.0, ndk;28.2.13676358 (verze ověř v CI workflow). CMake 3.31.x z Kitware
  (Ubuntu má jen 3.28), Gradle 8.13 (AGP 8.9 chce Gradle 8.11+).
- Licence Android SDK neodklikávej za mě přes „yes |". Když mám Android Studio, zkopíruj
  %LOCALAPPDATA%\Android\Sdk\licenses do image. Jinak se mě zeptej.
- Build skript v kontejneru: rsync /src (read-only mount enginu) do volume /work (build na bind-mountu
  z Windows je pomalý), smaž zdrojové překlady Content/Translations/*.po (zkompilované .mo zůstanou), pak
    cmake -B _build -G Ninja -D CMAKE_BUILD_TYPE=Release -D NCINE_BUILD_ANDROID=ON
      -D NCINE_UNIVERSAL_APK=OFF -D NCINE_NDK_ARCHITECTURES=$ARCH -D NCINE_RHI_GL_PROFILE=ES3
      -D NCINE_ASSEMBLE_APK=OFF -D NDK_DIR=$ANDROID_NDK_HOME -D GAME_SOURCE_DIR=/gamedata/Source
    cmake --build _build
    (cd _build/Android && gradle --no-daemon assembleRelease)
  ARCH=arm64-v8a pro telefon, x86_64 pro emulátor (jiný volume a jiná výstupní složka).
- Podpis: keytoolem vygeneruj vlastní keystore do <projekt>\out (když ještě není), APK zarovnej
  (zipalign -p 4) a podepiš (apksigner). Keystore si musím schovat, jinak další verze nepůjde
  nainstalovat přes starou.
- Pokud Docker Desktop nejde spustit s chybou „…sock.stale: The file cannot be accessed by the system",
  přejmenuj (ne mazat) složky %LOCALAPPDATA%\Docker\run a %LOCALAPPDATA%\docker-secrets-engine,
  když Docker neběží.

C) Data uvnitř APK + instalátor
- Zkopíruj hru do <projekt>\gamedata\Source BEZ: *.exe (Jazz2.exe, Jcs.exe, unins000.exe),
  unins000.*, HTML, JCSHelp, webcache.zip, goggame-*, gog.ico, support.ico, *.lnk a jazz2.log.
- V cmake/ncine_build_android.cmake: když je zadané GAME_SOURCE_DIR, zkopíruj jeho obsah do
  _build/Android/app/src/main/assets/Source (a když složka neexistuje, skonči chybou).
- V Bridge/app/build.gradle.in přidej androidResources { noCompress += [...] } pro přípony dat
  (j2a j2b j2d j2e j2l j2m j2s j2t j2v mod it s3m anm cfg ini lst txt).
- Nová aktivita DataInstallerActivity (čistá Java, bez XML, soubor .java.in s package
  @NCINE_REVERSE_DNS@, přidej ji do JAVA_FILES v ncine_build_android.cmake). Při prvním spuštění
  rozbalí assets/Source do getExternalFilesDir(null)/Source (tam engine data hledá, aplikace do své
  složky zapisuje bez oprávnění), s ukazatelem průběhu v MB a zapnutou obrazovkou. Místo zkontroluje
  s rezervou 3× velikost dat (engine si vedle postaví cache). Zapíše značkový soubor vázaný
  na lastUpdateTime balíčku, staré značky smaže a spustí MainActivity. Když značka existuje, spouští
  hru rovnou. Texty anglicky ve values/strings.xml.in, česky ve values-cs/strings.xml.in
  (i ten musí CMake zkopírovat přes configure_file).
- Manifest (AndroidManifest.xml.in): přidej DataInstallerActivity a oba activity-alias
  (MainActivityReforged, MainActivityLegacy) přesměruj targetActivity na ni. MainActivity dostane
  <meta-data android:name="android.app.lib_name" android:value="game" />.

D) Oprava pádu po startu
- Bez zásahu se do APK nedostane libc++_shared.so a hra po instalátoru hned spadne
  (chybějící knihovna, viz logcat). V build.gradle.in nastav, aby úlohy merge*JniLibFolders
  závisely na cmake úlohách (tasks.matching { it.name.startsWith('merge') &&
  it.name.endsWith('JniLibFolders') }.configureEach { dependsOn cmakeBuildTasks }).
  Po buildu ověř, že APK obsahuje lib/arm64-v8a/libc++_shared.so.

E) Název, ID, ikona
- V kořenovém CMakeLists.txt: NCINE_APP_NAME = můj název, NCINE_REVERSE_DNS = moje ID.
- Ikonu králíka vytáhni z Jazz2.exe (Python + ctypes: LoadLibraryEx jako datafile, RT_GROUP_ICON /
  RT_ICON, + Pillow). Zvětšuj nejdřív NEAREST, pak LANCZOS, ať pixel-art zůstane ostrý.
  Nahraď Bridge/app/res/mipmap-*/ic_launcher.png a v mipmap-xxhdpi ic_foreground.png,
  ic_background.png a ic_background_alt.png (adaptivní ikona v mipmap-anydpi-v26).

F) Drobné výchozí volby (PreferencesCache.cpp / .h)
- Vibrace dotykových tlačítek vypnout (EnableTouchVibration = false). Místo toho přidej volbu
  EnableKillVibration = true a v EnemyBase::OnPerish (když collider není nullptr) zavibruj 40 ms
  přes theApplication().Vibrate(40), obalené #if defined(NCINE_HAS_VIBRATIONS).
- Výchozí vyhlazení obrazu: ActiveRescaleMode = RescaleMode::_3xBrz | RescaleMode::UseAntialiasing
  (hra kreslí pevných 720×405, filtr je jediná páka na „rozlišení", v nastavení jde změnit).

G) Testování, než mi APK předáš
- Rozeber APK: apksigner verify, aapt dump badging (název, ID), native-code jen arm64-v8a,
  libc++_shared.so uvnitř, assets/Source se všemi daty nekomprimovanými.
- Když mám emulátor: postav x86_64 variantu do zvláštní složky a přes adb (input tap, screencap)
  mi ukaž instalátor dat, úvodní video, menu, seznam epizod, výběr postavy a první level
  s dotykovým ovládáním. Pozor: adb „input keyevent" do enginu nedojde, ovládej ho dotykem.
  V menu první dotyk vybírá, druhý potvrzuje.
- Po testu testovací aplikaci odinstaluj a emulátor vypni. Schránku na mém PC nepřepisuj.

H) Úpravy si ulož
- Všechny změny enginu ulož jako patch (git diff proti commitu z engine-upstream.txt)
  do <projekt>\nase-upravy.patch, ať jdou přenést na novější verzi enginu.

VÝSTUP:
- <projekt>\out\Jazz2.apk a keystore tamtéž.
- Na konci mi napiš: cestu k APK, jak ho nainstalovat do telefonu, co je ověřené a co ne (skutečný
  telefon jsi netestoval), co jsi změnil na disku (složky, Docker image a volumes) a že APK nesmím šířit.

Ovládání

Dotykové ovládání je vestavěné v enginu: vlevo směrový kříž, vpravo střelba, skok, běh a přepínání zbraní. Rozložení i velikost tlačítek se dají změnit v nastavení hry. Vibrace při stisku tlačítek jsem vypnul, telefon místo toho krátce zavibruje při zabití nepřítele.

První level Jazz Jackrabbit 2 na telefonu, vlevo dole je směrový kříž a vpravo kulatá tlačítka na střelbu a skok
První level Jazz Jackrabbit 2 na telefonu, vlevo dole je směrový kříž a vpravo kulatá tlačítka na střelbu a skok

Dvě drobnosti, které stojí za zmínku

Hra kreslí pevných 720 na 405 bodů, což je na dnešním displeji málo. Jediná páka je proto filtr, kterým se obraz zvětšuje. Zapnul jsem ten, který dopočítává pixel art a hrany vyhlazuje; vlevo je původní zvětšení, vpravo zapnutý filtr. V nastavení se dá kdykoliv přepnout.

Porovnání obrazu hry: vlevo prosté zvětšení s hrubými pixely, vpravo zapnutý filtr s vyhlazenými hranami
Porovnání obrazu hry: vlevo prosté zvětšení s hrubými pixely, vpravo zapnutý filtr s vyhlazenými hranami

Druhá věc je past, na kterou se dá narazit: bez malé úpravy se do aplikace nedostane jedna systémová knihovna a hra po rozbalení dat okamžitě spadne. V promptu je to ošetřené a je tam i kontrola, že knihovna uvnitř opravdu je.

Další díly

Tohle bylo na jedno odpoledne, ale stejným způsobem řeším i práci: vzít hotový open source základ a doplnit k němu to, co zákazníkovi chybí. Potřebujete něco podobného?

Napište mi