Démarrage Unity

Ajoutez le chat de support à un jeu Unity : mobile, ordinateur et Steam, à la souris, au tactile et à la manette.

Prérequis

Installer le paquet

Le paquet s'appelle gg.inngame.sdk. Dans Unity, ouvrez Window → Package Manager → + → Add package from git URL et collez :

https://github.com/inngame/sdk-unity.git#0.2.3

Ou ajoutez-le à Packages/manifest.json :

{
  "dependencies": {
    "gg.inngame.sdk": "https://github.com/inngame/sdk-unity.git#0.2.3"
  }
}

Si vous avez reçu le SDK sous forme de dossier, utilisez Add package from disk et choisissez son package.json. Le paquet contient un exemple Basic (bouton Aide dans le menu pause, avec pastille de non-lus) à importer depuis le Package Manager.

Configurer

Le plus rapide : Tools → Inngame → Settings, collez la clé de l'app (tableau de bord → votre app → Installation) et cliquez sur Send test message : le message arrive dans votre inbox. Cochez ensuite Auto initialize. Les réglages sont enregistrés dans Assets/Resources/InngameSettings.

Ou configurez par le code, une fois, dans votre première scène :

using InngameSDK;
using UnityEngine;

public class Boot : MonoBehaviour
{
    void Awake()
    {
        Inngame.Configure("app_XXXXXXXX", new InngameOptions
        {
            PauseGameWhileOpen = true,          // Time.timeScale = 0 while the chat is open
            ScreenshotHotkey = KeyCode.F8,      // desktop: capture + open chat
            ShakeToReport = true,               // mobile: shake to capture + open chat
        });

        // Or, if you used Tools > Inngame > Settings:
        // Inngame.ConfigureFromSettings(o => o.PauseGameWhileOpen = true);
    }
}

Appelez-le depuis le thread principal. Configure ne coûte presque rien : la session démarre à l'image suivante, et un Identify fait juste après part avec elle. Le rappeler avec la même clé met seulement les options à jour. Le SDK parle à https://app.inngame.app (Inngame.DefaultBaseUrl) sauf si vous renseignez BaseUrl. Les couleurs, textes et message d'accueil réglés dans le tableau de bord (Réglages → Apparence) remplacent le thème local : vous changez l'allure du chat sans nouveau build.

Ouvrir le chat

Placez une entrée Support ou Aide là où elle a sa place : menu pause, réglages, bouton flottant. Vérifiez qu'on l'atteint avec toutes les commandes que votre jeu gère.

helpButton.onClick.AddListener(Inngame.OpenChat);

// Unread badge on your own button
Inngame.UnreadChanged += n =>
{
    badge.SetActive(n > 0);
    badgeText.text = n > 9 ? "9+" : n.ToString();
};

// Optional hooks
Inngame.ChatOpened += () => audio.Pause();
Inngame.ChatClosed += () => audio.UnPause();
Inngame.MessageReceived += msg => Debug.Log("New reply from the team");

Sur téléphone, le chat occupe tout l'écran ; ailleurs, c'est un panneau centré. Imposez l'un ou l'autre avec InngameOptions.FullScreen.

Identifier le joueur

Chaque appareil commence avec un joueur anonyme : le chat marche avant toute connexion. Dès que vous savez qui joue, identifiez le joueur. Avec un jeton signé (JWT) ou Steam, son historique le suit sur tous les appareils où il se connecte ; un simple identifiant ne fait qu'étiqueter le joueur de cet appareil. Détails et modèle de sécurité : Identité des joueurs.

// Unverified: your account id (fine for most games)
await Inngame.Identify(player.Id, player.DisplayName, player.Email);

// Verified: a JWT signed by YOUR server with the app's identity secret
string jwt = await MyBackend.GetInngameToken();
await Inngame.IdentifyWithToken(jwt);

// Verified Steam identity (see below for the ticket helper)
await Inngame.IdentifySteam(GetSteamTicketHex);

