【Unity2018 Constraint】ConstraintSourceをスクリプトから動的に登録/削除するときの諸々

Unity2018で登場したConstraint系コンポーネント．
使い方はわかったけど，スクリプトでこのコンポーネントを操作するにはどうすればいいか．自分がはまったところを中心に語っていきたいと思います．なお，Constraintについての説明は省略気味で行きます．僕が説明するよりも，先人の人が詳しく説明しているので，下記を見てください．

##Unityのオブジェクトの親子関係について##
従来のUnityの親子関係は，親と子の関係は１対多になっています．(下の図のようにHierarchyで子オブジェクトの階層が下がってます．)
そして，親オブジェクトのTransformの変化を子オブジェクトも受けるというのが特徴的でした．
ただ，UIだと必ず子オブジェクトが前面に出てしまうという欠点がありました．親子関係を持つとレイヤー分けができないこととUIだとLayerの設定を変えても適用されないのが原因みたいですが．．．(参考：)
もちろん，SpriteRendererのSortingLayerで描画順を設定すればこの問題を解決できます．でも，これだとUIを使えないという悲しい結果になります．

##Constraintのアバウトな説明##
Constraintは直訳すると制限って意味です．このコンポーネントを使うとオブジェクトの動作について他のオブジェクトとの関係を使っての制限を設けることができます．

詳しくはコチラに書いてあります．
http://tsubakit1.hateblo.jp/entry/2018/01/12/233522

※注意※
今後の説明で影響を受けさせるオブジェクトのことを便宜上「親オブジェクト」と表現します．

Constraintは，主観的にいうと3点の特徴が挙げられます．

1:親子関係が多対１な形を形成できること．

2:親子関係のUIを作る必要がないために，Hierarchyの位置を変えるだけで，描画順を変えられる．
(※描画順の解決にはなってない)

3:Constraint(制限)を受ける場所について，Positonのみ，Rotationのみ，全部(Parent)の3種類があって，状況で使い分けができる．




#今回のスクリプト#
最近，GitHubでUnityのエディタがどのように動いているか公開されました．これを見ながらコードについて軽く説明します．
コードだけ知りたい人は，下記のスクリプトを見てください．

##コードの流れ##

１：Constarint系コンポーネントを取得

２：ConstraintSource構造体を作成し・どのオブジェクトによる制限を受けさせるか設定

３：Constraintに２で作成したConstraintSourceをAdd

##はまりポイント##

はまりポイントは主に２つ．

１：親オブジェクトの要素を追加する場合は，ConstarintSource構造体を作成してそこにオブジェクトのTransform系の情報を追加しないといけない．
２：１の追加に加えて，影響度Weightを1に設定する必要がある．

Weightは影響度のことで0~1のfloat値が入ります．0だと設定していても親オブジェクトへの支配が起きず，1であれば完全に支配します．
完全な支配になると親オブジェクトが変化(Transformが変化)しない限り，そのConstarintを付けられたオブジェクトは変化できないというものです．
スクリプトで追加するときには，Weightがデフォルトで0になっちゃう仕様のようで，これを見落としていたことが大きなはまりポイントでした．

ConstraintSourceについては，下記のGitHubのUnityリファレンスに書いてあります．これを踏まえて作っていきます．

https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Animation/ScriptBindings/Constraint.bindings.cs

##スクリプト##

今回は，PositionConstraint(位置のみ)とParentConstraint(親子関係みたいなもの)の2パターン用意しました．
はまりポイントを解決することを考慮するとちょっとくどい感じになっちゃいますね．
Constraintコンポーネントの有効無効は，isActiveでもいいかもしれません．

