数十行で書くなんちゃってIK

# 概要
UnityにはFinalIKというのがありますが、1万円くらいするし、なんとなくIKっぽく動いてくれるだけで良い、という時には過剰という感じですし、何より色々な事情でFinalIKやそれに類するものが使えなかったりする時向けに、簡易的なIKっぽいものを書いてみました。

# 使い方
FinalIKに近い感じに使えるようにしたつもりです。
以下にそれぞれのスクリプトを付けて、操作対象の部分を割り当てます。
個別の説明は以下のとおりです。
TargetHandL/R：腕を動かす
TargetFootL/R：足を動かす
TargetHead：頭を動かす
TargetRoot：腰を動かす
TargetChest：胸部を曲げる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286581/056bbbe1-b61d-d381-15cc-9170f4548aab.png)

# 使用例
https://twitter.com/120byte/status/1524788608976138241

<iframe width="560" height="315" src="https://www.youtube.com/embed/ToDd2NBHmRw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

動機として、Transformの基礎的な実装しか持たないVCI(lua)でIKのような動きをさせたかった。というのがあります。

# スクリプト
```cs:ArmIK.cs
using UnityEngine;

public class ArmIK : MonoBehaviour
{
    [SerializeField] Transform Target;
    [SerializeField] Transform Upper;
    [SerializeField] Transform Lower;
    [SerializeField] Transform Hand;
    [SerializeField] float LR; // 0:右、1:左

    float Distance;

    void Start()
    {
        Distance = Vector3.Distance(Upper.position, Hand.position);
    }

    void Update()
    {
        var position = Target.position - Upper.position;
        var rotation = Quaternion.LookRotation(position, Vector3.up);
        var offset = Quaternion.Euler(0, 90 * LR, 0);

        var distance = Vector3.Distance(Upper.position, Target.position);
        var max = Mathf.Max((Distance - distance) / Distance, 0);
        var upper = Quaternion.Euler(0, -90 * max * LR, 0);
        var lower = Quaternion.Euler(0, +90 * max * LR, 0);

        Upper.rotation = rotation * offset * upper;
        Lower.rotation = rotation * offset * lower;
        Hand.rotation = Target.rotation;
    }
}
```
```cs:LegIK.cs
using UnityEngine;

public class LegIK : MonoBehaviour
{
    [SerializeField] Transform Target;
    [SerializeField] Transform Upper;
    [SerializeField] Transform Lower;
    [SerializeField] Transform Foot;

    float Distance;

    void Start()
    {
        Distance = Vector3.Distance(Upper.position, Foot.position);
    }

    void Update()
    {
        var position = Target.position - Upper.position;
        var rotation = Quaternion.LookRotation(position, Vector3.forward);
        var offset = Quaternion.Euler(-90, 0, 0);

        var distance = Vector3.Distance(Upper.position, Target.position);
        var max = Mathf.Max((Distance - distance) / Distance, 0);
        var upper = Quaternion.Euler(-90 * max, 0, 0);
        var lower = Quaternion.Euler(+90 * max, 0, 0);

        Upper.rotation = rotation * offset * upper;
        Lower.rotation = rotation * offset * lower;
        Foot.rotation = Target.rotation;
    }
}
```
```cs:Look.cs
using UnityEngine;

public class Look : MonoBehaviour
{
    [SerializeField] Transform Target;
    [SerializeField] Transform Head;

    void Update()
    {
        var position = Target.position - Head.position;
        var rotation = Quaternion.LookRotation(position, Head.up);
        Head.rotation = rotation;
    }
}
```
```cs:Root .cs
using UnityEngine;

public class Root : MonoBehaviour
{
    [SerializeField] Transform Target;
    [SerializeField] Transform Model;

    void Update()
    {
        Model.position = Target.position;
        Model.rotation = Target.rotation;
    }
}
```
```cs:Chest.cs
using UnityEngine;

public class Chest : MonoBehaviour
{
    [SerializeField] Transform Target;
    [SerializeField] Transform Model;
    [SerializeField] Vector3 Offset;

    void Update()
    {
        Target.position = Model.position + Offset;
        Model.rotation = Target.rotation;
    }
}
```

# 補足
基本的な考え方ですが、LookAt的な動きに加えて、手足の長さに対してターゲットが近ければ肘膝を曲げる。という考え方です。頭はまさにLookAt的な動きそのままです。腰はただ移動しているだけ、胸は曲げてるだけ、になります。**<font color="Red">また、ここに書いてあるスクリプトのままだと、全体の方向が変わった時に足の向きとかおかしくなるので、ターゲットの正面を使うとかの対応が必要だと思います。</font>**