しごとのまっくにいれたい
- 入れておきたい
- Atom
- Unity Hub
- Unity
- Visual Studio Community
- Sketch
- SourceTree
- HomeBrew
TDD
UnityでTest書く
- Window -> General -> TestRunner
- EditModeタブ、[Create Test Assembly Folder]押す
原則 RED→GREEN→REFACTER→RED...
- まずテストを書き、エラーになることを確認して、
- 実装して、エラーを解消。
- 実装をリファクタ
using NUnit.FrameWork; using Unity.Engine; public class HogeTest { [Test] [TestCase(3)] [TestCase(6)] public void GetHogeTest(int num) { var hoge = new Hoge(); var value = new hoge.GetHoge(num); Assert.AreEqual("Hoge", value); } } public class Hoge { public string GetHoge(int num) { if(num % 3 == 0){ return "Hoge";} return ""; } }
なんかこんなかんじ
仮想抽象インターフェース
使わなかったらすごい忘れそう
interface インターフェース
- abstract 抽象
virtual 仮想
以下大体の使い方
public interface IBaseClass { int Level { get; } void SetLevel(int level); } public abstract class BaseClass : IBaseClass { public int Level { get; private set; } public void SetLevel(int level) { Level = level; } protected virtual string Path { get; private set; } protected abstract string Name { get; } public virtual void TestVirtual() { Debug.Log("TestVirtual"); } public abstract void TestAbstract(); } public class SuperClass : BaseClass { protected override string Path { get { return "Path"; } } protected override string Name { get { return "Name"; } } public override void TestVirtual() { base.TestVirtual(); Debug.Log("TestSuperVirtual"); } public override void TestAbstract() { Debug.Log("SuperAbstract"); } } public class TestClass { public TestClass() { //BaseClass baseClass = new BaseClass(); // ←抽象クラスはインスタンス作成できない。 SuperClass superClass = new SuperClass(); BaseClass baseClass = superClass; //これはできる IBaseClass baseInterface = superClass; //これもできる //interfaceで定義したなら派生クラスでもつかえる var level = baseInterface.Level; level = baseClass.Level; level = superClass.Level; baseInterface.SetLevel(level); baseClass.SetLevel(level); superClass.SetLevel(level); //baseInterface.TestVirtual();//interfaceでは定義されていないのでできない baseClass.TestVirtual();//BaseClassでは定義されているので使える superClass.TestVirtual();//SuperClassでoverrideしたMethodを定義している場合、そっちを使う baseClass.TestAbstract();//baseClassに代入されているSuperClassのMethodが走る superClass.TestAbstract();//同様 } }
UniRxでUI等を監視する
//監視されるがわ public class ObservableTest : MonoBehaviour { [SerializeField] Button button; public IObservable<Unit> OnClick { get { return button.OnClickAsObservable(); } } [SerializeField] Button returnTextButton; public IObservable<string> OnClickText { get { return returnTextButton .OnClickAsObservable() .Select(_ => return "SelectでStringをとったり"); } } [SerializeField] Slider slider; public IObservable<float> OnSlide { get { return slider.OnValueChangedAsObservable(); } } [SerializeField] Toggle toggle; public IObservable<bool> OnToggle { get { return toggle.OnValueChangedAsObservable(); } } public IObservable<Unit> OnStart { get { return onStart; } } Subject<Unit> onStart = new Subject<Unit>(); void Start() { onStart.OnNext(Unit.Default); } } //監視する側 public class ObserverTest { public void Init(ObservableTest observable) { //一行なら{}を書かなくても良い observable.OnClick.Subscribe(_ => Debug.Log("OnClick")); observable.OnSlide.Subscribe(f => Debug.Log("OnSlide:" + f.ToString())); observable.OnToggle.Subscribe(b => Debug.Log("OnToggle:" + b.ToString())); observable.OnStart.Subscribe(_ => Debug.Log("Start")); } }
UniRx ReactiveProperty めも
- 下記のようなやつを
public interface IReactiveTestModel { IReadOnlyReactiveProperty<int> Num { get; } IReadOnlyReactiveProperty<string> Text { get; } void UpdateProperties(int updateNum, string updateText) } public class ReactiveTestModel : IReactiveTestModel { public IReadOnlyReactiveProperty<int> Num { get {return num;} } public IReadOnlyReactiveProperty<string> Text { get {return text;} } IntReactiveProperty num = new IntReactiveProperty(); StringReactiveProperty text = new StringReactiveProperty(); public ReactiveTest(int initNum, string initText) { num.Value = initNum; text.Value = initText; } public void UpdateProperties(int updateNum, int updateText) { num.Value = updateNum; text.Value = updateText; } }
- 下記のように使う
public class ReactiveTest : MonoBehaviour { [SerializeField] Text level; [SerializeField] Text description; IReactiveTestModel model; void Init(IReactiveTestModel model) { this.model = model; model.num.Subscribe(n => {level.text = n.ToString(); }); model.text.Subscribe(t => { description.text = t; }); } void SetModelValues(int currentLevel, string currentDescription) { model.UpdateProperties(currentLevel, currentDescription); } }
- Modelの数値の変化を監視して、それを反映させることができる。
- 他にもReactiveCollectionとかいろいろある。
■
ScriptableObjectを使ってBuildProfileを簡単に作って、保存して、ビルド時に使い分けられるようにしたかったときのこと
- ScriptableObjectでPlayerSettingsに入れる値を保存する
- ただの箱
- これを継承した各プラットフォーム用のProfileClassを作成
- androidのKeyStoreName等
public class BuildProfile : ScriptableObject { public string artifactName;//生成ファイルの名前 public string bundleIdentifier; public string bundleVersion; public string productName; public string companyName; public bool release; public List<string> defineSymbols; public string iconPath; public int accelerometerFrequency; public List<string> ignorePaths;//どけときたいフォルダ }
- EditorWindowで簡単に扱えるようにしたい
- BuildProfileのリストを持ち、Build実行や、中身の確認をするEditorWindowを開くためのWindow
public class ProfileWindow : EditorWindow { [MenuItem("File/BuildProfiles")] static void Init() { GetWindow<ProfileWindow>("BuildProfiles"); } void OnGUI() { //例にアンドロイド. isAndroidFoldOut = EditorGUILayout.Foldout(isAndroidFoldOut, "Android"); if(isAndroidFoldOut) { EditorGUI.indentLevel++; foreach(var androidProfile in androidProfiles) { if(androidProfile == null) { RefreshBuildProfiles(); return; } ButtonLayout(androidProfile, BuildTarget.Android); } AddNewBuildProfileLayout(BuildTarget.Android); EditorGUI.indentLevel--; } GUILayout.EndVertical(); } void ButtonLayout(BuildProfile buildProfile, BuildTarget buildTarget) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(buildProfile.name); if(GUILayout.Button("Select")) { EditorGUIUtility.PingObject(buildProfile); Selection.objects = new[] { buildProfile }; } if(GUILayout.Button("Build")) { Build(buildProfile, buildTarget); } EditorGUILayout.EndHorizontal(); } }
- BuildProfileの中身を見るEditorWindowも欲しさ
- PlayerSettingsの値と見比べて、違うなら強調表示するやつがほしかった
- いわゆるPlaceHolder
- それぞれのプラットフォームに合わせて継承したクラスも作る
- PlayerSettingsの値と見比べて、違うなら強調表示するやつがほしかった
public class ProfileEditor : EditorWindow { public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); PropertyLayouter(); ButtonLayouter(); if(EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(buildProfile); } } protected virtual void PropertyLayouter() { buildProfile.artifactName = PlaceHolderTextField("artifactName",buildProfile.artifactName,buildProfile.name); //以下それぞれのプロパティ書いていく } //このWindowでもビルドしたり、Profile選択Window開きたい的な protected virtual void ButtonLayouter() { if(GUILayout.Button("Open Window")) { EditorWindow.GetWindow<ProfileWindow>("BuildProfiles"); } if(GUILayout.Button("Build " + buildTargetGroup.ToString())) { Build(); GUIUtility.ExitGUI(); } } protected string PlaceHolderTextField(string propertyName, string input, string placeHolder) { GUI.SetNextControlName(propertyName); if(!string.IsNullOrEmpty(input) || GUI.GetNameOfFocusedControl() == propertyName) { return EditorGUILayout.TextField(propertyName, input); } EditorGUILayout.TextField(propertyName, placeHolder, defaultStyle); return input; } }
- BuildPipeline.BuildPlayer()
ビルド中にScriptableObjectのインスタンスが破棄されるのでScriptableObjectにバックアップをとっておくとかはできない。
でもPlayerSettingsはバックアップを取りたい
- ので、全部コピー。
using System.IO; public class Builder { const string PlayerSettingsPath = "ProjectSettings/ProjectSettings.asset"; const string BackupPlayerSettingsPath = "Temp/BackupProjectSettings.asset"; public void BuildWithProfile(BuildProfile profile) { //他にもapk等の保存先を作るとかする BackupProjectSettings();//バックアップ SetProfile();//BuildProfile適用 Build();//実行 RestorePlayerSetting();//もとに戻す //バージョニングとかもする } void BackupCurrentPlayerSetting() { if(!File.Exists(PlayerSettingsPath)) { Debug.Log("not Exist"); return; } File.Copy(PlayerSettingsPath, BackupPlayerSettingsPath, true); } void SetProfile() { //PlayerSettingsにBuildProfileを入れる処理 } void Build() { //BuildPipeline.BuildPlayer() } void RestorePlayerSetting() { File.Copy(BackupPlayerSettingsPath, PlayerSettingsPath, true); File.Delete(BackupPlayerSettingsPath); AssetDatabase.Refresh(); } }
そういった次第だった