20210505のC#に関する記事は11件です。

【Unity】敵の視野を疑似的に表示するギズモをつくった

よくある扇状の視野範囲をギズモで擬似的に表示できます。 検知の方法は偉大なるこちらの方の記事を参考にさせていただくと良いと思います。 : Unityで敵が主人公を検知する範囲を視覚内に限定する Enemy_DetectionAI.cs using UnityEngine; public class Enemy_DetectionAI : MonoBehaviour { [SerializeField] private float searchRadius; [SerializeField] private float searchAngle = 130f; private void OnDrawGizmos() { Vector3 trans = transform.position; Gizmos.DrawWireSphere(trans, searchRadius); Gizmos.color = Color.red; float x, y, r, a; r = searchRadius; a = transform.eulerAngles.y + 90; x = r * Mathf.Cos(a * Mathf.PI / 180f); y = r * Mathf.Sin(a * Mathf.PI / 180f); Vector3 forwardPos = new Vector3(-x, 0, y); Gizmos.DrawSphere(trans + forwardPos, .1f); Gizmos.color = Color.green; float xr, yr; xr = r * Mathf.Cos((a + searchAngle) * Mathf.PI / 180f); yr = r * Mathf.Sin((a + searchAngle) * Mathf.PI / 180f); Vector3 rightPos = new Vector3(-xr, 0, yr); Gizmos.DrawSphere(trans + rightPos, .1f); Gizmos.color = Color.blue; float xl, yl; xl = r * Mathf.Cos((a - searchAngle) * Mathf.PI / 180f); yl = r * Mathf.Sin((a - searchAngle) * Mathf.PI / 180f); Vector3 leftPos = new Vector3(-xl, 0, yl); Gizmos.DrawSphere(trans + leftPos, .1f); Gizmos.color = Color.red; Gizmos.DrawLine(trans, trans + forwardPos); Gizmos.DrawLine(trans, trans + rightPos); Gizmos.DrawLine(trans, trans + leftPos); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VisualStudioで初めてのexe(ファイルをリネームコピーするだけの簡単なやつ)

何を作りたいん? リネームコピーしたいファイルをexeに放り込んだら… 複製しつつ、現在時刻でリネームしてくれるやーつ。 ファイルをただコピペするだけだと… 『てんぷれーと - コピー.clip』になったりして、描いた絵を画像で出力する時とかにファイル名重複しちゃう。 別フォルダーに切り取りコピーすると作成日が上書きされて、管理する上でちょっとよろしくない。 ということがあるので、ファイル名に時間情報を埋め込みしたいです。 でも毎回手でファイル名変えるのは面倒くさい! VisualStudoインストール インストールまでの環境構築は ここ 。 プロジェクト作成 機能追加に合わせてウィンドウとかボタンとか設置したい場合を考えて、.NET Frameworkにします。 .NET FrameworkのバージョンはWindows10にデフォルトで用意されている4.5にしました。 他の人のPCでも安定して動いてもらうことが必要なら、最新すぎず、かといって古すぎない4.5が丁度いいかなって感じです。 個人で使う分にはバージョンはあまり気にしなくてもよいと思います。 引数チェックと放り込まれたファイルのパスを表示するところまで Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace FileCopy { class Program { static void Main(string[] args) { //引数バリデート if(args.Length != 1) { MessageBox.Show("コピー元のファイルを1つだけドラッグドロップして下さい。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } Console.WriteLine(args[0]); Console.WriteLine("↑exeにドラッグドロップしたファイルのパスです"); Console.ReadKey(); } } } コーディングする時に入力サポートしてくれるのが、VisualStudioの良いところです。 sing System.Windows.Forms; が抜けてると下記のようにサポートしてくれますです。 ダブルクリックすると自動で sing System.Windows.Forms; を入れてくれます。 ビルドでexe作成 ビルドするとexeが作成されます! 動かしてみよう exeをただ起動したり、2ファイル以上を放り込むと下記になります。 ちゃんと1ファイルだけ放り込むと下記になります。 完成版 Program.cs using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; namespace FileCopy { class Program { static void Main(string[] args) { //引数バリデート if (args.Length != 1) { MessageBox.Show("コピー元のファイルを1つだけドラッグドロップして下さい。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } string from_file_path_ = args[0]; //コピーするファイルパスを決定するぞい string to_file_path_; //元ファイルと同じフォルダーにコピーします to_file_path_ = Path.GetDirectoryName(from_file_path_) + @"\"; //ファイル名は現在時刻を元に決定 //2021年05月01日18時25分なら『20210501_1825』 to_file_path_ += DateTime.Now.ToString("yyyyMMdd_HHmm"); //拡張子は元ファイルから引き継ぎます to_file_path_ += Path.GetExtension(from_file_path_); //ファイル上書きになる場合はコピーせずに中断 if (File.Exists(to_file_path_)) { MessageBox.Show("ファイルの上書きが発生してしまうので処理を中断します。" + to_file_path_, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } //ファイルコピー実行! File.Copy(from_file_path_, to_file_path_); } } } 蛇足 メッセージボックス出す。 ファイルパスからフォルダーパスを取得。 ファイルパスから拡張子を取得。 フォーマットした現在時刻を取得。 ファイル存在チェック。 ファイルコピー。 とかよく使うのを盛り込んでみました。 try catchとか無いけどまあチュートリアルということで… バージョン Windows 10 Pro バージョン 20H2 OSビルド 19042.928 Visual Studio Community 2019 Version 16.9.4 .NET Framework 4.5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】①アイテム、アイテムボックスの実装【3D脱出ゲーム制作】

【内容】 ・オブジェクトのクリック判定実装 ・アイテムの実装 ・アイテムボックスの実装 【オブジェクトのクリック判定実装】 今回、クリックしたらアイテムボックスへ取得されるアイテムの実装を行う。 上記アイテムを実装するには、以下のものが必要 ①Event Trigger ・ヒエラルキーに追加したアイテム内にコンポーネントとして追加するもの ・追加したアイテムに対して、どのような動作をするか設定できる ②Physics Raycaster ・メインカメラにコンポーネントとして追加 ・アイテムに対してレーザー的なものを当てて、判定するもの ③Event System ・ヒエラルキーにUIから追加するもの ・とりあえず必要なものと認識 要調査 ④クリック判定のスクリプト(ヒエラルキーのアイテムにそれぞれD&Dで追加) ・今回は、クリックをしたらアイテムボックスへ格納されるというスクリプトを作成(以下) ・下記スクリプトにより、クリックしたら後述する「Item.cs」と「ItemBox.cs」を動作させる形で記載 PickUpObj.cs public class PickUpObj : MonoBehaviour { [SerializeField] Item.Type item; //クリックしたら消す public void OnClickObj() { /*クリックされたらItemBox.csが動作、       アイテムが消え、ItemBoxへ格納される動きになる*/ ItemBox.instance.SetItem(item); gameObject.SetActive(false); } } 【アイテムの実装】 ①アイテムの列挙スクリプト ・アイテム自体は、あくまでプレイヤーが手に入れた「情報」として保持すればよいので、   MonoBehaviourは不要 Item.cs public class Item { //列挙型:種類を列挙 public enum Type { Cube, Ball, } } 【アイテムボックスの実装】 ①パネル ・ヒエラルキーにUIから追加 ・コンポーネントHorizontal Layout Groupを追加(のちに追加する枠の位置関係をこれで調整) ②アイテムが表示される枠 ・ヒエラルキーにUIからイメージを追加 ③ItemBoxのスクリプト ItemBox.cs public class ItemBox : MonoBehaviour { //ItemBoxをOnClickObjで実行するためにstaticなインスタンス public static ItemBox instance; private void Awake() { if (instance == null) { instance = this; } } /*PickUpObjがクリックされたら、スロットにアイテムを入れる   public static ItemBox instanceでどこからでも引っ張れる PickUpObj.csへ持っていける*/ public void SetItem(Item.Type type) { Debug.Log(type); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#でURLを既定のブラウザで開く

ネットで検索すると出てくる動かない例 MainForm.cs /// <summary> /// 「Googleを開く」ボタン「Click」イベント /// </summary> /// <param name="sender">sender</param> /// <param name="e">EventArgs</param> private void btnOpenGoogle_Click(object sender, EventArgs e) { string url = "https://www.google.co.jp"; OpenUrl(url); } /// <summary> /// URLを既定のブラウザで開く /// </summary> /// <param name="url">URL</param> /// <returns>Process</returns> private Process OpenUrl(string url) { return Process.Start(url); } 下記のエラーが発生します。 「System.ComponentModel.Win32Exception: '指定されたファイルが見つかりません。'」 正しく動く例 MainForm.cs /// <summary> /// URLを既定のブラウザで開く /// </summary> /// <param name="url">URL</param> /// <returns>Process</returns> private Process OpenUrl(string url) { ProcessStartInfo pi = new ProcessStartInfo() { FileName = url, UseShellExecute = true, }; return Process.Start(pi); } ポイント Process.Startメソッドの引数にURLを直接渡すのではなく、ProcessStartInfoのインスタンスを渡す。 その際、ProcessStartInfoのUseShellExecuteプロパティにtrueを設定する。 感想 ツールを作成していた際などに毎回はまるので記事にしました。 宣伝 メイドカフェマップ for Android 元メイドさんと一緒に作った~メイドカフェマップ for Androidを公開しています。 インストールして頂いて評価やコメントを頂けると喜びます メイドカフェマップ アプリ内からツイートできる機能がありますので、そちらをして頂くとさらに喜びます その他にもWikipediaの寄付依頼をブロックするアドオンを公開しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#を使ってAndoridでHello World する

今回はC#を用いて、Android アプリを作成していきます。 (似たようなことが書かれた記事が既にあるため、今更感はありますが…) Androidアプリで Hello Worldするだけならプロジェクトを作ってそのまま実行するだけで済むため、今回はもう少し動きを付けようかと思います。 開発環境 window10 Visual Studio android7端末(android10端末を使用したら実行できなかった…) フレームワークのインストール 一から説明するとなるとVisual Studioのインストールからということになりますが、インストールの手順は以前に書いた記事があるため、今回は省きます。 androidアプリ用プロジェクトを作成するとなると、それ用のフレームワークが必要になるため、Visual Studioインストーラーを起動してダウンロードします。 「ツール」「ツールと機能を取得」を押下して、Visual Studioインストーラーを起動し、「.NETによるモバイル開発」をダウンロード、インストールします。 プロジェクトの作成 フレームワークのインストールが終わったら、プロジェクトを作成します。 今回作成するのは「Androidアプリ(Xamarin)」です。 androidのバージョンに関して、実機を使用する場合は、実機に合わせたバージョンを使用しましょう。(バージョンの縛りがいろいろあったかと思うので、うまくデバッグできない場合はいろいろ試してみると良いかと思います。) プロジェクトを作成したら、その時点でHello Worldができる状態になっています。 ファイル構成に関しては、「/Resource/layout/」下に .xmlファイルがあり、デザインを見ながら編集することができます。 アプリに動きを付けたい場合は「MainActivity.cs」ファイルを編集するか、別の.csファイルを作成してロジックを作成します。 デバッグ時の設定 デバッグの方法に関しては、エミュレータを使用する方法と実機を使用する方法があり、今回は実機を使用します。 エミュレータを使用する場合は、デバッグ時に上部の「Android Emulator」を選ぶと、PC内にandroidのエミュレータを作成することができます。 実機でデバッグする場合には「開発者オプション」と「USBデバッグ」を有効にする必要があります。 調べるとすぐわかるため、細かく書きませんが、設定画面のビルド番号を複数回押下すると開発者オプションが変更可能になり、開発者オプション内のUSBデバッグをONにすると準備が整います。 個人的には、エミュレータを使用しようとすると1GBぐらいダウンロードする必要があり、動きも重い印象があるため、実機でデバッグする方が楽かと思います。 動作の設定 今回はコーディングでボタン押下時のイベントを追加しようかと思います。 既存の状態ではボタンがないため、「content_main.xml」のデザイン画面を開き、下記の様にRelativeLayout内にボタンを作成します。 content_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> 作成したボタンに動きを付けたいため、「MainActivity.cs」に書き込みを行います。 OnCreate()がアプリ起動時に走るため、その中にボタンのClickイベントを設定します。 表示画面上のボタンオブジェクトを取得するにはFindViewById()でボタンのidを指定してオブジェクトを取得し、.Clickイベントを設定します。 コードは下記の通りです。 MainActivity.cs public class MainActivity : AppCompatActivity { /// <summary>起動時のイベント </summary> protected override void OnCreate(Bundle savedInstanceState) { //ボタンを取得し、イベントを設定する(ボタンのidとして設定されているbutton1を指定) Button button1 = FindViewById<Button>(Resource.Id.button1); button1.Click += Button1_Click; button1.Text = "ボタン"; } /// <summary>"ボタン"クリック時</summary> private void Button1_Click(object sender, EventArgs e) { //通知出力 Toast.MakeText(Application.Context, "click " + DateTime.Now.ToString(), ToastLength.Long).Show(); } } 実行 デバッグを実行すると、USBで接続しているandroid端末で今回作成したアプリが立ち上がり、動作を確認できます。実行結果は下記の画像の様になります。 下記はボタン押下時です。 因みに、スマートフォン上のアプリのロゴは下記の様な表示になります。 おわりに 今回は自身で初めてスマートフォンアプリを作成しました。 androidアプリの作成ということならandroid studioでjavaを使えば良いという話になるのですが、筆者が所属している会社では、支給されているデバイスはiosで、社内でもandroidは使用されていないため、今回学習した内容を生かすということを考えるとjavaだと都合が悪いと思い、iosアプリもandroidアプリも作成できるC#について学び、記事にしました。 (筆者自身はandroid端末を四台も持っていますが、ios端末は会社支給の一台しか持っていません(笑)) Visual Studioを使用すればiosアプリも作成できるはずなので、いずれiosでも試そうかと思います。 また、せっかくスマートフォンアプリの作成方法について学んだので、今度はもっと複雑なロジックを作ってみようかと思います。 あと、android10 の端末でデバッグ実行しようとしたら、エラーが出て実行できなかったので、いずれこの問題は解決しようかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自作クラスをListに複数入れて自在に操作しよう (C#)

背景 とあるコードを書いていた際に複数の型変数を一纏めにして扱いたい、加えて、いくつのデータを扱うのか不確定なため、可変長に扱いたい、というシチュエーションに出会い、こんなかんじだろー、で書いていたら全然的外れでとても時間を溶かしたので、自身のまとめとしても書き留めた。 あと,classもカプセル化しており、カプセル化したclassからのデータの取り扱いについても書き留めている。 環境 Mac (macOS Big Sur 11.2.1) Visual studio for mac (8.9.8) 使用言語 C# (Ver 9.0) 対象者 自分で用意したclassをlistに入れて扱いたい人 classをカプセル化したい人 メリット 自在にlist内のプロパティを取り扱える 書き方(言語化) *前提条件: 今回は6人の生徒を160cmを基準にグループ分けして、体重が一番重たい人を探したいと思います。 1. クラスを用意する 2. クラスをカプセル化する 3. 与えられた人数分ループを回す 3.1. ループ内でStudentクラスのインスタンスを毎回生成する 3.2 身長を与えられるので、身長を基準値によってlistに振り分ける 4 体重と体重がconsoleから与えらるので、ListのFindメソッドで対応しているIdの人を見つけて割り当てる 5 連想配列を背が高いグループと背が低いグループで両方に用意する 6 ListのFindメソッドで一番体重が重たい人を見つける(Sortでもよかったんですが、今回はしていません。また次回) 7 console.logで出力用に各値を変数に格納する 8 イザ、出力!!!!! Program.cs using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel.Design; namespace class_capule { class Student { //クラス自体の変数は下記の4行で定義 private int id; private string name; private int weight; private int height; // 下記の4行でStudentクラスの変数に直接アクセスできない様にカプセル化 public int Id { get; set; } public string Name { get; set; } public int Weight { get; set; } public int Height { get; set; } } class Program { static void Main() { //渡されるデータが幾つなのかはコンソールから渡す var N = int.Parse(Console.ReadLine()); int[] represent = new int[2]; const int standard = 160; //今回は身長によって2つのクラスに分ける var listH = new List<Student>(); var listL = new List<Student>(); //渡されたN回数分をループで回す for (int i = 1; i <= N; i++) { //Ver. 9.0からStudentクラスをインスタンス化(newだけで以下のクラス名は省略できる) Student src = new(); //コンソールからまず身長が渡されるとする int studentTall = int.Parse(Console.ReadLine()); string hoge = studentTall >= standard ? "High": "Low"; //Student型の変数srcに新規内容として格納(iが出席番号) src.Id = i; src.Height = studentTall; //基準の身長で入れるリストを分ける if (hoge == "High") { listH.Add(src); } else { listL.Add(src); } } //体重がコンソールから入力される(半角スペース区切りで1行の入力) int[] weightArr = Array.ConvertAll(Console.ReadLine().Trim().Split(' '), int.Parse); //名前がコンソールから入力される(半角スペース区切りで1行の入力) string[] nameArr = Console.ReadLine().Trim().Split(' '); for (int i = 1; i <= N; i++) { //身長によって入れているListが異なるので条件分岐にて振り分ける //その際に出席番号としてidをclassで持たせているので //そのidがListに入っているStudentクラスにあるかどうかを判定する //listのメソッドのFindメソッドを利用して、 //その中でラムダ式にて該当idを探す ←これ大切です‼️‼️‼️‼️‼️ if (listH.Find(x => x.Id == i) != null) { //コンソールから取得した体重と名前をFIndメソッドで見つけたidのクラスに付与する listH.Find(x => x.Id == i).Weight = weightArr[i]; listH.Find(x => x.Id == i).Name = nameArr[i]; } else if (listL.Find(x => x.Id == i) != null) { //コンソールから取得した体重と名前をFIndメソッドで見つけたidのクラスに付与する listL.Find(x => x.Id == i).Weight = weightArr[i]; listL.Find(x => x.Id == i).Name = nameArr[i]; } } Dictionary<string, int> dictHighList = new() { { "studentId", 0 }, { "heaviest", 0 }, }; Dictionary<string, int> dictLowList = new() { { "studentId", 0 }, { "heaviest", 0 }, }; // 各グループの一番体重の重い人を調べる for (int i = 1; i <= N; i++) { // 各グループに存在するID で一番体重が重たい if (listH.Find(x => x.Id == i) != null && listH.Find(x => x.Id == i).Weight > dictHighList["heaviest"]) { dictHighList["studentId"] = listH.Find(x => x.Id == i).Id; dictHighList["heaviest"] = listH.Find(x => x.Id == i).Weight; } else if (listL.Find(x => x.Id == i) != null && listL.Find(x => x.Id == i).Weight > dictLowList["heaviest"]) { dictLowList["studentId"] = listL.Find(x => x.Id == i).Id; dictLowList["heaviest"] = listL.Find(x => x.Id == i).Weight; } } int highHeight = listH.Find(x => x.Id == dictHighList["studentId"]).Height; int highWeight = listH.Find(x => x.Id == dictHighList["studentId"]).Weight; string highName = listH.Find(x => x.Id == dictHighList["studentId"]).Name; int lowHeight = listL.Find(x => x.Id == dictLowList["studentId"]).Height; int lowWeight = listL.Find(x => x.Id == dictLowList["studentId"]).Weight; string lowName = listL.Find(x => x.Id == dictLowList["studentId"]).Name; Console.WriteLine("下記が出力結果です"); Console.WriteLine($"{standard}以上の一番重たい人:{highName} 身長:{highHeight}cm 体重:{highWeight}kg"); Console.WriteLine($"{standard}以下の一番重たい人:{lowName} 身長:{lowHeight}cm 体重:{lowWeight}kg"); Console.ReadKey(); } } } <入力結果> terminal 6 150 165 162 158 170 147 55 70 60 78 65 81 Keiko Toru Yumiko Sjinji Yuki Satoshi 下記が出力結果です 160以上の一番重たい人:Satoshi 身長:170cm 体重:81kg 160以下の一番重たい人:Toru 身長:151cm 体重:70kg まとめ なんだか、自作のクラスをlistに入れていって探すだけ、というお題目だったんですが、気づいたらとても長いコードになってしまいました。(反省) もう少し切り分けて書いてもよかったかもしれません。 また気づいた点や覚書きが必要そうなことがあれば書きたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C# From】タイトルバーの無いフォームに自作タイトルバーを作る

環境 C# - Winodws Form Application (Visual Studio 2017 , .NET Framework 4.5) タイトルバーの無いフォームに自作タイトルバーを作る こういうオサレアプリを作るとします。 これが果たしておしゃれかどうかはおいといて、こういったタイトルバーのないフォームを作った場合、最大化・最小化は不要、終了はthis.Close();でいいとして、地味に面倒くさいのが 掴んで移動ができないこと です。 実装方法はいろいろあるとは思いますが、ここでは、Win32APIを使って、上側にあるピクチャボックスをタイトルバーとして認識させたいと思います。 ‥‥といっても、この手法自体は.NETの前のVisualBasicの時代から通用するものであって、軽く調べても、dobon.netさんを始め、いくらでも記載が見つかります。 この記事では、昔調べた手法が今も使えるという意味合いで記しておきます。 (というか.NET内でやる方法がご存知でしたら教えてください) 実装方針 使うWin32APIは、ReleaseCaptureとSendMessageです。 まずピクチャボックスへのマウス押下に対して、押下状態を解除(ReleaseCapture)し、マウスの入力をキャンセルします。 次に、ウインドウメッセージを送信(SendMessage)し、タイトルバーにマウスが押されたことを通知します。 これにより、「ピクチャボックスを掴む」=「タイトルバーを掴んだ扱い」として扱われます。 サンプル ピクチャボックスpicHeaderを貼りつけ、MouseDownイベントを作ります。 //using System.Runtime.InteropServices; private void picHeader_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { Win32Api.MouseLeftDownOnFormTitleBar(this); } } private static class Win32Api { #region SendMessage [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); #endregion #region ReleaseCapture [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool ReleaseCapture(); #endregion #region MouseLeftDownOnFormTitleBar /// <summary> /// タイトルバーを左クリックする /// </summary> /// <param name="frm">対象のフォーム</param> public static void MouseLeftDownOnFormTitleBar(System.Windows.Forms.Form frm) { ReleaseCapture(); SendMessage(frm.Handle, (int)0x00A1, new IntPtr(0x0002), IntPtr.Zero); } #endregion } SendMessageの引数である0x00A1と0x0002が、それぞれWM_NCLBUTTONDOWN=マウス左ボタン押下、HT_CAPTION =タイトルバー部分を意味しています。定数にしておくのが正しいとは思います。 余談 趣味でやってると、オリジナルの見た目のフォームを作りたい時ってあると思います。そういう時に任意のコントロールをタイトルバーに出来ると表現の幅が広がります。 これ自体は結構有用なコードだと思うのですが、最近じゃFrameworkの方も優秀で、あえてWin32APIを使うべきケースも少ないもので、そんなことでAPIをわざわざ勉強するのもなぁって感じですね。 実際、SendMessageの他の引数とかを使ってやりたいことなどはなかなか思いつかないし、そんなわけでこのロジックそのものをタイトルバー処理専用と割り切って私は使っています。 独学のため正確でない可能性があります。 (っ・x・)っ きゅ 参考 https://dobon.net/vb/dotnet/form/preventmaximize.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C# Form】マルチモニタ環境での座標の扱い

ちょうどまとまっている資料が見つからなかったので、まとめてみました。 環境 C# - Winodws Form Application (Visual Studio 2017 , .NET Framework 4.5) マルチモニタ環境での座標の扱い 表示領域と移動について 自作のフォームアプリをマルチモニタ対応するために色々弄ってみました。 こういうマルチモニタ環境があるとします。 小さいディスプレイをサブモニタとして使用している私の環境です。 この状態では、表示領域はこうなってます。 プライマリディスプレイの左上が(0,0)であり、セカンダリディスプレイはその左側にあるのでX座標はマイナスになります。 つまり、フォームのLeft,Topプロパティに雑に座標を指定すれば自動的にその位置に応じたディスプレイ上に表示させることができます。 結論として、フォームの移動や配置についてのケアは殆ど要らないと言えるでしょう。 逆に言うと、「座標がマイナスならディスプレイからはみ出ている」というロジックの方を改めなければなりません。 ディスプレイ領域の取得 Formはどこのディスプレイ上にあるの? System.Windows.Forms.Screen.FromControlメソッドにフォームを引数で渡せばScreenオブジェクトを取ることができます。 あとはBoundsでディスプレイ領域の矩形が、WorkingAreaでタスクバーを除いた領域が取れます。 System.Drawing.Rectangle rect = System.Windows.Forms.Screen.FromControl(this).Bounds; 結果(矩形)だけが欲しいのなら、Screen.GetBoundsでも良いようです。 System.Drawing.Rectangle rect = System.Windows.Forms.Screen.GetBounds(this); 2つのモニタを跨った場合は、最大部分を保持するディスプレイの方が返されます。中央の座標を含む方ですね。 マウスがあるディスプレイは? 今の“アクティブ”なディスプレイは、マウスのある位置ではないかと思います。マウスのあるディスプレイにフォームを移動させたいこともあるでしょう。 ScreenにFromPointがあります。 System.Drawing.Rectangle rect = System.Windows.Forms.Screen.FromPoint(Cursor.Position).Bounds; また、GetBoundsの引数にpoint型も渡せます。 System.Drawing.Rectangle rect = System.Windows.Forms.Screen.GetBounds(Cursor.Position); ディスプレイの中に表示されているかどうかのチェック 基本的にはScreen.FromControlで行けると思いますが、ディスプレイの方から探す方法も抑えておくと良いかも。 public static bool IsOnScreen(System.Windows.Forms.Form frm) { foreach (System.Windows.Forms.Screen s in System.Windows.Forms.Screen.AllScreens) { if (s.WorkingArea.Contains(new System.Drawing.Rectangle(frm.Location, frm.Size))) { return true; } } return false; } これははみ出てない事に対するチェック。ちなみにこれ、モニタ2つを跨っている場合ははみ出てる判定になります。 ディスプレイの中にフォームを収めるには あとはまぁ、適当にやればよろしいかと。 if (this.WindowState == System.Windows.Forms.FormWindowState.Normal) { var rect = System.Windows.Forms.Screen.GetBounds(this); this.Left = (rect.Left + rect.Width > this.Left + this.Width) ? this.Left : rect.Left + rect.Width - this.Width; this.Left = (rect.Left < this.Left) ? this.Left : rect.Left; this.Top = (rect.Top + rect.Height > this.Top + this.Height) ? this.Top : rect.Top + rect.Height - this.Height; this.Top = (rect.Top < this.Top) ? this.Top : rect.Top; } これを入れておけば、「モニタ外してからアプリを起動したら行方不明に!」という事態は避けられると思います。 独学のため正確でない可能性があります。 (っ・x・)っ きゅ 参考 https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.screen.getbounds?view=net-5.0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

入力値の取得

はじめに 競技プログラミングで、頻出する入力値の取得に関するスニペットです。 競技プログラムの楽しいところは、アルゴリズムを使って解くところですので、入力値の処理のところはスニペットで楽してしまいましょう。 1つの入力値(INT型) サンプル using System; using System.Linq; namespace sample { class Program { static void Main(string[] args) { int input = int.Parse(Console.ReadLine()); } } } スペース区切りの入力値(INT型) サンプル using System; using System.Linq; namespace sample { class Program { static void Main(string[] args) { int[] input = Console.ReadLine().Split(' ').Select(int.Parse).ToArray(); int S = input[0]; int P = input[1]; } } } スペース区切りの入力値(String型) サンプル using System; using System.Linq; namespace sample { class Program { static void Main(string[] args) { string[] input = Console.ReadLine().Split(' '); } } } 入力値に含まれる特定の文字数を数える サンプル 1の数を数えます。 using System; using System.Linq; namespace sample { class Program { static void Main(string[] args) { int count = Console.ReadLine().Where(n => n.Equals('1')).Count(); } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C++の資源をDLLを介してC#から使う(メモ)

概要 C++ 側と c# 側とで可変長文字列をやり取りする際のメモリの扱いが異なり、癖があるのでメモ。 C++ 側に文字列を引数で渡し、【戻り値】で受け取る方法 DllTest.cs(C#) [TestClass] public class DllTest { [DllImport("MyDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.LPStr)] static extern string DllTestMethod(string hoge); [TestMethod] public void CanPassAndReceiveStringToDll() { string output = DllTestMethod("ああ"); Assert.AreEqual("ああてすと", output); } } API.h(C++) #pragma once #define DllExport __declspec( dllexport ) extern "C" { DllExport int __stdcall DllTestMethod(char* hoge); } API.cpp(C++) #include "stdafx.h" #include <iostream> #include "API.h" char* CStringToChar(LPCWSTR pStr) { // 変換後に必要なメモリサイズ取得 int iByte = WideCharToMultiByte(CP_ACP, 0, pStr, -1, NULL, 0, NULL, NULL); // 文字列格納領域確保 char* pszReturn = (char*)::CoTaskMemAlloc(iByte); // 変換 WideCharToMultiByte(CP_ACP, 0, pStr, -1, pszReturn, iByte, NULL, NULL); return pszReturn; } char* CStringToChar(CString str) { // 変換元取得 LPCWSTR pStr = str.GetString(); return CStringToChar(pStr); } char* __stdcall DllTestMethod(char* hoge) { std::cout << "Call C DLL!! - [" << hoge << "]" << std::endl; return CStringToChar(CString(hoge) + _T("てすと")); } C++ 側に文字列を引数で渡し、【引数】で受け取る方法 DllTest.cs(C#) [TestClass] public class DllTest { [DllImport("MyDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] static extern int DllTestMethod(string hoge, [MarshalAs(UnmanagedType.LPStr), Out] out string output); [TestMethod] public void CanPassAndReceiveStringViaDll() { int res = CanPassAndReceiveStringToDll("ああ", out string output); Assert.AreEqual(res, 123); Assert.AreEqual(output, "ああてすと"); } } API.h(C++) #pragma once #define DllExport __declspec( dllexport ) extern "C" { DllExport int __stdcall DllTestMethod(char* hoge, const char** output); } API.cpp(C++) #include "stdafx.h" #include <iostream> #include "API.h" char* CStringToChar(LPCWSTR pStr) { // 変換後に必要なメモリサイズ取得 int iByte = WideCharToMultiByte(CP_ACP, 0, pStr, -1, NULL, 0, NULL, NULL); // 文字列格納領域確保 char* pszReturn = (char*)::CoTaskMemAlloc(iByte); // 変換 WideCharToMultiByte(CP_ACP, 0, pStr, -1, pszReturn, iByte, NULL, NULL); return pszReturn; } char* CStringToChar(CString str) { // 変換元取得 LPCWSTR pStr = str.GetString(); return CStringToChar(pStr); } int __stdcall DllTestMethod(char* hoge, const char** output) { std::cout << "Call C DLL!! - [" << hoge << "]" << std::endl; *output = CStringToChar(CString(hoge) + _T("てすと")); return 123; }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】DeepLで文書翻訳してみた

1. はじめに ノベルワークスR&Dチームのユウガです。現在は, バーチャルオフィスの開発を行っています。 バーチャルオフィスの機能の1つとして, DeepL文書翻訳をやってみたので紹介します。 主に新しい技術のリサーチをしているため, 最新のドキュメントはだいたい英語だったりします。「このドキュメント日本語にして読みたいなぁ」と思った時に, すぐに翻訳できると便利です。また, 外国人とチャットでやりとりしていて, ファイルを共有された時に, すぐに母国語に翻訳できると便利ですよね。 DeepLを使えば日本語で作った製品案内とかを瞬時に翻訳できるので, 海外への営業・販売など比較的容易に展開できるようになるかもしれませんね。 所見としては, 文書翻訳はベータ版ではありますが, かなり精度も良くて, 全然使えると思いました。また, 画像が添付されてる場合も, レイアウトが崩れる事なく, 翻訳できた印象です。 環境 ・windows10 ・Unity2020.3.2f.1 2. DeepL文書翻訳 翻訳できるファイル形式は, Word(.docx), PowerPoint(.pptx), Text(.txt), HTML(.html) です。 ファイルサイズ&文字数の制限は, Word(.docx), PowerPoint(.pptx)が, 10MBまたは100万文字。Text(.txt)が, 1MBまたは100万文字。 HTML(.html)が, 5MBまたは100万文字です。 DeepL API プランの場合は, 好きなだけ文書翻訳できるそうです。 このプランは従量課金制で, 1か月の基本料金630円に加え, 1文字あたり0.0025円として料金が請求されます。 注意点としては, 文字数が少ない場合も, 1ファイルにつき最低5万文字分が請求されます。5万文字以上の場合は, 文字数分の料金が請求されます。 なので, 1ファイルにつき最低でも125円かかります. テストしてる時, これに気が付かなくて焦りました(-_-;) 3. 実装 DeeplFileTranslator.cs using UnityEngine; using UnityEngine.Networking; using System; using System.IO; using System.Collections.Generic; using System.Threading.Tasks; public static class DeeplFileTranslator { [Serializable] public class ResponseForUpload { public string document_id; public string document_key; } [Serializable] public class ResponseForCheck { public string document_id; public string status; public string seconds_remaining; public string billed_characters; } private static readonly string auth_key = "XXX"; public static async Task<string> Upload(string path, string filename, string target_lang, string content_type) { // pathにあるファイルを読み込む byte[] rawdata = File.ReadAllBytes(path); List<IMultipartFormSection> requestData = new List<IMultipartFormSection>(); requestData.Add(new MultipartFormDataSection("auth_key",auth_key)); requestData.Add(new MultipartFormDataSection("target_lang",target_lang)); requestData.Add(new MultipartFormFileSection("file", rawdata, filename, content_type)); UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document", requestData); await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError(request.error); return "error"; } return request.downloadHandler.text; } public static async Task<string> CheckStatus(string document_id, string document_key) { List<IMultipartFormSection> requestData = new List<IMultipartFormSection>(); requestData.Add(new MultipartFormDataSection("auth_key",auth_key)); requestData.Add(new MultipartFormDataSection("document_key",document_key)); UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document/"+document_id, requestData); await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError(request.error); return "error"; } return request.downloadHandler.text; } public static async Task<string> Download(string document_id, string document_key, string output_path) { List<IMultipartFormSection> requestData = new List<IMultipartFormSection>(); requestData.Add(new MultipartFormDataSection("auth_key",auth_key)); requestData.Add(new MultipartFormDataSection("document_key",document_key)); UnityWebRequest request = UnityWebRequest.Post("https://api.deepl.com/v2/document/"+document_id+"/result", requestData); await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.ConnectionError) { Debug.LogError(request.error); return "error"; } // output_pathにデータを書き込む byte[] rawdata = request.downloadHandler.data; File.WriteAllBytes(output_path, rawdata); return "success"; } } このコードは見てもらえば分かると思いますが, UnityWebRequestを使って, DeepLとやりとりしてます。 UnityWebRequestでasync/awaitを使いたかったので, こちらのコードも使いました。 auth_keyの所は, ダッシュボードにある Authentication Key を入力してください。 FileTranlateManager.cs using UnityEngine; using UnityEngine.UI; using System; using System.IO; using System.Threading.Tasks; public class FileTranslateManager : MonoBehaviour { [SerializeField] Button fileTranslateButton; private string document_id; private string document_key; void Start() { fileTranslateButton.onClick.AddListener(async () => { string path = @"C:\Users\username\Documetns\test.docx"; string target_lang = "EN-US"; await DeeplFileTranslate(path, target_lang); }); } private async Task DeeplFileTranslate(string path, string target_lang) { string ext = Path.GetExtension(path); string filenameWithoutExt = Path.GetFileNameWithoutExtension(path); string output_path = "C:\\Users\\" + Environment.UserName + "\\Downloads\\" + filenameWithoutExt + "_" + target_lang + ext; string jsonString = null; int millisecond = null; // Deeplにアップロード jsonString = await DeeplFileTranslator.Upload(path, filenameWithoutExt+ext, target_lang, GetContentType(ext)); if (jsonString == "error") return; var responsForUpload = JsonUtility.FromJson<DeeplFileTranslator.ResponseForUpload>(jsonString); document_id = responsForUpload.document_id; document_key = responsForUpload.document_key; // 翻訳状況をチェック while (true) { jsonString = await DeeplFileTranslator.CheckStatus(document_id, document_key); if (jsonString == "error") return; // 翻訳完了なら, ループを抜ける var responseForCheck = JsonUtility.FromJson<DeeplFileTranslator.ResponseForCheck>(jsonString); if (responseForCheck.status == "done") break; if (!String.IsNullOrEmpty(responseForCheck.seconds_remaining)) { millisecond = int.Parse(responseForCheck.seconds_remaining) * 1000; Debug.Log($"{millisecond / 1000}s remaining."); } else { millisecond = 1000; } await Task.Delay(millisecond); // ミリ秒待つ } // Deeplからダウンロード jsonString = await DeeplFileTranslator.Download(document_id, document_key, output_path); if (jsonString == "error") return; // 翻訳完了を知らせるために, ダウンロードフォルダを開く Application.OpenURL("file:C:\\Users\\" + Environment.UserName + "\\Downloads"); document_id = null; document_key = null; return; } private static string GetContentType(string ext) { switch(ext) { case ".docx": return "application/vnd.openxmlformats-officedocument.wordprocessingm"; case ".pptx": return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; case ".txt": return "text/plain"; case ".html": return "text/html"; default: return null; } } } このコードは, DeeplFileTranslatorのメソッドを呼び出して, 処理を実行してくれます。 翻訳状況をチェックの所で, 翻訳完了したらループを抜けるようにしてますが, あんまり良くないなぁと思ってるので, こう書いたら良いよって方がいたら教えてください!! 4. おわりに 個人的には, PDFも翻訳してくれたらありがたいのですが, DeeplAPIでは無理です。 なので, PDFを翻訳したければ, CloudConvertAPIを使って, word(.docx)に変換してから, DeepL翻訳すると良いかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む