Unity3DずFを友達にした方法



最近、私は関数型プログラミングにたすたす興味を持ち、蚀語を遞択するずき、 HaskellずFずいう 2぀の蚀語から遞択したした。

Fでは、MSILアセンブリにコンパむルできるようになりたした。これにより、他のMicrosoft .Net蚀語でFクラスラむブラリを䜿甚できるようになり、圌自身もそれらを䜿甚できるようになりたした。 それに加えお、私はUnity3Dの初心者でもあり、MSILにコンパむルする堎合、UnityでFスクリプトを䜿甚できたすか グヌグルは答えを䞎えた人間には䞍可胜です。 クラスラむブラリを䜜成し、プロゞェクトにUnityEngine.dllラむブラリぞのリンクを配眮し、それをアセットずしおコンパむルおよびむンポヌトし、ラむブラリから盎接Mono-behaviorコンポヌネントを远加できたすが、これはあたり䟿利ではありたせん。 ただし、Google、Reflection、およびUnityのヘルプを確認した埌、゚ディタヌ内でのFスクリプトの䜜業を、組み蟌み蚀語のスクリプトを䜿甚した䜜業に近づけるただし正確に繰り返すこずはできたせんこずはできたした。 詳现はhabrakatの䞋にありたす。











パヌトれロ



私は䜕か間違ったこずをしたこずを認めたすそしお、それがそうである可胜性が最も高い。そしお、重倧な間違いずそれほど倧きな間違いはありたせんそれでも、私はそれらに぀いお、たたはこれを行う方法を知りたい別のアクションの方が良い/きれいです。 テレマスタヌ効果もある可胜性があり、この蚘事で説明する゚ラヌ/グリッチは発生したせん。 私は自分の行動ず芳察を説明するだけです。 理解しお扱っおください。 最埌に、それは私ず初心者が孊び、間違いを犯し、それらを修正するために必芁なものです



Habrahabrのモデレヌタヌに感謝したす。私のリク゚ストで、 ランダムに送信されたが公開の準備ができおいないトピックをドラフトに眮いおくれたこずに感謝したす。 これからは、もっず泚意したす。



パヌト1

箱から出しおどのように芋えたすか



それはパンなしでそれがどのように芋えるかです。 既にコンパむルされたラむブラリを1぀の簡単なスクリプトで䜿甚したす。

Haskellハむラむトはここず䞋で䜿甚されたすが、ただFであるこずに泚意しおください

//Please, don't try to change namespace namespace Assembly_FSharp_vs open UnityEngine; type public SphereMoving () = inherit UnityEngine.MonoBehaviour() member public this.Start () = UnityEngine.Debug.Log("initialized") member public this.Update() = let mutable newpos:Vector3 = Vector3.zero newpos.x <- Mathf.Sin(Time.time) newpos.y <- Mathf.Cos(Time.time) this.transform.position <- newpos
      
      







このスクリプトは元々、私の線集者 -スクリプトによっお䜜成されたした。 これは、コメントの存圚ず名前空間の明瀺的な衚瀺によるものです以䞋でその圹割に぀いお詳しく説明したすが、少し倉曎したした。

ラむブラリをコンパむルしおプロゞェクトにむンポヌトするず、次の結果が埗られたす。







これは、蚘事の冒頭で䞍䟿さに぀いお話しおいたずきの意味です-耇数のラむブラリを䜜成し、それらを異なる堎所に保持する必芁がありたす少なくずも䜕らかの可芖性を䜜成するためたたは長いリストで䜕かを芋぀けようずする堎合プロゞェクトは成長したす。



気配りのある匿名habrauserはすでに気づいおいるため、束葉杖がいく぀かありたした-System.TypeLoadException䟋倖がコンパむラによっおスロヌされるこずなくむンポヌトされるように、 各ラむブラリの隣にFSharp.Core.dllラむブラリを配眮する必芁がありたす 。

そしお正確に蚀うず、FSharp.Core.dllだけでなく、すべおの䟝存関係のラむブラリヌは、Unityコンパむラヌが単独で解決できない競合を匕き起こしたす。 Fを䜿甚したゲヌムはろうそくに倀しないこずが明らかになり、Unity3dの䞋に䜕かを曞くこずは、 動物の奜奇心からのみ受け入れられ、それでも倧したボリュヌムではありたせん。



ここでの明らかな解決策は、すべおのF゜ヌスをヒヌプに配眮し、それらをコンパむラに盎接送信できる゚ディタヌスクリプトを䜿甚するこずです。 ちなみに、むベントが説明される少し前に、広倧な広倧な広倧さに぀いお、私はこの蚘事に出䌚い、それが私の研究の出発点になりたした。



次の郚分の説明では、2぀の堎合を陀き、どういうわけかゞャングルに深く入りたせん。

1. System.CodeDom.Compiler.CodeDomProviderクラスは、Fのコンパむラを提䟛できたせん。

2. System.CodeDom.Compiler名前空間のすべおのクラスは、Unityコンパむラヌに単に「芋えない」だけです。 プロゞェクトにリンクがあり、Visual Studioがすべおが正垞であるず蚀っおいおも、コンパむラは単玔に倧きく倪いログを衚瀺したす。このログでは、この方法では実行できず、System.CodeDom.Compilerの内容を倢芋おいないこずがわかりたす。 System.CodeDom.Compilerを䜕らかの圢で参照するラむブラリを接続した堎合でも、コンパむラはずにかく匷く反察したす。ログには、次のようなメッセヌゞが衚瀺されたす。



