Unity のスクリーンショットを Discord への画像アップロード投稿するメモ

Unity のスクリーンショットを Discord への画像アップロード投稿するメモ

Unity のスクリーンショットを Discord への画像アップロード投稿するメモです。

背景

Unity から LINE Notify にスクリーンショットの画像メッセージを送る ことは以前からできてたのですが、そういえば Discord で Unity のスクリーンショットを画像つきメッセージ送るのをやったことなかったなというところで、やってみます。

具体的には、

Webhook Resource | Documentation | Discord Developer Portal

こちらの Webhook 実行を使いつつ、

API Reference | Documentation | Discord Developer Portal

さらに埋め込む画像を一旦こちらの記事にあるファイルアップロードで Discord に配置して URL を得てからメッセージに反映するという 2 段構えの流れになります。

実際のプログラム

このような立方体が配置されているところに、EventSystem やカメラに Physics Raycaster を仕込んでおいて、ひとつの立方体に以下のプログラムを CubeEvent.cs として割り当てます。

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
using UnityEngine.Networking;
using System;
using System.Collections.Generic;

public class CubeEvent : MonoBehaviour, IPointerClickHandler
{
    // Discord 設定
    private string webhookUrl = "https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN";
    
    // Message 設定
    private string messageContent = "Unity スクリーンショット";
    private string embedTitle = "Unity Screenshot";
    private string embedDescription = "撮影されたスクリーンショット";
    private Color embedColor = Color.blue;

    // キャプチャされた画像データ
    private byte[] dataImage;

    // Discord JSON データ構造
    [Serializable]
    public class DiscordWebhookData
    {
        public string content;
        public DiscordEmbed[] embeds;
        public DiscordAttachment[] attachments;
    }

    [Serializable]
    public class DiscordEmbed
    {
        public string title;
        public string description;
        public int color;
        public DiscordEmbedImage image;
    }

    [Serializable]
    public class DiscordEmbedImage
    {
        public string url;
    }

    [Serializable]
    public class DiscordAttachment
    {
        public int id;
        public string description;
        public string filename;
    }

    [Serializable]
    public class DiscordResponse
    {
        public string id;
        public string content;
        public DiscordResponseAttachment[] attachments;
        public DiscordResponseEmbed[] embeds;
    }

    [Serializable]
    public class DiscordResponseAttachment
    {
        public string url;
        public string filename;
        public int size;
        public int width;
        public int height;
    }

    [Serializable]
    public class DiscordResponseEmbed
    {
        public string title;
        public string description;
        public int color;
        public DiscordResponseEmbedImage image;
    }

    [Serializable]
    public class DiscordResponseEmbedImage
    {
        public string url;
        public string proxy_url;
        public int width;
        public int height;
        public string content_type;
        public string description;
    }
    
    public void OnPointerClick(PointerEventData eventData)
    {
        StartCoroutine(TakeScreenshotAndUpload());
    }

    // スクリーンショット撮影 + Discord アップロード
    private IEnumerator TakeScreenshotAndUpload()
    {
        Debug.Log("スクリーンショット撮影を開始");
        
        // レンダリング後に処理を開始
        yield return new WaitForEndOfFrame();
        
        // Texture2D でスクリーンショットを取得
        Texture2D screenshot = ScreenCapture.CaptureScreenshotAsTexture();
        
        // JPEG に変換
        dataImage = screenshot.EncodeToJPG();
        Debug.Log($"撮影完了: {dataImage.Length / 1024:F1}KB");

        // メモリ解放
        UnityEngine.Object.Destroy(screenshot);

        // Discord にアップロード
        yield return StartCoroutine(UploadToDiscord());
    }