```CSVDataReader.cs

#define POSITION_CONSTRAINT
#undef PARENT_CONSTRAINT

using UnityEngine;
using UnityEngine.Animations; // これがないと Constraintは使えない

public class ConstraintTest : MonoBehaviour {

    private ConstraintSource myConstraintSource;        // ConstarintSource
#if POSITION_CONSTRAINT
    private PositionConstraint myPositionConstraint;    // Position Type
#endif
#if PARENT_CONSTRAINT
    private ParentConstraint myParentConstraint;        // Parent Type
#endif

    [SerializeField]
    private RectTransform SouceRectTransform;// ConstraintSourceの設定にはTransformももちろんOK

    /// <summary>
    /// ConstraintComponentの親オブジェクトを動的に追加する関数
    /// </summary>
    /// <param name="parent">Contraintの親オブジェクト</param>
    private void SetObject2ConstraintSource ( RectTransform parent ) {
        // Constraintの参照元を設定(この処理が一つでも欠けると追尾せず即死するので注意)
        this.myConstraintSource.sourceTransform = parent;
        this.myConstraintSource.weight = 1.0f; // 影響度を完全支配にする(Addの場合は0になるので)
#if POSITION_CONSTRAINT
        this.myPositionConstraint.AddSource(this.myConstraintSource);   // Constraintの参照元を追加
        this.myPositionConstraint.translationOffset = Vector3.zero;     // オフセットを0に
        this.myPositionConstraint.enabled = true;                       // 有効にする(使わないときはfalse)
#endif
#if PARENT_CONSTRAINT
        this.myParentConstraint.AddSource(this.myConstraintSource);     // Constraintの参照元を追加
        this.myParentConstraint.SetTranslationOffset(0, Vector3.zero);  // オフセットを0に
        this.myParentConstraint.enabled = true;                         // 有効にする(使わないときはfalse)
#endif
    }

    /// <summary>
    /// Constraintの設定を解除する
    /// </summary>
    private void DeleteSource ( ) {
#if POSITION_CONSTRAINT
        this.myPositionConstraint.RemoveSource(0);  // Sourceから削除
        this.myPositionConstraint.enabled = false;  // 無効にする 
#endif

#if PARENT_CONSTRAINT
        this.myParentConstraint.RemoveSource(0);  // Sourceから削除
        this.myParentConstraint.enabled = false;  // 無効にする 
#endif
    }

    private void Start ( ) {
#if POSITION_CONSTRAINT
        this.myPositionConstraint = GetComponent<PositionConstraint>( );
#endif
#if PARENT_CONSTRAINT
        this.myParentConstraint = GetComponent<ParentConstraint>();
#endif
    }

    private void Update ( ) {
        // Constarintの有効無効を設定する
        if (Input.GetKeyDown("r")) {
#if POSITION_CONSTRAINT
            if (this.myPositionConstraint.sourceCount > 0) {
                DeleteSource( );
            }
#endif
#if PARENT_CONSTRAINT
            if (this.myParentConstraint.sourceCount > 0) {
                DeleteSource( );
            }
#endif
            else {
                SetObject2ConstraintSource(this.SouceRectTransform);
            }
        }
    }
}

```

次に親オブジェクトにアタッチするスクリプトです．(ファイル名がてきとうなのは仕様です．)

``` Hoge.cs
using UnityEngine;

public class Hoge : MonoBehaviour {

    private bool canMove = false;           // 追尾可能かのフラグ
    private RectTransform myRectTransform;  // オブジェクト自身の位置

    /// <summary>
    /// マウスで移動する処理(UIバージョン)
    /// </summary>
    /// <param name="rTransform">位置</param>
    /// <param name="camera">変換用のカメラ</param>
    private void MoveOnMouse(ref RectTransform rTransform,Camera camera)
    {
        Vector3 mpos = Input.mousePosition;
        mpos.z = 10.0f;
        mpos = camera.ScreenToWorldPoint(mpos);
        rTransform.position = RectTransformUtility.WorldToScreenPoint(camera, mpos);
    }

	// Use this for initialization
    void Start () {
        this.myRectTransform = GetComponent<RectTransform>( );
    }
	
    // Update is called once per frame
    void Update () {
        // マウスカーソルに追尾するかどうかを切り替える
        if (Input.GetKeyDown("space")) {
            this.canMove = !this.canMove;
        }
        // 追尾可能ならマウスに追従させる
        if (this.canMove) {
            MoveOnMouse(ref this.myRectTransform, Camera.main);
        }
    }
}
```

#動作テスト#

では，実際に動かしてみましょう．今回はUIのImageを使っていきます．
Imageを2こSceneに配置し，それぞれ"Parent"，"Child"という名前にしてください．
そのあと，下記のように設定をしてください．

ここでChildの設定時に注意点が2つあります．
１：Constraint系コンポーネントでIsActiveにチェックを入れておくこと(デフォルトだと入ってません)
２：ConstraintTestスクリプトのSourceRectTransformにParentをアタッチすること(追従する親オブジェクトの登録ができません)

![00001.png](https://qiita-image-store.s3.amazonaws.com/0/96154/75abf4a7-b909-a45c-33cd-898ba546050d.png "図1.1：Parentの設定")　　![00002.png](https://qiita-image-store.s3.amazonaws.com/0/96154/c79b3748-5ff5-5c72-2ca0-7209f6d82ec7.png "図1.2：Childの設定")

最後に描画順を考慮して，上からChild，Parentとなるように配置してください．
これで完成です．
さっそくプレビューで動かしてみましょう．

Spaceキーを押すとParentがマウスカーソルの位置に追従します．ここでRキーを押すとChildオブジェクトもParentを追従しだし，マウスカーソルに追従するような動きを見せてくれるはずです．(ここまででうまくいかない場合は最初からやり直してみてください)
さて，一度Spaceキーでマウスカーソルの追従を止めて，ChildオブジェクトのInspectorを見てみましょう．

![00003.png](https://qiita-image-store.s3.amazonaws.com/0/96154/3d16b66d-aed4-1b2b-26d2-bcaff0521c59.png)

はい，ParentがSourcesに追加されていますね．横の数値が先述した影響度になります．ここでこれを0にして，再度Spaceキーを押してみましょう．Parentしか追従しなくなるはずです．


というわけで，Constraint系コンポーネントをスクリプトから動的に扱うための備忘録でした．
間違いがありましたらご指摘お願いします．