- 投稿日:2020-03-26T23:42:34+09:00
rotationでプレイヤーがランダムに傾く方法
勝手に倒れるオブジェクト
rotationを利用して、オブジェクトがランダムに傾くのをキーで操作する方法を紹介します。
z軸を使って回転します。
UnityではQuaternionで回転数を決めているので、先に宣言。
new Vector3のzの値にRandom.Rangeを使って1-4までの乱数を返します。
これにより、ランダムで傾きます。この傾きをキーを使って戻したり、するためには下記のコードを追加。
zの値は乱数の最大値よりも大きな値を設定します。下重心にする
しかし、上記のスクリプトをただオブジェクトにアタッチした場合、こうなります。
なので、今回は重心を下にしてplaneにCapsulの面が触れながら傾きたかったので、
空のGameObjectを作成します。
そしてpositionを重心を置きたい位置に合わせます。
今回はここ。
そして、空のGameObjectを親にして、Capsuleを子にします。
スクリプトをアタッチするのは空のGameObjectの方です。
すると、このように傾きます。
人間を傾ける時なんかは足がついていないといけないので、この方法が利用できるかなと思います。
- 投稿日:2020-03-26T20:38:58+09:00
IEnumerable<T>をピボット集計するライブラリ書いた
IEnumerableをピボット集計するライブラリ書いた
※MVC5用
世間がコロナコロナうっせーのでムシャクシャしてやった。
よくある話
「これをピボットみたいにしてほしい。Excelみたいに。Excelみたいに。」
すげぇ面倒。
面倒ポイント
- 列数が不定なのでタイトル行と明細行でそれぞれ横方向のループで描画する必要がある
- 列や行を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), } );しくみ
ざっくりとこう分けて
データ構成だけに着目するとColumn(0)..Column(n)をキーにしたツリー構造
と、見ることができる。
行列ヘッダ
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
- Enumerable.GroupBy メソッド (System.Linq) | Microsoft Docs
- IGrouping インターフェイス (System.Linq) | Microsoft Docs
構造的に行も列も同じ。
measure
measureは
- Tから値を取り出すValueGetter
- 値を集計する aggregater function
で成り立ってる。
Pivotから見れば行列の座標から抽出したリストに対して集計して値を出す部分を委譲するのがこのMeasure。
値の取り出し
この図のように、あるセルを表す集合
ListOfCell
はそのセルの座標となる各ヘッダーセルの値で元集合をフィルターしたものになる。
そこにMeasureを通すと出力すべき値が取り出せる。小計や合計の取り出し
小計や合計の値は、そのセルの座標となる各ヘッダーセルのうち合計ヘッダーセルでないものの値で元集合をフィルターしたものから算出できる。
今後とか
列定義と集計すべき値が定義としてあるんだから先に
GroupBy(Column(0)..Column(n)).Select(Column(0)..Column(n),Measure(0)..Measure(m))
みたいに集計しちゃえばCPUに優しい感じになりそうなんだけど、どうやって実装したもんか。コロナがすべてわるい。
- 投稿日:2020-03-26T14:18:26+09:00
【C#】UWPでプリンタに直接印刷する方法
UWPではプリンタに直接印刷できない!
初めてUWPの開発を行って、何もかもが初めてで四苦八苦しながら作っていたところ、どうもUWPは特定のプリンタに直接印刷することができないらしい。
最初から難しそうだなーなんて思ってた。
なんかお店とかだとタブレットからレシートに印刷をかけてるようなイメージがあったので、なんかやり方あるんだろとも思ってたけど、調べまくっても、ユーザーに印刷を促すコードばかりが出てくる。結論、UWPではセキュリティの関係上、印刷をユーザーに促すことはできても、天地がひっくり返っても直接特定のプリンタに印刷をすることはできない。
しかもカメラデバイスの利用許可のように一度だけ出る分にはまぁまぁとも思うけど、印刷の場合は常に促される形になるらしく、話にならんかった。コードで何も指定できなくて、どのプリンタに印刷するの?何ページ目を印刷するの?みたく聞いてくるならそりゃ毎度毎度聞くわなって感じ。
実現するには
UWPから直接実行する考えは切り捨て、UWPから.NET Frameworkで作られたexeを実行することで実現する。
参考にしたのは下記。
UWP – Print PDF Files Silently (Without Print Dialog)流れの概要は下記の通り。
情報の受け渡しにオブジェクトは利用できないので、Newtonsoft Json.NETとかを利用して、JSON文字列とかで受け渡しすればいい感じになる。
- UWP側で、印刷に必要な情報を渡す。
- UWP側で、.NET Frameworkのexeを実行する。
- .NET Frameworkのexeで、UWP側から渡された情報を受け取る。
- .NET Frameworkのexeで、印刷する。
実装の流れの補足
記事の便宜上
- UWPアプリケーションを UwpApp とする。
- .NET Frameworkアプリケーションを NetApp とする。
NetAppを作成する
- NetAppを.NET Frameworkでコンソールアプリケーションを作成する。
参照設定から、『C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd』を選択する。
パスの『10.0.17763.0』は、UwpAppで選択している最小バージョンに合わせたパスにある Windows.winmd を選択する。
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>それらしいコードを書く。
Windows.Storage.ApplicationData.Current.LocalSettings.Values["xxx"]で、UWP側から渡された値を取得できるようになる。NetApp-Example.csclass 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を修正する
参照設定で、Universal Windows -> 拡張 から、『Windows Desktop Extensions for the UWP』を選択する。
バージョンはUwpAppの最小バージョンに合わせたものを選択する。
ビルドしたNetApp.exe、NetApp.exe.configをプロジェクトに含める。
(NetApp.exe.configは、アプリケーション構成ファイルを操作したい場合)。
プロパティ 値 ビルドアクション コンテンツ 出力ディレクトリにコピー コピーしない NetApp.exe.config の中身が色々な都合で変わっちゃっていたら、元の形に変える。(.NET Core のDLLを参照してる時は変えないとダメそう)
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>NetApp.exe に渡したい値を設定する。
Windows.Storage.ApplicationData.Current.LocalSettings.Values["Key"] = "Example";NetApp.exe を実行する。
if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0)) { await Windows.ApplicationModel.FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(); }実際の動き
UWPアプリケーションの上に、キャンセルボタンが押せるダイアログが表示される。
印刷が終われば消えるし、UWPで画面が切り替わるわけでもないので、まぁまぁ求めた動作はする。
悩むこと
- NetApp.exeの変更が適用されない。
ビルドしても反映されない時があるみたいで、よくわからんので、UwpAppをリビルドした方が良さそう。NetAppでは、Windows.Storage.ApplicationData.Current.LocalSettings.Values[] によって値を取得しているため、入口がUWP側から実行してないと下記例外が発生してしまう。
System.InvalidOperationException: プロセスにパッケージ ID がありません。 (HRESULT からの例外:0x80073D54)前段としてどうやるとそこがうまいこと通るようになるのか分からなかったので、当該部分だけ望む値が渡ってきたという想定のコードに変えてデバッグしました。
- 投稿日:2020-03-26T09:25:16+09:00
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); } } } } }
- 投稿日:2020-03-26T03:49:48+09:00
IQ2でもLINQを理解できるように咀嚼してみた
LINQの用途
目的としては C#でコレクション, データベース, XML等の異なったデータ形式を共通の方法(LINQ)で操作するため。
LINQはC# 3.0で追加された。
LINQ操作手順(概要)
LINQクエリ(データへの問い合わせ)を使った操作の手順はすべて、次の3つの手順で構成されます。
1.データ ソースを取得します。
2.クエリ1を作成します。
3.クエリ結果を操作。LINQ操作手順(詳細)
- 操作対象となる配列(0~6の連番)を宣言
- 配列に対してどのようなデータ(偶数データ)を取得し、それらのデータをどのような形式と編成で返すかを記述
- クエリ結果を操作(コンソール画面に出力)
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の人はここで帰ってくれ
※次の記事でがっつり解説します
クエリとは、指定したデータ ソース (単一または複数) からどのようなデータを取得し、それらのデータをどのような形式と編成で返すかを説明した、命令のセット ↩
- 投稿日:2020-03-26T01:47:04+09:00
【Unity(C#)】MaterialPropertyBlock使って一つのマテリアルを使いまわす
MaterialPropertyBlock
下記記事で作成していたVRお絵描きアプリに
色の変更機能を設けようとした際にMaterialPropertyBlock
を活用しました。【参考リンク】:【Unity(C#)】ハンドトラッキングで簡易版VRお絵かきアプリ
描いた線ごとに色を変更したかったのですが、
描く予定の線の分だけマテリアルを用意するのは
あまりにも手間ですし、その都度Instantiateするのも
負荷的にどうなの?と感じたので
MaterialPropertyBlock
の使い方を学ぶことにしました。デモ
リアルタイムに色違いのキューブがランダムに生成されるデモです。
マテリアルは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型)
という処理を行わずに済み、負荷が軽くなります。参考リンク