- 投稿日:2021-06-21T22:47:35+09:00
ランタイム(exe)でVRM(MToon)をHDRP(Lit)表示
概要 ランタイム(exe)でVRM(MToon)をHDRP(Lit)表示します。 環境 Unity 2020.3.11f1 UniVRM 0.66.0 実装 using System.IO; using System.Collections.Generic; using UnityEngine; using VRM; public class MToonToLit : MonoBehaviour { [SerializeField] string FilePath; [SerializeField] Material Opaque; [SerializeField] Material Transparent; void Start() { // VRM読み込み var bytes = File.ReadAllBytes(FilePath); var context = new VRMImporterContext(); context.ParseGlb(bytes); context.Load(); context.ShowMeshes(); // MToonをLitに置き換え var renderers = context.Root.GetComponentsInChildren<Renderer>(); for (int i = 0; i < renderers.Length; i++) { var materials = new List<Material>(renderers[i].materials); for (int j = 0; j < renderers[i].materials.Length; j++) { if (renderers[i].materials[j].GetFloat("_BlendMode") != 2) { materials[j] = new Material(Opaque); } else { materials[j] = new Material(Transparent); } // 通常と透過を使い分けて色とテクスチャを反映 materials[j].color = renderers[i].materials[j].color; materials[j].mainTexture = renderers[i].materials[j].mainTexture; } renderers[i].materials = materials.ToArray(); } } } 補足 HDRPで作成したプロジェクトのシーンのどこかへ前述のスクリプトを張り付けて、FilePathに読み込みたいVRMファイルのパス、Opaqueに新規作成したHDRP/Litのマテリアルを設定、Transparentには新規作成したHDRP/LitのマテリアルのSurface TypeをTransparentにしてから設定すれば動くと思います。 既知問題として、このままだとUnlitが来た時に失敗して真っ黒になるので、_BlendModeの判定辺りをちゃんとやる必要があります。どうやれば良いかは調べれてません。 参考 左がHDRP、右がMToonです。 モデル:リンツちゃん改変 https://booth.pm/ja/items/1255264 サンプルアプリ https://120byte.booth.pm/items/3062310
- 投稿日:2021-06-21T22:29:06+09:00
PathGeometryを文字列から復元する方法
M51.806640625,40.0390625C47.8678359985352,40.0390625 44.8323554992676,41.3981132507324 42.7001953125,44.1162109375 40.5680313110352,46.8343124389648 39.501953125,50.68359375 39.501953125,55.6640625 39.501953125,65.5924491882324 43.017578125,70.556640625 50.048828125,70.556640625 53.1412734985352,70.556640625 56.1848945617676,69.384765625 ... PathGeometryを使っていると上記のような文字列に遭遇することがあるかと思います。 上記の形式の文字列はPathGeometryオブジェクトに変換することが可能です。 やり方は次の通りです。 using System.Windows.Media; var str = "M51.806640625,40.0390625C47.8678359985352,40.0390625 44.8323554992676,41.3981132507324 42.7001953125,44.1162109375..."; var geometry = Geometry.Parse(str); var pathGeometry = PathGeometry.CreateFromGeometry(geometry);
- 投稿日:2021-06-21T22:11:49+09:00
Unity実行時のマウスによるカメラ操作例
概要 Unity実行時のマウスによるカメラ操作例を記載します。 実装 オブジェクト構造 スクリプト using UnityEngine; public class CtrlView : MonoBehaviour { [SerializeField] Transform TargetCam; [SerializeField] Transform ViewPoint; [SerializeField] float Angle_Speed = 0.5f; [SerializeField] float PosXY_Speed = 0.005f; [SerializeField] float PosZ_Speed = 5; Vector3 MousePos; void Update() { // マウスの移動量を取得 var diff = MousePos - Input.mousePosition; MousePos = Input.mousePosition; if (Input.GetMouseButton(0)) { // 左クリック(回転) var tmp = diff; diff.x = +tmp.y; diff.y = -tmp.x; ViewPoint.eulerAngles += diff * Angle_Speed; } else if (Input.GetMouseButton(1)) { // 右クリック(移動) var tmp = diff; diff.x = -tmp.x; diff.y = +tmp.y; ViewPoint.position += diff * PosXY_Speed; } // スクロール(拡縮というか前後) var scroll = Input.GetAxis("Mouse ScrollWheel"); var pos = TargetCam.localPosition; pos.z += scroll * PosZ_Speed; TargetCam.localPosition = pos; } } 補足 スクリプトは同クラス名でファイルを作成してそのままコピペしたら、どこかテキトウなオブジェクトにアタッチして、TargetCamに操作したいカメラ、ViewPointに視点とするカメラの親オブジェクトを設定すればそのまま使えます。 対象を中心にカメラどうやって回せばいいの?と思って良く分からない計算しようと思ったりしたこともありましたが、カメラをオブジェクトの子にしてオブジェクトを回せば良いのでは?という方法になります。 利用シーンは多そうな気がするんですが基本的過ぎる?せいか、あまりド直球な例は見かけないため、自分用としてのメモもかねて記事にしました。
- 投稿日:2021-06-21T22:02:41+09:00
[C#]ListをXmlSerializeするのに便利なクラス(Tag命名規則が柔軟に行える)
いきなりソースコード public interface INamerFactory<T> { INamer<T> Create(); } public interface INamer<T> { void Init(); string NextName(T obj); string NextName(); } public abstract class SerializableList<T> : List<T>, IXmlSerializable { protected INamerFactory<T> NamerFactory { get; set; } = null; public SerializableList() : base() { NamerFactory = MakeNamerFactory(); } protected abstract INamerFactory<T> MakeNamerFactory(); public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { reader.ReadStartElement(); try { var namer = NamerFactory.Create(); while (reader.NodeType != XmlNodeType.EndElement) { var serializer = CreateSerializer(namer.NextName()); var item = (T)serializer.Deserialize(reader); Add(item); } } finally { reader.ReadEndElement(); } } public void WriteXml(XmlWriter writer) { var namer = NamerFactory.Create(); var ns = new XmlSerializerNamespaces(); ns.Add(String.Empty, String.Empty); foreach (var item in this) { var serializer = CreateSerializer(namer.NextName()); serializer.Serialize(writer, item, ns); } } 使い方 INamerFactoryを継承した具象NamerFactoryを作って、それを使うようにSerializableListのスーパークラスを作る 例 public class IndexNamerFactory<T> : INamerFactory<T> { protected string BaseName { get; set; } protected int StartIndex { get; set; } public IndexNamerFactory(string baseName,int startIndex = 0) { BaseName = baseName; StartIndex = startIndex; } public INamer<T> Create() { return new IndexNamer(BaseName, StartIndex); } public class IndexNamer : INamer<T> { private string BaseName { get; set; } private int StartIndex { get; set; } private int Index { get; set; } public IndexNamer(string baseName,int startIndex) { BaseName = baseName; StartIndex = startIndex; Init(); } public void Init() { Index = StartIndex; } public string NextName(T obj) { return NextName(); } public string NextName() { var name = BaseName + Index.ToString(); Index++; return name; } } } を作って、これはIndexをつけながら命名していくNamerを作るFactory。 public class TestClasses : SerializableList<TestClass> { public TestClasses():base() { NamerFactory = new IndexNamerFactory<TestClass>("TestClasses", 1); } } というようにXmlのタグ名のクラス等で継承しコンストラクタで具象NamerFactoryを作る。 全体 using System; using System.Threading; using System.Threading.Tasks; using System.Collections; using System.IO; using System.Xml.Serialization; using System.Collections.Generic; using System.Xml.Schema; using System.Xml; using System.Linq; namespace Test { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); var test1 = new TestClass() { Str = "ahoaho1", Id = 0, Bool = false, subClass = new TestSubClass() { A = "a" }, }; var test2 = new TestClass() { Str = "ahoaho2", Id = 1, Bool = false, subClass = new TestSubClass() { A = "b" }, }; var tests = new TestClasses(); tests.Add(test1); tests.Add(test2); var m = new Main() { classes = tests, }; var writer = new StringWriter(); var serializer = new XmlSerializer(typeof(Main)); serializer.Serialize(writer, m); var xml = writer.ToString(); Console.WriteLine(xml); var tests_ = (Main)serializer.Deserialize(new StringReader(xml)); writer.Flush(); serializer.Serialize(writer, tests_); Console.WriteLine(writer.ToString()); } } public interface INamerFactory<T> { INamer<T> Create(); } public interface INamer<T> { void Init(); string NextName(T obj); string NextName(); } public class UniformNamerFactory<T> : INamerFactory<T> { protected string Name { get; set; } public INamer<T> Create() { return new UniformNamer(Name); } public class UniformNamer : INamer<T> { private readonly string Name; internal UniformNamer(string name) { Name = name; } public void Init() { } public string NextName(T obj) { return NextName(); } public string NextName() { return Name; } } } public class IndexNamerFactory<T> : INamerFactory<T> { protected string BaseName { get; set; } protected int StartIndex { get; set; } public IndexNamerFactory(string baseName,int startIndex = 0) { BaseName = baseName; StartIndex = startIndex; } public INamer<T> Create() { return new IndexNamer(BaseName, StartIndex); } public class IndexNamer : INamer<T> { private string BaseName { get; set; } private int StartIndex { get; set; } private int Index { get; set; } public IndexNamer(string baseName,int startIndex) { BaseName = baseName; StartIndex = startIndex; Init(); } public void Init() { Index = StartIndex; } public string NextName(T obj) { return NextName(); } public string NextName() { var name = BaseName + Index.ToString(); Index++; return name; } } } public abstract class SerializableList<T> : List<T>, IXmlSerializable { protected INamerFactory<T> NamerFactory { get; set; } = null; public SerializableList() : base() { NamerFactory = MakeNamerFactory(); } protected abstract INamerFactory<T> MakeNamerFactory(); public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { reader.ReadStartElement(); try { var namer = NamerFactory.Create(); while (reader.NodeType != XmlNodeType.EndElement) { var serializer = CreateSerializer(namer.NextName()); var item = (T)serializer.Deserialize(reader); Add(item); } } finally { reader.ReadEndElement(); } } public void WriteXml(XmlWriter writer) { var namer = NamerFactory.Create(); var ns = new XmlSerializerNamespaces(); ns.Add(String.Empty, String.Empty); foreach (var item in this) { var serializer = CreateSerializer(namer.NextName()); serializer.Serialize(writer, item, ns); } } XmlSerializer CreateSerializer(string elementName) { // Create an XmlAttributes to override the default root element. var myXmlAttributes = new XmlAttributes(); // Create an XmlRootAttribute and set its element name and namespace. var myXmlRootAttribute = new XmlRootAttribute() { ElementName = elementName, }; // Set the XmlRoot property to the XmlRoot object. myXmlAttributes.XmlRoot = myXmlRootAttribute; var myXmlAttributeOverrides = new XmlAttributeOverrides(); /* Add the XmlAttributes object to the XmlAttributeOverrides object. */ myXmlAttributeOverrides.Add(typeof(T), myXmlAttributes); return new XmlSerializer(typeof(T), myXmlAttributeOverrides); } } public class Main { public TestClasses classes { get; set; } } public class TestClasses : SerializableList<TestClass> { protected override INamerFactory<TestClass> MakeNamerFactory() { return new IndexNamerFactory<TestClass>("TestClasses", 1); } } public class TestClass { public string Str { get; set; } public uint Id { get; set; } public bool Bool { get; set; } public TestSubClass subClass { get; set; } public string GetTag() { return "TestClass"; } } public class TestSubClass { public string A { get; set; } } }
- 投稿日:2021-06-21T20:57:06+09:00
【C#】メモ帳で開発(おまけ:ホスト計算ソフト)
メモ帳を開発はしません、メモ帳で開発します。 自分用覚書その2。 自宅のノートPCがおんぼろなためか、VSC# を動かすとカクカクになってしまうので、やむなくCLI開発。 (※VisualStudio C# 使える人はそっち使った方が絶対に効率良い) Windows には標準でC#のコンパイラやら何やらが入っているそうなので、それを使って開発していく。 C#のコンパイラのパスを通す C#はコンパイル言語なのでコンパイラでコンパイルが必要。 割とパスの通し方忘れるのでメモ。 左下のWindows マークを右クリック。 設定 → システム → 詳細情報 → システムの詳細設定 で、システムのプロパティを開き、「環境変数(N)」を押下。 ユーザーの環境変数もしくは、システムの環境変数の「Path」を選択し、「編集」を押下。 環境変数の編集のウィンドウで「新規(N)」を押下すると、左のリストにあらたにパスを加えられる状態になるので、コンパイラが存在するフォルダを指定。 C:¥Windows¥Microsoft.NET¥Framework64¥v4.0.30319¥ パスがとっているか、以下のコマンドで確認。 このコンパイラは~~みたいな英文がでたら成功。 csc コマンドプロンプトでソースコードを置くフォルダへ移動 なんかテキトーにフォルダ作ってそこに移動。 (例えばホームディレクトリに csharpProgramというフォルダ) cd C:¥Users¥hemuwan¥csharpProgram てきとーにプログラム書く。メモ帳起動はnotepad notepad hello.cs 別に .txt でもいい。 お試しハローワールドサンプル。 hello.cs using System; class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } 上記をコンパイルして実行すると、コンソールにHello Worldが表示される。 csharpProgram>csc hello.cs csharpProgram>hello.exe Hello World! それなりにいろいろできるよ フォームを表示させるには、Main() の中で、Formクラスを継承したクラスのインスタンスを作成し、Application.Run()の引数に入れて使う。 sample.cs using System.Windows.Forms; class Program { static void Main(string[] args) { Form1 form1 = new Form1(); Application.Run(form1); } } class Form1 : Form { } 上記のコードをコンパイルし、.exe ファイルを実行すれば、フォームだけ表示される。 おめでとう、これでメモ帳とコマンドプロンプトを使った C#の苦行開発環境が出来上がりだよ! おまけ、IPアドレスから2進表記と割り当て可能ホスト数を計算してくれるプログラム せっかくなんで作ってみた。 メーっちゃしんどいのでおとなしく統合開発環境を使うのが吉。 せっかくなので、試行錯誤のコメントもそのまま。 、、、んーしかし、汚い。 csharpIPConverter.cs using System; using System.Windows.Forms; using System.Drawing; //Formクラスを継承し、フォーム画面使えるように class WinMain : Form { public static void Main(string[] args) { WinMain win = new WinMain(); win.Text = "IPアドレスを2進数に変換"; //アプリケーション実行。これでフォームが立ち上がる。 Application.Run(win); }//Main() //各部品のインスタンスを作る //最初コンストラクタ内で宣言しちゃったから?値の引き渡し失敗した。 TextBox inputTxt = new TextBox(); TextBox binTxt = new TextBox(); TextBox hostCountTxt = new TextBox(); Button btn1 = new Button(); Label inputLbl = new Label(); //説明用 Label binLbl = new Label(); Label hostCountLbl = new Label(); //コンストラクタ。フォームの部品などを定義 //初期値を設定したり public WinMain() { //変数に入れるとき、this を付けた方が明示的か //各部品の情報を整える inputLbl.Text = "IPアドレスをCIDR表記で入力(例:192.168.1.0/24)"; inputLbl.Bounds = new Rectangle(10, 10, 250, 20); inputTxt.Text = "IPアドレスを入力してください。"; inputTxt.Bounds = new Rectangle(10, 30, 250, 20); btn1.Text = "実行"; btn1.Bounds = new Rectangle(10, 60, 80, 25); binLbl.Text = "2進数に変換すると"; binLbl.Bounds = new Rectangle(10, 110, 250, 20); binTxt.Bounds = new Rectangle(10, 130, 250, 20); hostCountLbl.Text = "割り当て可能なホスト数は"; hostCountLbl.Bounds = new Rectangle(10, 160, 250, 20); hostCountTxt.Bounds = new Rectangle(10, 180, 250, 20); //ボタンを押して離したときに発動するメソッドを追加 btn1.MouseUp += new MouseEventHandler(btn1_onclick); //部品をフォームに表示 Controls.Add(inputLbl); Controls.Add(inputTxt); Controls.Add(btn1); Controls.Add(binLbl); Controls.Add(binTxt); Controls.Add(hostCountLbl); Controls.Add(hostCountTxt); }//WinMain() コンストラクタ //btn1を押したときのメソッドを定義、名前に決まりはないみたいだけど //合わせておいた方がわかり易い。 public void btn1_onclick(object sender, MouseEventArgs e) { //入力テキストを受け取り、アドレス計算 IPaddrConverter(inputTxt.Text); } void IPaddrConverter(string IPaddress) { //変数名、雑すぎてやばい string IPaddr = IPaddress; string[] ip = IPaddr.Split('/'); string prefix = ip[1]; //別に代入せんでよかったのでは。 string[] oxet = ip[0].Split('.'); for(int i = 0; i < oxet.Length; i++) Console.WriteLine(oxet[i]);// 192 168 1 0 string[] convertOxet = new string[4]; //2進数に変換した後、 //00000000.00000000.00000000.00000000の表記にするため、頭に0付けていく for(int i = 0; i < oxet.Length; i++) { convertOxet[i] = Convert.ToString(Convert.ToInt32(oxet[i]), 2); if(convertOxet[i].Length < 8) { //loopCounter にループ回数を代入。 //最初これをそのままfor文に入れて、値が変わってうまくいかんかった。 int loopCounter = 8 - convertOxet[i].Length; for(int j = 0; j < loopCounter; j++) { //Console.WriteLine(8 - convertOxet[i].Length); convertOxet[i] = "0" + convertOxet[i]; } } Console.WriteLine(convertOxet[i]); } //192.168.1.0 を 2進数表記 00000000.00000000.00000000.00000000で表示 //192.168.1.0 -> 11000000.10101000.00000001.00000000 //Console.WriteLine("{0}", string.Join(".", convertOxet)); binTxt.Text = string.Join(".", convertOxet); //プレフィックス prefix を使って、割り当て可能ホスト数を割り出す。 //ビットを2進数に直して、それをまた10進に直して、2引くでいこう //32 - 24 = 8 -> 8bit = 256 -2 => 割り当て可能ホスト数 254 個 になるはず //うまくいかず。。32 - prefix はビットの数なので2進数に変換しても変になる //32 - prefix の数ぶん、1 を立てる int bitCounter = 32 - int.Parse(prefix);// 32 - 24 = 8 string hostBit = ""; for(int i = 0; i < bitCounter; i++) { hostBit = "1" + hostBit; } long hostCounter = Convert.ToInt64(hostBit, 2) - 1; hostCountTxt.Text = Convert.ToString(hostCounter); } }//class WinMain : Form xxx.xxx.xxx.xxx/xx の表記で入力してね。 違うとバグるよ。
- 投稿日:2021-06-21T19:06:51+09:00
[unityメモ] オブジェクトをキーで動かす方法!
unityメモ オブジェクトをキーで動かす方法! ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー using System.Collections; using System.Collections.Generic; using UnityEngine; public class Movenezu : MonoBehaviour { // Update is called once per frame void Update() { if (Input.GetKey("up")) { transform.Rotate(-2, 0, 0); } if (Input.GetKey(KeyCode.Space)) { this.transform.Translate (0.0f,0.0f,0.05f); } if (Input.GetKey("down")) { transform.Rotate(2, 0, 0); } if (Input.GetKey("right")) { transform.Rotate(0, 2, 0); } if (Input.GetKey("left")) { transform.Rotate(0, -2, 0); } } }
- 投稿日:2021-06-21T13:45:12+09:00
Windowsでローカルタイムゾーンの夏時間設定の確認方法
Windowsでローカルタイムゾーンの夏時間設定の確認方法 「夏時間に合わせて自動調整する」がオンになっているか否かを確認したい場合、 .NET Frameworkが提供するRegistryKeyクラス(Microsoft.Win32名前空間)を利用し、 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation]のキーの [DynamicDaylightTimeDisabled]の値を参照します。 Registry.GetValue メソッド このメソッドは、.NET Framework version 2.0 で新しく追加されたものです。 キーと値を変えてやれば、これ以外にもレジストの値を取得することが可能です。 (型は、呼び出すレジストリの値の型に合わせてやる必要があります。) GetRegistryKey.cs using System; using Microsoft.Win32; namespace GetRegistryKey { class Program { static void Main(string[] args) { // 操作するレジストリ・キーの名前 string rKeyName = @"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"; // 取得処理を行う対象となるレジストリの値の名前 string rGetValueName = "DynamicDaylightTimeDisabled"; // レジストリの取得 try { // レジストリ・キーのパスを指定してレジストリを開く RegistryKey rKey = Registry.LocalMachine.OpenSubKey(rKeyName); // レジストリの値を取得 Int32 location = (Int32)rKey.GetValue(rGetValueName); // 開いたレジストリを閉じる rKey.Close(); // コンソールに取得したレジストリの値を表示 Console.WriteLine("夏時間に合わせて自動的に調整しない:" + location); Console.ReadLine(); } catch (NullReferenceException) { // レジストリ・キーまたは値が存在しない Console.WriteLine("レジストリ[" + rKeyName + "]の[" + rGetValueName + "]がありません!"); } } }