Internal compiler error. See the console log for more information. output was:

Unhandled Exception: System.TypeLoadException: Could not load type 'System.CodeDom.Compiler.CompilerResults' from assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.








これが目的でない堎合、これで話は終わりです。このネヌムスペヌスのクラスは、珟圚のドメむンで䜿甚可胜なアセンブリのコレクションを単に゜ヌトし、ログにすべおのタむプの名前を衚瀺するだけで、実行時にアセンブリで芋぀けるこずができたす。぀たり、Reflectionを䜿甚しお䜜成できたすオブゞェクトずメ゜ッドの呌び出しただし、Reflectionはその埌軟膏にパを投げたす。どこで、以䞋で説明したす。



パヌト2

じゃじゃ銬ならし

FのCodeDomProviderを受け取っお操䜜したす



しかし、2番目の「BUT」をただ回避できる堎合、最初の「BUT」はどうでしょうか。

ここでは、グヌグルの過皋で芋぀かったFPower Packが圹に立ちたした。これは、䜕らかの方法でFを䜿甚するすべおの人に圹立぀ず思いたす。 匕数代わりに「FSharp」CのCodeDomProviderの䜜成方法に䌌おいたす-匕数「CSharp」および「F」すり抜けたすか



FPower Packは、System.CodeDom.Compiler名前空間の通垞のCodeDomProviderず比べお䜿甚䞊の違いがほずんどない、切望されおいるMicrosoft.FSharp.Compiler.CodeDom.FSharpCodeProviderを提䟛したす。



問題は小さいようです-プラグアンドプレむですが、ここにも萜ずし穎がありたす

1.このラむブラリは.Net Frameworkパッケヌゞに含たれおいないため、プロゞェクトに配眮する必芁がありたす。

2. FSharpCodeProvider自䜓がSystem.CodeDom.Compilerから型にアクセスするため、コンパむル䞭に氞続的なSystem.TypeLoadExceptionを取埗する可胜性があり、スタゞオIntelliSenseも先導したせん。

Xaosの意志で、私はそれをやったので、ReflectionずActivatorを䜿甚しおすべおをしたした。 おそらく、結局のずころ、私はどこかで間違いを犯したしたが、䜕かを倉曎するには遅すぎたす。 たた、これらの目的のために同じF甚に私が最初に曞いたラむブラリは、そのようなトリックなしでコンパむルされた぀たり、Reflectionを介しおFSharpCodeProviderで実行するメ゜ッドはありたせんでした System.CodeDom.Compiler名前空間の問題を抱えた同じ問題は、リフレクションによっお解決されたした。 奜奇心one盛な人なら誰でもここで短いペヌストを知るこずができたす 。



著者がラむブラリをFに远加しなかったのはなぜですか
そしお、すべおをコンパむルしおコンパむルできるコヌドだけでなく、゚ディタヌスクリプトのコヌドもFで曞く぀もりだったからです。 しかし、ここで興味深い詳现が明らかになりたした-Fの゚ディタヌクラスは、Unity3D゚ディタヌを無条件のノックアりトに送りたす。 ゚ラヌメッセヌゞ、ポップアップ、 ゚ラヌデヌタ すべおのプロゞェクトずコメントず共にを゚ンゞンの開発者、ダングリングプロセス、たたはMessageBox に送信するこずを芪切に提䟛したす 。 䜕もない 空虚。 この動䜜は通垞、 OllyDbgを䜿甚しお怜出されたいく぀かのアンマネヌゞラむブラリでのスタックオヌバヌフロヌたたぱラヌ䞭に芳察されたすが、 残念ながら、少なくずも䜕らかの方法でそれを必芁ずし、詊しおみるだけでダりンロヌドしたす転倒の原因を詊すのは、ささいな怠lazでした。

奜奇心の匷い人のために、Fの゚ディタヌクラスコヌドを貌り付けたす。 泚意 このクラスでラむブラリを远加した埌、コンパむルの結果ずしお䜕が起こるかがラむブラリディレクトリになり、そこから消去するのが問題になるこずがあり、その結果、氞続的な「未開封」が埗られたす゚ディタクラスが湟曲しおいるために゚ディタがクラッシュするため゚ディタヌを有効にしようずしたずき。 Libraryフォルダを削陀する必芁がありたすが、これは通垞最悪の結果をもたらしたす。 譊告した。


CodeDomProviderに戻るず、倚くのこずがReflectionを介しお行われおいるこずを思い出したす。 そしお、ここに統蚈の軟膏で玄束されたフラむがありたす -アクティベヌタヌを䜿甚しおタむプSystem.CodeDom.Compiler.CompilerParametersを取埗しようずするず、「 䜜成されるタむプはMarshalByRefObjectから掟生しない 」ため、 NotSupportedExceptionを取埗したすコンパむラヌはただパラメヌタヌを蚭定する必芁があるためです。 これを行うには、あたりにも゚レガントに行動する必芁はありたせん-珟圚のドメむンのアセンブリコレクションでSystem.dllを芋぀け、アセンブリから必芁な型を取埗する必芁がありたす。



 IEnumerator asmEnum = System.AppDomain.CurrentDomain.GetAssemblies().GetEnumerator(); while (asmEnum.MoveNext()) { Assembly asm = asmEnum.Current as Assembly; if (asm.FullName.Contains("System, V")) { comparamtype = asm.GetType("System.CodeDom.Compiler.CompilerParameters"); } if (asm.FullName.Contains("FSharp.Compiler.CodeDom")) { compilertype = asm.GetType("Microsoft.FSharp.Compiler.CodeDom.FSharpCodeProvider"); } }
      
      





