しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity for UWP で SNS 連携がしたい

Unity でゲームを開発する場合、twitterfacebookinstagram などの SNS サービスと連携させたいと思う場面が多くあります

この SNS 連携をかんたんに実装するための SocialConnector というライブラリがあります

ただし、対応プラットフォームは iOSAndroid のみ! Windows はありません!!

Unity の主要開発言語は Windows 向きの C# なのに残念!・・・というわけにもいかないので試してみました

tech.tanaka733.net

基本的には tanaka733 さん(とその先の MS 大西さん)の記事で紹介されている方法でいけそうです

f:id:matatabi_ux:20160124202857p:plain

Unity Editor で編集対象となるプロジェクト(~.CSharp)とは別に、2つクラスライブラリプロジェクトを追加します

1つは WinRT API を直接利用する DLL を出力するユニバーサル Windows クラスライブラリプロジェクト(~.UwpPlugin)

もう1つは上記 DLL と全く同じ名前でインタフェースだけ合わせた機能は空のただのクラスライブラリプロジェクト(~.UwpEditor) です

f:id:matatabi_ux:20160124204601p:plain

(~.UwpEditor)の方はアセンブリ名、名前空間をもう一つのクラスライブラリに合わせた上で、対象フレームワークを Unity 3.5 .net Subset Base Class Library の API を利用するように設定します

f:id:matatabi_ux:20160124204955p:plain

さらに(~.UwpPlugin)の方は DLL の出力先を (~.CSharp)の Assets/Plugin/UWP の配下に、(~.UwpEditor)は Assets/Plugin 配下に設定します

ここまでできたら(~.UwpPlugin)の方に ShareConnector という WinRT API を利用した SNS 連携サービスクラスを作ります

using System;
using System.Collections.Generic;
using System.Diagnostics;
#if NETFX_CORE
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Windows.Storage;
#endif

namespace NecoTank.UwpPlugin
{
    /// <summary>
    /// Unity UWP 用共有コントラクト連携サービス
    /// </summary>
    public class ShareConnector
    {
        /// <summary>
        /// 添付テキスト
        /// </summary>
        private string text;

        /// <summary>
        /// 添付 Uri
        /// </summary>
        private Uri uri;

        /// <summary>
        /// 添付ファイルパス
        /// </summary>
        private string filePath;

        /// <summary>
        /// 添付ファイル一時保存先
        /// </summary>
        public string ImageForderPath
        {
            get
            {

#if NETFX_CORE
                return ApplicationData.Current.TemporaryFolder.Path;
#else
                return string.Empty;
#endif
            }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public ShareConnector()
        {

#if NETFX_CORE
            DataTransferManager.GetForCurrentView().DataRequested
                += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.OnDataRequested);
#endif
        }

        /// <summary>
        /// 共有コントラクトを利用して外部アプリにデータを共有する
        /// </summary>
        /// <param name="text">添付テキスト</param>
        /// <param name="uri">添付 Uri</param>
        /// <param name="filePath">添付ファイルパス</param>
        public void Share(string text, string uri = null, string filePath = null)
        {
            this.text = text;
            this.uri = new Uri(uri, UriKind.RelativeOrAbsolute);
            this.filePath = filePath;

#if NETFX_CORE
            DataTransferManager.ShowShareUI();
        }

        /// <summary>
        /// 共有先アプリからのデータ要求イベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発行者</param>
        /// <param name="args">イベント引数</param>
        private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
        {
            // 要求を待ってもらう
            var deferral = args.Request.GetDeferral();

            var request = args.Request;
            request.Data.Properties.Title = "データの共有";

            if (!string.IsNullOrEmpty(this.text))
            {
                request.Data.SetText(this.text);
            }
            if (this.uri != null)
            {
                request.Data.SetWebLink(this.uri);
            }

            if (!string.IsNullOrEmpty(this.filePath))
            {
                string relativePath = null;
                try
                {
                    relativePath = this.filePath.Replace(ApplicationData.Current.TemporaryFolder.Path, "").TrimStart('/', '\\');

                    var file = await ApplicationData.Current.TemporaryFolder.GetFileAsync(relativePath);
                    request.Data.SetStorageItems(new List<IStorageItem> { file });
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }

            // 共有完了通知
            deferral.Complete();
#endif
        }
    }
}

中身はよくある UWP の共有コントラクト処理なので割愛しますが #if NETFX_CORE ディレクティブで WinRT 依存コードを囲むことで、(~.UwpEditor)にリンクとしてコードをコピーするようにしています

インタフェースがあっていれば別の方法でも大丈夫だと思いますが、これが一番かんたんにできそうですね

連携サービスクラスのコードが用意できたらいったん(~.UwpPlugin)と(~.UwpEditor)のプロジェクトをそれぞれリビルドして DLL を出力します

今度は Unity Editor 側、(~.CSharp)に (~.UwpEditor)から出力した方の DLL 参照を追加します(Unity Editor 側プロジェクトでは WinRT は利用できないので)

参照を追加したらさっそく呼び出しコードを次のような感じで実装

/// <summary>
/// Share ボタン押下イベントハンドラ
/// </summary>
public void OnShareButtonClicked()
{
    ShareConnector connector = null;
    string imagePath = null;
    UnityEngine.WSA.Application.InvokeOnUIThread(() =>
    {
        connector = new ShareConnector();

        imagePath = connector.ImageForderPath + "\\image.png";
    }, true);

    // スクリーンショット画像を撮影(UWP の UI スレッドでやると怒られる;)
    Application.CaptureScreenshot(imagePath);

    UnityEngine.WSA.Application.InvokeOnUIThread(() =>
    {
        connector.Share("ねこ戦車!", @"http://matatabi-ux.hateblo.jp/", imagePath);
    }, true);
}

共有コントラクト周りの API は UI スレッドで利用が必要で、Unity 側の画面キャプチャ API は逆に UI スレッドで利用すると怒られるので、面倒ですが上記のようにスレッドを行き来するコードを書きました

あとは Unity Editor 上の編集

f:id:matatabi_ux:20160124210742p:plain

Button を追加して、Click ハンドラとして上記の OnShareButtonClicked() を指定

また、Plugin に出力しておいた DLL を選択して Inspector ビューからプロパティを変更します

f:id:matatabi_ux:20160124211233p:plain

Plugin 配下の(~.UwpEditor)の DLL は Any Platform のチェックを外はずし、Editor だけにチェックをいれて OS を Windows に指定

f:id:matatabi_ux:20160124213907p:plain\

Plugin/UWP 配下の(~.UwpEditor)の DLL は WSAPlayer にチェックし、SDK を UWP に指定、これで選択中のプラットフォームごとに Unity Editor が参照 DLL を切り替えてくれるようです

f:id:matatabi_ux:20160124211803p:plain

さてここまで来たら [File]-[Build Settings] メニューから Windows 10 のアプリビルドプロジェクトを生成

Build And Run は UWP に対応してないっぽい・・・のでおとなしく Build だけして Visual Studio 起動・・・プロジェクトを開いたらそのまま F5 起動

f:id:matatabi_ux:20160124212053p:plain

UWP アプリとして Unity アプリが起動したので、おもむろに Share ボタンをクリックすると

f:id:matatabi_ux:20160124213357p:plain

共有コントラクトのチャームが表示されたので、twitter を共有先に選択するとホステッドビューが起動しちゃんとデータが埋め込まれました

めでたしめでたし!