Tous les appels d'identité renvoient un Task<bool>. Ajoutez les attributs du joueur utiles à l'équipe (progression, serveur, rang VIP…) avec SetMetadata : jusqu'à 50 clés ; valeurs texte (500 caractères au plus), nombres, booléens, ou null pour supprimer une clé. Les champs d'abonnement comme plan, paid ou subscription_status sont posés par le serveur seul et ignorés ici (voir Identité des joueurs).

await Inngame.SetMetadata(new Dictionary<string, object>
{
    ["vip"] = "gold",
    ["levelReached"] = 42,
    ["guild"] = null,   // removes the key
});

Achats. Chaque joueur a un jeton d'achat, un UUID émis par Inngame : Inngame.PurchaseToken (ou Inngame.CurrentUser.PurchaseToken), null avant la première session. Passez-le à la boutique (StoreKit appAccountToken, Google Play obfuscatedAccountId) ou comme identifiant d'utilisateur RevenueCat / Adapty / Superwall, pour que les événements d'abonnement retrouvent le joueur. Voir Intégrations.

Aide pour le ticket Steam

IdentifySteam prend une fonction qui renvoie un ticket Web API pour l'identité "inngame", en hexadécimal. Branchez d'abord Steam dans le tableau de bord pour que le serveur puisse le vérifier.

// Steamworks.NET
using Steamworks;
using System.Threading.Tasks;

static Callback<GetTicketForWebApiResponse_t> ticketCb;

static Task<string> GetSteamTicketHex()
{
    var tcs = new TaskCompletionSource<string>();
    ticketCb = Callback<GetTicketForWebApiResponse_t>.Create(r =>
    {
        if (r.m_eResult != EResult.k_EResultOK) { tcs.TrySetResult(null); return; }
        var sb = new System.Text.StringBuilder();
        for (int i = 0; i < r.m_cubTicket; i++) sb.Append(r.m_rgubTicket[i].ToString("x2"));
        tcs.TrySetResult(sb.ToString());
    });
    SteamUser.GetAuthTicketForWebApi("inngame");
    return tcs.Task;
}

// after SteamAPI.Init():
await Inngame.IdentifySteam(GetSteamTicketHex);
// Facepunch.Steamworks
await Inngame.IdentifySteam(async () =>
{
    var ticket = await SteamUser.GetAuthTicketForWebApiAsync("inngame");
    return ticket == null ? null : System.BitConverter.ToString(ticket.Data).Replace("-", "");
});

Contexte, journaux et captures

Chaque message du joueur emporte automatiquement, relevé au moment de l'envoi : la scène active et les 10 dernières scènes ; version de l'app, numéro de build (iOS CFBundleVersion, Android versionCode), bundle id, versions d'Unity et du SDK ; plateforme, modèle et type d'appareil, OS, processeur, mémoire vive, batterie et réseau ; GPU (nom, fabricant, API, mémoire) ; écran (résolution, DPI, fréquence, orientation, zone sûre) ; niveau de qualité, FPS visé, vSync, FPS moyen et minimum ; mémoire et GC ; langue du système, fuseau horaire (par ex. Europe/Paris) et décalage UTC ; temps depuis le lancement et échelle de temps ; et les 50 dernières lignes de journal, avec la pile d'appels des erreurs. Ajoutez vos propres clés :

// A value attached to every message from now on (null removes it)
Inngame.SetContext("level", currentLevel);
Inngame.SetContext("saveSlot", 2);

// Or compute values at send time
Inngame.ContextProvider = () => new Dictionary<string, object>
{
    ["coins"] = wallet.Coins,
    ["quest"] = quests.Current?.Id,
};

// What would be attached right now (debugging)
Debug.Log(Inngame.CurrentContext().Count);

Vos valeurs SetContext arrivent sous custom, et ce que renvoie le ContextProvider sous game. Le contexte est montré à l'équipe, jamais au joueur. Il est limité à 64 Ko par message (les lignes de journal les plus anciennes partent d'abord). LogLines = 0 coupe la capture des journaux.

