やりたかったこと
a. event をいい感じの observable に変換する handler
b. observer
c. subscribe したときに処理されるなんらか
この b と c わりと一緒のクラスに書いちゃうと思うんですが、分けたい!
b の inspector から c を差し替えられるようにしたい!!
例えば
- drag event をいい感じに n 本指ドラッグ、ピンチインアウトの observable にする handler
- observer は2個だけ書けばいい
- ドラッグ、ピンチとでそれぞれ複数の挙動を用意したい
- 1本指は回転、2本指は移動とか
- モデルを動かすか、カメラを動かすかとか
- 挙動を足す度に observer 部分を書きたくない!
uml
(書き方が正しいかあんまり自信ない)
理想
Observer
DragObserver.cs
public class DragObserver : MonoBehaviour
{
[SerializeField] private IDragHandler handler;
[SerializeField] private int pointerCount;
[Inject] private UIDragHandler dragHandler;
void Start()
{
dragHandler.OnDragDeltaAsObservable(pointerCount)
.Subscribe(handler.OnDrag);
}
}
interface
IDragHandler.cs
interface IDragHandler
{
void OnDrag(Vector2 delta);
}
実装
TransformRotator.cs
public class TransformRotator : MonoBehaviour, IDragHandler
{
public void OnDrag(Vector2 delta)
{
Rotate();
}
}
現実
当然 interface は inspector に表示されません。
どうしても interface を使いたい!案
[SerializeField]
を持つ側でよしなにする場合
base class
InterfaceSerializeFieldMonoBehaviour.cs
public class InterfaceSerializeFieldMonoBehaviour<T> : MonoBehaviour
where T : class
{
[SerializeField] private MonoBehaviour implementsMonoBehaviour;
private T _interface;
protected T Interface
{
get
{
if (_interface == default(T))
{
_interface = implementsMonoBehaviour as T;
}
return _interface;
}
}
protected void OnValidate()
{
var t = implementsMonoBehaviour as T;
if (t == default(T))
{
implementsMonoBehaviour = null;
}
}
}
参考: https://forum.unity3d.com/threads/c-interface-wont-show-in-inspector.383886/#post-2498475
Observer
DragObserver.cs
public class DragObserver : InterfaceSerializeFieldMonoBehaviour<IDragHandler>
{
[SerializeField] private int pointerCount;
[Inject] private UIDragHandler dragHandler;
void Start()
{
dragHandler.OnDragDeltaAsObservable(pointerCount)
.Subscribe(Interface.OnDrag);
}
}
ふむ
一応 IDragHandler
を実装していない MonoBehaviour
は刺せないようにはなっています。
abstract でお茶を濁す案
abstract class
DragObserverBase.cs
public abstract class DragObserverBase : MonoBehaviour
{
[SerializeField] private int pointerCount;
[Inject] private UIDragHandler dragHandler;
void Start()
{
dragHandler.OnDragDeltaAsObservable(pointerCount)
.Subscribe(OnDrag);
}
protected abstract void OnDrag(Vector2 delta);
}
実装
TransformRotator.cs
public sealed class TransformRotator : DragObserverBase
{
protected override void OnDrag(Vector2 delta)
{
Rotate(delta);
}
}
そりゃまあ動く
どちらもあまり美しくない
- 今回の場合は abstract でもまあいい気もしてきたけど、 interface で美しく書けないの辛い
- もしかして Unity クソなのでは?
- いやいやお前の頭がクソだよというマサカリ募集中