Installation
Folder Structure After Install
After purchasing, download the zip, extract it, and copy the addons/ folder into your Godot project root.
unity_ads_plugin — the AAR path is hardcoded in the export plugin.Enable the Plugin
- Open your project in Godot 4.4
- Go to Project → Project Settings → Plugins
- Find UnityAdsPlugin in the list and toggle it ON
- Godot automatically adds
UnityAdsas a global autoload singleton
Screenshot: Project Settings → Plugins → UnityAdsPlugin enabled
Unity Dashboard Setup
Get Your Game ID
- Go to cloud.unity.com and sign in
- Select your project (or create a new one)
- Go to Monetization → Ad Units in the left menu
- Your Game ID is shown at the top of this page — copy it
- Open
UnityAds.gdand replace theGAME_IDconstant
# Replace with your real Game ID from Unity Dashboard const GAME_ID = "YOUR_GAME_ID_HERE" const TEST_MODE = true # set false for production build
Screenshot: Unity Dashboard → Monetization → Ad Units — Game ID location
Placement IDs — Default vs Manual
Unity auto-creates Interstitial and Rewarded placements when you set up your project. Banner must be created manually.
| Ad Type | Default Placement ID | Created By |
|---|---|---|
| Interstitial | Interstitial_Android |
AUTO |
| Rewarded | Rewarded_Android |
AUTO |
| Banner | Banner_Android |
MANUAL |
Banner_Android, platform to Android, format to Banner. Without this, banner ads will fail silently.Screenshot: Unity Dashboard → Add Ad Unit → Banner_Android setup
UnityAds.gd — you don't need to change them as long as you use the default names above.Install Android Build Templates
- Go to Project → Install Android Build Template…
- Click Install and wait for Godot to finish
- The
android/build/folder will now exist in your project
Screenshot: Project → Install Android Build Template menu
Export Settings
1. Use a Release Keystore — debug keystore will not serve real ads.
2. Export as Release APK via Project → Export. Do NOT use One-Click Deploy.
Android Export Preset — Required Settings
── Gradle Build ────────────────────────────── [✓] Use Gradle Build [✓] Use Custom Build ── Permissions ─────────────────────────────── [✓] INTERNET [✓] ACCESS_NETWORK_STATE Note: AD_ID permission is auto-injected by plugin ── Keystore (REQUIRED for real ads) ────────── Release Keystore: path/to/your-release.keystore Release User: your_key_alias Release Password: your_password ── Export ──────────────────────────────────── Export Format: APK Build Mode: Release
Screenshot: Export dialog — Gradle Build enabled, permissions checked, Release keystore set
Using the Ads
3 Ad Types Available
Banner
320×50 strip. Top or bottom. Displays automatically after load_banner() succeeds.
Interstitial
Full-screen. Auto-reloads after closing or failed show.
Rewarded
Video with reward. Fires on_rewarded_earned on completion.
Call Order — initialize → load → show
on_initialization_complete before loading ads. And always wait for on_*_loaded before showing. The wrapper has built-in guards — calling show_interstitial() before it's ready just prints a warning and does nothing.extends Node func _ready() -> void: # 1. Connect ALL signals first, before calling initialize() UnityAds.on_initialization_complete.connect(_on_ads_ready) UnityAds.on_initialization_failed.connect(_on_ads_failed) UnityAds.on_interstitial_loaded.connect(_on_interstitial_loaded) UnityAds.on_interstitial_completed.connect(_on_interstitial_completed) UnityAds.on_interstitial_failed_to_load.connect(_on_interstitial_failed) UnityAds.on_rewarded_loaded.connect(_on_rewarded_loaded) UnityAds.on_rewarded_earned.connect(_on_reward_earned) UnityAds.on_rewarded_failed_to_load.connect(_on_rewarded_failed) UnityAds.on_banner_loaded.connect(_on_banner_loaded) UnityAds.on_banner_failed_to_load.connect(_on_banner_failed) UnityAds.on_banner_clicked.connect(_on_banner_clicked) # 2. Set GDPR consent if needed (call before initialize) UnityAds.set_consent(true, true) # 3. Now initialize — uses GAME_ID + TEST_MODE from UnityAds.gd UnityAds.initialize() func _on_ads_ready() -> void: print("Unity Ads ready — loading all ad types") # 4. Load ads AFTER init completes — not before UnityAds.load_interstitial() UnityAds.load_rewarded() UnityAds.load_banner("bottom") # "top" or "bottom" # Banner displays automatically once on_banner_loaded fires func _on_ads_failed(error: String, message: String) -> void: print("Unity Ads init failed: ", error, " — ", message) # ── Banner ────────────────────────────────────────────────────── func _on_banner_loaded(placement_id: String) -> void: print("Banner visible: ", placement_id) # No extra call needed — banner is already showing func _on_banner_failed(placement_id: String, error: String) -> void: print("Banner failed: ", error) func _on_banner_clicked(placement_id: String) -> void: print("Banner clicked: ", placement_id) func toggle_banner(show: bool) -> void: if show: UnityAds.load_banner("bottom") # loads and shows automatically else: UnityAds.hide_banner() # ── Interstitial ──────────────────────────────────────────────── func _on_interstitial_loaded(placement_id: String) -> void: print("Interstitial ready: ", placement_id) func _on_interstitial_failed(placement_id: String, error: String, message: String) -> void: print("Interstitial load failed: ", error) func on_level_complete() -> void: if UnityAds.is_interstitial_ready(): UnityAds.show_interstitial() # Do NOT change scene here — wait for on_interstitial_completed else: change_scene() # Ad not ready — proceed without it func _on_interstitial_completed(placement_id: String) -> void: # Ad closed by user — now safe to change scene # UnityAds.gd auto-reloads interstitial here change_scene() # ── Rewarded ──────────────────────────────────────────────────── func _on_rewarded_loaded(placement_id: String) -> void: print("Rewarded ready: ", placement_id) func _on_rewarded_failed(placement_id: String, error: String, message: String) -> void: print("Rewarded load failed: ", error) func offer_revive() -> void: if UnityAds.is_rewarded_ready(): UnityAds.show_rewarded() # Reward is given in _on_reward_earned, NOT here else: print("Rewarded not ready yet") func _on_reward_earned(placement_id: String) -> void: # Only fires when user COMPLETES the video — safe to give reward # UnityAds.gd auto-reloads rewarded here give_player_coins(100)
✓ UnityAds.on_rewarded_earned.connect(_on_reward_earned)✗ UnityAds.connect("on_rewarded_earned", _on_reward_earned)
Switching to Production
Two changes needed in UnityAds.gd before your release build:
# DEVELOPMENT const GAME_ID = "6020875" ← replace with your real Game ID const TEST_MODE = true ← set to false for release # PRODUCTION const GAME_ID = "YOUR_REAL_ID" ← from Unity Dashboard const TEST_MODE = false ← must be false in release APK
TEST_MODE = true. Test mode serves fake ads that don't generate revenue and will violate Unity Ads policies if left on in production.Signals Reference
All Available Signals
Connect to these from any script using Godot 4 dot syntax: UnityAds.signal_name.connect(your_func)
| SIGNAL | ARGS | WHEN IT FIRES |
|---|---|---|
on_initialization_complete | — | SDK ready — safe to load ads |
on_initialization_failed | error, message | SDK failed to initialize |
on_interstitial_loaded | placement_id | Interstitial ready to show |
on_interstitial_shown | placement_id | Interstitial appeared on screen |
on_interstitial_completed | placement_id | User closed interstitial ← change scene here |
on_interstitial_failed_to_load | placement_id, error, msg | Auto-retries after 5 seconds |
on_interstitial_failed_to_show | placement_id, error, msg | Show failed — reload and use fallback navigation |
on_interstitial_clicked | placement_id | User tapped the interstitial ad |
on_rewarded_loaded | placement_id | Rewarded ad ready to show |
on_rewarded_earned | placement_id | User completed the video ← give reward here |
on_rewarded_shown | placement_id | Rewarded video started |
on_rewarded_failed_to_load | placement_id, error, msg | Auto-retries after 5 seconds |
on_rewarded_failed_to_show | placement_id, error, msg | Show failed — handle gracefully |
on_rewarded_clicked | placement_id | User tapped the rewarded ad |
on_banner_loaded | placement_id | Banner visible on screen — no extra call needed |
on_banner_failed_to_load | placement_id, error | Banner failed — check placement ID in dashboard |
on_banner_clicked | placement_id | User tapped the banner ad |
Debugging & Common Issues
Step-by-Step Diagnostic — Ads Not Showing
| STEP | WHAT TO CHECK |
|---|---|
|
ST 1
Correct method
|
Use the singleton correctly. Call UnityAds.show_interstitial() not a local variable. Check UnityAds.is_interstitial_ready() returns true before showing. If it returns false, the ad hasn't loaded yet — wait for on_interstitial_loaded signal.
|
|
ST 2
Unity dashboard stats
|
Unity Ads takes time to serve real ads. Open Unity Dashboard → Monetization → Reporting. Check if ad requests are arriving. Zero requests means the SDK isn't initializing properly. Low fill rate is normal in the first days. |
|
ST 3
Play Store listing
|
Your app must be published on the Play Store to serve real ads. Unity Ads verifies your app through the store. Publish to at least internal testing on Play Console to pass verification and enable full ad serving. |
|
ST 4
TEST_MODE check
|
Confirm TEST_MODE matches your build type. TEST_MODE = true shows dummy ads (good for development). TEST_MODE = false required for real revenue in production APK.
|
Common Error Reference
| SYMPTOM | CAUSE & FIX |
|---|---|
| UnityAds singleton not found | Plugin not enabled. Go to Project Settings → Plugins → enable UnityAdsPlugin. Confirm folder name is exactly unity_ads_plugin. |
| Initialization fails immediately | Wrong or empty Game ID. Open UnityAds.gd and verify GAME_ID matches your Unity Dashboard. Never leave it as the default test value in production. |
| Banner never appears | Banner placement not created in dashboard. Go to Unity Dashboard → Monetization → Ad Units → Add Ad Unit. Create a Banner type with name exactly Banner_Android. |
| Ads work in test, not production | TEST_MODE still true, or debug keystore used. Set TEST_MODE = false and re-export with your release keystore. |
| Gradle build fails | Gradle Build not enabled in export. Check "Use Gradle Build" in your Android export preset. Also confirm INTERNET and ACCESS_NETWORK_STATE permissions are enabled. |
| App crashes on launch (Android) | Build templates not installed. Run Project → Install Android Build Template. Without it, the AAR cannot be bundled. |
| show_interstitial() does nothing | Called before loading or wrong signal syntax. Always call initialize() → wait for on_initialization_complete → load_interstitial() → wait for on_interstitial_loaded → then show. Also confirm you're using Godot 4 dot syntax for signals: UnityAds.on_interstitial_loaded.connect(func) |
| Signals never fire | Using old Godot 3 connect syntax. Replace UnityAds.connect("signal_name", func) with UnityAds.signal_name.connect(func) throughout your scripts. |
Logcat — Reading Android Logs
# Connect device via USB, then: adb logcat | grep -i "UnityAds" adb logcat | grep -i "unity3d" adb logcat | grep -i "Godot" # Key log messages to look for: "[UnityAds] Plugin found" ← plugin loaded ✓ "[UnityAds] Initialized successfully" ← ready to load ads ✓ "[UnityAds] Plugin NOT found" ← plugin missing ✗ "[UnityAds] Init failed" ← wrong Game ID ✗
Unity Ads vs AdMob — Which to Use?
🎮 Unity Ads — Best For
- Games with young / gaming audience
- Rewarded video heavy monetization
- Simpler setup (no manifest editing)
- Strong fill rate for game genres
📊 AdMob — Best For
- Maximum ad variety (5 ad types)
- Mediation across all networks
- Higher CPM for general apps
- Better analytics via Firebase