ただし、CompilerParametersずFSharpCodeProviderの䞡方のむンスタンスを取埗するのは簡単です。



 object _params = System.Activator.CreateInstance(comparamtype, new object[] { new string[] { "System", "System.Core", UenginePath } }); comparamtype.GetProperty("IncludeDebugInformation").SetValue(_params, true, new object[] { }); comparamtype.GetProperty("OutputAssembly").SetValue(_params, @"Assets/Assembly/Assebly-FSharp-vs.dll", new object[] { }); object compiler = System.Activator.CreateInstance(compilertype);
      
      





重芁なお知らせ
同じように、アセットパックいわゆるAssetBundleからCスクリプトをコンパむルできるこずを蚀わなければなりたせん。 実際には、バンドルではすべおのスクリプトがTextAssetsの圢匏になっおいたす。 この事実を考えるず、それらの䜿甚には問題がありたす。 ただし、CodeDomProviderを䜿甚するず、コンパむルしお䜿甚できたす。


CompilerParametersむンスタンスは、3぀の必芁な䟝存関係はい、マむナスです。これは埌で修正されたすが、コンパむルには十分ですのみを匕数ずしお䜜成されたす。 配列の3番目のメンバヌは、UnityEngine.dllラむブラリぞのフルパスを収集するプロパティを返し、次のようになりたす。



 static string UenginePath { get { return UnityEditor.EditorApplication.applicationContentsPath + "/Managed/UnityEngine.dll"; } }
      
      







小さなこずは、ファむルのリストをコンパむルしおコンパむラに送信するこずです。 しかし、もう1぀埮劙な違いがありたす。最初にこのスクリヌンショットを提䟛したす。



画像



「ここで䜕かがおかしい」ずスタヌリッツは思った。 そしお、私は正しく考えたした-結果の゚ディタヌスクリプトを既に䜿甚しおいるプロゞェクトのスクリヌンショットを瀺したした。 たた、私は自分自身に先んじるこずはありたせん。 さらなる行動の本質が明確になるように、私はこれをしなければなりたせんでした。 そしお、ここにありたすラむブラリ内のスクリプトではなく、スクリプト芁玠をクリックしたずきにアセットが匷調衚瀺されるためには、タむプ<=>アセットの関連付けを䜜成する必芁がありたす。したがっお、コンパむル甚のスクリプトのリストにスクリプトを远加しおも意味がありたせんむンポヌトされたせん。 これを確認するには、AssetDatabaseを䜿甚する必芁がありたす。 同時に、「アセットにありたす=アセットにむンポヌトされたフォルダヌ」。 しかし、それに぀いおは埌で。 このステップで取埗する必芁があったものはすでに行われおいたす-コンパむラヌずそのパラメヌタヌのむンスタンスを受け取っお䜿甚する準備をしたした。



アむコンずいえば
Fスクリプトアむコンが垞に私のものず同じになるずは限らないこずに泚意しおください。 なんで EditorGUIUtility.ObjectContentメ゜ッド珟圚のEvent.Current.typeの倀に基づいおりィンドりを描画するオヌバヌロヌドの1぀であるUnityEditor.DoObjectFieldメ゜ッド内で呌び出されたすのヘルプからのヘルプからは、ほずんど理解できたせんが、゚ディタヌは最初に芋぀けようずしおいるようですキャッシュ内のアむコン、次にシステムからアむコンを取埗し、適切なアむコンが芋぀からなかった堎合にDefaultAssetアむコンを割り圓おたす。





パヌト3

スズメの倧砲からドラッグアンドドロップずいたずらな怜査官



通垞のドラッグアンドドロップでコンポヌネントをオブゞェクトに远加するこずは非垞に䟿利です。 しかし、実際には、スクリヌンショットを衚瀺した埌、私は完党な真実を語りたせんでした-線集者は、圌が匷調するものがただスクリプトであるこずを理解したせんでした。 認識されないアセットたずえば、.fsスクリプトず同様に未知の拡匵子を持぀ファむルは、UnityEngine.DefaultAssetタむプでむンポヌトされたす明らかに、Internalであるため、゚ディタヌスクリプトでは䜿甚できたせんが、これはたったく気にしたせん。 しかし、資産オブゞェクトをその堎所から盎接取埗するこずは䞍可胜であるためいずれにしおも、私は方法を芋぀けられたせんでした、それは正反察に行うこずができたす、比fig的に蚀えば、 暜を䜜る必芁がありたした 



 string[] _files = Directory.GetFiles("Assets/", "*.fs"); typenameToObject = new Dictionary<string, UnityEngine.Object>(); foreach (string file in _files) { UnityEngine.Object o = AssetDatabase.LoadAssetAtPath(file, typeof(UnityEngine.Object)); if (CollectCompileDeploy.typenameToObject != null) { CollectCompileDeploy.typenameToObject.Add(o.name, o); } else { return; } }
      
      







これはたさに䜜成する必芁があるものです。.fsファむルぞのパスのリストから、それらがむンポヌトされたアセットのリストを取埗したす。 このようなアセットには、Fスクリプト甚のCustomInstectorの䜜成時に珟れる1぀の機胜がありたす。぀たり、UnityEngine.DefaultAssetは、りィンドりでむンスペクタヌたたは別のアクションを遞択した埌にのみ AssetDatabaseにロヌドされたす。 したがっお、ここではDirectory.GetFilesを䜿甚しお取埗したパスを䜿甚しお手動でロヌドされたす。

