Episode ini menyiapkan game kalian benar-benar siap rilis: membangun build pipeline otomatis dengan export headless, menjalankan QA dan regression testing dengan kerangka uji GUT, memilih channel distribusi dan strategi update, serta memasang analytics, crash reporting, dan feedback loop.

Di episode 18 kalian sudah merapikan arsitektur proyek: komponen modular, signal sebagai lem antar sistem, dan mekanik yang bisa dipakai ulang. Kode yang rapi itu tadi baru setengah perjalanan — setengah lainnya adalah mengantarnya keluar ke dunia nyata. Banyak game gagal bukan karena kodenya jelek, tapi karena proses rilisnya amburadul: build yang diuji beda dengan build yang dijual, bug yang lolos ke produksi, dan tidak ada cara tahu kenapa pemain berhenti bermain.
Episode 19 ini membahas operational readiness: build pipeline untuk rilis, QA dan regression testing, strategi packaging dan distribusi, serta analytics, crash reporting, dan feedback loop yang menjadikan rilis bukan akhir, melainkan awal dari siklus perbaikan.
Meng-export lewat menu Editor setiap kali rilis itu rapuh — mudah ada langkah terlewat, dan siapa yang menjamin build di laptop kalian sama dengan yang dipakai tester? Jawabannya: export headless via command line. Godot bisa dijalankan tanpa GUI dan mengekspor sesuai preset yang sudah disimpan di export_presets.cfg.
Preset dibuat sekali lewat Project > Export, lalu dipanggil ulang dari terminal:
godot --headless --export-release "Android" build/game.apkUntuk beberapa platform sekaligus, taruh semuanya dalam satu skrip pipeline:
godot --headless --export-release "Linux" build/linux/game.x86_64
godot --headless --export-release "Windows" build/windows/game.exe
godot --headless --export-release "Web" build/web/index.htmlDengan pipeline ini, build bisa dipicu dari CI — misalnya setiap commit ke cabang rilis. Skrip build-all.sh memastikan semua platform keluar dari versi kode yang sama, menghilangkan misteri "di laptop saya jalan".
Preset adalah kunci reproducibility. Ada dua hal yang wajib dikonfigurasi di dalamnya. Pertama, eksklusi file: jangan ikut mengekspor .godot, aset mentah, atau file konfigurasi yang berisi rahasia dev. Kedua, template per platform: Android butuh keystore dan pakai AAB untuk Play Store, Web butuh embed mode yang tepat. Perbedaan ini sebaiknya dicatat sebagai preset terpisah, bukan diubah manual setiap rilis. Semua konfigurasi ini tersimpan di export_presets.cfg dan ikut ter-version-control — audit perubahan export cukup dari diff file itu.
Variabel build juga dipisahkan dari kode. Alih-alih hardcode endpoint atau kunci API, baca dari environment saat game dijalankan:
extends Node
const ENDPOINT := "https://api.example.com/v1"
const BUILD_NUMBER := "1.2.0"
func _ready() -> void:
print("Running build %s against %s" % [BUILD_NUMBER, ENDPOINT])Menuliskan BUILD_NUMBER ke layar atau file log terdengar sepele, tapi saat bug datang dari pemain, angka itu satu-satunya petunjuk build mana yang mereka jalankan. Debugging jarak jauh selalu dimulai dari identitas build — tidak mungkin menelusuri bug yang versinya tidak jelas.
QA dimulai dari dalam editor. Godot menyediakan debugger yang bisa menghentikan eksekusi, menginspeksi variabel, dan melompat antar frame. Untuk bug yang hanya muncul saat bermain, hidupkan remote scene tree: lewat tab Debugger, kalian bisa melihat pohon node game yang sedang berjalan di perangkat atau browser — memeriksa nilai live tanpa mencetak ratusan baris print.
Yang tidak boleh ditunda adalah regression testing — memastikan fitur lama tidak rusak saat fitur baru masuk. Di Godot, kebiasaan terbaiknya adalah menulis unit test untuk logika murni (skor, damage formula, state machine) dan mengintegrasikannya ke CI. Kerangka yang paling populer adalah GUT (Godot Unit Test), yang bisa diunduh sebagai plugin.
GUT membungkus logika game dan memanggilnya dari test. Contoh, kita punya formula damage di komponen yang dipakai player dan musuh:
extends Node
@export var base_damage: int = 10
func calculate_damage(critical: bool) -> int:
var dmg := base_damage
if critical:
dmg *= 2
return dmgTest-nya menempati file terpisah di folder res://test/ dan dieksekusi oleh runner GUT:
extends GutTest
func test_normal_damage() -> void:
var comp := preload("res://systems/damage_component.gd").new()
comp.base_damage = 10
assert_eq(comp.calculate_damage(false), 10)
func test_critical_damage() -> void:
var comp := preload("res://systems/damage_component.gd").new()
comp.base_damage = 10
assert_eq(comp.calculate_damage(true), 20)Perhatikan bahwa test ini memanggil calculate_damage tanpa melibatkan scene — ia menguji logika murni, sehingga hasilnya deterministik dan cepat. Itulah kenapa kita memisahkan logika dari node di episode 18: yang bisa diuji terpisah, diuji terpisah.
Di CI, GUT dijalankan headless:
godot --headless --script res://addons/gut/gut_cmdln.gd -gdir=res://test -gexitSekarang setiap perubahan logika damage, misalnya menambah armor, akan langsung ketahuan oleh test_critical_damage jika angkanya meleset. Bug hunting tetap perlu manusia, tapi regression yang membosankan bisa diserahkan ke mesin.
Warning
Jangan menulis test demi mengejar jumlah. Prioritaskan logika yang pernah rusak atau yang paling sering disentuh: damage formula, sistem skor, state machine, dan parser save file. Sepuluh test yang bermakna lebih baik daripada seratus test yang hanya memvalidasi hal sepele.
Setelah build hijau dan test lolos, kalian berhadapan dengan channel distribusi. Setiap channel punya konvensi sendiri: Steam dan itch.io menerima binary zip untuk desktop, Google Play mengharuskan AAB yang ditandatangani, App Store menuntut notarisasi. Riset syarat tiap toko jauh sebelum rilis — menunggu sampai build jadi baru membaca aturan adalah resep terlambat.
Strategi update mengikuti channel tersebut:
git dan tag versi; Steam mengelola update otomatis dari build yang kalian unggah.Konsistensi nomor versi itu penting. Gunakan semver — major.minor.patch — dan catat perubahan di changelog. Pemain yang melaporkan bug dari versi lama sebaiknya bisa kalian arahkan ke build terbaru, bukan di-diagnosa berdasarkan build yang sudah digantikan.
Rilis bukan garis finis; ia memulai putaran umpan balik. Tanpa data, setiap keputusan (naikkan difficulty? perpendek level 2?) hanya tebakan. Dua instrumen wajib:
HTTPRequest biasa.Error.report_callstack() atau push_error() untuk melacak error. Gabungkan dengan logger yang menulis ke file, lalu kirim otomatis saat startup berikutnya.Skrip minim berikut menangkap error tak tertangani dan menulisnya ke file lokal:
extends Node
func _init() -> void:
ErrorHandler.install(self)
func report(message: String, stack: String) -> void:
var file := FileAccess.open("user://crash.log", FileAccess.WRITE)
file.store_line(message)
file.store_line(stack)Feedback loop adalah siklusnya: analytics menunjukkan tempat pemain berhenti, crash log menunjukkan bug yang lolos, test regression memastikan perbaikan tidak merusak yang lain, dan build berikutnya memulai siklus lagi. Tim yang matang melepaskan versi kecil secara rutin dengan ritme ini, bukan sekali melepas dan berdoa.
Success
Mulailah analytics dengan lima event saja: game start, level start, level complete, game over, dan session end. Lima event ini sudah cukup untuk menemukan kebocoran pemain di awal funnel — sisanya ditambahkan saat ada hipotesis spesifik.
Episode 19 melengkapi mode produksi kalian: build pipeline yang bisa dipicu dari CI lewat export headless, preset export yang disimpan sebagai sumber kebenaran, QA dengan remote scene tree dan unit test GUT untuk regression, strategi packaging dan update per channel distribusi, serta analytics, crash reporting, dan feedback loop yang menutup siklus perbaikan.
Inti yang harus dibawa pulang:
godot --headless --export-release dan panggil dari CI agar semua platform keluar dari versi kode yang sama.Dengan pipeline yang otomatis dan data yang mengalir, kalian siap merilis dengan percaya diri. Di episode 20 berikutnya kita turun dari proses ke konteks: real-world use cases & project types — bagaimana arsitektur dan alur kerja kalian berubah untuk 2D platformer, puzzle, roguelike, hingga simulation, plus planning, MVP, monetization, dan pola game jam. Sampai jumpa!