254 lines
8.0 KiB
C#
254 lines
8.0 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace CreatureControl {
|
|
|
|
/// <summary>
|
|
/// A leg of a creature
|
|
/// </summary>
|
|
[System.Serializable]
|
|
public class Leg {
|
|
[SerializeField]
|
|
private Transform _coxa;
|
|
/// <summary>
|
|
/// The hip bone
|
|
/// </summary>
|
|
public Transform coxa {
|
|
get => this._coxa;
|
|
set {
|
|
this._coxa = value;
|
|
this._femurLength = 0;
|
|
}
|
|
}
|
|
|
|
public readonly static string nameOfFemur = nameof(_femur);
|
|
[SerializeField]
|
|
private Transform _femur;
|
|
/// <summary>
|
|
/// The upper leg or thigh bone
|
|
/// </summary>
|
|
public Transform femur {
|
|
get => this._femur;
|
|
set {
|
|
this._femur = value;
|
|
this._femurLength = 0;
|
|
}
|
|
}
|
|
|
|
public readonly static string nameOfTibia = nameof(_tibia);
|
|
[SerializeField]
|
|
private Transform _tibia;
|
|
/// <summary>
|
|
/// The lower leg or shank bone
|
|
/// </summary>
|
|
public Transform tibia {
|
|
get => this._tibia;
|
|
set {
|
|
this._tibia = value;
|
|
this._femurLength = 0;
|
|
this._tibiaLength = 0;
|
|
}
|
|
}
|
|
|
|
public readonly static string nameOfTarsus = nameof(_tarsus);
|
|
[SerializeField]
|
|
private Transform _tarsus;
|
|
/// <summary>
|
|
/// The foot bone
|
|
/// </summary>
|
|
public Transform tarsus {
|
|
get => this._tarsus;
|
|
set {
|
|
this._tarsus = value;
|
|
this._tibiaLength = 0;
|
|
this._tarsusLength = 0;
|
|
}
|
|
}
|
|
|
|
public readonly static string nameOfPhalanges = nameof(_phalanges);
|
|
[SerializeField]
|
|
private Transform _phalanges;
|
|
/// <summary>
|
|
/// The toes
|
|
/// </summary>
|
|
public Transform phalanges {
|
|
get => this._phalanges;
|
|
set {
|
|
this._phalanges = value;
|
|
this._tarsusLength = 0;
|
|
this._phalangesLength = 0;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private Transform _end;
|
|
/// <summary>
|
|
/// The end of the leg
|
|
/// </summary>
|
|
public Transform end {
|
|
get => this._end;
|
|
set {
|
|
this._end = value;
|
|
this._phalangesLength = 0;
|
|
}
|
|
}
|
|
|
|
#region Bones
|
|
|
|
[SerializeField]
|
|
private float _femurLength;
|
|
/// <summary>
|
|
/// The length of the femur bone
|
|
/// </summary>
|
|
public float femurLength {
|
|
get {
|
|
if (_femurLength <= 0 && this.femur != null && this.tibia != null)
|
|
_femurLength = Vector3.Distance(this.femur.position, this.tibia.position);
|
|
return _femurLength;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float _tibiaLength;
|
|
/// <summary>
|
|
/// The length of the tibia bone
|
|
/// </summary>
|
|
public float tibiaLength {
|
|
get {
|
|
if (_tibiaLength <= 0 && this.tibia != null && this.tarsus != null)
|
|
_tibiaLength = Vector3.Distance(this.tibia.position, this.tarsus.position);
|
|
return _tibiaLength;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float _tarsusLength;
|
|
/// <summary>
|
|
/// The length of the tarsus bone
|
|
/// </summary>
|
|
public float tarsusLength {
|
|
get {
|
|
if (_tarsusLength <= 0 && this.tarsus != null && this.phalanges != null)
|
|
_tarsusLength = Vector3.Distance(this.tarsus.position, this.phalanges.position);
|
|
return _tarsusLength;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float _phalangesLength;
|
|
/// <summary>
|
|
/// The length of the phalanges
|
|
/// </summary>
|
|
public float phalangesLength {
|
|
get {
|
|
if (_phalangesLength <= 0 && this.phalanges != null && this.end != null)
|
|
_phalangesLength = Vector3.Distance(this.phalanges.position, this.end.position);
|
|
return _phalangesLength;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The length of the leg from hip to foot
|
|
/// </summary>
|
|
/// This consists of the femur and tibia
|
|
public float length => femurLength + tibiaLength;
|
|
|
|
/// <summary>
|
|
/// The size of the foot
|
|
/// </summary>
|
|
/// This consists of the tarsus and phalanges
|
|
public float footLength => tarsusLength + phalangesLength;
|
|
|
|
public void ResetLengths() {
|
|
_femurLength = 0;
|
|
_tibiaLength = 0;
|
|
_tarsusLength = 0;
|
|
_phalangesLength = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to determine the leg bones
|
|
/// </summary>
|
|
/// <param name="firstLegBone">the first bone of the leg (could be coxa or femur depending on chain length)</param>
|
|
public void DetectBones(Transform root) {
|
|
if (root == null)
|
|
return;
|
|
|
|
// gather a straight chain following single-child links
|
|
List<Transform> chain = new();
|
|
Transform current = root;
|
|
chain.Add(current);
|
|
while (current.childCount == 1) {
|
|
current = current.GetChild(0);
|
|
chain.Add(current);
|
|
}
|
|
|
|
int count = chain.Count;
|
|
if (count <= 1)
|
|
// With one bone (and no end bone) it is not possible to compute the IK
|
|
return;
|
|
|
|
// the detected end bone is the last element in the collected chain
|
|
// this.end = chain[^1];
|
|
// count--;
|
|
|
|
if (count == 1)
|
|
this.femur = chain[0];
|
|
else if (count == 2) {
|
|
this.femur = chain[0];
|
|
this.tibia = chain[1];
|
|
}
|
|
else if (count == 3) {
|
|
this.femur = chain[0];
|
|
this.tibia = chain[1];
|
|
this.tarsus = chain[2];
|
|
}
|
|
else if (count == 4) {
|
|
this.femur = chain[0];
|
|
this.tibia = chain[1];
|
|
this.tarsus = chain[2];
|
|
this.phalanges = chain[3];
|
|
}
|
|
else // count >= 5
|
|
{
|
|
this.coxa = chain[0];
|
|
this.femur = chain[1];
|
|
this.tibia = chain[2];
|
|
this.tarsus = chain[3];
|
|
this.phalanges = chain[4];
|
|
}
|
|
}
|
|
|
|
#endregion Bones
|
|
|
|
/// <summary>
|
|
/// Check if all bones of the legs have been configured
|
|
/// </summary>
|
|
/// <returns>True when all bones are configured</returns>
|
|
public bool isConfigured =>
|
|
this.femur != null &&
|
|
this.tibia != null &&
|
|
this.tarsus != null;
|
|
// && this.phalanges != null
|
|
// && this.end != null;
|
|
|
|
public Side side;
|
|
public bool isLeft => side == Side.Left;
|
|
public bool isRight => side == Side.Right;
|
|
|
|
private Vector3 _kneeAxis = Vector3.zero;
|
|
public Vector3 kneeAxis {
|
|
get {
|
|
if (this._kneeAxis.sqrMagnitude == 0) {
|
|
Vector3 femurWorldDirection = (this.tibia.position - this.femur.position).normalized;
|
|
Vector3 tibiaWorldDirection = (this.tarsus.position - this.tibia.position).normalized;
|
|
Vector3 kneeWorldAxis = Vector3.Cross(femurWorldDirection, tibiaWorldDirection);
|
|
Vector3 kneeLocalAxis = this.femur.InverseTransformDirection(kneeWorldAxis);
|
|
this._kneeAxis = kneeLocalAxis;
|
|
}
|
|
return this._kneeAxis;
|
|
}
|
|
}
|
|
}
|
|
|
|
} |