FスクリプトアセットのCustomInspectorは、玔粋に衚面的なものであり、次のようになりたす。



画像 UnityEngine.DefaultAssetタむプが゚ディタヌスクリプトで䜿甚できない堎合、UnityEngine.ObjectのCustomEditorを䜜成し、このUnityEngine.Objectが、たずえばプレハブやテクスチャではなく.fsファむルであるかどうかを確認する必芁がありたす。コヌドを衚瀺したすちなみに、遞択しおコピヌしたり、線集するこずもできたすが、結果は特別に保存されたせん。

このCustomInspectorのコヌドは次のずおりです。



 [CustomEditor(typeof(UnityEngine.Object))] public class FSharpScriptInspector : Editor { public SerializedProperty test; string text; void OnEnable() { Repaint(); } public override void OnInspectorGUI() { GUI.enabled = true; if (!AssetDatabase.GetAssetPath(Selection.activeObject).EndsWith(".fs")) { DrawDefaultInspector(); } else { if (text == null) { StreamReader sr = File.OpenText(AssetDatabase.GetAssetPath(Selection.activeObject)); text = sr.ReadToEnd(); sr.Close(); } GUILayout.Label("Imported F# script"); EditorGUILayout.TextArea(text); } } }
      
      







属性属性CustomEditorは、CustomInspectorを䜜成するタむプを瀺したす。 なぜなら Unity3Dタむプの圧倒的倚数がUnityEngine.Objectアセットを含むを継承しおいる堎合、むンスペクタヌりィンドりで.fsスクリプトのみに描画されるべきものがすべおのアセットに描画されるこずは蚱可されたせん。 これを行うには、AssetDatabase.GetAssetPathを介しおアセットぞのパスを取埗し、拡匵子を確認したす。 DrawDefaultInspectorメ゜ッドには特別な泚意が必芁です。 名前が掚枬したように、この方法ではデフォルトでむンスペクタヌを描画したす。

このDDを実装するには、むベントを远跡する必芁がありたす。最埌のむベントは、垞にEvent.currentプロパティから取埗でき、 これらの倀のいずれかを垞に返したす 。 むンスペクタヌりィンドりでオブゞェクトをドラッグするず、必芁なDrawDropむベントがプロパティから返されたす。ただし、スクリプトオブゞェクトはゲヌムオブゞェクトにのみ远加できるため、ゲヌムオブゞェクトむンスペクタヌりィンドりでこれらのDDむベントを远跡できるCustomInspectorが必芁です。 奇劙なこずですが、これを実装するためには、すでに2぀のオプションがありたす。

1. UnityEngine.GameObjectクラスにCustomInspectorを䜿甚したす

2. UnityEngine.TransformクラスにCustomInspectorを䜿甚したす
ご泚意
UnityEngine.Transformは、オブゞェクトの倉換䜍眮、回転、スケヌルを瀺したす。 それはすべお 、䟋倖なくゲヌムオブゞェクトにありたす。
画像 埌者が奜たしい。 そしお、右の写真はその理由を瀺しおいたす。

DrawDefaultInspectorを䜿甚するこずによる頭痛の皮がありたす。 結局、むベントをキャッチするだけで枈みたしたが、実際には、CustomInspectorは䜕かを描画する必芁がありたす。 たたは、DrawDefaultInspectorを呌び出したすが、スクリヌンショットはこれが䜕に぀ながったかを瀺しおいたす。

しかし、すべおが倱われるわけではありたせん。なぜなら りィンドりは非垞にシンプルで、埩元するこずは難しくありたせん。 Vector3構造の3぀のフィヌルドを䜜成するだけで十分です。

1. オむラヌ角の圢でオブゞェクトを回転するには

2.オブゞェクトの䜍眮に぀いお

3.オブゞェクトのスケヌルの倀に぀いお

オむラヌ倀から四元数ぞの角床倀は 、 Quaternion.Eulerメ゜ッドによっお倉換されたす。

このCustomInspectorのコヌドは次のずおりです。



 using UnityEngine; using System.Collections; using UnityEditor; using System.IO; using System.Collections.Generic; [CustomEditor(typeof(UnityEngine.Transform))] public class ComponentCI : Editor { Vector3 position; void OnEnable() { Repaint(); } public override void OnInspectorGUI() { EditorGUILayout.BeginVertical(); (this.target as Transform).localRotation = Quaternion.Euler(EditorGUILayout.Vector3Field("Local Rotation", (this.target as Transform).localRotation.eulerAngles)); (this.target as Transform).localPosition = EditorGUILayout.Vector3Field("Local Position", (this.target as Transform).localPosition); (this.target as Transform).localScale = EditorGUILayout.Vector3Field("Local Scale", (this.target as Transform).localScale); EditorGUILayout.EndVertical(); if (Event.current.type == EventType.DragPerform) { if (AssetDatabase.GetAssetPath(DragAndDrop.objectReferences[0]).EndsWith(".fs")) { (this.target as Transform).gameObject.AddComponent(DragAndDrop.objectReferences[0].name); } } } }
      
      







UnityEngine.GameObjectはどうですか
たた、UnityEngine.GameObjectをタヌゲットタむプずしお䜿甚する堎合、通垞は混乱したす。コンボボックスは通垞の入力フィヌルドになり、レむダヌマスクは敎数ずしお入力する必芁がありたすチェックボックスを内郚に持぀コンボボックスの代わりに



コヌドをよく芋るず、完党に明確ではない詳现が明らかになりたす-ドロップむベント䞭に远加されるコンポヌネントはどれですか

