20200326のC#に関する記事は6件です。

rotationでプレイヤーがランダムに傾く方法

勝手に倒れるオブジェクト

rotationを利用して、オブジェクトがランダムに傾くのをキーで操作する方法を紹介します。

dqz4d-ebqdc.gif

z軸を使って回転します。
UnityではQuaternionで回転数を決めているので、先に宣言。
new Vector3のzの値にRandom.Rangeを使って1-4までの乱数を返します。
これにより、ランダムで傾きます。

スクリーンショット 2020-03-26 19.52.13.png

この傾きをキーを使って戻したり、するためには下記のコードを追加。
zの値は乱数の最大値よりも大きな値を設定します。

スクリーンショット 2020-03-26 18.36.58.png

下重心にする

しかし、上記のスクリプトをただオブジェクトにアタッチした場合、こうなります。
movie1.gif

なので、今回は重心を下にしてplaneにCapsulの面が触れながら傾きたかったので、
空のGameObjectを作成します。
そしてpositionを重心を置きたい位置に合わせます。
今回はここ。
スクリーンショット 2020-03-26 19.01.26.png
そして、

空のGameObjectを親にして、Capsuleを子にします。

スクリプトをアタッチするのは空のGameObjectの方です。
スクリーンショット 2020-03-26 18.56.12.png

すると、このように傾きます。
人間を傾ける時なんかは足がついていないといけないので、この方法が利用できるかなと思います。
dqz4d-ebqdc.gif

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

IEnumerable<T>をピボット集計するライブラリ書いた

IEnumerableをピボット集計するライブラリ書いた

※MVC5用

世間がコロナコロナうっせーのでムシャクシャしてやった。

よくある話

「ここにこういう表があるじゃろ」
ss1.png

「これをピボットみたいにしてほしい。Excelみたいに。Excelみたいに。」
ss2.png

すげぇ面倒。

面倒ポイント

  • 列数が不定なのでタイトル行と明細行でそれぞれ横方向のループで描画する必要がある
  • 列や行をrowspanやcolspanで結合させるためにそのセルの下位に位置するセルを計算する必要がある。
  • 行方向だと特に前の行でセルが結合済みだったりするのでどの列ヘッダから描画すべきかフラグ管理が鬱陶しい。
  • 小計や合計を出そうとすると更に倍ぐらい面倒くさい。

書いた

qyen/ToPivotTable: Convert IEnumerable to pivot table on C#

model.pivot = DB.ToPivotTable(
        new List<PivotColumn<MockData>>() {
            // carの頭文字を逆順で集計 
            new PivotColumn<MockData>("initial",(t)=>t.car.Substring(0,1),(t)=>t.car.Substring(0,1)){
                Order=PivotOrder.Descending,
            },
            new PivotColumn<MockData>("car"),
        },
        new List<PivotColumn<MockData>>() {
            //スペース区切りのJobの最初の単語をカテゴリとして集計
            new PivotColumn<MockData>("category",(t)=>t.Job.Split(' ').First(),(t)=>t.Job.Split(' ').First()),
            //スペース区切りのJobの2番め以降をJobとして集計
            new PivotColumn<MockData>("Job",(t)=>string.Join(" ",t.Job.Split(' ').Skip(1))),
        },
        new List<PivotMeasure<MockData>>() {
            // 集計するのはcashの平均値
            PivotMeasure<MockData>.Average("Avg.Cash",(t)=>t.cash),
        }
    );

しくみ

ss4.png

ざっくりとこう分けて

列ヘッダだけで見ると
ss5.png
こういうデータ構成になってる。

データ構成だけに着目するとColumn(0)..Column(n)をキーにしたツリー構造

image.png

と、見ることができる。

行列ヘッダ

LinqのIEnumerable<T>.GroupBy()IEnumerable<IGrouping<T>>を返し、IGroupingはIEnumerableのサブクラスであるため、

foreach (var group0 in source.GroupBy(<Column0>)){
  foreach (var group1 in group0.GroupBy(<Column1>)){
    foreach (var group2 in group1.GroupBy(<Column2>)){
    : 
    }
  }
} 

という形で深堀りできるので、再帰を使って一気にツリーを生成してる。
ToPivotTable/PivotTable.cs at master · qyen/ToPivotTable

構造的に行も列も同じ。

measure

measureは

  • Tから値を取り出すValueGetter
  • 値を集計する aggregater function

で成り立ってる。

Pivotから見れば行列の座標から抽出したリストに対して集計して値を出す部分を委譲するのがこのMeasure。

