Unity Integration (C#)

This guide shows how to integrate Scrya ads into a Unity project using UnityWebRequest. The pattern works for any Unity version supporting .NET 4.x or later.

Setup

No package to install — Scrya is a REST API. You just need your API key and these helper methods.

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Text;

public class ScryaAds : MonoBehaviour
{
    private const string API_BASE = "https://api.scrya.com/api/ads";
    private string apiKey = "sk_live_your_key_here";
    private string gameId = "your_game_id";

    private IEnumerator PostJson(string endpoint, string json,
        System.Action<string> onSuccess, System.Action<string> onError = null)
    {
        var request = new UnityWebRequest(API_BASE + endpoint, "POST");
        request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json));
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader("Content-Type", "application/json");
        request.SetRequestHeader("Authorization", "Bearer " + apiKey);

        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
            onSuccess?.Invoke(request.downloadHandler.text);
        else
            onError?.Invoke(request.error);
    }
}

Step 1: Match an Ad

Call this when generating a scene (dilemma, dialogue, cutscene):

[System.Serializable]
public class AdMatchRequest
{
    public string game_id;
    public string user_id;
    public PlayerGeo player_geo;
    public GameContext game_context;
    public string session_id;
}

[System.Serializable]
public class PlayerGeo { public string country_code; public string state_code; }

[System.Serializable]
public class GameContext
{
    public string theme_id;
    public string category;
    public string narrative;
    public string[] mood_tags;
}

[System.Serializable]
public class AdMatchResponse
{
    public string campaign_id;
    public string creative_id;
    public string narrative_hook;
    public string creative_type;
    public string brand_name;
    public int cpm_bid_cents;
}

public IEnumerator MatchAd(string playerId, string narrative, string[] moods,
    System.Action<AdMatchResponse> callback)
{
    var request = new AdMatchRequest
    {
        game_id = gameId,
        user_id = playerId,
        player_geo = new PlayerGeo { country_code = "US" },
        game_context = new GameContext
        {
            theme_id = "3",
            category = "simulation",
            narrative = narrative,
            mood_tags = moods
        },
        session_id = System.Guid.NewGuid().ToString()
    };

    yield return PostJson("/match", JsonUtility.ToJson(request), (json) =>
    {
        var match = JsonUtility.FromJson<AdMatchResponse>(json);
        callback?.Invoke(match);
    }, (error) =>
    {
        Debug.Log("No ad matched (normal): " + error);
        callback?.Invoke(null);
    });
}

Step 2: Apply Visual Bias

If the match includes a biased_pipeline, apply it to your scene:

// After getting a match:
if (match != null)
{
    // Inject narrative hook into dialogue
    dialogueSystem.AddLine(match.narrative_hook);

    // Optionally simulate to get visual bias
    StartCoroutine(SimulateBias(match.campaign_id, "crisis", "tense, kinetic",
        (sim) =>
        {
            // Apply biased lighting
            if (!string.IsNullOrEmpty(sim.biased_lighting))
                sceneLighting.SetPreset(sim.biased_lighting);

            // Apply biased effects
            if (!string.IsNullOrEmpty(sim.biased_effects))
                postProcessing.SetPreset(sim.biased_effects);
        }));
}

Step 3: Record the Impression

[System.Serializable]
public class ImpressionRequest
{
    public string campaign_id;
    public string creative_id;
    public string game_id;
}

public IEnumerator RecordImpression(AdMatchResponse match)
{
    if (match == null) yield break;

    var request = new ImpressionRequest
    {
        campaign_id = match.campaign_id,
        creative_id = match.creative_id,
        game_id = gameId
    };

    yield return PostJson("/impressions", JsonUtility.ToJson(request),
        (json) => Debug.Log("Impression recorded"),
        (error) => Debug.LogWarning("Impression failed: " + error));
}

Full Example: Scene Generation

public IEnumerator GenerateSceneWithAds(string playerId, string narrative)
{
    AdMatchResponse match = null;

    // 1. Match
    yield return MatchAd(playerId, narrative,
        new[] { "tense", "urgent" },
        (m) => match = m);

    // 2. Inject
    if (match != null)
    {
        dialogueSystem.AddLine(match.narrative_hook);
        Debug.Log($"Ad from {match.brand_name}: {match.narrative_hook}");
    }

    // ... render the scene ...

    // 3. Record (after player sees it)
    yield return RecordImpression(match);
}

Async/Await Alternative

If using Unity 2023+ with async/await support or UniTask:

public async Task<AdMatchResponse> MatchAdAsync(string playerId, string narrative)
{
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiKey);

    var json = JsonUtility.ToJson(new AdMatchRequest { /* ... */ });
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    var response = await client.PostAsync(API_BASE + "/match", content);

    if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
        return null; // No matching ads

    var responseJson = await response.Content.ReadAsStringAsync();
    return JsonUtility.FromJson<AdMatchResponse>(responseJson);
}

Next Steps