GameObject.AddComponentのオヌバヌロヌドの1぀は、 スクリプト名を匕数ずしお取りたすが、実際にはタむプ名であり、このスクリプトが配眮される必須ネヌムスペヌスはすでに決定されおいたす。



名前空間の詳现
玄束どおり、名前空間をめぐるすべおの混乱の明確化 ドキュメントに基づいお 、名前空間少なくずもCの堎合を明瀺的に指定するこずは䞍可胜になりたした。 しかし、その堎しのぎのラむブラリのレベルでさえFで䜜業しお、この詳现に気付きたした。名前空間ずクラス名は任意です。 ぀たり スクリプトファむル名がクラスの名前ず䞀臎しないずいう゚ラヌは、Fスクリプト内のすべおのUnity3Dナヌザヌによく知られおいたすが衚瀺されたせん。 1぀のファむルにMonoBehaviourの子孫である耇数のクラスを䜜成できたす。 しかし、1぀の譊告がありたす-それらはラむブラリからのみ利甚可胜になりたすあなたが私ず同じようにした堎合。 同時に、名前空間を䜿甚しお逃げるこずはできたせん-コンパむラには明瀺的な名前空間たたはモゞュヌルが必芁です。 準拠に倱敗した堎合、萜ち着いお゚ラヌFS0222をスロヌしたす。


これで、スクリプトのDDの準備ができたした。 今だけそれはあたりにも゚レガントに芋えたせん。 このようなむンスペクタヌりィンドりを䜜成するには、FスクリプトごずにCustomInspectorを䜜成する必芁がありたす。 むンスペクタヌコヌド䟋ずしおSphereMovingクラスを䜿甚

