// 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");
}
}