Steam et Unity 3D : intégrer des fonctions basiques de Steamworks

Si vous publiez votre jeu Unity 3D sur ordinateur, une des plate-formes de distribution de choix est Steam. Avec 120 million de joueurs actifs chaque mois en 2020, et les formalités de publication relativement simple pour les développeurs indépendants.

En publiant votre jeu sur Steam, vous pouvez bénéficier de diverses fonctionnalités, recommandées ou même exigées par Steam. Pour un utilisateur de la plate-forme, il attend en tous cas que certaines fonctions soient en place, et la présence de certaines fonctionnalités comme les succès Steam peuvent inciter des clients à acheter votre jeu !

En plus, ça permet de sécuriser votre jeu contre le piratage, en vérifiant que le jeu ou les DLC sont bien achetés sur un compte Steam, en implantant un système anti-triche…

Bref, il y a beaucoup d’avantages à ajouter des fonctions Steam dans votre jeu, et ce n’est pas compliqué à implémenter une fois qu’on sait comment faire.

Dans tous les cas, vous aurez besoin de d’abord créer l’application sur Steam, car les fonctionnalités Steamworks doivent à moitié être gérées dans la page de l’application.

Steamworks.NET, un wrapper pour le C# et Unity

Steamworks.NET est un projet gratuit et publié sous la licence MIT. C’est un wrapper .NET qui permet de faciliter l’utilisation de Steamworks pour les applis Unity particulièrement, mais également pour les applications C# .NET.

Le rôle d’un wrapper, c’est de simplifier le fonctionnement, par exemple en automatisant les tâches de base (comme vérifier la bonne connexion, récupérer certaines données, etc.). Steamworks.NET dispose de tout ce qu’il faut, avec une documentation complète et un SteamManager prêt à l’emploi pour les fonctions de base.

Installer Steamworks.NET dans votre projet

Le wrapper est disponible dans un format .unitypackage, disponible depuis le GitHub officiel (il existe une version « Standalone » pour les projets non Unity). Téléchargez-le et importez-le dans votre projet Unity.

Lors de l’installation, le package créée un fichier steam_appid.txt dans lequel il vous faut indiquer le numéro d’appid de votre application Steam. Puis fermer et rouvrir le projet.

À partir de ce moment, votre jeu est associé à l’application.

Sécuriser le fonctionnement de SteamWorks

Après avoir intégré Steamworks dans votre projet, vous allez ajouter les éléments dans vos différents systèmes de jeu (idéalement, dans une classe dédiées, par exemple un SteamManager).

Quand vous ferez ceci, vous pouvez sécuriser votre programme avec deux méthodes complémentaire :

  • des instructions préprocesseur, qui ne prennent en compte le bloc de code que si la constante DISABLESTEAMWORKS
  • la vérification que le SteamManager est bien initialisé.
#if !DISABLESTEAMWORKS
    if (SteamManager.Initialized)
    {
        // Fonctionnement avec Steamworks
    }
#else
    // Fonctionnement sans SteamWorks
#endif

Le « else » vous permet de prévoir différent cas où Steamworks n’est pas activé. Donc vous pouvez y coder votre propre affichage des succès lors des builds de test de votre jeu, ou y prévoir le comportement sur d’autres plate-formes que Steam.

Pour éviter que l’Éditeur plante et demande absolument que Steam ne soit ouvert pour fonctionner, vous pouvez ajouter ces lignes :

#if UNITY_EDITOR
#define DISABLESTEAMWORKS
#endif

Pour faire des tests « en condition réelles » des fonctions Steam sans télécharger le build sur Steam, vous pouvez le faire depuis l’éditeur.

Il vous suffit d’avoir le logiciel Steam ouvert, associé à un compte où l’application est accessible (même en version beta). Quand vous lancerez le mode « Play » dans l’éditeur Unity, le jeu démarrera sur Steam.

Cependant, attention : à partir de là, Steam prend en compte l’éditeur Unity comme application. Donc il continuera à indiquer « jeu en cours » si vous fermez l’application via le bouton de Steam, ça sera l’éditeur qui s’arrêtera.