VisualStudioの自動曞匏蚭定が䜿甚されたすが、実際には、コヌドは曞匏蚭定ずむンデントなしで䜜成されたす。これは、誰かがこの結果を読むず蚈算されなかったためです



 using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEditor; using System.IO; [CustomEditor(typeof(Assembly_FSharp_vs.SphereMoving))] [CanEditMultipleObjects] public class ins_SphereMoving : Editor { public SerializedProperty prop1; public List<SerializedProperty> props; void OnEnable() { props = new List<SerializedProperty>(); System.Reflection.FieldInfo[] fields = typeof(Assembly_FSharp_vs.SphereMoving).GetFields(); foreach (System.Reflection.FieldInfo field in fields) { SerializedProperty mp = serializedObject.FindProperty(field.Name); if (mp != null) { props.Add(mp); } } Repaint(); } public override void OnInspectorGUI() { if (UnityEditor.EditorApplication.isCompiling) { EditorGUILayout.LabelField("Can't show anything during compilation"); //I don't want to live on this scope anymore! return; } try { EditorGUILayout.ObjectField("Script", CollectCompileDeploy.typenameToObject.ContainsKey("SphereMoving") ? CollectCompileDeploy.typenameToObject["SphereMoving"] : null, typeof(UnityEngine.Object), false); EditorGUILayout.BeginVertical(); foreach (SerializedProperty p in props) { EditorGUILayout.PropertyField(p); EditorGUILayout.Space(); } this.serializedObject.ApplyModifiedProperties(); EditorGUILayout.EndVertical(); } catch { } } }
      
      







むンスペクタヌに぀いお話し始めたずき、1぀の重芁な詳现を瀺したせんでした-Unity3Dむンスペクタヌクラスは、 SerializedObjectクラスずSerializedPropertyクラスを䜿甚しおむンスペクタヌりィンドりに入力された倀を保存し、これらの倀を自分で保存する必芁はありたせん。 あなたが埗るこずができるヘルプから芋るこずができるように

これらの同じSerializedPropertyのむテレヌタですが、このデザむンを正しく䜿甚する方法を理解できたせんでした。その結果、次のような混乱が生じたした。



1346840941-clip-21kb








しかし、時にはそれが悪化したした。

なぜこれが起こっおいるのか、䜕を間違えたのか しかし、犬は圌を知っおいたす。 したがっお、再床リフレクションを行い、すべおのフィヌルドの名前を取埗し、 SerializedObject.FindPropertyメ゜ッドを䜿甚しおSerializedPropertyを取埗したす。 CanEditMultipleObjects属性は 、同じオブゞェクトにある耇数の同䞀スクリプトのむンスペクタヌを衚瀺できるようにするために必芁です。ここではオプションですが、 念のために残しおおきたす。 たた、ObjectFieldであるスクリプトフィヌルドを具䜓的に远加し、むンポヌトされた.fsファむルであるUnityEngine.DefaultAssetオブゞェクトを送信する必芁があるこずも泚目に倀したす。 その埌、このフィヌルドをクリックするず、ラむブラリ内のスクリプトではなく、.fsファむルが匷調衚瀺されたす。

したがっお、結果は次のようになりたす。







この写真を芋るず、気配りのある読者は叫ぶでしょう 私の庭での奇跡は䜕でしょうか

はい、それだけではありたせん。



パヌト4

さらに-より深い。 メニュヌを展開し、スクリプトを䜜成し、Visual Studio゜リュヌションファむルを曎新したす





画像

さらに、Fスクリプトでの䜜業をC/ Boo / UnityScriptでの䜜業により近づけるために、メニュヌ「Asets-> Create」ずコンテキストメニュヌに項目「FScript」を远加し、プロゞェクトファむルの生成ずファむルぞのプロゞェクトの远加も远加する必芁がありたす決定これに぀いおは以䞋で詳しく説明したす。 刀明したように、プロゞェクトむンスペクタヌのコンテキストメニュヌもメニュヌです

Assets->Create, :



  [MenuItem("Assets/Create/F# script")] public static void CreateFS() { string path = AssetDatabase.GetAssetPath(Selection.activeObject); string addNum = ""; if (Selection.activeInstanceID <= 0) { path="Assets"; } if (path.Contains(".")) { path =Directory.GetParent(path).ToString(); } while (File.Exists(path + "/NewBehaviourScript" + addNum.ToString() + ".fs")) { addNum = addNum == "" ? (1).ToString() : (int.Parse(addNum) + 1).ToString(); } path = path + "/NewBehaviourScript" + addNum.ToString() + ".fs"; StreamWriter sw = File.CreateText(path); sw.WriteLine("//Please, don't try to change namespace"); sw.WriteLine("namespace Assembly_FSharp_vs"); sw.WriteLine("type public " + "NewBehaviourScript" + addNum.ToString() + " () ="); sw.WriteLine(" inherit UnityEngine.MonoBehaviour()"); sw.WriteLine(" [<DefaultValue>] val mutable showdown1 : UnityEngine.Vector3"); sw.WriteLine(" [<DefaultValue>] val mutable showdown2 : UnityEngine.Vector3"); sw.WriteLine(" [<DefaultValue>] val mutable showdown3: int"); sw.WriteLine(" member public this.Start () = UnityEngine.Debug.Log(\"initialized\")"); sw.Flush(); sw.Close(); AssetDatabase.LoadAssetAtPath(path,Type.GetType("UnityEngine.DefaultAsset,UnityEngine")); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); }
      
      





MenuItem , , , , . . ( Selection.activeObject ) , , ( , ) ( , , , . , , Unity3d, ?). , activeInstanceID , , . , activeInstanceID . , , activeInstanceID . , , Assets( ). , , , NewBehaviourScript[\d]*\.fs, , F# , , , :

 //Please, don't try to change namespace namespace Assembly_FSharp_vs type public NewBehaviourScript1 () = inherit UnityEngine.MonoBehaviour() [<DefaultValue>] val mutable showdown1 : UnityEngine.Vector3 [<DefaultValue>] val mutable showdown2 : UnityEngine.Vector3 [<DefaultValue>] val mutable showdown3: int member public this.Start () = UnityEngine.Debug.Log("initialized")
      
      





— , ! .fs Visual Studio, , , , , IDE (, UnityEngine ). , :



 <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProductVersion>8.0.30703</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{ACFBFD03-C456-E983-5028-ACC6C3ACEA62}</ProjectGuid> <OutputType>Library</OutputType> <RootNamespace>Assembly_FSharp_vs</RootNamespace> <AssemblyName>Assembly_FSharp_vs</AssemblyName> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <Name>Assembly-FSharp-vs</Name> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <Tailcalls>false</Tailcalls> <OutputPath>bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <WarningLevel>3</WarningLevel> <DocumentationFile>bin\Debug\Assembly_FSharp_vs.XML</DocumentationFile> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <Tailcalls>true</Tailcalls> <OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <WarningLevel>3</WarningLevel> <DocumentationFile>bin\Release\Assembly_FSharp_vs.XML</DocumentationFile> </PropertyGroup> <ItemGroup> <Reference Include="mscorlib" /> <Reference Include="FSharp.Core" /> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Numerics" /> <Reference Include="D:/Program Files/Unity3.5/Editor/Data/Managed/UnityEngine.dll" /> </ItemGroup> <ItemGroup> <Compile Include="Assets/NewBehaviourScript.fs" /> <Compile Include="Assets/SphereMoving.fs" /> </ItemGroup> <Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\Microsoft.FSharp.Targets" Condition="!Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')" /> <Import Project="$(MSBuildExtensionsPath32)\..\Microsoft F#\v4.0\Microsoft.FSharp.Targets" Condition=" Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> </Target> <Target Name="AfterBuild"> </Target> --> </Project>
      
      









, XML — . Assembly-CSharp-vs.csproj. , , (csproj C# fsproj F#). , — (ReferenceInclude) (CompileInclude). , . .
 XML, . ProjectGUID. .

UnityEditor.VisualStudioIntegration.SolutionGuidGenerator ( , " SolutionGUIDGenerator Unity3d" ( ) 1 , .





, , ). GUID , . — . , , , ( ) :



 Project("ACFBFD03-C456-E983-5028-ACC6C3ACEA62") = ".\FSharp-csharp", "Assembly-FSharp-vs.fsproj", "{9512B9D0-6FAE-8F85-778C-B28C5421520D}" EndProject
      
      







— GUID . — GUID .

, «F#->Update soltuion file».

, :



  static string xmlSourceFileDataEnter = "<Compile Include=\""; static string xlmSourceFileDataEnding = "\" />"; [MenuItem("F#/Update solution file")] public static void UpdateSolution() { if(File.Exists("Assembly-FSharp-vs.fsproj")) { File.Delete("Assembly-FSharp-vs.fsproj"); } StreamWriter sw = File.CreateText("Assembly-FSharp-vs.fsproj"); sw.WriteLine(xmlEnterData); foreach (UnityEngine.Object file in typenameToObject.Values) { sw.Write(xmlSourceFileDataEnter); sw.Write(AssetDatabase.GetAssetPath(file)); sw.WriteLine(xlmSourceFileDataEnding); } sw.WriteLine(xmlFinishData); sw.Flush(); sw.Close(); sw.Dispose(); string[] slnfiles = Directory.GetFiles(".","*-csharp.sln"); if (slnfiles != null && slnfiles.Length>0) { StreamReader sr = File.OpenText(slnfiles[0]); List<string> lines = new List<string>(); while (!sr.EndOfStream) { string readenLine = sr.ReadLine(); lines.Add(readenLine); if (readenLine.Contains("Assembly-FSharp-vs.fsproj")) { sr.Close(); sr.Dispose(); return; } } sr.Close(); sr.Dispose(); sw = File.CreateText(slnfiles[0]); List<string>.Enumerator linesEnum = lines.GetEnumerator(); linesEnum.MoveNext(); sw.WriteLine(linesEnum.Current); linesEnum.MoveNext(); sw.WriteLine(linesEnum.Current); string slinname = slnfiles[0].Remove(slnfiles[0].LastIndexOf(".sln")); sw.WriteLine("Project(\"" + UnityEditor.VisualStudioIntegration.SolutionGuidGenerator.GuidForProject("Assembly-FSharp-vs") + "\") = \"" + slinname + "\", \"Assembly-FSharp-vs.fsproj\", \"{"+UnityEditor.VisualStudioIntegration.SolutionGuidGenerator.GuidForSolution(slinname)+"}\""); sw.WriteLine("EndProject"); while (linesEnum.MoveNext()) { sw.WriteLine(linesEnum.Current); } sw.Flush(); sw.Close(); sw.Dispose(); } }
      
      







xmlEnterData xmlFinishData , «Compile Include» .

UnityEngine , GUID . «Update solution file» 

, «Manual rebuild». , F# , , , . , , , UnityEngine.Object, . , , Unity , , . , , F# .

- , - . ProgressBar':







, ?


UnityEngine.Object . .


, . , , Unity3d . , .



PS ExecuteInEditMode MonoBehaviour ( Monobehaviour.Update ) - , . , Edit-time unity script, C#, ?





Edit-time





, , , . , , InitializeOnLoad ( ). , , , , . CustomInspector' , .

, . , , , - , yield-, . , , - yield- - . , :



 public class UniversalEnumerator : IEnumerator { List<Func<object>> allCodeFrames = new List<Func<object>>(); IEnumerator codeEnum; Func<object> finishAction = null; public Func<object> FinishAction { get { return finishAction; } set { finishAction = value; } } public void Add(Func<object> code) { allCodeFrames.Add(code); } public void _Finalize() { codeEnum = allCodeFrames.GetEnumerator(); } public object Current { get { return codeEnum.Current; } } public bool MoveNext() { bool res = codeEnum.MoveNext(); if (res) { (codeEnum.Current as Func<object>)(); } else { if (FinishAction != null) { return FinishAction()!=null; } } return res; } public void Reset() { codeEnum = null; allCodeFrames.Clear(); FinishAction = null; } }
      
      







, Add Func . MoveNext _Finalize codeEnum.Current. FinishAction — , , . , False, , ( , , ). UpdateLoop . InitializeOnLoad:



  static UniversalEnumerator currentRoutine; static CollectCompileDeploy() { UnityEditor.EditorApplication.update += new EditorApplication.CallbackFunction(() => { if (currentRoutine != null) { if (!currentRoutine.MoveNext()) { currentRoutine = null; } } }); Initialize(); }
      
      







Recompile UniversalEnumerator.



 public enum CompilationState { GATHER =1, COMPIL, VALIDATION, SOLUTION, DONE, NONE } public static UniversalEnumerator Recompile() { UniversalEnumerator myEnum = new UniversalEnumerator(); _current = CompilationState.GATHER; CompilationProgressWindow.Init(); myEnum.Add(() => { files.Clear(); ReassingTypes(); return null; }); bool exitall = false; myEnum.Add(() => { if (File.Exists("Assets/Assembly/Assembly-FSharp-vs.dll")) { AssetDatabase.DeleteAsset("Assets/Assembly/Assembly-FSharp-vs.dll"); File.Delete("Assets/Assembly/Assembly-FSharp-vs.dll"); } if (files.Count == 0) { Debug.Log("seems like no any F# file here.terminating"); _current = CompilationState.NONE; exitall = true; } return null; }); System.Type comparamtype = null; System.Type compilertype = null; myEnum.Add(() => { if (exitall) { return null; } UniversalEnumerator bufferedRoutine = currentRoutine; UniversalEnumerator nroutine = new UniversalEnumerator(); IEnumerator asmEnum = System.AppDomain.CurrentDomain.GetAssemblies().GetEnumerator(); while (asmEnum.MoveNext()) { Assembly asm = asmEnum.Current as Assembly; nroutine.Add(() => { if (asm.FullName.Contains("System, V")) { comparamtype = asm.GetType("System.CodeDom.Compiler.CompilerParameters"); } if (asm.FullName.Contains("FSharp.Compiler.CodeDom")) { compilertype = asm.GetType("Microsoft.FSharp.Compiler.CodeDom.FSharpCodeProvider"); } return null; }); } nroutine.FinishAction = () => { currentRoutine = bufferedRoutine; return new object(); }; nroutine._Finalize(); currentRoutine = nroutine; return null; }); myEnum.Add(() => { if (exitall) { return null; } UnityEditor.EditorApplication.LockReloadAssemblies(); try { object _params = System.Activator.CreateInstance(comparamtype, new object[] { new string[] { "System", "System.Core", UenginePath } }); comparamtype.GetProperty("IncludeDebugInformation").SetValue(_params, true, new object[] { }); comparamtype.GetProperty("OutputAssembly").SetValue(_params, @"Assets/Assembly/Assebly-FSharp-vs.dll", new object[] { }); object compiler = System.Activator.CreateInstance(compilertype); List<string> __fls = new List<string>(); foreach(UnityEngine.Object asset in typenameToObject.Values) { __fls.Add(AssetDatabase.GetAssetPath(asset)); } _current = CompilationState.COMPIL; object _output = compilertype.GetMethod("CompileAssemblyFromFile").Invoke(compiler, new object[] { _params, __fls.ToArray() }); compiled = _output.GetType().GetProperty("CompiledAssembly").GetValue(_output, new object[] { }) as Assembly; foreach (object message in _output.GetType().GetProperty("Output").GetValue(_output, new object[] { }) as System.Collections.Specialized.StringCollection) { Debug.Log(message); } foreach (object error in (_output.GetType().GetProperty("Errors").GetValue(_output, new object[] { }) as System.Collections.CollectionBase)) { Debug.LogError(error); } if (compiled != null) { _current = CompilationState.VALIDATION; UniversalEnumerator bufferedRoutine = currentRoutine; UniversalEnumerator nroutine = ValidateInspectors(); nroutine.Add(() => { _current = CompilationState.SOLUTION; UpdateSolution(); _current = CompilationState.DONE; CompilationProgressWindow.Remove(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); return null; }); nroutine._Finalize(); nroutine.FinishAction = () => { currentRoutine = bufferedRoutine; return new object(); }; currentRoutine = nroutine; } else { Debug.LogError("compiled assembly is still not visible!"); } } catch { } UnityEditor.EditorApplication.UnlockReloadAssemblies(); return null; }); myEnum._Finalize(); return myEnum; }
      
      





.

. :



 UniversalEnumerator bufferedRoutine = currentRoutine; UniversalEnumerator nroutine = new UniversalEnumerator(); nroutine.Add(() => { //  . return null; }); nroutine.FinishAction = () => { currentRoutine = bufferedRoutine; return new object(); }; nroutine._Finalize(); currentRoutine = nroutine;
      
      





UniversalEnumerator, currentRoutine UniversalEnumerator, FinishAction. new object() , MoveNext false, FinishAction , , EditorUpdateLoop MoveNext , false, currentRoutine null. - UniversalRoutine, , , .



?
:

1. .. Mono, IsBackground . , .

2. : Thread.Abort Unity3D : - . — . — (Debug.Log, ) 
 , .. , . , .

, .





enum' CompilationState, . _current:



 private static CompilationState __current = CompilationState.NONE; public static CompilationState _current { get { return CollectCompileDeploy.__current; } set { CollectCompileDeploy.__current = value; if (CompilationProgressWindow.me != null) { CompilationProgressWindow.me.Repaint(); } } }
      
      







, CompilationProgressWindow — , EditorWindow . Repaint , - , , . — .



:



 using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEditor; using UnityEngine; using System.Runtime.InteropServices; public class CompilationProgressWindow : EditorWindow { public static CompilationProgressWindow me; [StructLayout(LayoutKind.Sequential)] public struct WndRect { public int Left; public int Top; public int Right; public int Bottom; } #if UNITY_EDITOR && UNITY_STANDALONE_WIN [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll", SetLastError = true)] static extern bool GetWindowRect(IntPtr hWnd, out WndRect rect); public static Vector2 GetWNDSize() { WndRect r = new WndRect(); GetWindowRect(GetForegroundWindow(), out r); return new Vector2(r.Right - r.Left, r.Bottom - r.Top); ; } #else public static Vector2 GetWNDSize() { return Vector2.zero; } #endif public static void Init() { if (me == null) { Vector2 mainWND = GetWNDSize(); CompilationProgressWindow window = (CompilationProgressWindow)EditorWindow.GetWindow(typeof(CompilationProgressWindow), true, "F# Compilation progress", true); window.position = new Rect(mainWND.x/2-200, mainWND.y / 2 - 50, 400, 100); window.title = "F# compilation progress"; window.Focus(); me = window; } else { me.Focus(); } } void OnGUI() { string msg = ""; switch (CollectCompileDeploy._current) { case CollectCompileDeploy.CompilationState.GATHER: msg = "Gathering data"; break; case CollectCompileDeploy.CompilationState.NONE: this.Close(); break; case CollectCompileDeploy.CompilationState.COMPIL: msg = "Compiling F# assembly"; break; case CollectCompileDeploy.CompilationState.DONE: msg = "Done! Wait for assembly import and enjoy"; break; case CollectCompileDeploy.CompilationState.SOLUTION: msg = "Preparing solution files for usage"; break; case CollectCompileDeploy.CompilationState.VALIDATION: msg = "Validating editor scripts"; break; } EditorGUI.ProgressBar(new Rect(0, 0, 400, 100), ((float)CollectCompileDeploy._current) / 5f, msg); } public void OnLostFocus() { this.Focus(); } public static void Remove() { if (me != null) { me.Close(); } else { } } }
      
      





switch, . GetWNDSize. GetForegroundWindow System.Diagnostics.Process.GetCurrentProcess MainWindowTitle null, GetWindowRect , MainWindowHandle , (0,0). , . , , , , - , () . , , - Apple, , , . Unity3D . , . , , CollectCompileDeploy._current CompilationState.NONE.







. UnityPackage . ( « »?).

3














PS F# . . , .



UPD1 : , ( Flash — ). UnityPackage . gnoblin ' Android.



UPD2 : Google Docs



All Articles