値の取り出し

ss6.png

この図のように、あるセルを表す集合ListOfCellはそのセルの座標となる各ヘッダーセルの値で元集合をフィルターしたものになる。
そこにMeasureを通すと出力すべき値が取り出せる。

小計や合計の取り出し

ss7.png

小計や合計の値は、そのセルの座標となる各ヘッダーセルのうち合計ヘッダーセルでないものの値で元集合をフィルターしたものから算出できる。

今後とか

列定義と集計すべき値が定義としてあるんだから先にGroupBy(Column(0)..Column(n)).Select(Column(0)..Column(n),Measure(0)..Measure(m))みたいに集計しちゃえばCPUに優しい感じになりそうなんだけど、どうやって実装したもんか。

コロナがすべてわるい。

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

【C#】UWPでプリンタに直接印刷する方法

UWPではプリンタに直接印刷できない!

初めてUWPの開発を行って、何もかもが初めてで四苦八苦しながら作っていたところ、どうもUWPは特定のプリンタに直接印刷することができないらしい。

最初から難しそうだなーなんて思ってた。
なんかお店とかだとタブレットからレシートに印刷をかけてるようなイメージがあったので、なんかやり方あるんだろとも思ってたけど、調べまくっても、ユーザーに印刷を促すコードばかりが出てくる。

結論、UWPではセキュリティの関係上、印刷をユーザーに促すことはできても、天地がひっくり返っても直接特定のプリンタに印刷をすることはできない。
しかもカメラデバイスの利用許可のように一度だけ出る分にはまぁまぁとも思うけど、印刷の場合は常に促される形になるらしく、話にならんかった。

コードで何も指定できなくて、どのプリンタに印刷するの?何ページ目を印刷するの?みたく聞いてくるならそりゃ毎度毎度聞くわなって感じ。

実現するには

UWPから直接実行する考えは切り捨て、UWPから.NET Frameworkで作られたexeを実行することで実現する。

参考にしたのは下記。
UWP – Print PDF Files Silently (Without Print Dialog)

流れの概要は下記の通り。
情報の受け渡しにオブジェクトは利用できないので、Newtonsoft Json.NETとかを利用して、JSON文字列とかで受け渡しすればいい感じになる。

  1. UWP側で、印刷に必要な情報を渡す。
  2. UWP側で、.NET Frameworkのexeを実行する。
  3. .NET Frameworkのexeで、UWP側から渡された情報を受け取る。
  4. .NET Frameworkのexeで、印刷する。

実装の流れの補足

記事の便宜上

  • UWPアプリケーションを UwpApp とする。
  • .NET Frameworkアプリケーションを NetApp とする。

NetAppを作成する

  1. NetAppを.NET Frameworkでコンソールアプリケーションを作成する。
  2. 出力の種類をWindowsアプリケーションにする。
    3.png

  3. 参照設定から、『C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd』を選択する。
    パスの『10.0.17763.0』は、UwpAppで選択している最小バージョンに合わせたパスにある Windows.winmd を選択する。
    2.png

  4. App.configを設定する。

    App.config
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
      </startup>
    
      <appSettings>
        <add key="PrinterName" value="FinePrint" />
      </appSettings>
    </configuration>
    
  5. それらしいコードを書く。
    Windows.Storage.ApplicationData.Current.LocalSettings.Values["xxx"]で、UWP側から渡された値を取得できるようになる。

    NetApp-Example.cs
    class Example
    {
        private string printingText;
        private Font printFont;
    
        public void Execute()
        {
            // 印刷するプリンタ名を取得
            var printerName = ConfigurationManager.AppSettings["PrinterName"];
    
            // UWPから渡ってきた値を取得する
            var text = Windows.Storage.ApplicationData.Current.LocalSettings.Values["Key"].ToString();
    
            // 印刷
            printingText = text;
            printFont = new Font("MS Pゴシック", 10);
            System.Drawing.Printing.PrintDocument pd = new System.Drawing.Printing.PrintDocument();
            pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
            pd.Print();
        }
    
        private void pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            // 都合いい感じに印刷処理を書く
            e.Graphics.DrawString(printingText, printFont, Brushes.Black, 0, 0);
        }
    }
    