Captures d'écran. Le chat a un bouton de capture. Vous pouvez aussi capturer par le code, par un raccourci clavier sur ordinateur (F8 par défaut, ScreenshotHotkey) ou, si vous activez ShakeToReport (désactivé par défaut), en secouant le téléphone. Le chat n'apparaît jamais sur la capture. Les captures sont en JPEG, 1600 px au plus sur le grand côté (ScreenshotQuality, ScreenshotMaxSize).

Inngame.CaptureScreenshot();            // capture, attach to the next message, open the chat
Inngame.CaptureScreenshot(openChat: false);

await Inngame.AttachImage(pngBytes, "image/png");  // an image you already have

Les photos de la galerie demandent du code natif : implémentez IImagePicker et passez-le dans InngameOptions.ImagePicker ; un bouton galerie apparaît alors dans la zone de saisie. Les photos sont modérées avant d'être stockées, et les boutons appareil photo et galerie disparaissent quand l'app désactive les photos (Réglages → Modération).

class GalleryPicker : IImagePicker   // e.g. with yasirkula's NativeGallery
{
    public void PickImage(Action<byte[], string> done) =>
        NativeGallery.GetImageFromGallery(path =>
            done(path == null ? null : File.ReadAllBytes(path), "image/jpeg"));
}

Manette, Steam Deck et ordinateur

// Steamworks.NET
options.OnScreenKeyboard = (current, done) =>
{
    SteamUtils.ShowGamepadTextInput(EGamepadTextInputMode.k_EGamepadTextInputModeNormal,
        EGamepadTextInputLineMode.k_EGamepadTextInputLineModeMultipleLines, "Message", 4000, current);
    pendingKeyboardCallback = done; // in your GamepadTextInputDismissed_t callback:
                                    // SteamUtils.GetEnteredGamepadTextInput(out var text, len); done(text);
};

Apparence

Créez un thème avec Assets → Create → Inngame → Theme (ou InngameTheme.CreateDark() / CreateLight()) : couleurs, arrondi des coins, taille du texte, police classique ou police TextMeshPro (TextBackend : Auto, Legacy ou TextMeshPro), titre, textes d'accueil et texte d'invite. Les réglages d'apparence du tableau de bord le remplacent à l'exécution et sont gardés en cache pour le lancement suivant.

Aux couleurs de votre jeu. Donnez au thème vos propres sprites d'interface (en 9-slice : réglez la bordure dans le Sprite Editor) et le chat est dessiné avec eux plutôt qu'avec des rectangles arrondis unis :

var t = InngameTheme.CreateDark();
t.LegacyFont = myFont;                    // or TmpFont
t.Logo = myLogoSprite;                    // header; null = the logo uploaded in the dashboard
t.PanelSprite = framePanel;               // the chat window
t.HeaderSprite = barSprite;               // ComposerSprite defaults to it
t.IncomingBubbleSprite = stoneButton;     // team / AI
t.OutgoingBubbleSprite = redButton;       // player
t.InputSprite = pillSprite;
t.ButtonSprite = roundButton;             // send button and card buttons
t.SpriteScale = 2.5f;                     // thinner borders on big source art
t.TintSprites = false;                    // keep the sprites' own colors (only text colors apply)
Inngame.Configure("app_XXXXXXXX", new InngameOptions { Theme = t });

Le texte des bulles reste à l'intérieur des bordures du sprite ; SkinPadding ajoute de la marge pour les cadres plus épais.

