2025-06-08 00:55:10 +09:00

146 lines
3.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class CCDIKSolver : MonoBehaviour
{
/* MEMO
* --------------------
* IKボーンにこのスクリプトを適用して、Solveを呼び出す。
* target = ターゲットIKボーンを目指すボーン
* chains = IK影響下ボーン
* --------------------
*/
// Target
// IKボーンを目指すボーン
public Transform target;
// Loop count
// ushortだとInspectorに出ない
public int iterations;
// rad limit
// "単位角"
public float controll_weight;
// IK影響下ボーン
public Transform[] chains;
// レイの描画(デバッグ用)
public bool drawRay = false;
// 計算
public void Solve()
{
// 有効化されてなかった場合は実行しない
if (!this.enabled) return;
// ループ回数で計算していく
for (int tries = 0, _mt = iterations; tries < _mt; tries++)
{
// IK影響下ボーンごとに
for (int _i = 0, _m = chains.Length; _i < _m; _i++)
{
var bone = chains[_i];
var bonePos = bone.position;
// エフェクタ設定
var effectorPos = target.position;
var effectorDirection = (effectorPos - bonePos);
// ターゲット設定
var targetDirection = (transform.position - bonePos);
// 線を引いといてみる
if (drawRay)
{
Debug.DrawRay(bonePos, effectorDirection, Color.green);
Debug.DrawRay(bonePos, targetDirection, Color.red);
}
// 内積
effectorDirection = effectorDirection.normalized;
targetDirection = targetDirection.normalized;
float rotateDot = Vector3.Dot(effectorDirection, targetDirection);
// 角度算出。
// controll_weightによる一回の計算での制限
float rotateAngle = Mathf.Acos(rotateDot);
if (float.IsNaN(rotateAngle)) continue;
var limit = 4 * controll_weight * (_i + 1);
if (rotateAngle > limit)
rotateAngle = limit;
if (rotateAngle < -limit)
rotateAngle = -limit;
rotateAngle *= Mathf.Rad2Deg;
// 外積で回転軸算出
// lockDirectionを見て、回転制限 => 一旦なし
var rotateAxis = Vector3.Cross(effectorDirection, targetDirection).normalized;
// 大丈夫そうなら
if (float.IsNaN(rotateAxis.x) || float.IsNaN(rotateAxis.y) || float.IsNaN(rotateAxis.z))
continue;
// 回す
var rotate = Quaternion.AngleAxis(rotateAngle, rotateAxis);
bone.rotation = rotate * bone.rotation;
// 角度制限
limitter(bone);
}
}
}
// 角度制限
void limitter(Transform bone)
{
// 足首のZ回転
if (bone.name.Contains("足首"))
{
var vv = bone.localEulerAngles;
vv.z = 0;
bone.localRotation = Quaternion.Euler(vv);
return;
}
// 本来なら設定値に基づいてやるけど、とりあえず膝限定
if (!bone.name.Contains("ひざ"))
return;
// オイラー角を取得
var v = bone.localEulerAngles;
// y,z回転を無効化
if (adjust_rot(v.y) == adjust_rot(v.z))
{
v.y = adjust_rot(v.y);
v.z = adjust_rot(v.z);
}
// 逆に曲がらないように、制限してあげる
if (v.x < 90 && v.x > 2 && ((v.y == 0 && v.z == 0) || (v.y == 180 && v.z == 180)))
v.x = 360 - v.x * 0.99f;
bone.localRotation = Quaternion.Euler(v);
}
// 0か180か近い方に固定
int adjust_rot(float n)
{
if (Mathf.Abs(n) > Mathf.Abs(180 - n) && Mathf.Abs(360 - n) > Mathf.Abs(180 - n))
return 180;
else
return 0;
}
// MMDEngine.cs の方でIKボーン全体も保持してるので、
// そっちからSolve()を呼び出している。
//--------------------
//void LateUpdate()
//{
// Solve();
//}
}