nccgs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/.claude/agents/nccgs-accessibility-specialist.md +26 -0
  2. package/.claude/agents/nccgs-adversarial-reviewer.md +26 -0
  3. package/.claude/agents/nccgs-ai-programmer.md +26 -0
  4. package/.claude/agents/nccgs-analytics-engineer.md +26 -0
  5. package/.claude/agents/nccgs-art-direction-lead.md +26 -0
  6. package/.claude/agents/nccgs-audio-direction-lead.md +26 -0
  7. package/.claude/agents/nccgs-creative-director.md +26 -0
  8. package/.claude/agents/nccgs-documentation-manager.md +26 -0
  9. package/.claude/agents/nccgs-economy-designer.md +26 -0
  10. package/.claude/agents/nccgs-engine-programmer.md +26 -0
  11. package/.claude/agents/nccgs-game-design-lead.md +26 -0
  12. package/.claude/agents/nccgs-game-designer.md +26 -0
  13. package/.claude/agents/nccgs-gameplay-programmer.md +26 -0
  14. package/.claude/agents/nccgs-level-designer.md +26 -0
  15. package/.claude/agents/nccgs-live-ops-designer.md +26 -0
  16. package/.claude/agents/nccgs-localization-lead.md +26 -0
  17. package/.claude/agents/nccgs-narrative-lead.md +26 -0
  18. package/.claude/agents/nccgs-network-programmer.md +26 -0
  19. package/.claude/agents/nccgs-performance-analyst.md +26 -0
  20. package/.claude/agents/nccgs-producer.md +26 -0
  21. package/.claude/agents/nccgs-production-coordinator.md +23 -0
  22. package/.claude/agents/nccgs-programming-lead.md +26 -0
  23. package/.claude/agents/nccgs-prototyper.md +26 -0
  24. package/.claude/agents/nccgs-qa-engineer.md +26 -0
  25. package/.claude/agents/nccgs-qa-lead.md +26 -0
  26. package/.claude/agents/nccgs-release-engineer.md +26 -0
  27. package/.claude/agents/nccgs-release-lead.md +26 -0
  28. package/.claude/agents/nccgs-security-engineer.md +26 -0
  29. package/.claude/agents/nccgs-sound-designer.md +26 -0
  30. package/.claude/agents/nccgs-systems-designer.md +26 -0
  31. package/.claude/agents/nccgs-technical-architect.md +26 -0
  32. package/.claude/agents/nccgs-technical-artist.md +26 -0
  33. package/.claude/agents/nccgs-technical-director.md +26 -0
  34. package/.claude/agents/nccgs-tools-programmer.md +26 -0
  35. package/.claude/agents/nccgs-ui-programmer.md +26 -0
  36. package/.claude/agents/nccgs-unity-build-specialist.md +26 -0
  37. package/.claude/agents/nccgs-unity-content-specialist.md +26 -0
  38. package/.claude/agents/nccgs-unity-implementer.md +26 -0
  39. package/.claude/agents/nccgs-unity-rendering-specialist.md +26 -0
  40. package/.claude/agents/nccgs-unity-systems-specialist.md +26 -0
  41. package/.claude/agents/nccgs-unity-ui-specialist.md +26 -0
  42. package/.claude/agents/nccgs-ux-designer.md +26 -0
  43. package/.claude/agents/nccgs-verification-engineer.md +26 -0
  44. package/.claude/agents/nccgs-world-builder.md +26 -0
  45. package/.claude/agents/nccgs-writer.md +26 -0
  46. package/.claude/nccgs/THIRD_PARTY_NOTICES.md +13 -0
  47. package/.claude/nccgs/VERSION +1 -0
  48. package/.claude/nccgs/constitution.md +131 -0
  49. package/.claude/nccgs/hooks/agent-audit.mjs +10 -0
  50. package/.claude/nccgs/hooks/common.mjs +41 -0
  51. package/.claude/nccgs/hooks/post-compact.mjs +1 -0
  52. package/.claude/nccgs/hooks/pre-compact.mjs +8 -0
  53. package/.claude/nccgs/hooks/protect-git.mjs +22 -0
  54. package/.claude/nccgs/hooks/protect-write.mjs +17 -0
  55. package/.claude/nccgs/hooks/session-start.mjs +15 -0
  56. package/.claude/nccgs/hooks/session-stop.mjs +7 -0
  57. package/.claude/nccgs/protocols/agent-contract.md +34 -0
  58. package/.claude/nccgs/protocols/context-packets.md +13 -0
  59. package/.claude/nccgs/protocols/evidence.md +15 -0
  60. package/.claude/nccgs/protocols/model-routing.md +22 -0
  61. package/.claude/nccgs/protocols/orchestration.md +26 -0
  62. package/.claude/nccgs/protocols/unity-boundary.md +11 -0
  63. package/.claude/nccgs/settings.fragment.json +77 -0
  64. package/.claude/nccgs/studio.json +347 -0
  65. package/.claude/nccgs/tools/configure-models.mjs +39 -0
  66. package/.claude/nccgs/unity-skills-manifest.json +727 -0
  67. package/.claude/nccgs/workflow-catalog.json +317 -0
  68. package/.claude/rules/nccgs-canonical-docs.md +19 -0
  69. package/.claude/rules/nccgs-editor-tools.md +12 -0
  70. package/.claude/rules/nccgs-localization.md +12 -0
  71. package/.claude/rules/nccgs-networking.md +13 -0
  72. package/.claude/rules/nccgs-performance.md +14 -0
  73. package/.claude/rules/nccgs-rendering.md +16 -0
  74. package/.claude/rules/nccgs-security.md +13 -0
  75. package/.claude/rules/nccgs-tests.md +18 -0
  76. package/.claude/rules/nccgs-ui.md +14 -0
  77. package/.claude/rules/nccgs-unity-assets.md +21 -0
  78. package/.claude/rules/nccgs-unity-code.md +22 -0
  79. package/.claude/skills/accessibility-review/SKILL.md +14 -0
  80. package/.claude/skills/architecture-decision/SKILL.md +14 -0
  81. package/.claude/skills/asset-audit/SKILL.md +14 -0
  82. package/.claude/skills/audit/SKILL.md +16 -0
  83. package/.claude/skills/audit/references/dimensions.md +46 -0
  84. package/.claude/skills/balance-review/SKILL.md +14 -0
  85. package/.claude/skills/bug-triage/SKILL.md +14 -0
  86. package/.claude/skills/build-live-game/SKILL.md +317 -0
  87. package/.claude/skills/build-live-game/references/achievements.md +779 -0
  88. package/.claude/skills/build-live-game/references/apis.md +280 -0
  89. package/.claude/skills/build-live-game/references/authentication.md +437 -0
  90. package/.claude/skills/build-live-game/references/battlepass.md +860 -0
  91. package/.claude/skills/build-live-game/references/cloud-code.md +563 -0
  92. package/.claude/skills/build-live-game/references/cloud-save.md +474 -0
  93. package/.claude/skills/build-live-game/references/deployment.md +216 -0
  94. package/.claude/skills/build-live-game/references/player-account.md +813 -0
  95. package/.claude/skills/build-live-game/references/remote-config.md +96 -0
  96. package/.claude/skills/build-live-game/references/tooling.md +431 -0
  97. package/.claude/skills/closure/SKILL.md +14 -0
  98. package/.claude/skills/code-review/SKILL.md +14 -0
  99. package/.claude/skills/compatibility-review/SKILL.md +14 -0
  100. package/.claude/skills/context-pack/SKILL.md +14 -0
  101. package/.claude/skills/dependency-review/SKILL.md +14 -0
  102. package/.claude/skills/design/SKILL.md +20 -0
  103. package/.claude/skills/design-review/SKILL.md +14 -0
  104. package/.claude/skills/evidence-review/SKILL.md +14 -0
  105. package/.claude/skills/hotfix/SKILL.md +14 -0
  106. package/.claude/skills/implement-in-app-purchases/README.md +233 -0
  107. package/.claude/skills/implement-in-app-purchases/SKILL.md +158 -0
  108. package/.claude/skills/implement-in-app-purchases/references/api-notes.md +562 -0
  109. package/.claude/skills/implement-in-app-purchases/references/codeless-catalog.md +331 -0
  110. package/.claude/skills/implement-in-app-purchases/references/convert-adapty.md +268 -0
  111. package/.claude/skills/implement-in-app-purchases/references/convert-essentialkit.md +239 -0
  112. package/.claude/skills/implement-in-app-purchases/references/convert-revenuecat.md +275 -0
  113. package/.claude/skills/implement-in-app-purchases/references/convert-unipay.md +145 -0
  114. package/.claude/skills/implement-in-app-purchases/references/migration-v4-to-v5.md +204 -0
  115. package/.claude/skills/implement-in-app-purchases/references/path-add-iap-to-new-project.md +198 -0
  116. package/.claude/skills/implement-in-app-purchases/references/path-convert-native-google-billing.md +356 -0
  117. package/.claude/skills/implement-in-app-purchases/references/path-convert-native-storekit.md +446 -0
  118. package/.claude/skills/implement-in-app-purchases/references/path-implement-iap-d2c.md +680 -0
  119. package/.claude/skills/implement-in-app-purchases/references/platform-notes.md +204 -0
  120. package/.claude/skills/implement-in-app-purchases/references/pre-check.md +205 -0
  121. package/.claude/skills/incident-recovery/SKILL.md +14 -0
  122. package/.claude/skills/initialize-ai-navigation/SKILL.md +95 -0
  123. package/.claude/skills/initialize-ai-navigation/references/navigation-system.md +794 -0
  124. package/.claude/skills/levelplay-unity-integration/CHANGELOG.md +57 -0
  125. package/.claude/skills/levelplay-unity-integration/README.md +74 -0
  126. package/.claude/skills/levelplay-unity-integration/SKILL.md +1126 -0
  127. package/.claude/skills/levelplay-unity-integration/references/banner-api.md +920 -0
  128. package/.claude/skills/levelplay-unity-integration/references/best-practices.md +536 -0
  129. package/.claude/skills/levelplay-unity-integration/references/ilrd-api.md +337 -0
  130. package/.claude/skills/levelplay-unity-integration/references/initialization-api.md +630 -0
  131. package/.claude/skills/levelplay-unity-integration/references/interstitial-api.md +899 -0
  132. package/.claude/skills/levelplay-unity-integration/references/ios-setup.md +491 -0
  133. package/.claude/skills/levelplay-unity-integration/references/migration-sdk-9.md +666 -0
  134. package/.claude/skills/levelplay-unity-integration/references/privacy-settings.md +608 -0
  135. package/.claude/skills/levelplay-unity-integration/references/rewarded-api.md +902 -0
  136. package/.claude/skills/localization/SKILL.md +135 -0
  137. package/.claude/skills/localization/references/api-notes.md +76 -0
  138. package/.claude/skills/localization/resources/L10nBatchProcessor.cs +69 -0
  139. package/.claude/skills/localization/resources/LocalizedFontAsset.cs +18 -0
  140. package/.claude/skills/localize-game/SKILL.md +14 -0
  141. package/.claude/skills/migrate-project/SKILL.md +21 -0
  142. package/.claude/skills/migrate-project/references/procedure.md +63 -0
  143. package/.claude/skills/milestone-review/SKILL.md +14 -0
  144. package/.claude/skills/new-unity-project/SKILL.md +179 -0
  145. package/.claude/skills/optimize-audio/SKILL.md +199 -0
  146. package/.claude/skills/optimize-audio/resources/audio-import-api.md +146 -0
  147. package/.claude/skills/optimize-audio/resources/platform-settings.md +48 -0
  148. package/.claude/skills/optimize-text-mesh-pro/SKILL.md +182 -0
  149. package/.claude/skills/optimize-web/SKILL.md +393 -0
  150. package/.claude/skills/optimize-web/resources/WebOptimizer.cs +21 -0
  151. package/.claude/skills/optimize-web/resources/toktx-examples.sh +11 -0
  152. package/.claude/skills/performance-audit/SKILL.md +14 -0
  153. package/.claude/skills/physics-3d-collision/SKILL.md +442 -0
  154. package/.claude/skills/physics-3d-collision/references/troubleshooting.md +41 -0
  155. package/.claude/skills/physics-3d-collision/resources/CollisionDebugger.cs +33 -0
  156. package/.claude/skills/plan-feature/SKILL.md +14 -0
  157. package/.claude/skills/playtest/SKILL.md +14 -0
  158. package/.claude/skills/project-stage/SKILL.md +14 -0
  159. package/.claude/skills/prototype-feature/SKILL.md +14 -0
  160. package/.claude/skills/qa-plan/SKILL.md +14 -0
  161. package/.claude/skills/release/SKILL.md +18 -0
  162. package/.claude/skills/release-readiness/SKILL.md +14 -0
  163. package/.claude/skills/retrospective/SKILL.md +14 -0
  164. package/.claude/skills/review/SKILL.md +16 -0
  165. package/.claude/skills/security-audit/SKILL.md +14 -0
  166. package/.claude/skills/setup-multiplayer-services/SKILL.md +39 -0
  167. package/.claude/skills/setup-multiplayer-services/references/dgs-entrypoint.md +79 -0
  168. package/.claude/skills/setup-multiplayer-services/references/entrypoints.md +213 -0
  169. package/.claude/skills/setup-multiplayer-services/references/examples.md +33 -0
  170. package/.claude/skills/setup-multiplayer-services/references/implementation-fit.md +30 -0
  171. package/.claude/skills/setup-multiplayer-services/references/underlying-services.md +11 -0
  172. package/.claude/skills/setup-multiplayer-services/references/workflows-prerequisites.md +17 -0
  173. package/.claude/skills/setup-vivox-voice-chat/SKILL.md +118 -0
  174. package/.claude/skills/setup-vivox-voice-chat/evals/.env.example +6 -0
  175. package/.claude/skills/setup-vivox-voice-chat/evals/README.md +101 -0
  176. package/.claude/skills/setup-vivox-voice-chat/evals/promptfooconfig.yaml +32 -0
  177. package/.claude/skills/setup-vivox-voice-chat/evals/tests/init-and-login.yaml +76 -0
  178. package/.claude/skills/setup-vivox-voice-chat/evals/tests/text-chat.yaml +65 -0
  179. package/.claude/skills/setup-vivox-voice-chat/evals/tests/voice-channels.yaml +74 -0
  180. package/.claude/skills/setup-vivox-voice-chat/references/events-and-participants.md +79 -0
  181. package/.claude/skills/setup-vivox-voice-chat/references/init-and-login.md +92 -0
  182. package/.claude/skills/setup-vivox-voice-chat/references/text-chat.md +93 -0
  183. package/.claude/skills/setup-vivox-voice-chat/references/troubleshooting.md +47 -0
  184. package/.claude/skills/setup-vivox-voice-chat/references/voice-channels.md +90 -0
  185. package/.claude/skills/shader-graph-create-custom-node/SKILL.md +25 -0
  186. package/.claude/skills/shader-graph-create-custom-node/resources/all_hints.hlsl +182 -0
  187. package/.claude/skills/sprint-plan/SKILL.md +14 -0
  188. package/.claude/skills/sprite-editor/SKILL.md +66 -0
  189. package/.claude/skills/sprite-editor/references/api_reference.md +151 -0
  190. package/.claude/skills/sprite-editor/references/background.md +112 -0
  191. package/.claude/skills/sprite-editor/references/templates.md +72 -0
  192. package/.claude/skills/sprite-editor/scripts/AutomaticSliceTexture.cs +40 -0
  193. package/.claude/skills/sprite-editor/scripts/GenerateNewSpriteRects.cs +200 -0
  194. package/.claude/skills/sprite-editor/scripts/GetTextureSourceImageSize.cs +32 -0
  195. package/.claude/skills/sprite-editor/scripts/GetTextureToSlice.cs +55 -0
  196. package/.claude/skills/sprite-editor/scripts/GridSliceTexture.cs +40 -0
  197. package/.claude/skills/sprite-editor/scripts/IsometricSliceTexture.cs +141 -0
  198. package/.claude/skills/sprite-editor/scripts/README.md +134 -0
  199. package/.claude/skills/sprite-editor/scripts/SetPivotExample.cs +58 -0
  200. package/.claude/skills/sprite-editor/scripts/SpriteToPng.cs +88 -0
  201. package/.claude/skills/status/SKILL.md +16 -0
  202. package/.claude/skills/story-readiness/SKILL.md +14 -0
  203. package/.claude/skills/test/SKILL.md +16 -0
  204. package/.claude/skills/ui/SKILL.md +142 -0
  205. package/.claude/skills/ui-imgui/SKILL.md +186 -0
  206. package/.claude/skills/ui-imgui/references/gui-elements.md +156 -0
  207. package/.claude/skills/ui-imgui/references/templates.md +141 -0
  208. package/.claude/skills/ui-review/SKILL.md +14 -0
  209. package/.claude/skills/ui-ugui/SKILL.md +282 -0
  210. package/.claude/skills/ui-ugui/references/scrollview-setup.md +29 -0
  211. package/.claude/skills/ui-uitk/SKILL.md +235 -0
  212. package/.claude/skills/ui-uitk/references/common-issues.md +74 -0
  213. package/.claude/skills/ui-uitk/references/custom-elements.md +241 -0
  214. package/.claude/skills/ui-uitk/references/painter2d.md +282 -0
  215. package/.claude/skills/ui-uitk/references/pointermanipulator-guide.md +94 -0
  216. package/.claude/skills/ui-uitk/references/svg-icons.md +136 -0
  217. package/.claude/skills/ui-uitk/references/ui-runtime-binding.md +234 -0
  218. package/.claude/skills/ui-uitk/references/uss-guide.md +138 -0
  219. package/.claude/skills/unity-cli/CHANGELOG.md +233 -0
  220. package/.claude/skills/unity-cli/SECURITY.md +22 -0
  221. package/.claude/skills/unity-cli/SKILL.md +414 -0
  222. package/.claude/skills/unity-cli/references/auth-license-cloud.md +146 -0
  223. package/.claude/skills/unity-cli/references/build-run-test.md +349 -0
  224. package/.claude/skills/unity-cli/references/collaboration.md +472 -0
  225. package/.claude/skills/unity-cli/references/config-hub.md +103 -0
  226. package/.claude/skills/unity-cli/references/diagnostics-maintenance.md +326 -0
  227. package/.claude/skills/unity-cli/references/editors-install.md +327 -0
  228. package/.claude/skills/unity-cli/references/integration-advanced.md +472 -0
  229. package/.claude/skills/unity-cli/references/projects-templates.md +574 -0
  230. package/.claude/skills/unity-package-management/SKILL.md +304 -0
  231. package/.claude/skills/unity-package-management/references/select-packages.md +108 -0
  232. package/.claude/skills/urp-postprocessing/SKILL.md +188 -0
  233. package/.claude/skills/urp-postprocessing/references/code-templates.md +119 -0
  234. package/.claude/skills/urp-postprocessing/references/effect-reference.md +86 -0
  235. package/.claude/skills/validate-urp-render-graph-renderer-feature/SKILL.md +269 -0
  236. package/.claude/skills/work/SKILL.md +36 -0
  237. package/.claude/skills/work/references/classification.md +41 -0
  238. package/.claude/skills/work/references/closure.md +50 -0
  239. package/.claude/skills/work/references/feature-contracts.md +32 -0
  240. package/.claude/skills/work/references/verification.md +26 -0
  241. package/CLAUDE.md +6 -0
  242. package/LICENSE +21 -0
  243. package/README.md +162 -0
  244. package/THIRD_PARTY_NOTICES.md +23 -0
  245. package/UPGRADING.md +32 -0
  246. package/VERSION +1 -0
  247. package/docs/ARCHITECTURE.md +73 -0
  248. package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +324 -0
  249. package/docs/MIGRATION-MATRIX.md +23 -0
  250. package/docs/PROJECT-POLICY.md +67 -0
  251. package/docs/WORKFLOWS.md +61 -0
  252. package/package-assets/setup-vivox-voice-chat-evals.gitignore +7 -0
  253. package/package.json +41 -0
  254. package/scaffold/.nccgs/bugs/.gitkeep +1 -0
  255. package/scaffold/.nccgs/closures/.gitkeep +1 -0
  256. package/scaffold/.nccgs/context/.gitkeep +1 -0
  257. package/scaffold/.nccgs/decisions/.gitkeep +1 -0
  258. package/scaffold/.nccgs/evidence/.gitkeep +1 -0
  259. package/scaffold/.nccgs/features/.gitkeep +1 -0
  260. package/scaffold/.nccgs/migrations/.gitkeep +1 -0
  261. package/scaffold/.nccgs/playtests/.gitkeep +1 -0
  262. package/scaffold/.nccgs/project.yaml +103 -0
  263. package/scaffold/.nccgs/requirements.yaml +10 -0
  264. package/scaffold/.nccgs/reviews/.gitkeep +1 -0
  265. package/scaffold/.nccgs/state.md +40 -0
  266. package/scaffold/.nccgs/templates/agent-handoff.md +25 -0
  267. package/scaffold/.nccgs/templates/architecture-decision.md +27 -0
  268. package/scaffold/.nccgs/templates/closure-record.md +51 -0
  269. package/scaffold/.nccgs/templates/context-packet.yaml +17 -0
  270. package/scaffold/.nccgs/templates/evidence-record.md +23 -0
  271. package/scaffold/.nccgs/templates/feature-contract.md +35 -0
  272. package/scaffold/.nccgs/templates/migration-plan.md +40 -0
  273. package/scaffold/.nccgs/templates/waiver.md +11 -0
  274. package/scripts/cli.mjs +56 -0
  275. package/scripts/install.mjs +267 -0
  276. package/scripts/sync-unity-skills.mjs +126 -0
  277. package/scripts/validate.mjs +205 -0
  278. package/tests/framework.test.mjs +121 -0