Dans le tableau de bord (Réglages → Apps → votre app → Apparence), vous habillez le chat sans build : envoyez un logo (PNG ou JPEG, carré, 512 Ko au plus ; il remplace l'initiale dans l'en-tête du chat), choisissez six couleurs pour les thèmes clair et sombre (accent, fond, texte, en-tête, bulle de l'équipe, bulle du joueur), ou partez d'un modèle (Inngame, Dark fantasy, Cartoon, Sci-fi, Minimal) qui remplit les deux thèmes d'un clic. Les couleurs d'en-tête et de bulles laissées vides suivent le fond et l'accent ; le texte des bulles est choisi pour rester lisible. L'aperçu montre le résultat au fur et à mesure. L'interface du chat existe en 25 langues (en, fr, de, es, it, pt, ru, ja, ko, zh, tr, pl, nl, sv, da, nb, fi, cs, uk, ar, hi, th, vi, id, ro). TextMeshPro est utilisé s'il est présent ; sinon le composant texte classique, dont les polices de secours du système gèrent le chinois, le japonais et le coréen sans rien faire.

Événements et automatisations

Envoyez des événements du jeu ; les automatisations réglées dans le tableau de bord y réagissent (un message, une offre, une demande d'avis, ou l'ouverture du chat).

await Inngame.Track("level_failed", new Dictionary<string, object>
{
    ["level"] = 9,
    ["attempts"] = 5,
});

// Let an automation open the chat right away if it wants to
await Inngame.Track("checkout_cancelled", opensChat: true);

Noms d'événements : lettres, chiffres, _, . et -, 64 caractères au plus.

Cartes : objets, offres, écrans, avis

L'équipe et l'IA peuvent envoyer des cartes interactives. Branchez celles que vous utilisez :

// "screen" cards open a screen of your game (the chat closes first)
Inngame.RegisterScreen("shop", tab => ShopUI.Open(tab));

// "offer" cards: show your own store or paywall
Inngame.OnOffer = offerId => Paywall.Show(offerId);

// "restore" cards: restore purchases with your IAP library
Inngame.OnRestorePurchases = () => Iap.Restore();

// "item" cards: the player claimed a gift (each card can be claimed once)
Inngame.OnItemGranted = item =>
    Debug.Log($"Claimed {item.Quantity} x {item.Name}");
TypeEffet du tap
reviewOuvre la page d'avis de la boutique : iOS itms-apps://…?action=write-review, Android market://details?id=…, sur ordinateur la page d'évaluation Steam (page du Mac App Store sur macOS sans identifiant Steam) ; sinon l'url de la carte.
linkOuvre l'url de la carte (http(s) ou lien profond d'app uniquement).
refundSur iOS, la feuille de remboursement d'Apple pour la transaction de la carte (Transaction.beginRefundRequest, iOS 15 et plus ; Apple décide). Ailleurs, et sur les iOS plus anciens, la page « Signaler un problème » d'Apple.
screenFerme le chat et appelle le gestionnaire enregistré avec RegisterScreen(card.screen, …), avec la valeur de la carte.
itemAffiche Récupérer ; appelle OnItemGranted(item) (Id, Name, Quantity, MessageId) une fois la récupération confirmée.
offerFerme le chat et appelle OnOffer(offerId).
restoreAppelle OnRestorePurchases().
ratingQuestion de satisfaction avec cinq boutons (voir plus bas). Rien à brancher.

Chaque tap est signalé à l'équipe (tapped, ou failed s'il n'y a pas de gestionnaire, pas d'écran enregistré pour cet identifiant, ou pas de lien utilisable). Les boutons portent le libellé de l'équipe ; les cartes d'avis et de remboursement envoyées avec le libellé anglais par défaut du serveur (defaultLabel: true) affichent à la place le texte du SDK dans la langue du joueur. Dans le code, InngameCard.UsesDefaultLabel indique lequel, et InngameCard.CustomLabel donne le libellé de l'équipe ou null.

OnItemGranted n'est appelé qu'après confirmation par Inngame : un objet n'est jamais donné deux fois, même avec deux appareils ou une connexion perdue, ni après que l'équipe a retiré la carte.

Donnez les objets côté serveur. OnItemGranted sert à rafraîchir votre interface. Pour créditer vraiment le joueur, écoutez le webhook item.claimed sur votre backend : un client modifié ne peut pas le falsifier.