Et dans tous les cas, n’oubliez pas que les fonctions Steam doivent être gérées avec une logique de singleton, une classe qui n’a qu’une instance unique. Vous pouvez le gérer dans le GameManager ou un SteamManager, par exemple.

Exemples d’intégrations Steamworks dans un jeu sous Unity 3D

Pour les différentes fonctions suivantes, il vous faudra inclure le namespace Steamworks en tête de vos scripts.

using Steamworks;

Récupérer l’ID Steam du joueur

En exemple de fonctionnement de Steamworks.NET, le guide propose le code suivant :

public class SteamScript : MonoBehaviour {
	void Start() {
		if(SteamManager.Initialized) {
			string name = SteamFriends.GetPersonaName();
			Debug.Log(name);
		}
	}
}

La méthode SteamFriends.GetPersonaName(); permet de récupérer dans une chaîne de caractère le nom d’utilisateur Steam lié au compte qui exécute l’application. Ainsi, il peut être présenté sur les noms des sauvegardes, sur le nom du joueur dans l’application (dans les jeux en ligne, par exemple), etc.

Vérifier au démarrage du jeu que Steam est bien exécuté

Dans le SteamManager fourni dans le package Steamworks.NET, il y a plusieurs fonctions prêtes à l’emploi, dont celle qui vérifie que le jeu est bien exécuté depuis Steam.

Cherchez « RestartArtIfNecessary » dans le SteamManager et remplacez l’argument de votre fonction par votre App Id, précédé par le cast (AppId_t).

if (SteamAPI.RestartAppIfNecessary((AppId_t)000000)) {
    Application.Quit();
    return;
}

Cela ferme l’application si l’exécution de Steam n’est pas détectée. Cette vérification ne se fait pas si vous exécutez l’application depuis l’éditeur.

Intégrer les succès et statistiques Steam

Pour les succès et les statistiques, rendez-vous dans l’espace « Admin de l’application » sur Steamworks, dans la rubrique « Statistiques et succès ».

Vous allez pouvoir créer un Succès. Ce qui est important, c’est le nom d’API de votre succès, tout en majuscules et aux mots séparés par des underscore. Un succès est aussi défini par son nom, sa description, et ses visuels. Le nom d’API est unique quelque soit la langue.

Ajouter un succès dans Steamworks

Ensuite, il y a principalement deux fonctions à utiliser :

  • GetAchievement(string apiName, out bool gotAchievement); permet de vérifier si l’utilisateur a débloqué le succès qui porte le nom apiName. La méthode sort un booléen gotAchievement qui indique si le succès est débloqué.
  • SetAchievement(string apiName); permet d’activer le succès portant le nom apiName chez l’utilisateur.

Donc, typiquement, nous aurons une méthode qui est capable de vérifier si le succès est débloqué, et le cas échéant, de l’ajouter.

On termine par la méthode SteamUserStats.StoreStats(), qui sauvegarde en ligne l’état des succès débloqué.

Ici, c’est codé directement dans la méthode, mais on pourrait imaginer un système qui met à jour plusieurs succès si nécessaire avant d’appeler la méthode StoreStats().

internal void UnlockAchievement(string achievementApiName)
{
#if !DISABLESTEAMWORKS
	if (SteamManager.Initialized)
	{		SteamUserStats.GetAchievement(achievementApiName, out 
bool gotAchievement);
		if (!gotAchievement && achievementApiName != string.Empty)
		{
			SteamUserStats.SetAchievement(achievementApiName);
			SteamUserStats.StoreStats();
		}
	}
#endif	
}

De la même manière, vous pouvez créer une statistique. C’est un chiffre qui peut être une valeur entière ou flottante, directement lié à une statistique de jeu. Là aussi, elle est définie par un nom pour l’API.

Ajout d’une statistique dans Steamworks

Là, nous avons une fonction pour lire la valeur distante de la statistique

