// naughty.games SDK for Unity WebGL (SDK v1): lifecycle and ad breaks. // Guide: https://naughty.games/guides/in-game-ads-sdk // // Setup: // 1. Put this file anywhere under Assets/ and NaughtyGamesSdk.jslib in // Assets/Plugins/WebGL/. // 2. Add an empty GameObject to your first scene and attach this component. // It survives scene loads and keeps one instance. // 3. Add inside // of your WebGL template's index.html. // // Use: // NaughtyGamesSdk.Instance.GameLoadingFinished(); // NaughtyGamesSdk.Instance.CommercialBreak(() => LoadNextLevel()); // NaughtyGamesSdk.Instance.RewardedBreak(granted => { if (granted) GiveCoins(50); }); // // Rules: grant a reward only when RewardedBreak calls back with true. A // break can finish at once (no ad available, or too soon after the last // one), so always continue the game from the callback. using System; using System.Runtime.InteropServices; using UnityEngine; public class NaughtyGamesSdk : MonoBehaviour { public static NaughtyGamesSdk Instance { get; private set; } /// "naughty" on naughty.games, "local" on localhost (test ads), "inert" anywhere else. public string Env { get; private set; } = "inert"; public bool Ready { get; private set; } public event Action Initialized; public event Action Paused; public event Action Resumed; public event Action Muted; public event Action Unmuted; [Tooltip("Set Time.timeScale to 0 while an ad plays, and back afterwards.")] public bool pauseTimeDuringAds = true; [Tooltip("Mute all audio while an ad plays, and restore it afterwards.")] public bool muteAudioDuringAds = true; [Tooltip("In the Editor there is no ad: should RewardedBreak grant, so you can test your reward code?")] public bool editorGrantsRewarded = true; private Action commercialDone; private Action rewardedDone; private float timeScaleBeforeAd = 1f; private float volumeBeforeAd = 1f; #if UNITY_WEBGL && !UNITY_EDITOR [DllImport("__Internal")] private static extern void NgSdk_Start(string objectName); [DllImport("__Internal")] private static extern void NgSdk_GameLoadingFinished(); [DllImport("__Internal")] private static extern void NgSdk_GameplayStart(); [DllImport("__Internal")] private static extern void NgSdk_GameplayStop(); [DllImport("__Internal")] private static extern void NgSdk_CommercialBreak(string objectName); [DllImport("__Internal")] private static extern void NgSdk_RewardedBreak(string objectName); #else // Editor and non-WebGL builds: no SDK. Calls answer at once. private void NgSdk_Start(string objectName) { OnNgInit("inert"); } private static void NgSdk_GameLoadingFinished() { } private static void NgSdk_GameplayStart() { } private static void NgSdk_GameplayStop() { } private void NgSdk_CommercialBreak(string objectName) { OnNgCommercialBreakDone(); } private void NgSdk_RewardedBreak(string objectName) { OnNgRewardedBreakDone(editorGrantsRewarded ? "true" : "false"); } #endif private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); NgSdk_Start(gameObject.name); } public void GameLoadingFinished() { NgSdk_GameLoadingFinished(); } public void GameplayStart() { NgSdk_GameplayStart(); } public void GameplayStop() { NgSdk_GameplayStop(); } /// Shows an ad between levels if one is due. onDone always runs, often at once. public void CommercialBreak(Action onDone) { if (commercialDone != null) { onDone?.Invoke(); // one break at a time return; } commercialDone = onDone ?? (() => { }); NgSdk_CommercialBreak(gameObject.name); } /// Offers a rewarded ad. onDone(true) only when the player watched it to the end. public void RewardedBreak(Action onDone) { if (rewardedDone != null) { onDone?.Invoke(false); // one break at a time return; } rewardedDone = onDone ?? (_ => { }); NgSdk_RewardedBreak(gameObject.name); } // ---- Called from NaughtyGamesSdk.jslib through SendMessage ---- private void OnNgInit(string env) { Env = env; Ready = true; Initialized?.Invoke(); } private void OnNgEvent(string name) { switch (name) { case "pause": if (pauseTimeDuringAds) { timeScaleBeforeAd = Time.timeScale; Time.timeScale = 0f; } Paused?.Invoke(); break; case "resume": if (pauseTimeDuringAds) Time.timeScale = timeScaleBeforeAd; Resumed?.Invoke(); break; case "mute": if (muteAudioDuringAds) { volumeBeforeAd = AudioListener.volume; AudioListener.volume = 0f; } Muted?.Invoke(); break; case "unmute": if (muteAudioDuringAds) AudioListener.volume = volumeBeforeAd; Unmuted?.Invoke(); break; } } private void OnNgCommercialBreakDone() { var done = commercialDone; commercialDone = null; done?.Invoke(); } private void OnNgRewardedBreakDone(string granted) { var done = rewardedDone; rewardedDone = null; done?.Invoke(granted == "true"); } }