20190711のC#に関する記事は8件です。

Dapperを使う時、毎回ConnectionのOpenとかをしなくて済ます為に作ったメソッド

THE 備忘録

dapperを使う時、毎回ConnectionのOpenとかTransactionのBeginをしなくて済ます為に作ったメソッド。
MessageBoxでいちいち表示するのは、ヘタクソなクエリでどんな例外が出るのか見当すらつかない為。

//SELECT
public IEnumerable<dynamic> SELECT<dynamic>(string sql)
{
    IEnumerable<dynamic> modelEnum = null;

    using (var con = new NpgsqlConnection(ConnectionString))
    {
        con.Open();
        using (var trans = con.BeginTransaction())
        {
            try
            {
                modelEnum = con.Query<dynamic>(sql);
                trans.Commit();
                return modelEnum;
            }
            catch (NpgsqlException ex)
            {
                MessageBox.Show(ex.Message,"");
                trans.Rollback();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "");
                trans.Rollback();
            }
        }
    }
    return modelEnum;
}
//INSERTとかUPDATE(複数テーブル更新する時等、いわゆるaffectedrowsの数がカウント出来ない)
public void Execute(List<string> sql)
{
    using(var con = new NpgsqlConnection(ConnectionString))
    {
        con.Open();
        using(var trans = con.BeginTransaction())
        {
            try
            {
                sql.ForEach(s => con.Execute(s));
                trans.Commit();
            }
            catch(NpgsqlException ex)
            {
                MessageBox.Show(ex.Message, "");
                trans.Rollback();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "");
                trans.Rollback();
            }
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シリアル(RS232C), Ethernet で MELSEC Q PLC とパソコン通信

MELSEC PLCに関するライブラリにはPLC製造元が販売している「MX Component」がもっとも利用されています。フリーのライブラリは2019年7月11日の時点ではない(と思う)ので作成しました。,NET DLLです。
・形式2-5(RS232C), 3E,4E(Ethernet)対応
・スレッド通信
・一括読書(ワード単位、ダブルワード単位、ビット単位)、ワード単位ランダム読書き
・XMLインテリセンスもついている。
・デバクはシングルCPU単一ネットワーク内環境のみだが、PLC構成パラメータとしては某社製品に順ずるのを用意している。

http://urx.space/V6Jg
https://www.vector.co.jp/soft/winnt/hardware/se519997.html

【機器の設定など】https://www.youtube.com/watch?v=AM8FuK_eI-0&feature=youtu.be

インスタンスの生成
     Dim ins As New QEther.QEther
設定(共通)
  ins.TIMEOUT = 100 '10msec単位 (Etherなら10(100msec), RS232Cなら100(1sec)程度が目安)
'タイムアウト時間を長くしても通信成功時はただちに終了するので長めにとってもかまいません
設定(Ethernetの場合)
   ins.ActHostAddress = "192.168.1.10"
        ins.ActPortNumber = 2000
        ins.BinaryASCII = EnumBinaryASCII.Binary 'PLCに設定したもの
        ins.MCProtocol = EnumMCProtocol.フレーム3E '3Eか4E(通常はどちらでもよい)
設定(シリアル(RS232C)の場合)
       ins.MCProtocol = EnumMCProtocol.形式2  'PLCにあわせてください
       ' ins.CFrame = EnumCFrame.フレーム4C 'フレームは通常は特に設定する必要はありません
       ins.ActBaudRate = 19200 'PLC, およびRS232Cポートにあわせてください
   '     ins.ActDatabits = 7  '形式2-4は7bit, 形式5は8bit固定ですので設定する必要はありません。
      '  ins.ActDtrEnable = True  'RS232Cポートにあわせてください
        ins.ActParity = EnumParity.Even偶数 'PLCおよびRS232Cポートにあわせてください
        ins.ActCOMPort = "COM8"   'RS232Cポートにあわせてください
        ins.ActStopbits = EnumStopbits.One'RS232Cポートにあわせてください
ワード単位ランダム一括読書き(引数にデバイス、値を並べる場合)
ins.WriteDeviceRandom2(3, "D200", &HCCCC, "D202", &HDDDD, "D204", &H2222, "D207", &H6666)
ワード単位ランダム一括読書き(オブジェクト配列で渡す場合)
        Dim word(10) As UShort      
  Dim objword(10, 1) As Object
  For i = 0 To word.Length - 1
            word(i) = i + 10
            objword(i, 0) = "D" & i
            objwordr(i, 0) = "D" & i
            objword(i, 1) = word(i)
        Next
ins.WriteDeviceRandom2(3, objword)

などとこんな感じで利用します。

あとは以下

サンプル
Imports QEther.QEther
Public Class QEtherDLLSample
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim ins As New QEther.QEther

        ins.ActHostAddress = "192.168.1.10"
        ins.ActPortNumber = 2000

        ins.BinaryASCII = EnumBinaryASCII.Binary
        ins.MCProtocol = EnumMCProtocol.形式2  'Ethernet(3E,4E)のバイナリ, 形式5(4Cフレームバイナリ)に対応しています。
        ins.CFrame = EnumCFrame.フレーム4C

        '############################   RS232 通信のさいに必要な設定です  ####################################
       ins.TIMEOUT = 700
        ins.ActBaudRate = 19200
        ins.ActDatabits = 7
        ins.ActDtrEnable = True
        ins.ActParity = EnumParity.Even偶数
        ins.ActCOMPort = "COM8"
        ins.ActStopbits = EnumStopbits.One
        '####################################################################################



        Dim word(10) As UShort
        Dim word2(word.Length) As UShort
        Dim dword(10) As UInteger
        Dim dword2(word.Length) As UInteger

        Dim objword(10, 1) As Object
        Dim objwordr(10, 0) As Object
        Dim s As String

        For i = 0 To word.Length - 1
            word(i) = i + 10
            objword(i, 0) = "D" & i
            objwordr(i, 0) = "D" & i
            objword(i, 1) = word(i)
        Next





        MsgBox("ランダムワードD0-10点書込み")

        ' ins.WriteDeviceRandom2(3, objword)

        '  ins.WriteDeviceRandom2DWord(3, "D200", &HAAAABBBB, "D202", &HAAAABBBB, "D204", &HAAAABBBB, "D207", &HAAAABBBB)
        ' ins.WriteDeviceRandom2(3, "D200", &HCCCC, "D202", &HDDDD, "D204", &H2222, "D207", &H6666)


        '  ins.WriteDeviceRandom2ArrayBIT(3, "M100", 1, "M107", 0, "M105", 1)

        ins.WriteRandomBlock2(2, "D0", 2, 123, 234, "M0", 3, &HAAAA, &HBBBB, &HFFFF)






        Dim randomread As New RetrunRandomWORD

        MsgBox("ランダムワード読込")

        randomread = ins.ReadDeviceRandom2("D0", "D1", "D2")
        For i = 0 To randomread.UShortArray.Length - 1
            s = s & randomread.UShortArray(i)
        Next
        MsgBox(s)
        s = ""


        For i = 0 To word.Length - 1
            word(i) = i * 10
        Next
        MsgBox("一括ワードD0-10点書込み")

        ins.WriteDeviceBlock2("D0", word, word.Length)


        MsgBox("一括ワード読込")

        word2 = ins.ReadDeviceBlock2("D0", word.Length).UShortArray
        For i = 0 To word2.Length - 1
            s = s & randomread.UShortArray(i)
        Next
        MsgBox(s)
        s = ""


        For i = 0 To dword.Length - 1
            dword(i) = i * &HFFFF + &HFFFF
        Next
        MsgBox("一括DワードD0-10点書込み")

        ins.WriteDeviceBlock2DWord("D0", dword, dword.Length)


        MsgBox("一括Dワード読込")

        dword2 = ins.ReadDeviceBlock2DWord("D0", dword.Length).UIntegerArray
        For i = 0 To dword2.Length - 1
            s = s & dword2(i).ToString("X8")
        Next
        MsgBox(s)
        s = ""



        Dim bit(50) As Byte
        Dim bit2() As Byte

        For i = 0 To bit.Length - 1
            bit(i) = i Mod 2
        Next
        bit(0) = &H1


        '##############   スレッドを使っているので書込みのときは通信終了を待たずに次の命令を実行できて便利な反面があります。

        MsgBox("一括ビットM0書込み")
        MsgBox("1点")
        ins.WriteDeviceBlock2ArrayBit("M0", bit, 1)
        MsgBox("2点")
        ins.WriteDeviceBlock2ArrayBit("M0", bit, 2)
        MsgBox("3点")
        ins.WriteDeviceBlock2ArrayBit("M0", bit, 3)
        MsgBox("16点")
        ins.WriteDeviceBlock2ArrayBit("M0", bit, 16)
        MsgBox("33点")
        ins.WriteDeviceBlock2ArrayBit("M0", bit, 33)


        '###### しかし読込のときは注意が必要です。通信終了を待たずに次にいくので、通信終了まで待ち、
        '### かつ、DataOKなら次を実施するということが必要です。



        MsgBox("一括ビットM0読込")
        Dim insbit As New ReturnBIT

        insbit = ins.ReadDeviceBlock2ArrayBit("M0", 1)

        While Not insbit.Finished : End While ' 通信が終わるまで(異常終了した場合も抜ける)待つ

        If insbit.DataOK Then               '通信終了後、データOKフラグがたってたらデータ格納
            bit2 = insbit.ByteArray
            For i = 0 To bit2.Length - 1
                s = s & bit2(i)
            Next
            MsgBox(s)
            s = ""
        End If





        insbit = ins.ReadDeviceBlock2ArrayBit("M0", 16)

        While Not insbit.Finished : End While ' 通信が終わるまで(異常終了した場合も抜ける)待つ

        If insbit.DataOK Then


            bit2 = insbit.ByteArray
            For i = 0 To bit2.Length - 1
                s = s & bit2(i)
            Next
            MsgBox(s)
            s = ""
        End If





    End Sub

    Private Sub Button3_Click(sender As Object, e As EventArgs)
        Dim ins As New QEther.QEther
        ins.ActHostAddress = "192.168.1.10"
        ins.ActPortNumber = 2000

        ins.BinaryASCII = EnumBinaryASCII.ASCII
        ins.MCProtocol = EnumMCProtocol.形式2  'Ethernet(3E,4E)のバイナリ, 形式5(4Cフレームバイナリ)に対応しています。
        ins.CFrame = EnumCFrame.フレーム4C

        '############################   RS232 通信のさいに必要な設定です  ####################################
        ins.TIMEOUT = 700
        ins.ActBaudRate = 19200
        ins.ActDatabits = 7
        ins.ActDtrEnable = True
        ins.ActParity = EnumParity.Even偶数
        ins.ActCOMPort = "COM7"
        ins.ActStopbits = EnumStopbits.One
        '####################################################################################

        Dim bit As Byte()
        Dim word As UShort()


        word = ins.ReadDeviceBlock2BIT("M0", 1).UShortArray

        MsgBox(word(0).ToString("X4"))




    End Sub

End Class
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SeleniumがCannot start the driver service on http://localhost:${port} を吐いて死ぬ

セレニウムを用いた再起テストを設計しなおすに当たって、C#で書くことを検討した
が、なぜか上記のエラーを吐いて永遠に動かなかった

結論

プロキシよまたお前か
環境変数にexport no_proxy=localhostを追加しましょう。
今回はC#だったので

 Environment.SetEnvironmentVariable("no_proxy","localhost");

こちらをSeleniumのドライバを呼ぶ前に刺しておくことでことなきを得ました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AngularでCSVをダウンロード

Angularの書き方が分からず悩んでいます。
どなたかお解りの方がいらっしゃいましたら力を貸してくださると幸いです。
よろしくお願いします。

実現したい事

C#のWebAPIから、CSVファイルをダウンロードしたいです。

分からないこと

画面側の Angular の処理の書き方がわかりません。
(APIはFileContentResultを返しています)

【呼び出し元】

getCSV(): Observable<object> {
this.service.getCSV()
      .subscribe((apiResponse: any) => {
      });

【サービスクラス】

getCSV(): Observable<object> {
    const apiURL = "api/getCSV";
    return this.http.get(apiURL);
  }

開発環境

下記の環境で開発しております。

フロント:Angular7
サーバー:C#(.NET Core 2.2)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AzurePlayFabのランキング機能を使ってみた&ついでにランキング報酬も配ってみた

まえがき

オンラインゲームにかかせないランキング機能、自分で作るのは少し面倒ですよね。
PlayFab にはユーザーのランキングを管理したり、ランキングに応じて報酬を配ったりできる機能があります。

今回は以前作ったユニティちゃんの縄跳びゲームに PlayFab のランキング機能を組み込んでみましたが、簡単便利で素敵だったので記事に残しておこうと思います。

成果物

※ゲームプレイ後に超簡易的なランキング画面が表示されます。
JumpRope.gif

使用する PlayFab の API

1. 事前準備

1.1. クライアントから PlayFab への統計情報のポストを許可しておく

PlayFab は統計情報(スコアなど)をクライアントから送信することをデフォルトでは禁止しています。

チート対策のために基本的にはサーバー側で処理してね、とのことですが今回は個人制作の趣味のゲームですし、クライアントからサクッと値を送信したいと思います。

そのために GameManager の設定画面で クライアントにプレイヤー統計情報のポストを許可する を有効にします。
image.png

これを忘れるとクライアントから統計情報を送信したときに下記のエラーがでるので注意してください。
image.png

1.2. ランキングの定義を作成しておく

GameManager で新しいランキングを作成します。
image.png

続いてどのようなランキングを作成するか決めます。
image.png

  • 統計情報名
    わかりやすい名前ならなんでも大丈夫です。
    一度決めると変更できないので慎重に。

  • リセット頻度
    今回は手動にしますが、毎時/毎日/毎週/毎月なども選べます。
    ランキングリセットの際は自動で報酬を配布したりすることもできます。(後述)

  • 集計方法
    今回はプレイヤーごとの最多ジャンプ成功数をランキング表示したいので 最大 にしておきます。

1.3. ユーザーの DisplayName を登録しておく

ランキングにユーザー名を表示するためにはユーザーの DisplayName を登録しておく必要があります。
ゲーム内でユーザー名を設定する画面を作り、以下のようなコードで PlayFab へ登録しておきましょう。

    private void SetUserName(string userName)
    {
        var request = new UpdateUserTitleDisplayNameRequest
        {
            DisplayName = userName
        };

        PlayFabClientAPI.UpdateUserTitleDisplayName(request, OnSuccess, OnError);

        void OnSuccess(UpdateUserTitleDisplayNameResult result)
        {
            Debug.Log("success!");
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

ちなみに登録した DisplayName は GameManager のプレイヤー一覧などにも自動的に表示されます。
地味に嬉しいですね!
image.png

2. ランキングの実装

2.1. 統計情報(スコア)の送信

以下のようなコードで統計情報を送信することができます。

    private void SendPlayScore(int score)
    {
        var statisticUpdate = new StatisticUpdate
        {
            // 統計情報名を指定します。
            StatisticName = "JumpCount",
            Value = score,
        };

        var request = new UpdatePlayerStatisticsRequest
        {
            Statistics = new List<StatisticUpdate>
            {
                statisticUpdate
            }
        };

        PlayFabClientAPI.UpdatePlayerStatistics(request, OnSuccess, OnError);

        void OnSuccess(UpdatePlayerStatisticsResult result)
        {
            Debug.Log("success!");
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

※送信した統計情報がランキングに反映されるまで1~2秒かかる場合があります。
※ですので送信後すぐにランキングを取得するような設計は控えましょう。
こちらの記事で詳しく解説されています。

2.2. ランキングの取得

以下のようなコードでランキングを取得することができます。

    private void GetRanking()
    {
        var request = new GetLeaderboardRequest
        {
            StatisticName = "JumpCount", // 統計情報名を指定します。
            StartPosition = 0, // 何位以降のランキングを取得するか指定します。
            MaxResultsCount = 100 // ランキングデータを何件取得するか指定します。最大が100です。
        };

        PlayFabClientAPI.GetLeaderboard(request, OnSuccess, OnError);

        void OnSuccess(GetLeaderboardResult leaderboardResult)
        {
            // 実際は良い感じのランキングを表示するコードにします。
            foreach (var item in leaderboardResult.Leaderboard)
            {
                // Positionは順位です。0から始まるので+1して表示しています。
                Debug.Log($"{item.Position + 1}位: {item.DisplayName} - {item.StatValue}回");
            }
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

実行すると以下のような結果が得られます。
これを良い感じに UI に組み込むだけでランキングの完成です!
image.png

ちなみにランキングのデータは GameManager で確認することが可能です。便利ですね。
image.png

そういえば今回は1位からのランキングを取得/表示しましたが、実際は自分の+-5位のランキングを表示したかったりすると思います。

その場合は GetLeaderboard の代わりに GetLeaderboardAroundPlayer を使います。
コードは殆ど同じです。

    private void GetRanking()
    {
        var request = new GetLeaderboardAroundPlayerRequest
        {
            StatisticName = "JumpCount", // 統計情報名を指定します。
            MaxResultsCount = 11 // 自分と+-5位をあわせて合計11件を取得します。
        };

        PlayFabClientAPI.GetLeaderboardAroundPlayer(request, OnSuccess, OnError);

        void OnSuccess(GetLeaderboardAroundPlayerResult leaderboardResult)
        {
            // 実際は良い感じのランキングを表示するコードにします。
            foreach (var item in leaderboardResult.Leaderboard)
            {
                // Positionは順位です。0から始まるので+1して表示しています。
                Debug.Log($"{item.Position + 1}位: {item.DisplayName} - {item.StatValue}回");
            }
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

ゲーム内の実装の話はここまでです。
最後にランキング報酬を配ってみましょう。

3. ランキング報酬の配布

GameManager でランキングを開きます。
image.png

プライズテーブルを開いて、新しいプライズテーブルを作成します。
image.png

ランキング1~5位には魔法石10個、6~10位には魔法石5個を配る設定を作ってみました。
画面が直感的に操作できて素敵です。(スクショが長くてごめんなさい)
image.png
※アイテムは複数種類を一度に配布することも可能です。
※無料プランではランキング10位までしか報酬を配れないので注意してください。
※$99.99課金すると1000位まで配れるようになります。
※報酬を配る以外にも、ランキングに応じてメールを送信する、プッシュ通知を送る、自分で書いたスクリプトを実行するなどの処理が可能です。

JumpCount のランキング表示に戻って、今すぐリセットを押します。
イベントが終わったらポチッとリセットする感じの運用ですね。
image.png

確認画面が表示されるのでリセットしてください。
これでランキングがリセットされて、同時に報酬が配布されます。
image.png

先程1位だったNishiさんのインベントリを覗いてみると、ちゃんと魔法石が配布されていますね。
image.png

あとがき

ゲームにランキングを簡単に実装できると聞いて試してみましたが、想像以上に簡単で驚きました。
報酬の配布もとても便利で最高ではないでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AzurePlayFabのとても便利なランキング機能を使ってみた&ついでにランキング報酬も配ってみた

まえがき

オンラインゲームにかかせないランキング機能、自分で作るのは少し面倒ですよね。
PlayFab にはユーザーのランキングを管理したり、ランキングに応じて報酬を配ったりできる機能があります。

試しに使ってみたところ、とても便利な機能だったので記事に残しておこうと思います。

成果物

※ゲームプレイ後に超簡易的なランキング画面が表示されます。
JumpRope.gif

使用する PlayFab の API

1. 事前準備

1.1. クライアントから PlayFab への統計情報のポストを許可しておく

PlayFab は統計情報(スコアなど)をクライアントから送信することをデフォルトでは禁止しています。

チート対策のために基本的にはサーバー側で処理してね、とのことですが今回は個人制作の趣味のゲームですし、クライアントからサクッと値を送信したいと思います。

そのために GameManager の設定画面で クライアントにプレイヤー統計情報のポストを許可する を有効にします。
image.png

これを忘れるとクライアントから統計情報を送信したときに下記のエラーがでるので注意してください。
image.png

1.2. ランキングの定義を作成しておく

GameManager で新しいランキングを作成します。
image.png

続いてどのようなランキングを作成するか決めます。
image.png

  • 統計情報名
    わかりやすい名前ならなんでも大丈夫です。
    一度決めると変更できないので慎重に。

  • リセット頻度
    今回は手動にしますが、毎時/毎日/毎週/毎月なども選べます。
    ランキングリセットの際は自動で報酬を配布したりすることもできます。(後述)

  • 集計方法
    今回はプレイヤーごとの最多ジャンプ成功数をランキング表示したいので 最大 にしておきます。

1.3. ユーザーの DisplayName を登録しておく

ランキングにユーザー名を表示するためにはユーザーの DisplayName を登録しておく必要があります。
ゲーム内でユーザー名を設定する画面を作り、以下のようなコードで PlayFab へ登録しておきましょう。

    private void SetUserName(string userName)
    {
        var request = new UpdateUserTitleDisplayNameRequest
        {
            DisplayName = userName
        };

        PlayFabClientAPI.UpdateUserTitleDisplayName(request, OnSuccess, OnError);

        void OnSuccess(UpdateUserTitleDisplayNameResult result)
        {
            Debug.Log("success!");
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

ちなみに登録した DisplayName は GameManager のプレイヤー一覧などにも自動的に表示されます。
地味に嬉しいですね!
image.png

2. ランキングの実装

2.1. 統計情報(スコア)の送信

以下のようなコードで統計情報を送信することができます。

    private void SendPlayScore(int score)
    {
        var statisticUpdate = new StatisticUpdate
        {
            // 統計情報名を指定します。
            StatisticName = "JumpCount",
            Value = score,
        };

        var request = new UpdatePlayerStatisticsRequest
        {
            Statistics = new List<StatisticUpdate>
            {
                statisticUpdate
            }
        };

        PlayFabClientAPI.UpdatePlayerStatistics(request, OnSuccess, OnError);

        void OnSuccess(UpdatePlayerStatisticsResult result)
        {
            Debug.Log("success!");
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

※送信した統計情報がランキングに反映されるまで1~2秒かかる場合があります。
※ですので送信後すぐにランキングを取得するような設計は控えましょう。
こちらの記事で詳しく解説されています。

2.2. ランキングの取得

以下のようなコードでランキングを取得することができます。

    private void GetRanking()
    {
        var request = new GetLeaderboardRequest
        {
            StatisticName = "JumpCount", // 統計情報名を指定します。
            StartPosition = 0, // 何位以降のランキングを取得するか指定します。
            MaxResultsCount = 100 // ランキングデータを何件取得するか指定します。最大が100です。
        };

        PlayFabClientAPI.GetLeaderboard(request, OnSuccess, OnError);

        void OnSuccess(GetLeaderboardResult leaderboardResult)
        {
            // 実際は良い感じのランキングを表示するコードにします。
            foreach (var item in leaderboardResult.Leaderboard)
            {
                // Positionは順位です。0から始まるので+1して表示しています。
                Debug.Log($"{item.Position + 1}位: {item.DisplayName} - {item.StatValue}回");
            }
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

実行すると以下のような結果が得られます。
これを良い感じに UI に組み込むだけでランキングの完成です!
image.png

ちなみにランキングのデータは GameManager で確認することが可能です。便利ですね。
image.png

そういえば今回は1位からのランキングを取得/表示しましたが、実際は自分の+-5位のランキングを表示したかったりすると思います。

その場合は GetLeaderboard の代わりに GetLeaderboardAroundPlayer を使います。
コードは殆ど同じです。

    private void GetRanking()
    {
        var request = new GetLeaderboardAroundPlayerRequest
        {
            StatisticName = "JumpCount", // 統計情報名を指定します。
            MaxResultsCount = 11 // 自分と+-5位をあわせて合計11件を取得します。
        };

        PlayFabClientAPI.GetLeaderboardAroundPlayer(request, OnSuccess, OnError);

        void OnSuccess(GetLeaderboardAroundPlayerResult leaderboardResult)
        {
            // 実際は良い感じのランキングを表示するコードにします。
            foreach (var item in leaderboardResult.Leaderboard)
            {
                // Positionは順位です。0から始まるので+1して表示しています。
                Debug.Log($"{item.Position + 1}位: {item.DisplayName} - {item.StatValue}回");
            }
        }

        void OnError(PlayFabError error)
        {
            Debug.Log($"{error.Error}");
        }
    }

ゲーム内の実装の話はここまでです。
最後にランキング報酬を配ってみましょう。

3. ランキング報酬の配布

GameManager でランキングを開きます。
image.png

プライズテーブルを開いて、新しいプライズテーブルを作成します。
image.png

ランキング1~5位には魔法石10個、6~10位には魔法石5個を配る設定を作ってみました。
画面が直感的に操作できて素敵です。(スクショが長くてごめんなさい)
image.png
※アイテムは複数種類を一度に配布することも可能です。
※無料プランではランキング10位までしか報酬を配れないので注意してください。
※$99.99課金すると1000位まで配れるようになります。
※報酬を配る以外にも、ランキングに応じてメールを送信する、プッシュ通知を送る、自分で書いたスクリプトを実行するなどの処理が可能です。

JumpCount のランキング表示に戻って、今すぐリセットを押します。
イベントが終わったらポチッとリセットする感じの運用ですね。
image.png

確認画面が表示されるのでリセットしてください。
これでランキングがリセットされて、同時に報酬が配布されます。
image.png

先程1位だったNishiさんのインベントリを覗いてみると、ちゃんと魔法石が配布されていますね。
image.png

あとがき

ゲームにランキングを簡単に実装できると聞いて試してみましたが、想像以上に簡単で驚きました。
報酬の配布もとても便利で最高ではないでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

COMオブジェクトを含んだネイティブ関数のP/Invokeメソッド変種

COMオブジェクトを生成する、ファクトリー関数をP/Invokeメソッドとして宣言する場合、(COMオブジェクトを含まなくてもそうだけど)いろいろな宣言が可能なので、そのメモ。

ネイティブ関数

SHCreateDefaultContextMenu を例にする。

SHSTDAPI SHCreateDefaultContextMenu(_In_ const DEFCONTEXTMENU *pdcm, _In_ REFIID riid, _Outptr_ void **ppv);

メソッドいろいろ

素直なP/Invokeメソッド

C# 7.2から使用できるin引数を使用している。

[DllImport("shell32.dll", PreserveSig = false)]
void SHCreateDefaultContextMenu(in DEFCONTEXTMENU pdcm, in Guid riid, out IContextMenu ppv);

C# 7.2より前

Guid構造体には、UnmanagedType.LPStructが使えるらしいので、ref を付けずにポインタを渡せる。

[DllImport("shell32.dll", PreserveSig = false)]
void SHCreateDefaultContextMenu(ref DEFCONTEXTMENU pdcm, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IContextMenu ppv);

COMオブジェクトをinterfaceではなく、objectで受け取る

[DllImport("shell32.dll", PreserveSig = false)]
void SHCreateDefaultContextMenu(ref DEFCONTEXTMENU pdcm, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)]out object ppv);

戻り値をHRESULTのまま受け取る

DllImport 属性のPreserveSigtrue(デフォルト)にしておけば、ネイティブ関数のHRESULTの値をそのまま受け取れる。
その代わり、エラーチェックは自分でやる必要がある。

[DllImport("shell32.dll", PreserveSig = true)]
int SHCreateDefaultContextMenu(in DEFCONTEXTMENU pdcm, in Guid riid, out IContextMenu ppv);

最後の引数を戻り値として受け取る

COMのIDLには retval属性があり、メソッドの戻り値があるかどうかは最後の引数の属性で決まる。
単なる属性なので、P/Invokeでもシグニチャ次第となる。
PreserveSig = falseが付与されている前提で、

  • 戻り値をvoidにすれば戻り値なしになる
  • 最後の引数を戻り値に移動させればそれが戻り値になる
[DllImport("shell32.dll", PreserveSig = false)]
IContextMenu SHCreateDefaultContextMenu(in DEFCONTEXTMENU pdcm, in Guid riid);

付録:上記で使用している型の宣言

struct DEFCONTEXTMENU
{
    public IntPtr hwnd;
    public IContextMenuCB pcmcb;
    public PCIDLIST_ABSOLUTE pidlFolder;
    public IShellFolder psf;
    public uint cidl;
    public PCUITEMID_CHILD_ARRAY apidl;
    [MarshalAs(UnmanagedType.IUnknown)]
    public object punkAssociationInfo;
    public uint cKeys;
    // const HKEY            *aKeys;
    public IntPtr[] aKeys;
}

[ComImport]
[SuppressUnmanagedCodeSecurity]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e4-0000-0000-c000-000000000046")]
internal interface IContextMenu
{
    void QueryContextMenu(
        /* [annotation][in] */
        IntPtr hmenu,
        /* [annotation][in] */
        uint indexMenu,
        /* [annotation][in] */
        uint idCmdFirst,
        /* [annotation][in] */
        uint idCmdLast,
        /* [annotation][in] */
        uint uFlags);

    void InvokeCommand(
        /* [annotation][in] */
        in CMINVOKECOMMANDINFO pici);

    void GetCommandString(
        /* [annotation][in] */
        UIntPtr idCmd,
        /* [annotation][in] */
        uint uType,
        /* [annotation][in] */
        UIntPtr pReserved,
        /* [annotation][out] */ 
        StringBuilder pszName,
        /* [annotation][in] */
        uint cchMax);
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#で作ったCOMをExcel VBAから呼ぶ

事象

dllをregasmで登録。その後VBAエディタで参照設定すると、インテリセンスは効くがオブジェクト生成できない
→実行時エラー「ActiveXコンポーネントはオブジェクトを作成できません。」

原因

32bitのregasmで登録していたから。
もしExcelが32bitなら、64bit版のregasm使わなくてもいけたのかもしれない。

[NG]
%WINDIR%\Microsoft.NET\Framework\v4.0.30319\regasm CCC.dll /codebase /tlb

[OK]
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\regasm CCC.dll /codebase /tlb

[おまけ、登録解除]
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\regasm /tlb /u CCC.dll

発生環境

windows10 1803 64bit
VS2019 16.0.3
Excel 16.0.11727 64bit

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む