はじめに
最近UnityのUIElement
とUIBuilder
という新機能を試してみるために、お遊びでこんなものを作ってみました。
UIElement&UIBuilderでモールス信号スクリプトエディタを作ってみました!
— はなちる@ゲーム制作 (@hanaaaaaachiru) December 22, 2019
モールス信号からUnityで実行できるC#スクリプトを生成してくれます
普通にC#を書くのと比べて当社比10~20倍の時間が必要となる優れものです!!#unity pic.twitter.com/nlr3y0V3m8
これについて少し紹介をしたいと思います。
コード
使い方を書いていきたいと思ったのですが、おそらく使いたい人は誰一人いないと察したのでメインのコードをべたっと貼って終わりたいと思います。
程よく手を抜くことも人生肝心ですよね。
あとこのエディタ拡張はUIBuilder
を用いて作ったのですが、全部スタイルを.uxml
にべた書きになってしまっています。.uss
が空白になってしまっていますが、まあいいでしょう。
MorseCode.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements"> <ui:VisualElement style="flex-direction: row; width: 397px;"> <Style src="MorseCode.uss" /> <ui:VisualElement style="flex-direction: column; min-width: 70%;"> <ui:VisualElement style="display: flex;"> <ui:ScrollView> <ui:TextField picking-mode="Ignore" label="Script Name" value="filler text" text="Name" name="ScriptName" /> <ui:Label text="-" name="MorseText" style="white-space: normal; -unity-font-style: bold; font-size: 15px; border-left-width: 1px; border-right-width: 1px; border-top-width: 1px; border-bottom-width: 1px; border-top-left-radius: 3px; border-bottom-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-left-color: rgba(0, 0, 0, 255); border-right-color: rgba(0, 0, 0, 255); border-top-color: rgba(0, 0, 0, 255); border-bottom-color: rgba(0, 0, 0, 255); margin-left: 3px; margin-right: 3px; margin-top: 3px; margin-bottom: 3px;" /> </ui:ScrollView> <ui:Button text="・" name="TonButton" style="-unity-font-style: bold; font-size: 50px; flex-direction: column; position: relative; top: auto; right: auto; left: auto; bottom: auto; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> <ui:Button text="-" name="TuButton" style="-unity-font-style: bold; font-size: 50px; flex-direction: column-reverse; position: relative; top: auto; right: auto; left: auto; bottom: auto; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> </ui:VisualElement> <ui:VisualElement style="margin-top: 5%; margin-bottom: 0;"> <ui:Button text="SPACE" name="SpaceButton" style="font-size: 30px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> <ui:Button text="lowercase character" name="CharacterButton" focusable="false" style="font-size: 20px; display: flex; visibility: visible; opacity: 1; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> <ui:Button text="DELETE" name="DeleteButton" style="font-size: 20px; margin-top: 10px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> </ui:VisualElement> <ui:VisualElement style="margin-top: 5%; margin-bottom: 0;"> <ui:Button text="DEBUG" name="DebugButton" style="font-size: 20px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> <ui:Button text="SAVE" name="SaveButton" style="font-size: 20px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> <ui:Button text="CLEAR" name="ClearButton" style="font-size: 20px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0);" /> </ui:VisualElement> </ui:VisualElement> <ui:VisualElement style="min-width: 32%;"> <ui:ScrollView name="MorseList" style="margin-right: 0; border-left-color: rgba(0, 0, 0, 255); border-right-color: rgba(0, 0, 0, 255); border-top-color: rgba(0, 0, 0, 255); border-bottom-color: rgba(0, 0, 0, 255); border-right-width: 0; border-left-width: 1px; margin-left: 0;" /> </ui:VisualElement> </ui:VisualElement> </ui:UXML>
MorseCodeEditor.cs
using UnityEngine; using UnityEditor; using UnityEngine.UIElements; using System.Text.RegularExpressions; using System; using System.IO; using System.Collections.Generic; using System.Linq; namespace hanachiru { public class MorseCodeEditor : EditorWindow { public static MorseCodeList MorseCode { get; private set; } private static MorseCodeContainer _container; private string TopDirectoryPath => Directory.GetDirectories("Assets", "*", System.IO.SearchOption.AllDirectories).FirstOrDefault(path => System.IO.Path.GetFileName(path) == "MorseCodeEditor"); private const string MAKE_SCRIPT = @" using UnityEngine; public class [ClassName] : MonoBehaviour { private void Start() { [Script] } } "; [MenuItem("Tools/MorseCoding")] public static void Create() { _container = new MorseCodeContainer(); MorseCode = null; EditorWindow.GetWindow<MorseCodeEditor>(); } private void OnEnable() { // UXMLを取得 var UXML = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(TopDirectoryPath + "/Editor/Resources/MorseCode.uxml").CloneTree(); // rootに追加する rootVisualElement.Add(UXML); rootVisualElement.Query<Button>("TonButton").AtIndex(0).clicked += OnClickTonButton; rootVisualElement.Query<Button>("TuButton").AtIndex(0).clicked += OnClickTuButton; rootVisualElement.Query<Button>("SpaceButton").AtIndex(0).clicked += OnClickSpaceButton; rootVisualElement.Query<Button>("CharacterButton").AtIndex(0).clicked += OnClickCharacterButton; rootVisualElement.Query<Button>("DebugButton").AtIndex(0).clicked += () => Debug.Log(_container.ToString()); rootVisualElement.Query<Button>("SaveButton").AtIndex(0).clicked += OnClickSaveButton; rootVisualElement.Query<Button>("ClearButton").AtIndex(0).clicked += OnClickClearButton; rootVisualElement.Query<Button>("DeleteButton").AtIndex(0).clicked += OnClickDeleteButton; rootVisualElement.Query<Label>("MorseText").AtIndex(0).text = " "; if (MorseCode == null) { // モールス信号の対応リストを読み込む var path = TopDirectoryPath + "/Editor/Resources/MorseCodeSetting.asset"; MorseCode = AssetDatabase.LoadAssetAtPath<MorseCodeList>(path); if (MorseCode == null) Debug.LogError("モールス信号の対応リスト(MorseCodeSetting)を読み取れませんでした"); // 一覧をウィンドウの右側に表示する var morseList = rootVisualElement.Query<ScrollView>("MorseList").AtIndex(0); foreach (var item in MorseCode.morseCodes) { var newLabel = new Label(" " + item.LowercaseChar + " 「" + item.Key + "」 "); morseList.Add(newLabel); } } } private void OnClickTonButton() { _container.Add("・"); rootVisualElement.Query<Label>("MorseText").AtIndex(0).text = _container.CodeText; } private void OnClickTuButton() { _container.Add("-"); rootVisualElement.Query<Label>("MorseText").AtIndex(0).text = _container.CodeText; } private void OnClickSpaceButton() { _container.Space(); rootVisualElement.Query<Label>("MorseText").AtIndex(0).text = _container.CodeText; } private void OnClickCharacterButton() { var text = _container.ChangeCharacterType(); rootVisualElement.Query<Button>("CharacterButton").AtIndex(0).text = text; } private void OnClickSaveButton() { var scriptName = rootVisualElement.Query<TextField>("ScriptName").AtIndex(0).text; if (String.IsNullOrEmpty(scriptName)) scriptName = "ScriptMadeByMorseCode"; var filePath = $"{TopDirectoryPath}/{scriptName}.cs"; var assetPath = AssetDatabase.GenerateUniqueAssetPath(filePath); var script = MAKE_SCRIPT.Replace("[ClassName]", scriptName) .Replace("[Script] ", _container.ToString()); File.WriteAllText(assetPath, script); AssetDatabase.Refresh(); } private void OnClickClearButton() { _container.Clear(); rootVisualElement.Query<Label>("MorseText").AtIndex(0).text = " "; } private void OnClickDeleteButton() { _container.Delete(); rootVisualElement.Query<Label>("MorseText").AtIndex(0).text = _container.CodeText; } } internal class MorseCodeContainer { private List<string> _codes; private string Code => string.Join("", _codes); public string CodeText => Regex.Replace(Code, "\\[[^\\]]*?\\]", String.Empty); private enum CharacterType { LowerCase, UpperCase } private CharacterType _characterType = CharacterType.LowerCase; public MorseCodeContainer() { _codes = new List<string>(); } public void Add(string text) { if (_characterType == CharacterType.UpperCase && (_codes.Count == 0 || _codes[_codes.Count - 1] == " ")) { _codes.Add("[upper]" + text); } else { _codes.Add(text); } } public void Space() { if (_codes[_codes.Count - 1] == " " || _codes[_codes.Count - 1] == "[upper] ") { _codes.Add("[space]"); } else { _codes.Add(" "); } } public string ChangeCharacterType() { if (_characterType == CharacterType.LowerCase) { _characterType = CharacterType.UpperCase; return "UPPERCASE CHARACTER"; } else { _characterType = CharacterType.LowerCase; return "lowercase character"; } } public void Delete() => _codes.RemoveAt(_codes.Count - 1); public void Clear() => _codes = new List<string>(); public override string ToString() { string text = " " + Code + " "; // 大文字に変更 foreach (var morse in MorseCodeEditor.MorseCode.morseCodes) { text = text.Replace(" [upper]" + morse.Key + " ", morse.UppercaseChar); } // 小文字に変更 foreach (var morse in MorseCodeEditor.MorseCode.morseCodes) { text = text.Replace(" " + morse.Key + " ", morse.LowercaseChar); } // 空白の削除 text = text.Replace(" ", String.Empty); // スペースの作成 text = text.Replace("[space]", " "); // 余分な箇所を削除 text = text.Replace("・", String.Empty).Replace("-", String.Empty); return text; } } }
さいごに
一応GitHubにて.unitypackage
を公開しておくので、もし使ってみたいという方がいれば是非。
github.com