Disponibilité. Sous le titre du chat, les joueurs voient votre délai de réponse habituel (« Répond en général en quelques heures », d'après votre première réponse moyenne sur 30 jours) et, hors de vos horaires, quand vous revenez, à leur heure locale (« Absents · de retour lun. 09:00 »). Réglez les deux dans l'inbox → Réglages → Apps → Général. Hors horaires, un joueur qui écrit reçoit aussi une réponse automatique dans sa langue, au plus une fois toutes les 12 heures par conversation ; vous pouvez en changer le texte ou la couper. Inngame.Config.Availability donne les valeurs brutes si vous voulez les afficher dans votre propre interface.

Annonces. Pendant une panne ou pour un bug connu, écrivez un mot dans l'inbox → Réglages → Apps → Général : il s'affiche en haut du chat pour tous les joueurs, traduit, jusqu'à l'heure choisie. Rien à brancher.

Satisfaction. Si vous activez la demande de note aux joueurs (inbox → Réglages → Apps → Avis), fermer une conversation envoie une carte de note : la question dans la langue du joueur et cinq boutons, accessibles à la manette. Rien à brancher. La note sert aux statistiques de votre équipe et ne décide jamais de qui reçoit une carte d'avis ; un 1 ou un 2 signale la conversation à l'équipe. Les notes apparaissent dans les Statistiques et dans le webhook conversation.rated.

Avis après une conversation résolue. Activez Proposer l'avis sur la boutique après une conversation résolue (même onglet) : chaque joueur dont la conversation est fermée reçoit la carte d'avis, quelle que soit sa note de satisfaction et même s'il n'a pas noté. Une fois par joueur, jamais en mode mineurs.

Les cartes d'avis ouvrent la page d'avis du store lui-même, jamais une fenêtre de note dans le jeu. Si la carte ne porte pas les identifiants, le SDK prend AppStoreId, PlayStorePackage et SteamAppId dans vos options, puis les identifiants des réglages Avis de l'app dans le tableau de bord (le package Play vaut par défaut votre bundle id).

La feuille de remboursement iOS vient d'un petit fichier Swift dans Runtime/Plugins/iOS (StoreKit 2), que Unity compile dans le projet Xcode avec le reste du SDK.

Notifications push

Quand l'équipe répond et que le chat est fermé, Inngame envoie un push par APNs ou FCM. Récupérez le jeton avec votre plugin habituel (Unity Mobile Notifications, Firebase Messaging) et transmettez-le. Branchez APNs ou FCM dans le tableau de bord.

// iOS: com.unity.mobile.notifications
using Unity.Notifications.iOS;
using (var req = new AuthorizationRequest(AuthorizationOption.Alert | AuthorizationOption.Badge | AuthorizationOption.Sound, true))
{
    while (!req.IsFinished) yield return null;
    if (!string.IsNullOrEmpty(req.DeviceToken))
        Inngame.SetPushToken(req.DeviceToken, Debug.isDebugBuild ? PushKind.ApnsSandbox : PushKind.Apns);
}

// Android: Firebase Cloud Messaging
Firebase.Messaging.FirebaseMessaging.TokenReceived += (_, e) => Inngame.SetPushToken(e.Token, PushKind.Fcm);

// Unregister
Inngame.SetPushToken(null, PushKind.Fcm);

SetPushToken renvoie un Task<bool>. Logout() efface aussi le jeton sur le serveur.

Hors ligne, temps réel et support indisponible

Déconnexion et suppression des données

// The player logged out of your game: the device continues as a new anonymous player
await Inngame.Logout();

// Right to erasure: deletes the player, their conversations and photos on the server, then local data
await Inngame.DeleteData();

Placez DeleteData derrière un bouton « Supprimer mes données de support » dans vos réglages de confidentialité. Ce que le SDK collecte, et quoi déclarer dans les formulaires de confidentialité des boutiques : Confidentialité et données.

Référence de l'API

Tout se trouve sur la classe statique InngameSDK.Inngame.

MembreRôle
Configure(appKey, options)Démarre le SDK. Peut être rappelé sans risque.
ConfigureFromSettings(customize)Démarre depuis Resources/InngameSettings. Renvoie false s'il manque.
Shutdown()Ferme le chat et la connexion.
IsConfiguredIndique si le SDK tourne.
Identify(userId, name, email)Identité non vérifiée.
IdentifyWithToken(jwt)Identité vérifiée (JWT HS256).
IdentifySteam(getTicketHex)Identité Steam vérifiée.
SetMetadata(dict)Attributs du joueur pour l'équipe.
Logout() / DeleteData()Nouveau joueur anonyme / tout effacer.
CurrentUserLe joueur courant vu par le serveur (Id, Name, Verified, Identified, Blocked, Language, PurchaseToken).
PurchaseTokenLe jeton d'achat Inngame (UUID) de ce joueur, pour StoreKit / Google Play / RevenueCat. null avant la première session.
OpenChat() / CloseChat() / IsChatOpenAfficher ou masquer le chat.
UnreadCountMessages de l'équipe non lus.
IsUnavailabletrue tant que votre support est indisponible (votre espace Inngame est suspendu). Le chat affiche alors « Le support est indisponible pour le moment. Réessayez plus tard. » dans la langue du joueur et masque la zone de saisie ; les messages non envoyés attendent et partent au retour du support. Le SDK revérifie toutes les 10 minutes chat ouvert et à chaque ouverture, et revient à la normale tout seul. En attendant, Identify, Track, Logout et DeleteData renvoient false.
ConfigRéglages venus de l'inbox, rafraîchis à chaque session et quand l'équipe les change : Appearance, PhotosEnabled, VerifiedOnly, Translation, Availability (Online, ReplyTime, NextOpenAt). null avant Configure.
MessagesMessages chargés (lecture seule).
SendMessage(text)Envoyer comme si le joueur l'avait tapé (avec contexte et capture en attente).
Track(name, props, opensChat)Événement du jeu pour les automatisations. Renvoie true une fois accepté par le serveur.
SetContext(key, value) / ContextProviderContexte personnalisé joint à chaque message.
CurrentContext()Le contexte qui serait joint maintenant (débogage).
CaptureScreenshot(openChat) / AttachImage(bytes, mime)Joindre des images au prochain message.
SetPushToken(token, kind)S'inscrire au push (Apns, ApnsSandbox, Fcm).
RegisterScreen(id, open)Cibles des cartes « écran ».
OnItemGranted, OnOffer, OnRestorePurchasesRappels des cartes.
UnreadChanged, ChatOpened, ChatClosed, MessageReceivedÉvénements.
LanguageLangue de l'interface et de la traduction (celle du système, sauf réglage dans les options).
SdkVersion / DefaultBaseUrl"0.2.3" / "https://app.inngame.app".

InngameOptions

OptionPar défautRemarques
BaseUrlhttps://app.inngame.appLe serveur Inngame (Inngame.DefaultBaseUrl).
LanguagesystèmeBCP-47, par ex. "fr", "pt-BR".
ThemesombreUn asset InngameTheme (couleurs, arrondi, taille du texte). L'apparence du tableau de bord le remplace.
PauseGameWhileOpenfalseMet Time.timeScale à 0 chat ouvert.
UnlockCursorWhileOpentrueAffiche et libère le curseur, puis le remet comme avant.
LogLines50Lignes de journal récentes par message. 0 pour couper.
ContextProvider—Contexte supplémentaire calculé à l'envoi.
ScreenshotHotkeyKeyCode.F8Ordinateur uniquement. KeyCode.None pour couper.
ShakeToReportfalseMobile : secouer le téléphone pour capturer l'écran et ouvrir le chat.
ScreenshotQuality / ScreenshotMaxSize80 / 1600Qualité JPEG, grand côté en pixels.
ImagePicker—Votre IImagePicker pour la galerie.
TokenStorePlayerPrefsTokenStoreOù vivent le jeton de l'appareil et les caches (ITokenStore).
OnScreenKeyboard—Saisie de texte pour Steam Deck / consoles.
UseWebSockettrueRepli sur l'interrogation périodique si indisponible (WebGL).
PollIntervalClosed / PollIntervalOpen15 / 4 sInterrogation quand il n'y a pas de socket.
AppStoreId, PlayStorePackage, SteamAppId—Pour les cartes d'avis.
SortingOrder30000Ordre du canvas, au-dessus de votre interface.
FullScreenautotrue / false pour imposer une mise en page.
LogLevelWarningJournal du SDK.