internal void SetStatValue(string statApiName, int statValue)
{
#if !DISABLESTEAMWORKS
	if (SteamManager.Initialized)
	{
		SteamUserStats.GetStat(statApiName, out int distantValue);
		if(distantValue < statValue)
		{
			SteamUserStats.SetStat(statApiName, statValue);
			SteamUserStats.StoreStats();
		}
	}
#endif	
}

Statistiques et succès peuvent aller de pair : en définissant un succès sur l’interface Steamworks, vous pouvez le lier à une statistique. Si c’est le cas, un compteur avec la valeur de la statistique s’affichera dans le succès. C’est cependant bien à vous de débloquer manuellement le succès une fois que la statistique arrive au chiffre cible.

N.B. : vous pouvez tout à fait tester le fonctionnement des succès et statistiques sur une version beta de votre jeu via Steam, puis tout remettre à zéro, via cette fonction :

SteamUserStats.ResetAllStats(true);

Attention, c’est une fonction à ne pas utiliser sur une version en production : ce n’est pas du tout dans les usages et ça risque d’être très mal vu par votre joueur, voire de rendre le jeu non publiable si il est testé par un employé Steam avant sa mise en ligne.

Gérer le comportement du jeu lorsque l’overlay Steam est ouvert

On s’attend à ce que lorsque l’overlay Steam est ouvert et recouvre le jeu (avec Shift + Tab ou le bouton central d’un gamepad), le jeu se mette en pause, à minima (la plupart des jeux affichent le menu principal dans un tel cas de figure).

Pour ça, il vous faut dans un script qui créée un Callback et l’associe à l’appel de la méthode OnGameOverlayActivated().

Le code ci-dessous, que vous pourrez ajouter dans votre GameManager ou dans un SteamManager de votre cru, créer le callback à l’activation du script. Typiquement, quand l’overlay s’ouvre, la fonction est appelée et vous pouvez forcer dans le jeu le comportement de votre choix.

protected Callback<GameOverlayActivated_t> m_GameOverlayActivated;

private void OnEnable()
{
	if (SteamManager.Initialized)
	{
		m_GameOverlayActivated = Callback<GameOverlayActivated_t>.Create(OnGameOverlayActivated);
	}
}

private void OnGameOverlayActivated(GameOverlayActivated_t pCallback)
{
	if (pCallback.m_bActive != 0)
	{
		// Code pour mettre le jeu en pause.
		GameManager.Instance.Pause();
	}
}

Aller plus loin avec Steamworks

C’est un aperçu des possibilités de Steamworks. Mais cette API de Steam permet de faire plein de choses et d’aller encore plus loin.

Quelques exemples de fonctions possibles :

  1. Sauvegarde le fichier de sauvegarde dans le cloud
  2. Récupérer l’avatar du joueur (et des contacts Steam)
  3. Récupérer la langue configurée dans Steam et passer le jeu automatiquement dans cette langue
  4. Système de saisie de texte propre à Steam (qui s’adaptera à ce qu’utilise l’utilisateur, par exemple le gamepad)
  5. Gérer les contrôles pour les différents supports, permettre aux gens d’utiliser la personnalisation des contrôles de Steam
  6. Présence enrichie sur Steam (dans le chat et d’autre endroit, sous le nom d’utilisateur, le nom du jeu accompagné du niveau ou de la mission en cours, par exemple)
  7. Gestion du multijoueur, matchmaking et système anti-triche
  8. Gérer le lecteur de musique de Steam, l’enregistrement d’audio, etc.

Ça peut être des sujets vastes et spécifiques, qui pourront faire l’objet d’autres articles sur GameCodeClub.

Dans tous les cas, quand vous développez une intégration Steam, prévoyez un fonctionnement par défaut pour toutes les fonctions (même s’il s’agit juste de désactiver une fonction). Cela rendra votre jeu plus simple à adapter sur d’autres plateformes.

One thought on “Steam et Unity 3D : intégrer des fonctions basiques de Steamworks

Commentaires désactivés