- 投稿日:2019-02-28T23:55:35+09:00
力尽く!!「Leap Motion + Unity」で自作モデルを動かす
はじめに
この記事は、公式ドキュメントを読むことをあきらめた一匹のゴリラが、腕力にものを言わせてLeap Motionで自作モデルを動かすまでのストーリーです。
公式ドキュメントにならった綺麗な実装が書いてあるわけではないのであしからず
ターゲット
以下の同士(ゴリラ)諸氏におかれては、ためになるかもしれない
- 公式ドキュメント読むの疲れたよー
- そもそもドキュメントが英語でよくわからないよー
- 「Final IK」ってなに?
- Leap Motion買って、金欠だよー
- とりあえず、それっぽく動けばいいや
あらすじ
ゴリラ「Leap Motion買ったし、SDKも準備したし、手のモデルも作ったし、そろそろ動かすか!」
ゴリラ「設定項目多すぎ、26個もQuaternion手打ちとか、正気の沙汰じゃない」
~公式ドキュメント(英語)を眺めるゴリラ~
ゴリラ「AutoRigで自動設定とかあるんだ、やってみよ」
ゴリラ「手の形変わってるし、握ったとき指重なってるし、なんかやだ」どうするの
公式ドキュメントを探し続ければ、確信的な設定項目が見つかるかもしれないが、すでにサンプルシーンではそれらしく動く手が存在している。
もうさ、こいつの動きをトレースしちゃおうよ、やっちゃおうよこうするの
ソースコード
Offset.cs/// <summary> /// Quaternionの差分の保持 /// </summary> private class Offset { /// <summary> /// 対象モデルのQuaternion /// </summary> private Quaternion Model; /// <summary> /// 参照モデルのQuaternion /// </summary> private Quaternion Leap; /// <summary> /// Quaternionの差分を保持し、参照モデルのQuaternionの変化を対象モデルへと伝搬する /// </summary> /// <param name="model">対象モデル</param> /// <param name="leap">参照モデル</param> public Offset(Quaternion model, Quaternion leap) { Model = model; Leap = Quaternion.Inverse(leap); } /// <summary> /// オフセットの適用 /// </summary> /// <param name="currentLeap">現在の参照モデルのQuaternion</param> /// <returns>オフセットを適用した対象モデルのQuaternion</returns> public Quaternion ApplyOffset(Quaternion currentLeap) { return currentLeap * Leap * Model; } } /// <summary> /// 指のオフセット /// </summary> private class FingerOffset { private Offset Bone1; private Offset Bone2; private Offset Bone3; private Offset Bone4; public FingerOffset(Finger model, Finger leap) { if (model.Bone1 != null && leap.Bone1 != null) { Bone1 = new Offset(model.Bone1.transform.rotation, leap.Bone1.transform.rotation); } Bone2 = new Offset(model.Bone2.transform.rotation, leap.Bone2.transform.rotation); Bone3 = new Offset(model.Bone3.transform.rotation, leap.Bone3.transform.rotation); Bone4 = new Offset(model.Bone4.transform.rotation, leap.Bone4.transform.rotation); } public void ApplyOffset(Finger model, Finger leap) { if (Bone1 != null) { model.Bone1.rotation = Bone1.ApplyOffset(leap.Bone1.rotation); } model.Bone2.rotation = Bone2.ApplyOffset(leap.Bone2.rotation); model.Bone3.rotation = Bone3.ApplyOffset(leap.Bone3.rotation); model.Bone4.rotation = Bone4.ApplyOffset(leap.Bone4.rotation); } } private class HandOffset { private FingerOffset Thumb; private FingerOffset Index; private FingerOffset Middle; private FingerOffset Ring; private FingerOffset Pinky; private Offset Wirst; private Vector3 OffsetWirst; public HandOffset(Hand model, Hand leap) { Thumb = new FingerOffset(model.Thumb, leap.Thumb); Index = new FingerOffset(model.Index, leap.Index); Middle = new FingerOffset(model.Middle, leap.Middle); Ring = new FingerOffset(model.Ring, leap.Ring); Pinky = new FingerOffset(model.Pinky, leap.Pinky); Wirst = new Offset(model.Wirst.rotation, leap.Wirst.rotation); OffsetWirst = model.Wirst.position - leap.Wirst.position; } public void ApplyOffset(Hand model, Hand leap) { Thumb.ApplyOffset(model.Thumb, leap.Thumb); Index.ApplyOffset(model.Index, leap.Index); Middle.ApplyOffset(model.Middle, leap.Middle); Ring.ApplyOffset(model.Ring, leap.Ring); Pinky.ApplyOffset(model.Pinky, leap.Pinky); model.Wirst.rotation = Wirst.ApplyOffset(leap.Wirst.rotation); model.Wirst.position = leap.Wirst.position + OffsetWirst; } }LeapMotion.cs[SerializeField] private Hand HandLeft; [SerializeField] private Hand HandLeapLeft; [SerializeField] private Hand HandRight; [SerializeField] private Hand HandLeapRight; private HandOffset HandOffsetLeft; private HandOffset HandOffsetRight; private void Awake() { HandOffsetLeft = new HandOffset(HandLeft, HandLeapLeft); HandOffsetRight = new HandOffset(HandRight, HandLeapRight); GameObject obj; obj = new GameObject("LeftHandOffset"); obj.transform.parent = gameObject.transform; obj = new GameObject("RightHandOffset"); obj.transform.parent = gameObject.transform; } private void Update() { HandOffsetLeft.ApplyOffset(HandLeft, HandLeapLeft); HandOffsetRight.ApplyOffset(HandRight, HandLeapRight); }ソース全体はこちら
解説
特に解説する余地もない、力技のソースですが、簡単に説明すると
- サンプルのモデル(参照モデル)と自作モデル(対象モデル)の各関節ごとのQuaternionの差分を保持する。
- 参照モデルの動き(Quaternonの変化)にオフセットを適用して、対象モデルのQuaternionを更新する。
- 1.2.をすべての手、すべての指に適用する。
おわりに
実行結果
今後の課題
結果としてAutoRigより見た目はマシだが、依然として設定項目が多いので(指(5本) × 骨(4本)のTransformを設定する必要がある)、このスクリプトにもAutoRigのような自動で指のボーンのTransformを設定するスクリプトを組み込みたい。
感想
個人的にはそれなりに動くし、及第点ではあるんだけど、設定項目が多すぎるのがやっぱりつらいところ
いっそのこと、公式ドキュメントを漁った方がより早く幸せになれたかもしれない。
より早く幸せになった人は、是非ともその方法を教えてください。自分の場合は、AutoRig微妙ではあったんだけど、モデル作成の段階からきれいに動かすノウハウとかあるのかもしれない。
そんなノウハウを知ってる方は(略
- 投稿日:2019-02-28T23:46:19+09:00
C#終了時処理覚書
終了時の処理記述に詰まったので覚書
Appのロード時に処理
FormにLoadのイベントハンドラを追加
private void Form1_Load(object sender, EventArgs e) { ///ここに開始時処理を記述/// }Appの終了時に処理
FormにFormClosingのイベントハンドラを追加
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { ///ここに終了時処理を記述/// }使用例
Formの開始時にtimelog.txtを探し、あれば追記、無ければ作成し、現在時刻を書き込む。終了時にも現在時刻を書き込み、改行する。
timelog.csusing System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; using System.IO; namespace hoge { public partial class Form1 : Form { //Form開始時の処理 private void Form1_Load(object sender, EventArgs e) { //開始時に現在時刻の記録 DateTime localTime = DateTime.Now; File.AppendAllText(@"timelog.txt", localTime+" ", Encoding.UTF8); } //Form終了時の処理 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { //終了時に現在時刻の記録 DateTime localTime = DateTime.Now; File.AppendAllText(@"timelog.txt", localTime + Environment.NewLine, Encoding.UTF8); } } }timelog.txt2019/02/28 23:25:55///開始時 2019/02/28 23:25:58///終了時 2019/02/28 23:45:13 2019/02/28 23:45:19
- 投稿日:2019-02-28T22:25:02+09:00
初めて学ぶ言語として C# をおすすめしない理由
C# は最高です。好きです。使ってほしいと思います。
でも最初に学ぶ言語としておすすめしません。では何言語を学べばよいか?まずAltair 8800でパソコン操作を覚えてからBASICとかやればいいんじゃない?
とりあえずC#から始めるべきではない理由を説明します。C# の典型的Hello World!はこちらです。
using System; namespace Hell { class Hello { static void Main() { Console.WriteLine("Hello World!"); } } }初めて使うクラスは System.Console クラスでしょう。
人生でたった1度しかない「初めて使うクラス」が System.Console なのです。
このクラスを通じて、プログラミングを学んでゆくのです。
さて、このクラスのメンバをいくつか見てみましょう。SetWindowSize メソッド
BackGroundColor プロパティこれは「コンソールウィンドウ」の操作を行うためのものです。
ところがこれらのメソッドはどうでしょうか?
Read メソッド
Write メソッド「コンソールウィンドウ」に文字を読み書きするためのものですか?違います。
結果的にはコンソールウィンドウに文字が出ることがありますが、コンソールウィンドウと直接関係のないメソッドです。
標準入出力をするためのものです。「標準入出力」と「コンソールウィンドウ操作」が同居するクラス それが System.Console なのです。
これらはまったく分離されるべきことではないでしょうか。別のものなのにグループとして覚えてしまうと、
ひどい場合「標準入出力」=「コンソールウィンドウによるユーザーとの対話」という認識をもつ可能性があります。私のことなんですけどね。例えば VisualStudio Code のデバッガをexeとして実装するとき、標準入出力でエディタとデバッガとをプロセス間通信させます。
そこに「コンソールウィンドウによるユーザーとの対話」なんてまったく登場しません。
「標準入出力」=「コンソールウィンドウによるユーザーとの対話」という認識があったら、
標準入出力によるプロセス間通信を理解することができません。初心者に「標準入出力」=「コンソールウィンドウによるユーザーとの対話」という誤解を与えかねない
System.Console は基本クラスライブラリから削除せよ!!
- 投稿日:2019-02-28T17:02:18+09:00
[SerializeField] 属性をつけていると、CS0649警告が出るようになった。消したい。
- 環境 2018.3.5f1
Unity 2018.3 にアップグレードしたら、
.NET 3.5 (deprecated) ということで、.NET 4.x にしました。そしたら、とてもたくさんの・・・
100個以上たくさんの、、Warning が表示される
xxx.cs(23,37): warning CS0649: Field 'xxx.obj' is never assigned to, and will always have its default value null
邪魔すぎるので、消す方法を調べる。
Warning CS0649 not suppressed properly when field is marked as [SerializeField] - Unity Forum
before
[SerializeField] GameObject obj;
方法1 [SerializeField]を忘れて、すべてを 'public'にする
public GameObject obj;方法2 #pragma warning disable 649
#pragma warning disable 649 [SerializeField] GameObject obj; #pragma warning restore 649方法3 '-nowarn:0649'を含むcsc.rspファイル
Assets直下に、「csc.rsp」というファイルを作成する
csc.rsp-nowarn:0649方法4 デフォルト値で初期化する
[SerializeField] GameObject obj = default;検索:
\[SerializeField\]([^=]+?);
置換:[SerializeField]$1 = default;
Visual Studio for Mac だと、
Command + Shift + F キー
[フォルダーを指定して検索]/[フォルダーを指定して置換]方法1だけは無いなと思うし、個人的には方法4が
この中ではお気に入りです。おわり
- 投稿日:2019-02-28T16:12:56+09:00
WPS(C#)にOpenGLを実装
本稿では,Visual Studio の WPS(C#) に OpenGL を実装するための手順を簡潔に述べます.
OpenTKのインストール
1.新規作成→プロジェクトで,テキトウに新規プロジェクトを作成する.
2.[ツール]→[NuGet パッケージマネージャ]→[パッケージマネージャコンソール]
(下にPM>と書いてある,コマンドを入力できるウィンドウが出てくる)
3.PM> Install-Package OpenTK.GLControl と入力.エンターキーを押す.終わり.
OpenGLで描画する領域をメインウィンドウに作る
[表示]→[ツールボックス]→[すべてのWPFコントロール]→[WindowsFormsHost]をメインウィンドウにドラッグ&ドロップ
MainWindow.xaml と MainWindow.xaml.cs を次のように編集
MainWindow.xaml<Window x:Class="(入力したプロジェクト名).MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:OpenTK="clr-namespace:OpenTK;assembly=OpenTK.GLControl" xmlns:local="clr-namespace:(入力したプロジェクト名)" mc:Ignorable="d" Title="(入力したプロジェクト名)" Height="350" Width="525"> <Grid> <WindowsFormsHost> <OpenTK:GLControl x:Name="glControl" Load="glControl_Load" Resize="glControl_Resize" Paint="glControl_Paint" /> </WindowsFormsHost> </Grid> </Window>MainWindow.xaml.csusing System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; namespace (入力したプロジェクト名) { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { /// <summary> /// コンストラクタ /// </summary> public MainWindow() { InitializeComponent(); } ///以下の関数を追記 private void glControl_Load(object sender, EventArgs e) { GL.ClearColor(Color4.Black); GL.Enable(EnableCap.DepthTest); } private void glControl_Resize(object sender, EventArgs e) { GL.Viewport(0, 0, glControl.Size.Width, glControl.Size.Height); GL.LoadIdentity(); // 平行投影 GL.Ortho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); } private void glControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Color4(Color4.White); GL.Begin(BeginMode.Polygon); GL.Vertex3(0.5, 0.5, 0.0); GL.Vertex3(-0.5, 0.5, 0.0); GL.Vertex3(-0.5, -0.5, 0.0); GL.Vertex3(0.5, -0.5, 0.0); GL.End(); glControl.SwapBuffers(); } } }[▷開始]を押す.
四角が描画されているはず.黒い謎の四角を消す
[デバッグ]→[オプション]→[全般]→(下の方にスクロール)→[xamlのUIデバッグツールを有効にする]のチェックを外す.
- 投稿日:2019-02-28T16:12:56+09:00
WPF(C#)にOpenGLを実装
おはよう,皆の諸君.
本稿では,Visual Studio の WPF(C#) に OpenGL を実装するための手順を簡潔に述べる.OpenTKのインストール
1.新規作成→プロジェクトで,テキトウに新規プロジェクトを作成する.
2.[ツール]→[NuGet パッケージマネージャ]→[パッケージマネージャコンソール]
(下にPM>と書いてある,コマンドを入力できるウィンドウが出てくる)
3.PM> Install-Package OpenTK.GLControl と入力.エンターキーを押す.終わり.
OpenGLで描画する領域をメインウィンドウに作る
[表示]→[ツールボックス]→[すべてのWPFコントロール]→[WindowsFormsHost]をメインウィンドウにドラッグ&ドロップ
MainWindow.xaml と MainWindow.xaml.cs を次のように編集
MainWindow.xaml<Window x:Class="(入力したプロジェクト名).MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:OpenTK="clr-namespace:OpenTK;assembly=OpenTK.GLControl" xmlns:local="clr-namespace:(入力したプロジェクト名)" mc:Ignorable="d" Title="(入力したプロジェクト名)" Height="350" Width="525"> <Grid> <WindowsFormsHost> <OpenTK:GLControl x:Name="glControl" Load="glControl_Load" Resize="glControl_Resize" Paint="glControl_Paint" /> </WindowsFormsHost> </Grid> </Window>MainWindow.xaml.csusing System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; namespace (入力したプロジェクト名) { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { /// <summary> /// コンストラクタ /// </summary> public MainWindow() { InitializeComponent(); } ///以下の関数を追記 private void glControl_Load(object sender, EventArgs e) { GL.ClearColor(Color4.Black); GL.Enable(EnableCap.DepthTest); } private void glControl_Resize(object sender, EventArgs e) { GL.Viewport(0, 0, glControl.Size.Width, glControl.Size.Height); GL.LoadIdentity(); // 平行投影 GL.Ortho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); } private void glControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Color4(Color4.White); GL.Begin(BeginMode.Polygon); GL.Vertex3(0.5, 0.5, 0.0); GL.Vertex3(-0.5, 0.5, 0.0); GL.Vertex3(-0.5, -0.5, 0.0); GL.Vertex3(0.5, -0.5, 0.0); GL.End(); glControl.SwapBuffers(); } } }[▷開始]を押す.
四角が描画されているはず.黒い謎の四角を消す
[デバッグ]→[オプション]→[全般]→(下の方にスクロール)→[xamlのUIデバッグツールを有効にする]のチェックを外す.
- 投稿日:2019-02-28T02:53:41+09:00
タスクバーにあるウィンドウサムネイル(?)に再生ボタンを付けてみる
経緯
オーディオプレイヤーを作ってみたのですが、ウィンドウを出してくるのも手間だったので実装しようと思いました。
まずはやってみる
ざっと、こんな感じです。
MainWindow.xaml<Window x:Class="Test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Test" mc:Ignorable="d" Title="Window" Height="485" Width="536" ResizeMode="NoResize"> <!-- ↓ここから重要! --> <Window.TaskbarItemInfo> <TaskbarItemInfo> <TaskbarItemInfo.ThumbButtonInfos> <ThumbButtonInfo x:Name="tb_rep_c" ImageSource="icon/1_rep.png" Description="1曲リピート" /> <ThumbButtonInfo x:Name="tb_plpa_c" ImageSource="icon/play.png" Description="再生/一時停止" Click="tb_play" /> <ThumbButtonInfo x:Name="tb_dm_c" ImageSource="icon/rep.png" Description="てすと" /> </TaskbarItemInfo.ThumbButtonInfos> </TaskbarItemInfo> </Window.TaskbarItemInfo> <!-- ↓後はご自由に --> <Grid>はい。これだけです。
スクリーンショット
まとめ
<Window.TaskbarItemInfo> <TaskbarItemInfo> <TaskbarItemInfo.ThumbButtonInfos> <ThumbButtonInfo x:Name="任意の名前" ImageSource="画像リンク" Description="メッセージ" /> <ThumbButtonInfo x:Name="任意の名前" ImageSource="画像リンク" Description="メッセージ" /> <ThumbButtonInfo x:Name="任意の名前" ImageSource="画像リンク" Description="てすと" /> </TaskbarItemInfo.ThumbButtonInfos> </TaskbarItemInfo> </Window.TaskbarItemInfo>後は自由に設定すればいいと思います。
参考にしたページ
使用した素材サイト
http://icooon-mono.com/
なんだかんだ言っても、このサイトのアイコンが使いやすいし、種類も多いのでWeb制作にも活用できそうですね。最後に
初めてQiitaで記事を書きました。
まだ慣れていないので、色々書いてみて慣れていきたいです。
- 投稿日:2019-02-28T01:51:38+09:00
(書きかけ) Streamという考え方 Windowsで育ったプログラマがsocketにfprintfできるようになるには?
学校等で体系的にコンピュータやプログラミングを学んだ人にとってはあたりまえの内容かもしれません。
物心ついたときからWindows PCで遊んでて、何かの間違いでプログラマになってしまった人向けの記事です。私がはじめて触ったコンピュータはWindowsでした。
プログラミングをしようと思えばVisualStudioが無料で手に入って、すぐに書いて実行できる環境でした。意外と多いんです Stream(ストリーム) を知らないプログラマは!!
1人に聞いて1人は知らなかったので100%でした。
Windowsは悪くないけど、これはWindowsのせいにしておきます。この記事では Stream をまったく知らないプログラマが、
一人でも減ることを願い、私の実体験をもとに書いています。
たぶん socket に fprintf できるようになれば、まったく知らないということはなくなると思います。Windowsプログラマのための Stream 診断
質問の答えに納得できればあなたも仲間です!
質問1
次に挙げるものは何をするためにありますか?
- C の printf関数
- C++ の std::coutオブジェクト
- C# の Console.Writeメソッド
質問1の答え
「標準出力」? はい違いま~~~す。
コンピュータっていうのはパソコンのことで、Game For Windows LIVEのことで Core 2 Duo のことなんだよ。
何?標準出力って?もう!具体的に言って!当然私たちの答えは「コンソール画面に文字列を表示するため」ですね。
当たり前です。VisualCなんとかで実行したら結果そうなるんだから。
次に覚えたのが scanf, std::cin , Console.Read とかでユーザーの「キーボード入力」を取得して、ちょっとしたゲームも作れちゃう!質問2
プログラミングをはじめたばかりの人が質問してきました。
「C#で文字列をテキストファイルに保存したいです。どうすればいいですか?」質問2の答え
当然、このコードが思い浮かびますね。
using(var sw = new System.IO.StreamWriter("ファイルパス")){ sw.Write("書き込みたいテキスト"); }当たり前です。インターネットで検索して出てくるサンプルコードがこれですから。
StreamWriterというのでテキストファイルが作れるらしいぞ!そろそろふざけるのはやめにします……。
上の答えは不正解です。
どうして?
最初にまずC#を使う理由を聞いてほしいの……そして Stream を教えてほしいの……
Stream について
StreamReader/StreamWriter は Stream を読み書きする
ディスク上のテキストファイルを読み書きするためにあるのではありません。
それは出来ることの一部であって、本質ではありません。たとえば冷蔵庫は納豆を冷やすことができますが、冷やせるものは納豆だけではありません。ものを冷やすことが本質です。
友達の家の冷蔵庫を見ると全然違いますよね。へぇ~冷蔵庫って豆腐も冷やせるんだぁ~って感じです。Stream にテキストを読み書きすることが StreamReader/StreamWriter の本質です。
Stream はなんやねん
Stream は次の2つのうち、少なくとも1つのことができます。
void write(byte[] buffer) // bufferを「誰か」にコピーします int read(byte[] buffer) // 「誰か」からbufferにコピーします 戻り値はコピーした長さです俺のbyte[]をもらってくれ!俺にbyte[]をよこせ!
そのどちらか、または両方をしたい欲求を叶えるのが Stream です。「誰か」に当たるものはたくさんあります。
- コンソール画面 【※要注意】
- ディスク上のファイル
- ネットワーク越しのコンピュータ
- ブラックホール
CanRead CanSeek CanTimeout CanWrite System.ConsolePal.WindowsConsoleStream
(VisualStudioで実行Console.OpenStandardOutput)× × × 〇 System.ConsolePal.WindowsConsoleStream
(VisualStudioで実行Console.OpenStandardInput)〇 × × × System.Net.Sockets.NetworkStream 〇 × 〇 〇 System.IO.FileStream
(File.Create)〇 〇 × 〇 System.IO.FileStream
(File.OpenRead)〇 〇 × × System.IO.FileStream
(File.OpenWrite)× 〇 × 〇 StreamReader/StreamWriter のコンストラクタはファイルパスだけじゃなくて、Streamを引数に取れます。
FileStream を渡せばファイルのテキスト読み書きができます。
NetworkStream を渡せばネットワークで通信ができる。
Stream が重要です。StreamReader/StreamWriterはStreamに対するテキスト読み書き便利クラスにすぎません!【※要注意】
質問1の答えについて関連します。
Console.Write は コンソール書く としか読めません。
Console.Write は 標準出力だ と説明されることが多いです。「標準出力 を具体的に言ったら コンソール書く」
と考えてしまうのが自然だと思うのですが、それこそWindowsで育った私たちの最も悲惨な出来事です。
その説明をします。Console.Write という名前のやっかいさ
Console.Write という名前は非常にやっかいです。
どう考えてもコンソール画面に直接書けといっているようにしか思えません。
これは重大な誤解です。自分の書いたプログラムがコンソール画面に命令する、
「プログラムが主、コンソール画面が従である」という錯覚に陥ります。でもそう考えるのが当然ではありませんか?
Windowsではまずダブルクリックを覚えます、ペイントやマインスイーパで遊びます。
誰かの作ったコンソールアプリケーションの実行ファイルをダブルクリックし、黒いコンソール画面が立ち上がるのを見るでしょう。そのうちプログラミングを始めます。VisualStudioのインストーラーをダブルクリックするだけで始められます。
さっそく Hello, World! するでしょう。デバッグ実行、またはデバッグ無し実行をしたとき、コンソール画面が出ます。
「コンソールアプリケーションはコンソール画面を出す機能を持っているんだ」Console.Write したとき
「アプリケーションがコンソール画面に命令して文字列を表示させているんだ」最初からカラーCRTか液晶ディスプレイがあって、マウス・キーボードでアイコンをダブルクリックし
眼に映るものだけからコンピュータを理解しようとする。そして何となくプログラムを書いたら動いてしまう。
私たちはこの罠にまんまとはまっていたのです。
人間はなんのために生まれて、何をして生きるのでしょうか。次に「パイプ」を使って標準入出力の意義を説明します。socketにfprintfに近づいています。
パイプ
サンプルプログラム F.exe, M.exe を作ります。
Fは対話的なアプリケーションですね、しゃべる内容は決まってますけど。
適切なことばを投げかければ返してくれます。using System; namespace F { class Program { static void Main(string[] args) { if (Console.ReadLine() == "あの、すいません、お願いが。") { Console.WriteLine("なにかな"); if (Console.ReadLine() == "見抜きさせてもらえないでしょうか・・・?") { Console.WriteLine("見抜き?"); if (Console.ReadLine() == "はい。") { Console.WriteLine("あ-"); if (Console.ReadLine() == "はい。") { Console.WriteLine("判った、そういうことか・・・"); if (Console.ReadLine() == "いいでしょうか?") { Console.WriteLine("うーん。"); Console.WriteLine("たまってる、ってやつなのかな?"); if (Console.ReadLine() == "はい") { Console.WriteLine("しょうがないにゃあ・・"); Console.WriteLine("いいよ。"); } } } } } } } } }Mはただしゃべるだけです。このアプリケーション、なんの意味もないのでは……?
using System; namespace M { class Program { static void Main(string[] args) { Console.WriteLine("あの、すいません、お願いが。"); Console.WriteLine("見抜きさせてもらえないでしょうか・・・?"); Console.WriteLine("はい。"); Console.WriteLine("はい。"); Console.WriteLine("いいでしょうか?"); Console.WriteLine("はい"); } } }コマンド プロンプトを起動してください。PowerShellユーザーはごめんなさい cmd 実行してください。
次のコマンドを実行してください。.\M.exe | .\F.exe結果
なにかな 見抜き? あ- 判った、そういうことか・・・ うーん。 たまってる、ってやつなのかな? しょうがないにゃあ・・ いいよ。M.exe を実行してるのに、「あの、すいません、お願いが。」が出てこないぞ?
F.exe はConsole.ReadLineでキーボード入力を待っているんじゃないのか?
これはいったいどういうことなのか?この場合、パイプ(|)によって、Mの標準出力とFの標準入力が繋がりました。
MのConsole.WriteLineはFがConsole.ReadLineで受け取っているのです。Console.ReadLineはコンソール画面にキーボードで入力された文字列を取得するメソッドではありません。
標準入力を読み取るためにあります。Mの標準出力はFの標準入力に流れるだけですから、コンソール画面には表示されません。
Fのあとにはプログラムがありませんから、Fの標準出力は行き場を失います。
行き場を失った出力がコンソール画面に来ます。コンソール画面は人間がプログラムと対話するための装置です。
プログラムが自発的にコンソール画面を出しているわけではありません。
入力をくれるやつは誰でもいいし、出力先が誰かなんて知らーーんということです。ユーザーがプログラムに指令を与え、報告を受け取ることを目的にコンソール画面を表示しているのです。
プログラムは従順に働くだけです。プログラムが人を動かすのではなく、人がプログラムを動かすのだということをパイプ(|)は教えてくれます。
パイプはUNIXのものだそうです。UNIXもUNIX系OSも触ったことないから知らんけど、UNIXで育っていたら違ったんだろうなと思います。
そういう意味でWindowsのせいと言ってしまいました。パイプを使えば Console.Write することの意味が分かります。
質問1 には「標準出力」と答えるしかない。なにそれ具体的には?っていわれたら
しょうがないにゃ~と言ってパイプを使って説明するのがいいんじゃないでしょうか。socketにfprintfする方法
もはや説明することはありません。これからはコンピュータを使うターンです。
socketにfprintfする方法?そんなことここに書く必要はないはずです。
調べなくても書けるようになります。google検索でサンプルコードを探す必要はなくなります。
Stream を理解していれば、ちょっとした基本的な関数だけで、呼吸するように書けるでしょう。私はgoogleがないとコードまったく書けません。
- 投稿日:2019-02-28T00:44:07+09:00
[Entity Framework] トランザクションのスコープ制御(EF6:Model/Database First)
EF6 では TransactionScope より DbContext.Database.BeginTransaction/UseTransaction メソッドが推奨されるようになりました。
トランザクションのスコープの明示的な制御をEF6の Model/Database First で行う例を以下に示します。※EF4.1~5 の場合は「トランザクションのスコープ制御(EF4.1~:Model/Database First)」をご覧ください。
※Code First 版も公開予定です。複数回の SaveChanges をまたぐトランザクション
// コンテキスト using (var context = new NorthwindEntities()) { // トランザクション開始 using (var transaction = context.Database.BeginTransaction()) { // 1つめの SaveChanges() var product = await context.Products.SingleAsync(p => p.ProductID == 1).ConfigureAwait(false); product.ProductName = "New Product Name"; await context.SaveChangesAsync().ConfigureAwait(false); // 2つめの SaveChanges() var employee = await context.Employees.SingleAsync(e => e.EmployeeID == 1).ConfigureAwait(false); employee.Title = "New Title"; await context.SaveChangesAsync().ConfigureAwait(false); // まとめてコミット transaction.Commit(); } }複数のコンテキストをまたぐトランザクション
// 接続準備 var workspace = NorthwindEntities.GetMetadataWorkspace(); using (var entityConnection1 = new EntityConnection("name=NorthwindEntities")) using (var sqlConnection = entityConnection1.StoreConnection) using (var entityConnection2 = new EntityConnection(workspace, sqlConnection, false)) { // あらかじめ接続を開いておく。 sqlConnection.Open(); // トランザクション開始 using (var transaction = sqlConnection.BeginTransaction()) { // 1つ目のコンテキストを操作する。 using (var context = new NorthwindEntities(entityConnection1, false)) { context.Database.UseTransaction(transaction); var product = await context.Products.SingleAsync(p => p.ProductID == 1).ConfigureAwait(false); product.ProductName = "New Product Name"; await context.SaveChangesAsync().ConfigureAwait(false); } // 別の EntityConnection を使って2つ目のコンテキストを操作する。 // ※同じ EntityConnection を使用すると InvalidOperationException が発生する。 using (var context = new NorthwindEntities(entityConnection2, false)) { context.Database.UseTransaction(transaction); var employee = await context.Employees.SingleAsync(e => e.EmployeeID == 1).ConfigureAwait(false); employee.Title = "New Title"; await context.SaveChangesAsync().ConfigureAwait(false); } // まとめてコミット transaction.Commit(); } } // コンテキストの部分クラス public partial class NorthwindEntities : DbContext { /// <summary> /// コンストラクタ。 /// </summary> /// <param name="existingConnection">コンテキストで使用する接続。</param> /// <param name="contextOwnsConnection">false を指定すると、コンテキストが Dispose されたときに接続を Dispose しない。</param> public NorthwindEntities(DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection) { } /// <summary> /// メタデータワークスペースを取得する。 /// </summary> /// <returns></returns> public static MetadataWorkspace GetMetadataWorkspace() { using (var context = new NorthwindEntities()) { var objectContext = ((IObjectContextAdapter)context).ObjectContext; return objectContext.MetadataWorkspace; } } }