Discord Integration
The Popcorning Guinea Pig engine provides Discord Rich Presence integration through the IDiscordService, allowing games to display rich status information in Discord.
Overview
Discord Rich Presence allows your game to:
- Show current game status and activity
- Display custom artwork and images
- Show elapsed time or time remaining
- Enable "Join" and "Spectate" buttons for multiplayer games
- Display party information and player counts
IDiscordService Interface
public interface IDiscordService
{
void Initialize(string clientId);
void SetActivity(DiscordActivity activity);
void ClearActivity();
void UpdateActivity(Action<DiscordActivity> updateAction);
void SetPartySize(int currentSize, int maxSize);
void SetTimestamps(DateTime? start, DateTime? end);
void Dispose();
}
Setting Up Discord Integration
1. Create Discord Application
First, create a Discord application at the Discord Developer Portal:
- Click "New Application"
- Enter your game name
- Note the Client ID - you'll need this for integration
2. Upload Rich Presence Assets
Upload images for your Rich Presence:
- Go to "Rich Presence" → "Art Assets"
- Upload images (recommended: 1024x1024 PNG)
- Note the asset keys - use these in your code
3. Initialize Discord Service
public class MyGame : Game
{
private readonly IDiscordService _discord;
public MyGame()
{
_discord = GetService<IDiscordService>();
// Initialize with your Discord Client ID
_discord.Initialize("YOUR_CLIENT_ID_HERE");
// Set initial activity
SetMenuActivity();
}
private void SetMenuActivity()
{
var activity = new DiscordActivity
{
State = "In Main Menu",
Details = "Selecting game mode",
LargeImageKey = "game_logo",
LargeImageText = "Popcorning Guinea Pig",
SmallImageKey = "menu_icon",
SmallImageText = "Main Menu"
};
_discord.SetActivity(activity);
}
}
DiscordActivity Properties
The DiscordActivity class contains all the information displayed in Discord:
public class DiscordActivity
{
public string State { get; set; } // What the player is currently doing
public string Details { get; set; } // What the player is doing in detail
public string LargeImageKey { get; set; } // Key for large image asset
public string LargeImageText { get; set; } // Tooltip for large image
public string SmallImageKey { get; set; } // Key for small image asset
public string SmallImageText { get; set; } // Tooltip for small image
public DateTime? StartTimestamp { get; set; } // When activity started
public DateTime? EndTimestamp { get; set; } // When activity will end
public int PartySize { get; set; } // Current party size
public int PartyMax { get; set; } // Maximum party size
public string PartyId { get; set; } // Unique party identifier
public string JoinSecret { get; set; } // Secret for join button
public string SpectateSecret { get; set; } // Secret for spectate button
}
Common Usage Patterns
Menu State
private void ShowMenuActivity()
{
var activity = new DiscordActivity
{
State = "In Main Menu",
Details = "Navigating menus",
LargeImageKey = "game_logo",
LargeImageText = "Popcorning Guinea Pig",
StartTimestamp = DateTime.UtcNow
};
_discord.SetActivity(activity);
}
Gameplay State
private void ShowGameplayActivity(string levelName, int currentLevel)
{
var activity = new DiscordActivity
{
State = $"Level {currentLevel}",
Details = $"Playing {levelName}",
LargeImageKey = "gameplay",
LargeImageText = "In Game",
SmallImageKey = "level_icon",
SmallImageText = levelName,
StartTimestamp = DateTime.UtcNow
};
_discord.SetActivity(activity);
}
Multiplayer State
private void ShowMultiplayerActivity(int playersInGame, int maxPlayers)
{
var activity = new DiscordActivity
{
State = "Multiplayer Match",
Details = "Battle Royale",
LargeImageKey = "multiplayer",
LargeImageText = "Multiplayer Game",
PartySize = playersInGame,
PartyMax = maxPlayers,
StartTimestamp = DateTime.UtcNow
};
_discord.SetActivity(activity);
}
Paused State
private void ShowPausedActivity()
{
var activity = new DiscordActivity
{
State = "Game Paused",
Details = "Taking a break",
LargeImageKey = "paused",
LargeImageText = "Paused",
SmallImageKey = "pause_icon",
SmallImageText = "Paused"
// Note: No timestamp to avoid showing elapsed time while paused
};
_discord.SetActivity(activity);
}
Advanced Features
Timed Activities
Show countdown timers or elapsed time:
// Show elapsed time since game started
private void ShowElapsedTime(DateTime gameStartTime)
{
_discord.SetTimestamps(gameStartTime, null);
}
// Show countdown to event
private void ShowCountdown(DateTime eventTime)
{
_discord.SetTimestamps(null, eventTime);
}
// Update activity with timestamps
private void UpdateGameActivity()
{
_discord.UpdateActivity(activity =>
{
activity.State = "In Combat";
activity.Details = "Boss Fight";
activity.StartTimestamp = DateTime.UtcNow;
});
}
Party Management
Handle party/lobby information:
public class MultiplayerManager
{
private readonly IDiscordService _discord;
private readonly List<Player> _players = new();
public void UpdatePartyInfo()
{
_discord.SetPartySize(_players.Count, 4); // Current/Max players
_discord.UpdateActivity(activity =>
{
activity.State = $"In Lobby ({_players.Count}/4)";
activity.Details = "Waiting for players";
activity.PartyId = GetCurrentLobbyId();
});
}
}
Join/Spectate Buttons
Enable players to join games through Discord:
private void EnableJoinButton(string lobbySecret)
{
_discord.UpdateActivity(activity =>
{
activity.JoinSecret = lobbySecret;
activity.State = "In Multiplayer Game";
activity.Details = "Join to play together!";
});
}
// Handle join requests (implement in your networking code)
private void OnDiscordJoinRequest(string secret)
{
// Connect to game using the secret
JoinGameBySecret(secret);
}
Activity Management
Scene-Based Activity Updates
Automatically update Discord status based on current scene:
public abstract class Scene
{
protected virtual DiscordActivity GetDiscordActivity()
{
return new DiscordActivity
{
State = GetType().Name,
Details = "Playing game",
LargeImageKey = "game_logo"
};
}
}
public class MainMenuScene : Scene
{
protected override DiscordActivity GetDiscordActivity()
{
return new DiscordActivity
{
State = "In Main Menu",
Details = "Navigating menus",
LargeImageKey = "main_menu"
};
}
}
public class GameplayScene : Scene
{
private readonly string _levelName;
protected override DiscordActivity GetDiscordActivity()
{
return new DiscordActivity
{
State = $"Playing {_levelName}",
Details = "In game",
LargeImageKey = "gameplay",
StartTimestamp = DateTime.UtcNow
};
}
}
// In your scene manager
public void ChangeScene(Scene newScene)
{
_currentScene = newScene;
var discord = GetService<IDiscordService>();
discord.SetActivity(newScene.GetDiscordActivity());
}
Activity Helper Class
Create a helper class for managing common Discord activities:
public static class DiscordActivities
{
public static DiscordActivity MainMenu()
{
return new DiscordActivity
{
State = "In Main Menu",
Details = "Selecting options",
LargeImageKey = "main_menu",
LargeImageText = "Main Menu"
};
}
public static DiscordActivity Playing(string level, DateTime startTime)
{
return new DiscordActivity
{
State = $"Playing {level}",
Details = "In game",
LargeImageKey = "gameplay",
LargeImageText = level,
StartTimestamp = startTime
};
}
public static DiscordActivity Multiplayer(int current, int max)
{
return new DiscordActivity
{
State = "Multiplayer Game",
Details = $"Players: {current}/{max}",
LargeImageKey = "multiplayer",
PartySize = current,
PartyMax = max,
StartTimestamp = DateTime.UtcNow
};
}
public static DiscordActivity Settings()
{
return new DiscordActivity
{
State = "In Settings",
Details = "Configuring game",
LargeImageKey = "settings",
LargeImageText = "Settings Menu"
};
}
}
// Usage
_discord.SetActivity(DiscordActivities.Playing("Forest Level", gameStartTime));
Best Practices
Asset Management
- Image sizes: Use 1024x1024 PNG images for best quality
- Asset naming: Use descriptive, lowercase names with underscores
- Fallbacks: Always provide fallback text for images
Activity Updates
- Update frequency: Don't update too frequently (max once per second)
- Meaningful states: Provide useful information to other Discord users
- Timestamps: Use appropriate timestamps for activities with duration
Performance
- Initialize once: Call
Initialize()only once at game startup - Batch updates: Use
UpdateActivity()for multiple property changes - Cleanup: Call
Dispose()when shutting down the game
Example: Complete Integration
public class MyGame : Game
{
private readonly IDiscordService _discord;
private DateTime _gameStartTime;
public MyGame()
{
_discord = GetService<IDiscordService>();
_discord.Initialize("123456789012345678"); // Your Client ID
_gameStartTime = DateTime.UtcNow;
UpdateDiscordActivity();
// Update Discord activity when scenes change
SceneManager.OnSceneChanged += OnSceneChanged;
}
private void OnSceneChanged(Scene oldScene, Scene newScene)
{
UpdateDiscordActivity();
}
private void UpdateDiscordActivity()
{
var currentScene = SceneManager.CurrentScene;
switch (currentScene)
{
case MainMenuScene:
_discord.SetActivity(DiscordActivities.MainMenu());
break;
case GameplayScene gameplay:
var activity = DiscordActivities.Playing(gameplay.LevelName, _gameStartTime);
_discord.SetActivity(activity);
break;
case SettingsScene:
_discord.SetActivity(DiscordActivities.Settings());
break;
default:
_discord.ClearActivity();
break;
}
}
protected override void OnClosing()
{
_discord?.Dispose();
base.OnClosing();
}
}
Discord integration adds a social element to your game, helping players share their gaming experience with friends on Discord.