概要
UnityにはFinalIKというのがありますが、1万円くらいするし、なんとなくIKっぽく動いてくれるだけで良い、という時には過剰という感じですし、何より色々な事情でFinalIKやそれに類するものが使えなかったりする時向けに、簡易的なIKっぽいものを書いてみました。
使い方
FinalIKに近い感じに使えるようにしたつもりです。
以下にそれぞれのスクリプトを付けて、操作対象の部分を割り当てます。
個別の説明は以下のとおりです。
TargetHandL/R:腕を動かす
TargetFootL/R:足を動かす
TargetHead:頭を動かす
TargetRoot:腰を動かす
TargetChest:胸部を曲げる
使用例
動機として、Transformの基礎的な実装しか持たないVCI(lua)でIKのような動きをさせたかった。というのがあります。
スクリプト
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;
}
}
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;
}
}
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;
}
}
using UnityEngine;
public class Root : MonoBehaviour
{
[SerializeField] Transform Target;
[SerializeField] Transform Model;
void Update()
{
Model.position = Target.position;
Model.rotation = Target.rotation;
}
}
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的な動きそのままです。腰はただ移動しているだけ、胸は曲げてるだけ、になります。また、ここに書いてあるスクリプトのままだと、全体の方向が変わった時に足の向きとかおかしくなるので、ターゲットの正面を使うとかの対応が必要だと思います。