UwpAppを修正する

  1. 参照設定で、Universal Windows -> 拡張 から、『Windows Desktop Extensions for the UWP』を選択する。
    バージョンはUwpAppの最小バージョンに合わせたものを選択する。
    1.png

  2. ビルドしたNetApp.exe、NetApp.exe.configをプロジェクトに含める。
    (NetApp.exe.configは、アプリケーション構成ファイルを操作したい場合)。
    4.png

    プロパティ
    ビルドアクション コンテンツ
    出力ディレクトリにコピー コピーしない
  3. NetApp.exe.config の中身が色々な都合で変わっちゃっていたら、元の形に変える。(.NET Core のDLLを参照してる時は変えないとダメそう)

  4. Package.appxmanifest をコード表示して Exceptions、Capabilities 部分を加えて、NetApp.exeを実行できるようにする。

    Package.appxmanifest
    <?xml version="1.0" encoding="utf-8"?>
    <Package>
      ・・・
      <Applications>
        <Application>
          ・・・
          <Extensions xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10">
            <desktop:Extension Category="windows.fullTrustProcess" Executable="NetApp.exe" />
          </Extensions>
        </Application>
      </Applications>
      ・・・
      <Capabilities>
        <rescap:Capability xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" Name="runFullTrust" />
      </Capabilities>
      ・・・
    </Package>
    
  5. NetApp.exe に渡したい値を設定する。

    Windows.Storage.ApplicationData.Current.LocalSettings.Values["Key"] = "Example";
    
  6. NetApp.exe を実行する。

    if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
    {
        await Windows.ApplicationModel.FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
    }
    

実際の動き

UWPアプリケーションの上に、キャンセルボタンが押せるダイアログが表示される。
印刷が終われば消えるし、UWPで画面が切り替わるわけでもないので、まぁまぁ求めた動作はする。
無題.png

悩むこと

  1. NetApp.exeの変更が適用されない。
    ビルドしても反映されない時があるみたいで、よくわからんので、UwpAppをリビルドした方が良さそう。
  2. NetAppでは、Windows.Storage.ApplicationData.Current.LocalSettings.Values[] によって値を取得しているため、入口がUWP側から実行してないと下記例外が発生してしまう。

    System.InvalidOperationException:
    プロセスにパッケージ ID がありません。 (HRESULT からの例外:0x80073D54)
    

    前段としてどうやるとそこがうまいこと通るようになるのか分からなかったので、当該部分だけ望む値が渡ってきたという想定のコードに変えてデバッグしました。

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