    // Discord Webhook用のJSONペイロードを作成
    private string CreateDiscordPayload(string filename)
    {
        string currentDateTime = DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss");

        DiscordEmbedImage embedImage = new DiscordEmbedImage();
        embedImage.url = $"attachment://{filename}";

        DiscordEmbed embed = new DiscordEmbed();
        embed.title = $"{embedTitle} - {currentDateTime}";
        embed.description = $"{embedDescription} (撮影日時: {currentDateTime})";
        embed.color = ColorToInt(embedColor);
        embed.image = embedImage;

        DiscordAttachment attachment = new DiscordAttachment();
        attachment.id = 0;
        attachment.description = "Unity で撮影されたスクリーンショット";
        attachment.filename = filename;

        DiscordWebhookData webhookData = new DiscordWebhookData();
        webhookData.content = messageContent;
        
        List<DiscordEmbed> embedsList = new List<DiscordEmbed>();
        embedsList.Add(embed);
        webhookData.embeds = embedsList.ToArray();
        
        List<DiscordAttachment> attachmentsList = new List<DiscordAttachment>();
        attachmentsList.Add(attachment);
        webhookData.attachments = attachmentsList.ToArray();

        return JsonUtility.ToJson(webhookData);
    }

    // 画像データを Discord に送信(IMultipartFormSection使用)
    private IEnumerator UploadToDiscord()
    {
        Debug.Log("Discord への画像アップロードを開始");

        // ファイル名を生成
        string filename = $"unity_screenshot_{DateTime.Now:yyyyMMdd_HHmmss}.jpg";
        
        // Discord ペイロードを作成
        string jsonPayload = CreateDiscordPayload(filename);
        Debug.Log($"JSON Payload: {jsonPayload}");

        // マルチパートフォームデータを構築
        List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
        
        // JSON ペイロード部分
        MultipartFormDataSection jsonSection = new MultipartFormDataSection("payload_json", jsonPayload, "application/json");
        formData.Add(jsonSection);
        
        // 画像ファイル部分
        MultipartFormFileSection fileSection = new MultipartFormFileSection("files[0]", dataImage, filename, "image/jpeg");
        formData.Add(fileSection);

        // UnityWebRequest を作成して送信
        UnityWebRequest request = UnityWebRequest.Post(webhookUrl, formData);
        Debug.Log($"Discord に送信中... (サイズ: {dataImage.Length / 1024:F1}KB)");
        
        yield return request.SendWebRequest();
        
        // Discord からのレスポンスを処理
        switch (request.result)
        {
            case UnityWebRequest.Result.Success:
                Debug.Log("Discord アップロード成功!");
                Debug.Log($"Response: {request.downloadHandler.text}");
                
                // レスポンスから画像情報を取得して表示
                DiscordResponse response = JsonUtility.FromJson<DiscordResponse>(request.downloadHandler.text);
                Debug.Log($"メッセージID: {response.id}");
                Debug.Log($"投稿内容: {response.content}");
                
                var image = response.embeds[0].image;
                Debug.Log("画像アップロード成功!");
                Debug.Log($"画像URL: {image.url}");
                Debug.Log($"サイズ: {image.width}x{image.height}");
                Debug.Log($"形式: {image.content_type}");
                
                // 画像URLを明確に出力
                Debug.Log("=== 画像URL (コピー用) ===");
                Debug.Log(image.url);
                Debug.Log("========================");
                break;

            case UnityWebRequest.Result.ConnectionError:
            case UnityWebRequest.Result.DataProcessingError:
            case UnityWebRequest.Result.ProtocolError:
                Debug.LogError($"Discord アップロードエラー: {request.error}");
                Debug.LogError($"ステータスコード: {request.responseCode}");
                Debug.LogError($"レスポンス: {request.downloadHandler.text}");
                break;

            default:
                Debug.LogWarning($"不明なレスポンス状態: {request.result}");
                break;
        }
        
        request.Dispose();
    }

    // Color を Discord の整数カラーに変換
    private int ColorToInt(Color color)
    {
        int r = Mathf.RoundToInt(Mathf.Clamp01(color.r) * 255);
        int g = Mathf.RoundToInt(Mathf.Clamp01(color.g) * 255);
        int b = Mathf.RoundToInt(Mathf.Clamp01(color.b) * 255);
        return (r << 16) | (g << 8) | b;
    }
}

その上で、

    // Discord 設定
    private string webhookUrl = "https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN";

こちらに投稿したい Discord Webhook URL に書き換えます。

出来上がったら、クリックして動かしてみましょう。

設定が正しければ、このように、Unity のスクリーンショットを Discord への画像アップロード投稿ができます!