@@ -0,0 +1,902 @@
1
+ # Rewarded Ads API Reference
2
+
3
+ ## Contents
4
+ - [Overview](#overview)
5
+ - [Key Characteristics](#key-characteristics)
6
+ - [Implementation Pattern](#implementation-pattern)
7
+ - [API Reference](#api-reference)
8
+ - [Data Types](#data-types)
9
+ - [Best Practices](#best-practices)
10
+ - [Common Issues](#common-issues)
11
+ - [Testing Checklist](#testing-checklist)
12
+
13
+ ## Overview
14
+
15
+ Rewarded ads are video ads that users choose to watch in exchange for in-app rewards. They're the highest-earning ad format and provide excellent user experience when implemented correctly.
16
+
17
+ ## Key Characteristics
18
+
19
+ - **User-initiated**: Users explicitly choose to watch
20
+ - **Win-win**: Users get valuable rewards, developers get revenue
21
+ - **Best for**: Extra lives, hints, currency, power-ups, skipping wait times
22
+
23
+ ## Implementation Pattern
24
+
25
+ ### Basic Rewarded Ad Implementation
26
+
27
+ ```csharp
28
+ using UnityEngine;
29
+ using Unity.Services.LevelPlay;
30
+
31
+ public class RewardedAdManager : MonoBehaviour
32
+ {
33
+ private LevelPlayRewardedAd rewardedAd;
34
+ private string adUnitId = "YOUR_REWARDED_AD_UNIT_ID";
35
+
36
+ void Start()
37
+ {
38
+ // Create the rewarded ad object and register event listeners.
39
+ // Do not call LoadAd() here — load is triggered explicitly by a publisher-controlled
40
+ // action (e.g. a button press, scene entry, or a point in gameplay where the ad
41
+ // should be available). See LoadAd() below.
42
+ rewardedAd = new LevelPlayRewardedAd(adUnitId);
43
+
44
+ // Register event listeners
45
+ rewardedAd.OnAdLoaded += OnAdLoaded;
46
+ rewardedAd.OnAdLoadFailed += OnAdLoadFailed;
47
+ rewardedAd.OnAdDisplayed += OnAdDisplayed;
48
+ rewardedAd.OnAdDisplayFailed += OnAdDisplayFailed;
49
+ rewardedAd.OnAdRewarded += OnAdRewarded;
50
+ rewardedAd.OnAdClosed += OnAdClosed;
51
+ rewardedAd.OnAdClicked += OnAdClicked;
52
+ rewardedAd.OnAdInfoChanged += OnAdInfoChanged; // Fires when the loaded ad updates after a new auction result
53
+ }
54
+
55
+ void OnDestroy()
56
+ {
57
+ // Unregister event listeners
58
+ if (rewardedAd != null)
59
+ {
60
+ rewardedAd.OnAdLoaded -= OnAdLoaded;
61
+ rewardedAd.OnAdLoadFailed -= OnAdLoadFailed;
62
+ rewardedAd.OnAdDisplayed -= OnAdDisplayed;
63
+ rewardedAd.OnAdDisplayFailed -= OnAdDisplayFailed;
64
+ rewardedAd.OnAdRewarded -= OnAdRewarded;
65
+ rewardedAd.OnAdClosed -= OnAdClosed;
66
+ rewardedAd.OnAdClicked -= OnAdClicked;
67
+ rewardedAd.OnAdInfoChanged -= OnAdInfoChanged;
68
+ }
69
+ }
70
+
71
+ // Call LoadAd() from a publisher-controlled trigger (e.g. a button or scene entry).
72
+ // The SDK does not auto-load, so LoadAd() must be called explicitly.
73
+ public void LoadAd()
74
+ {
75
+ Debug.Log("Loading rewarded ad...");
76
+ rewardedAd.LoadAd();
77
+ }
78
+
79
+ public void ShowAd()
80
+ {
81
+ if (rewardedAd.IsAdReady())
82
+ {
83
+ Debug.Log("Showing rewarded ad");
84
+ rewardedAd.ShowAd();
85
+ }
86
+ else
87
+ {
88
+ Debug.LogWarning("Rewarded ad is not ready yet");
89
+ }
90
+ }
91
+
92
+ // Event Callbacks
93
+ private void OnAdLoaded(LevelPlayAdInfo adInfo)
94
+ {
95
+ Debug.Log("Rewarded ad loaded successfully");
96
+ }
97
+
98
+ private void OnAdLoadFailed(LevelPlayAdError error)
99
+ {
100
+ Debug.LogWarning($"Rewarded ad failed to load: {error.ErrorMessage}");
101
+ // Retry loading after a delay
102
+ Invoke(nameof(LoadAd), 30f);
103
+ }
104
+
105
+ private void OnAdDisplayed(LevelPlayAdInfo adInfo)
106
+ {
107
+ Debug.Log("Rewarded ad displayed");
108
+ }
109
+
110
+ private void OnAdDisplayFailed(LevelPlayAdInfo adInfo, LevelPlayAdError error)
111
+ {
112
+ Debug.LogError($"Rewarded ad failed to display: {error.ErrorMessage}");
113
+ // Load a new ad
114
+ LoadAd();
115
+ }
116
+
117
+ private void OnAdRewarded(LevelPlayAdInfo adInfo, LevelPlayReward reward)
118
+ {
119
+ Debug.Log($"User earned reward: {reward.Amount} {reward.Name}");
120
+ // Grant the reward to the user
121
+ GrantReward(reward);
122
+ }
123
+
124
+ private void OnAdClosed(LevelPlayAdInfo adInfo)
125
+ {
126
+ Debug.Log("Rewarded ad closed");
127
+ // LoadAd() is not called here automatically. If your game preloads rewarded ads
128
+ // eagerly (so one is always ready), call LoadAd() here. If load is triggered by
129
+ // a player action, wait for that action instead.
130
+ }
131
+
132
+ private void OnAdClicked(LevelPlayAdInfo adInfo)
133
+ {
134
+ Debug.Log("Rewarded ad clicked");
135
+ }
136
+
137
+ private void OnAdInfoChanged(LevelPlayAdInfo adInfo)
138
+ {
139
+ // Optional: log or update analytics with the latest revenue estimate
140
+ Debug.Log($"Rewarded ad info changed - network: {adInfo.AdNetwork}, revenue: ${adInfo.Revenue}");
141
+ }
142
+
143
+ private void GrantReward(LevelPlayReward reward)
144
+ {
145
+ // Implement your reward logic here
146
+ Debug.Log($"Granting {reward.Amount} {reward.Name} to the user");
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Advanced: Multiple Rewarded Ad Placements
152
+
153
+ ```csharp
154
+ using UnityEngine;
155
+ using Unity.Services.LevelPlay;
156
+ using System.Collections.Generic;
157
+
158
+ public class MultiPlacementRewardedAdManager : MonoBehaviour
159
+ {
160
+ // Single ad unit ID, multiple placements
161
+ private string adUnitId = "YOUR_REWARDED_AD_UNIT_ID";
162
+ private LevelPlayRewardedAd rewardedAd;
163
+
164
+ // Track which placement is currently being shown
165
+ private string currentPlacement;
166
+
167
+ void Start()
168
+ {
169
+ // Create single rewarded ad object
170
+ rewardedAd = new LevelPlayRewardedAd(adUnitId);
171
+
172
+ // Register callbacks
173
+ rewardedAd.OnAdLoaded += OnAdLoaded;
174
+ rewardedAd.OnAdLoadFailed += OnAdLoadFailed;
175
+ rewardedAd.OnAdRewarded += OnAdRewarded;
176
+ rewardedAd.OnAdClosed += OnAdClosed;
177
+ rewardedAd.OnAdDisplayFailed += OnAdDisplayFailed;
178
+
179
+ // Load ad
180
+ rewardedAd.LoadAd();
181
+ }
182
+
183
+ void OnDestroy()
184
+ {
185
+ if (rewardedAd != null)
186
+ {
187
+ rewardedAd.OnAdLoaded -= OnAdLoaded;
188
+ rewardedAd.OnAdLoadFailed -= OnAdLoadFailed;
189
+ rewardedAd.OnAdRewarded -= OnAdRewarded;
190
+ rewardedAd.OnAdClosed -= OnAdClosed;
191
+ rewardedAd.OnAdDisplayFailed -= OnAdDisplayFailed;
192
+ }
193
+ }
194
+
195
+ // Public methods for different placements
196
+ public void ShowAdForHints()
197
+ {
198
+ ShowAdWithPlacement("hints");
199
+ }
200
+
201
+ public void ShowAdForExtraLives()
202
+ {
203
+ ShowAdWithPlacement("extra_lives");
204
+ }
205
+
206
+ public void ShowAdForCoins()
207
+ {
208
+ ShowAdWithPlacement("bonus_coins");
209
+ }
210
+
211
+ private void ShowAdWithPlacement(string placementName)
212
+ {
213
+ if (rewardedAd.IsAdReady())
214
+ {
215
+ // Check if placement is capped
216
+ if (LevelPlayRewardedAd.IsPlacementCapped(placementName))
217
+ {
218
+ Debug.LogWarning($"Placement '{placementName}' is capped");
219
+ return;
220
+ }
221
+
222
+ currentPlacement = placementName;
223
+ Debug.Log($"Showing rewarded ad with placement: {placementName}");
224
+ rewardedAd.ShowAd(placementName: placementName);
225
+ }
226
+ else
227
+ {
228
+ Debug.LogWarning("Rewarded ad is not ready");
229
+ }
230
+ }
231
+
232
+ private void OnAdLoaded(LevelPlayAdInfo adInfo)
233
+ {
234
+ Debug.Log("Rewarded ad loaded");
235
+ }
236
+
237
+ private void OnAdLoadFailed(LevelPlayAdError error)
238
+ {
239
+ Debug.LogWarning($"Rewarded ad load failed: {error.ErrorMessage}");
240
+ Invoke(nameof(LoadAd), 30f);
241
+ }
242
+
243
+ private void OnAdRewarded(LevelPlayAdInfo adInfo, LevelPlayReward reward)
244
+ {
245
+ Debug.Log($"User rewarded: {reward.Amount} {reward.Name} for placement: {currentPlacement}");
246
+
247
+ // Grant placement-specific rewards
248
+ switch (currentPlacement)
249
+ {
250
+ case "hints":
251
+ GrantHints(reward);
252
+ break;
253
+ case "extra_lives":
254
+ GrantExtraLives(reward);
255
+ break;
256
+ case "bonus_coins":
257
+ GrantCoins(reward);
258
+ break;
259
+ }
260
+ }
261
+
262
+ private void OnAdClosed(LevelPlayAdInfo adInfo)
263
+ {
264
+ Debug.Log("Rewarded ad closed");
265
+ currentPlacement = null;
266
+ LoadAd();
267
+ }
268
+
269
+ private void OnAdDisplayFailed(LevelPlayAdInfo adInfo, LevelPlayAdError error)
270
+ {
271
+ Debug.LogError($"Rewarded ad display failed: {error.ErrorMessage}");
272
+ currentPlacement = null;
273
+ LoadAd();
274
+ }
275
+
276
+ private void LoadAd()
277
+ {
278
+ if (rewardedAd != null)
279
+ {
280
+ rewardedAd.LoadAd();
281
+ }
282
+ }
283
+
284
+ // Reward granting methods
285
+ private void GrantHints(LevelPlayReward reward)
286
+ {
287
+ Debug.Log("Granting hints to player");
288
+ // Your hint logic here
289
+ }
290
+
291
+ private void GrantExtraLives(LevelPlayReward reward)
292
+ {
293
+ Debug.Log("Granting extra lives to player");
294
+ // Your lives logic here
295
+ }
296
+
297
+ private void GrantCoins(LevelPlayReward reward)
298
+ {
299
+ Debug.Log("Granting bonus coins to player");
300
+ // Your coins logic here
301
+ }
302
+ }
303
+ ```
304
+
305
+ ## API Reference
306
+
307
+ ### Constructor
308
+
309
+ #### `new LevelPlayRewardedAd(string adUnitId)`
310
+ Create a rewarded ad object.
311
+
312
+ **Parameters:**
313
+ - `adUnitId`: The ad unit identifier from your LevelPlay dashboard
314
+
315
+ **Returns:** `LevelPlayRewardedAd` object
316
+
317
+ **Usage:**
318
+ ```csharp
319
+ LevelPlayRewardedAd rewardedAd = new LevelPlayRewardedAd("your_ad_unit_id");
320
+ ```
321
+
322
+ **Important:** Call this only after `LevelPlay.Init()` has completed successfully (in the `OnInitSuccess` callback).
323
+
324
+ #### `new LevelPlayRewardedAd(string adUnitId, Config config)`
325
+ Create a rewarded ad object with custom configuration.
326
+
327
+ **Parameters:**
328
+ - `adUnitId`: The ad unit identifier
329
+ - `config`: Optional configuration object
330
+
331
+ **Usage:**
332
+ ```csharp
333
+ var configBuilder = new LevelPlayRewardedAd.Config.Builder();
334
+ configBuilder.SetBidFloor(1.0); // Minimum bid price in USD
335
+ var config = configBuilder.Build();
336
+ LevelPlayRewardedAd rewardedAd = new LevelPlayRewardedAd("your_ad_unit_id", config);
337
+ ```
338
+
339
+ ### Configuration Builder
340
+
341
+ #### `LevelPlayRewardedAd.Config.Builder()`
342
+ Create a configuration builder for rewarded ads.
343
+
344
+ **Returns:** `LevelPlayRewardedAd.Config.Builder` object
345
+
346
+ **Usage:**
347
+ ```csharp
348
+ var configBuilder = new LevelPlayRewardedAd.Config.Builder();
349
+ ```
350
+
351
+ **Set minimum bid price:**
352
+ ```csharp
353
+ configBuilder.SetBidFloor(1.0); // Minimum bid price in USD ($1.00 CPM)
354
+ ```
355
+
356
+ **When to use:** Set a minimum bid price in USD for ad requests.
357
+
358
+ #### `Build()`
359
+ Build the configuration object.
360
+
361
+ **Returns:** `LevelPlayRewardedAd.Config` object
362
+
363
+ **Usage:**
364
+ ```csharp
365
+ var config = configBuilder.Build();
366
+ ```
367
+
368
+ ### Core Methods
369
+
370
+ #### `LoadAd()`
371
+ Load a rewarded ad.
372
+
373
+ **Usage:**
374
+ ```csharp
375
+ rewardedAd.LoadAd();
376
+ ```
377
+
378
+ **When to call:**
379
+ - After SDK initialization completes
380
+ - After showing an ad (to load the next one)
381
+ - After a load or display failure
382
+
383
+ #### `ShowAd()`
384
+ Show the rewarded ad without a placement name.
385
+
386
+ **Usage:**
387
+ ```csharp
388
+ if (rewardedAd.IsAdReady())
389
+ {
390
+ rewardedAd.ShowAd();
391
+ }
392
+ ```
393
+
394
+ **Important:** Always check `IsAdReady()` before calling `ShowAd()`.
395
+
396
+ #### `ShowAd(placementName: string)`
397
+ Show the rewarded ad with a named placement for analytics tracking.
398
+
399
+ **Parameters:**
400
+ - `placementName`: Placement name configured in LevelPlay dashboard
401
+
402
+ **Usage:**
403
+ ```csharp
404
+ rewardedAd.ShowAd(placementName: "extra_lives");
405
+ ```
406
+
407
+ **When to use:** When tracking multiple placements within the same ad unit for analytics.
408
+
409
+ #### `GetReward(string placementName = null)`
410
+ Get the reward configuration for a placement.
411
+
412
+ **Parameters:**
413
+ - `placementName` (optional): The placement name to query. If null, returns default reward.
414
+
415
+ **Returns:** `LevelPlayReward` object with Name and Amount
416
+
417
+ **Usage:**
418
+ ```csharp
419
+ // Get default reward
420
+ LevelPlayReward defaultReward = rewardedAd.GetReward();
421
+ Debug.Log($"Default reward: {defaultReward.Amount} {defaultReward.Name}");
422
+
423
+ // Get placement-specific reward
424
+ LevelPlayReward placementReward = rewardedAd.GetReward("extra_lives");
425
+ Debug.Log($"Extra lives reward: {placementReward.Amount} {placementReward.Name}");
426
+ ```
427
+
428
+ **When to use:** To display reward information to users before they watch the ad (e.g., "Watch ad to earn 5 coins").
429
+
430
+ #### `IsAdReady()`
431
+ Check if a rewarded ad is loaded and ready to show.
432
+
433
+ **Returns:** `bool` - true if ready, false otherwise
434
+
435
+ **Usage:**
436
+ ```csharp
437
+ if (rewardedAd.IsAdReady())
438
+ {
439
+ // Ad is ready to show
440
+ rewardedAd.ShowAd();
441
+ }
442
+ else
443
+ {
444
+ // Ad not ready, maybe disable the button
445
+ }
446
+ ```
447
+
448
+ **Best practice:** Check this before showing ad UI buttons to users.
449
+
450
+ #### `LevelPlayRewardedAd.IsPlacementCapped(string placementName)` (Static)
451
+ Check if a placement has reached its capping limit.
452
+
453
+ **Parameters:**
454
+ - `placementName`: The placement name to check
455
+
456
+ **Returns:** `bool` - true if capped, false otherwise
457
+
458
+ **Usage:**
459
+ ```csharp
460
+ if (LevelPlayRewardedAd.IsPlacementCapped("extra_lives"))
461
+ {
462
+ Debug.Log("Extra lives placement is capped");
463
+ }
464
+ ```
465
+
466
+ **When to use:** Before showing an ad to check if the placement frequency cap is reached.
467
+
468
+ ### Events
469
+
470
+ All events are properties of the `LevelPlayRewardedAd` object.
471
+
472
+ **Threading:** All ad callbacks run on the Unity main thread, so you can safely call Unity APIs (update UI, access GameObjects, etc.) directly in these callbacks. This is different from the ILRD impression callback (see `references/ilrd-api.md`), which runs on a background thread.
473
+
474
+ #### `OnAdLoaded`
475
+ Fired when a rewarded ad is successfully loaded.
476
+
477
+ **Signature:** `event Action<LevelPlayAdInfo>`
478
+
479
+ **Usage:**
480
+ ```csharp
481
+ rewardedAd.OnAdLoaded += (adInfo) =>
482
+ {
483
+ Debug.Log("Rewarded ad loaded");
484
+ // Enable UI button, etc.
485
+ };
486
+ ```
487
+
488
+ #### `OnAdLoadFailed`
489
+ Fired when a rewarded ad fails to load.
490
+
491
+ **Signature:** `event Action<LevelPlayAdError>`
492
+
493
+ **Usage:**
494
+ ```csharp
495
+ rewardedAd.OnAdLoadFailed += (error) =>
496
+ {
497
+ Debug.LogWarning($"Load failed: {error.ErrorMessage}");
498
+ // Retry loading
499
+ };
500
+ ```
501
+
502
+ **Best practice:** Implement retry logic with exponential backoff.
503
+
504
+ #### `OnAdDisplayed`
505
+ Fired when a rewarded ad is displayed on screen.
506
+
507
+ **Signature:** `event Action<LevelPlayAdInfo>`
508
+
509
+ **Usage:**
510
+ ```csharp
511
+ rewardedAd.OnAdDisplayed += (adInfo) =>
512
+ {
513
+ Debug.Log("Rewarded ad displayed");
514
+ // Optional: Pause game
515
+ };
516
+ ```
517
+
518
+ #### `OnAdDisplayFailed`
519
+ Fired when a rewarded ad fails to display.
520
+
521
+ **Signature:** `event Action<LevelPlayAdInfo, LevelPlayAdError>`
522
+
523
+ **Usage:**
524
+ ```csharp
525
+ rewardedAd.OnAdDisplayFailed += (adInfo, error) =>
526
+ {
527
+ Debug.LogError($"Display failed: {error.ErrorMessage}");
528
+ // Load a new ad
529
+ rewardedAd.LoadAd();
530
+ };
531
+ ```
532
+
533
+ #### `OnAdRewarded`
534
+ Fired when the user has earned the reward.
535
+
536
+ **Signature:** `event Action<LevelPlayAdInfo, LevelPlayReward>`
537
+
538
+ **Parameters:**
539
+ - `adInfo`: Information about the ad
540
+ - `reward`: The reward object containing `Name` and `Amount`
541
+
542
+ **Usage:**
543
+ ```csharp
544
+ rewardedAd.OnAdRewarded += (adInfo, reward) =>
545
+ {
546
+ Debug.Log($"User earned: {reward.Amount} {reward.Name}");
547
+ // Grant the reward to the user
548
+ GrantReward(reward);
549
+ };
550
+ ```
551
+
552
+ **Critical:** This is where you grant rewards to the user. Don't wait for `OnAdClosed` - users may close before the ad finishes.
553
+
554
+ #### `OnAdClosed`
555
+ Fired when the rewarded ad is closed.
556
+
557
+ **Signature:** `event Action<LevelPlayAdInfo>`
558
+
559
+ **Usage:**
560
+ ```csharp
561
+ rewardedAd.OnAdClosed += (adInfo) =>
562
+ {
563
+ Debug.Log("Rewarded ad closed");
564
+ // Resume game. Whether to call LoadAd() here depends on your load strategy:
565
+ // call it if you preload eagerly; otherwise wait for the player's next load trigger.
566
+ };
567
+ ```
568
+
569
+ #### `OnAdClicked`
570
+ Fired when the user clicks on the rewarded ad.
571
+
572
+ **Signature:** `event Action<LevelPlayAdInfo>`
573
+
574
+ **Usage:**
575
+ ```csharp
576
+ rewardedAd.OnAdClicked += (adInfo) =>
577
+ {
578
+ Debug.Log("Rewarded ad clicked");
579
+ // Optional analytics tracking
580
+ };
581
+ ```
582
+
583
+ #### `OnAdInfoChanged`
584
+ Fired when ad information changes, such as when a new winning ad becomes available after auction.
585
+
586
+ **Signature:** `event Action<LevelPlayAdInfo>`
587
+
588
+ **Usage:**
589
+ ```csharp
590
+ rewardedAd.OnAdInfoChanged += (adInfo) =>
591
+ {
592
+ Debug.Log($"Ad info changed - New ad from: {adInfo.AdNetwork}");
593
+ Debug.Log($"Estimated revenue: ${adInfo.Revenue}");
594
+ };
595
+ ```
596
+
597
+ **Important:** This event indicates that a new ad with potentially different revenue has become available. The SDK automatically selects the winning ad from the auction when you call `ShowAd()`, but this event lets you know when the ad info has been updated. This is particularly important for revenue optimization as it signals when a new auction winner has loaded.
598
+
599
+ **When it fires:**
600
+ - After a new ad loads with different properties than the previous one
601
+ - When the waterfall selects a different ad network
602
+ - When ad auction results change
603
+
604
+ **Why it matters:** The updated `LevelPlayAdInfo` contains the latest revenue estimates and network information, which directly impacts your monetization. Always use the most recent `adInfo` when logging or analyzing ad performance.
605
+
606
+ **If you're using ILRD** (`references/ilrd-api.md`): the `LevelPlayImpressionData` you receive in the ILRD impression callback already contains the final revenue value, so `OnAdInfoChanged` is mostly useful for in-Editor debugging of the waterfall. Most publishers can leave it as a logging hook.
607
+
608
+ ## Data Types
609
+
610
+ ### LevelPlayReward
611
+
612
+ Represents the reward earned by the user.
613
+
614
+ **Properties:**
615
+ - `Name` (string): The reward name (e.g., "coins", "life")
616
+ - `Amount` (int): The reward amount (e.g., 10, 1)
617
+
618
+ **Usage:**
619
+ ```csharp
620
+ private void OnAdRewarded(LevelPlayAdInfo adInfo, LevelPlayReward reward)
621
+ {
622
+ Debug.Log($"Reward: {reward.Amount} {reward.Name}");
623
+
624
+ if (reward.Name == "coins")
625
+ {
626
+ playerCoins += reward.Amount;
627
+ }
628
+ }
629
+ ```
630
+
631
+ ### LevelPlayAdInfo
632
+
633
+ Contains information about the ad.
634
+
635
+ **Properties:**
636
+ - `AdId` (string): Unique identifier for this specific ad instance
637
+ - `AdUnitId` (string): The ad unit identifier
638
+ - `AdUnitName` (string): The ad unit name
639
+ - `AdSize` (LevelPlayAdSize): The ad size (may be null; banner-relevant)
640
+ - `AdFormat` (string): The ad format (e.g., "REWARDED")
641
+ - `PlacementName` (string): Placement name where ad was shown
642
+ - `AuctionId` (string): Unique auction identifier
643
+ - `CreativeId` (string): Creative identifier
644
+ - `Country` (string): User's country code (ISO 3166-1)
645
+ - `Ab` (string): A/B test segment identifier
646
+ - `SegmentName` (string): User segment name
647
+ - `AdNetwork` (string): Ad network that served the ad
648
+ - `InstanceName` (string): Ad network instance name
649
+ - `InstanceId` (string): Ad network instance identifier
650
+ - `Revenue` (double?): Estimated revenue in USD (nullable - check for null)
651
+ - `Precision` (string): Revenue precision level
652
+ - `EncryptedCPM` (string): Encrypted CPM value
653
+
654
+ ### LevelPlayAdError
655
+
656
+ Contains error information when ad operations fail.
657
+
658
+ **Properties:**
659
+ - `ErrorCode` (int): Numeric error code
660
+ - `ErrorMessage` (string): Human-readable error description
661
+ - `AdUnitId` (string): The ad unit identifier where the error occurred
662
+ - `AdId` (string): Unique identifier for the specific ad instance
663
+
664
+ ## Best Practices
665
+
666
+ ### Loading Strategy
667
+
668
+ Unlike the legacy IronSource rewarded video (where the SDK managed loading automatically), the MADU rewarded API requires `LoadAd()` to be called explicitly. The ad object is created in `OnInitSuccess`, but loading is a separate step triggered by the publisher.
669
+
670
+ **Explicit load pattern (matches official sample):**
671
+ ```csharp
672
+ // In OnInitSuccess: create the object and subscribe events only — do not load yet.
673
+ void OnInitSuccess(LevelPlayConfiguration config)
674
+ {
675
+ rewardedAd = new LevelPlayRewardedAd(adUnitId);
676
+ rewardedAd.OnAdLoaded += OnAdLoaded;
677
+ rewardedAd.OnAdClosed += OnAdClosed;
678
+ // LoadAd() is called later, from a publisher-controlled trigger.
679
+ }
680
+
681
+ // Example trigger: called from a UI button or a scene entry point.
682
+ public void OnPlayerReachesRewardedAdOpportunity()
683
+ {
684
+ rewardedAd.LoadAd();
685
+ }
686
+ ```
687
+
688
+ **Eager preload pattern (optional):** If the game always needs a rewarded ad ready the moment the player reaches certain screens, call `LoadAd()` immediately in `OnInitSuccess` after creating the object, and optionally again in `OnAdClosed`. This is a valid choice but makes the manual load requirement less visible — use it consciously, not as a default.
689
+
690
+ ```csharp
691
+ void OnInitSuccess(LevelPlayConfiguration config)
692
+ {
693
+ rewardedAd = new LevelPlayRewardedAd(adUnitId);
694
+ rewardedAd.OnAdLoaded += OnAdLoaded;
695
+ rewardedAd.OnAdClosed += OnAdClosed;
696
+ rewardedAd.LoadAd(); // Eager preload — conscious choice, not automatic legacy behavior.
697
+ }
698
+
699
+ void OnAdClosed(LevelPlayAdInfo adInfo)
700
+ {
701
+ rewardedAd.LoadAd(); // Reload immediately so one is ready for the next opportunity.
702
+ }
703
+ ```
704
+
705
+ ### UI Integration
706
+
707
+ **Show/hide buttons based on ad availability:**
708
+ ```csharp
709
+ public Button watchAdButton;
710
+
711
+ void Update()
712
+ {
713
+ // Enable button only if ad is ready
714
+ watchAdButton.interactable = rewardedAd != null && rewardedAd.IsAdReady();
715
+ }
716
+
717
+ void OnAdLoaded(LevelPlayAdInfo adInfo)
718
+ {
719
+ watchAdButton.interactable = true;
720
+ }
721
+
722
+ void OnAdLoadFailed(LevelPlayAdError error)
723
+ {
724
+ watchAdButton.interactable = false;
725
+ }
726
+ ```
727
+
728
+ ### Reward Granting
729
+
730
+ **Grant rewards in `OnAdRewarded`, not `OnAdClosed`:**
731
+ ```csharp
732
+ // ✅ CORRECT
733
+ private void OnAdRewarded(LevelPlayAdInfo adInfo, LevelPlayReward reward)
734
+ {
735
+ GrantReward(reward); // Grant immediately
736
+ }
737
+
738
+ // ❌ WRONG - user may close ad early
739
+ private void OnAdClosed(LevelPlayAdInfo adInfo)
740
+ {
741
+ // Don't grant reward here!
742
+ }
743
+ ```
744
+
745
+ **Note:** `OnAdRewarded` and `OnAdClosed` are asynchronous — `OnAdRewarded` may fire after `OnAdClosed`. Do not write logic in `OnAdClosed` that assumes the reward has already been granted.
746
+
747
+ ### Error Handling
748
+
749
+ **Handle ad load failures:**
750
+ ```csharp
751
+ private void OnAdLoadFailed(LevelPlayAdError error)
752
+ {
753
+ Debug.LogWarning($"Ad load failed: {error.ErrorMessage}");
754
+
755
+ // Retry loading after a delay
756
+ Invoke(nameof(RetryLoad), 30f);
757
+ }
758
+
759
+ private void RetryLoad()
760
+ {
761
+ rewardedAd.LoadAd();
762
+ }
763
+ ```
764
+
765
+ ### Multiple Placements
766
+
767
+ **Use placements to track different reward contexts:**
768
+ - Create placements in LevelPlay dashboard (e.g., "hints", "extra_lives")
769
+ - Use `ShowAd(placementName: "name")` to track separately
770
+ - Check `IsPlacementCapped()` before showing
771
+ - Handle rewards differently based on placement in `OnAdRewarded`
772
+
773
+ ### Memory Management
774
+
775
+ **Always unsubscribe from events:**
776
+ ```csharp
777
+ void OnDestroy()
778
+ {
779
+ if (rewardedAd != null)
780
+ {
781
+ rewardedAd.OnAdLoaded -= OnAdLoaded;
782
+ rewardedAd.OnAdLoadFailed -= OnAdLoadFailed;
783
+ rewardedAd.OnAdDisplayed -= OnAdDisplayed;
784
+ rewardedAd.OnAdDisplayFailed -= OnAdDisplayFailed;
785
+ rewardedAd.OnAdRewarded -= OnAdRewarded;
786
+ rewardedAd.OnAdClosed -= OnAdClosed;
787
+ rewardedAd.OnAdClicked -= OnAdClicked;
788
+ rewardedAd.OnAdInfoChanged -= OnAdInfoChanged;
789
+ }
790
+ }
791
+ ```
792
+
793
+ ## Common Issues
794
+
795
+ ### Issue: Error 1037 (Load during load)
796
+
797
+ **Cause:** Calling `LoadAd()` repeatedly in `Update()` or other high-frequency loops before the previous load completes.
798
+
799
+ **Common mistake:**
800
+ ```csharp
801
+ void Update()
802
+ {
803
+ // WRONG - This will cause error 1037!
804
+ if (!rewardedAd.IsAdReady())
805
+ {
806
+ rewardedAd.LoadAd();
807
+ }
808
+ }
809
+ ```
810
+
811
+ **Why this fails:** If the ad is still loading, calling `LoadAd()` again triggers error 1037: "Cannot load while another load is in progress."
812
+
813
+ **Solution:** Track load state manually and only call `LoadAd()` once:
814
+ ```csharp
815
+ private bool isLoadingAd = false;
816
+
817
+ void Start()
818
+ {
819
+ LoadRewardedAd();
820
+ }
821
+
822
+ void LoadRewardedAd()
823
+ {
824
+ if (isLoadingAd) return; // Prevent duplicate loads
825
+
826
+ isLoadingAd = true;
827
+ rewardedAd.LoadAd();
828
+ }
829
+
830
+ void OnAdLoaded(LevelPlayAdInfo adInfo)
831
+ {
832
+ isLoadingAd = false;
833
+ }
834
+
835
+ void OnAdLoadFailed(LevelPlayAdError error)
836
+ {
837
+ isLoadingAd = false;
838
+ // Optionally retry after delay
839
+ }
840
+ ```
841
+
842
+ ### Issue: Reward not granted to user
843
+
844
+ **Possible causes:**
845
+ - Granting reward in `OnAdClosed` instead of `OnAdRewarded`
846
+ - User closes ad before completion
847
+ - Reward logic has bugs
848
+
849
+ **Solution:**
850
+ Always grant rewards in `OnAdRewarded`, which fires when the user has earned the reward (typically after watching enough of the ad).
851
+
852
+ ### Issue: Ad not loading
853
+
854
+ **Possible causes:**
855
+ - SDK not initialized before creating ad object
856
+ - No internet connection
857
+ - Ad inventory not available in test region
858
+ - Incorrect ad unit ID
859
+
860
+ **Solutions:**
861
+ - Create ad object only after `OnInitSuccess` callback
862
+ - Check network connectivity
863
+ - Verify ad unit ID in LevelPlay dashboard
864
+ - Test on real device with good connection
865
+
866
+ ### Issue: Button always disabled
867
+
868
+ **Possible causes:**
869
+ - Not checking `IsAdReady()` correctly
870
+ - Ad failed to load and no retry logic
871
+ - Event subscriptions not working
872
+
873
+ **Solutions:**
874
+ - Implement `OnAdLoaded` callback to enable button
875
+ - Add retry logic in `OnAdLoadFailed`
876
+ - Debug log all callbacks to verify they fire
877
+
878
+ ### Issue: Multiple reward grants
879
+
880
+ **Possible causes:**
881
+ - `OnAdRewarded` callback subscribed multiple times
882
+ - Not tracking if reward already granted
883
+
884
+ **Solutions:**
885
+ - Unsubscribe in `OnDestroy()`
886
+ - Only subscribe once in initialization
887
+ - Use flag to track if reward granted for this ad
888
+
889
+ ## Testing Checklist
890
+
891
+ - [ ] Ad loads successfully after SDK initialization
892
+ - [ ] `IsAdReady()` returns true when ad is loaded
893
+ - [ ] Ad displays correctly when `ShowAd()` is called
894
+ - [ ] `OnAdRewarded` fires and reward is granted
895
+ - [ ] UI button enables when ad is ready
896
+ - [ ] UI button disables when ad is not ready
897
+ - [ ] Ad reloads automatically after being shown
898
+ - [ ] Retry logic works when ad fails to load
899
+ - [ ] Multiple placements track correctly (if using)
900
+ - [ ] Placement capping works (if configured)
901
+ - [ ] Memory leaks prevented (events unsubscribed)
902
+ - [ ] Tested on multiple devices and network conditions