Qiitaに投稿した記事のバックアップを取得する (C#)

雑いけど。

https://qiita.com/settings/applications で取得したアクセストークンを pat 変数に設定し実行すると、指定したフォルダにitems.jsonと画像がダウンロードされる。記事本文の Markdown は items.json に含まれる。

投稿した記事が多い場合は↓のURLと保存するファイル名を変えればよさげ。
https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=100

// NuGet で Newtonsoft.Json を要インストール

public class Qiita
{
    // generate personal access token
    // https://qiita.com/settings/applications
    static string pat = Util.GetPassword("qiita");

    // set output directory
    static string outdir = @"C:\qiita";

    static HttpClient httpClient = new HttpClient();

    static async Task Main()
    {
        string itemsjson;
        var itemsjsonfile = Path.Combine(outdir, "items.json");
        if (File.Exists(itemsjsonfile))
        {
            itemsjson = File.ReadAllText(itemsjsonfile);
        }
        else
        {
            httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", pat);
            itemsjson = await httpClient.GetStringAsync("https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=100");
            httpClient.DefaultRequestHeaders.Authorization = null;
            File.WriteAllText(itemsjsonfile, itemsjson);
        }

        var jobject = JArray.Parse(itemsjson);
        foreach (var item in jobject)
        {
            var title = (string)item["title"];
            var url = (string)item["url"];
            var markdown = (string)item["body"];

            Regex re = new Regex(@"https://qiita-image-store.s3.amazonaws.com/.*\.png");
            var imgurls = re.Matches(markdown);

            foreach (Match imgurlm in imgurls)
            {
                var imgurl = imgurlm.Value;
                string fname = Path.Combine(outdir, imgurl.Split('/').Last());
                if (!File.Exists(fname))
                {
                    var data = await httpClient.GetByteArrayAsync(imgurl);
                    File.WriteAllBytes(fname, data);
                }
            }
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IQ2でもLINQを理解できるように咀嚼してみた

LINQの用途

目的としては C#でコレクション, データベース, XML等の異なったデータ形式を共通の方法(LINQ)で操作するため。

LINQはC# 3.0で追加された。

LINQ操作手順(概要)

LINQクエリ(データへの問い合わせ)を使った操作の手順はすべて、次の3つの手順で構成されます。
1.データ ソースを取得します。
2.クエリ1を作成します。
3.クエリ結果を操作。

LINQ操作手順(詳細)

  1. 操作対象となる配列(0~6の連番)を宣言
  2. 配列に対してどのようなデータ(偶数データ)を取得し、それらのデータをどのような形式と編成で返すかを記述
  3. クエリ結果を操作(コンソール画面に出力)
LINQ操作手順
class IntroToLINQ
{        
    static void Main()
    {
        // The Three Parts of a LINQ Query:
        // 1. Data source.
        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

        // 2. Query creation.
        // (クエリ式)
        var numQuery =
            from num in numbers
            where (num % 2) == 0
            select num;
         //(メソッド式)
         //var numQuery = numbers.where(num => num % 2 == 0);

        // 3. Query execution.
        foreach (int num in numQuery)
        {
            Console.Write("{0,1} ", num);
        }
    }
}

IQ2の人はここで帰ってくれ

ダウンロード (1).jfif

※次の記事でがっつり解説します


  1. クエリとは、指定したデータ ソース (単一または複数) からどのようなデータを取得し、それらのデータをどのような形式と編成で返すかを説明した、命令のセット 

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

【Unity(C#)】MaterialPropertyBlock使って一つのマテリアルを使いまわす

MaterialPropertyBlock

下記記事で作成していたVRお絵描きアプリに
色の変更機能を設けようとした際にMaterialPropertyBlockを活用しました。

【参考リンク】:【Unity(C#)】ハンドトラッキングで簡易版VRお絵かきアプリ

描いた線ごとに色を変更したかったのですが、
描く予定の線の分だけマテリアルを用意するのは
あまりにも手間ですし、その都度Instantiateするのも
負荷的にどうなの?と感じたので
MaterialPropertyBlockの使い方を学ぶことにしました。

デモ

リアルタイムに色違いのキューブがランダムに生成されるデモです。

MaterialPropertyBlock.gif

マテリアルは1つしか使っていません。

コード

using System.Collections;
using UnityEngine;

/// <summary>
/// 色違い場所違いキューブ自動生成プログラム
/// </summary>
public class CubeCreate : MonoBehaviour
{
    [SerializeField] private GameObject _cube;

    private MaterialPropertyBlock _materialPropertyBlock;

    private int propertyID;

    private void Start()
    {
        _materialPropertyBlock = new MaterialPropertyBlock();

        //プロパティーのIDを取得しておく SetColorをstringで指定しても結局intに変換してるらしく、無駄らしい
        propertyID = Shader.PropertyToID("_Color");

        StartCoroutine(InstantiateColorCube());
    }

    /// <summary>
    /// ランダムな位置にランダムな色のキューブを生成 
    /// </summary>
    private IEnumerator InstantiateColorCube()
    {
        while (true)
        {
            //ランダムな値
            float randomValueA = Random.Range(-1.0f, 1.0f);
            float randomValueB = Random.Range(-1.0f, 1.0f);
            float randomValueC = Random.Range(-1.0f, 1.0f);

            //ランダムな値
            float randomMagnification = Random.Range(0.0f, 5.0f);

            //ランダムな位置にキューブ生成
            Vector3 randomPos = new Vector3(randomValueA, randomValueB, randomValueC);
            GameObject tmp = Instantiate(_cube, randomPos*randomMagnification, Quaternion.identity);

            //MaterialPropertyBlockで色を変更 元のマテリアルの色はそのまま
            MeshRenderer mr = tmp.GetComponent<MeshRenderer>();
            Color randomColor = new Color(randomValueA, randomValueB, randomValueC);
            _materialPropertyBlock.SetColor(propertyID, randomColor*randomMagnification);
            mr.SetPropertyBlock(_materialPropertyBlock);

            yield return null;
        }
    }
}

SetColor , SetPropertyBlock

色をセットするためにSetColor を使います。
第一引数にShaderで定義されている色のProperty名を指定します。
(今回はIDで指定しています)

その後、Rendererに反映させるためにSetPropertyBlockを呼び出します。

Shader.PropertyToID

Shader.PropertyToIDを使うことで
Shader内で定義されている特定のプロパティ名を
ID(int型)に変換することができます。

先ほどのSetColorの第一引数に渡すことができます。

メリットしてはStart関数内でIDを取得しているので
何度も指定したShader内プロパティの文字列ID(int型)
という処理を行わずに済み、負荷が軽くなります。

参考リンク

【Unity】【シェーダ】MaterialPropertyBlockの使い方

【Unity】MaterialのPropertyIDについて

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