Files
BABA_YAGA/Assets/Scripts/Player/Generic/BodySnapSystem/Scripts/vBodySnappingControl.cs
2026-06-04 10:42:23 +07:00

216 lines
7.2 KiB
C#

using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Invector
{
[vClassHeader("Body Snapping Control", openClose = false)]
public class vBodySnappingControl : vMonoBehaviour
{
[vButton("Create New BodyStruct", "NewBodyStruct", typeof(vBodySnappingControl), false)]
[vButton("Load Bones", "LoadBones", typeof(vBodySnappingControl), false)]
//[vHelpBox("To create a new BodyStruct for a new Character to go: \n<b>*Menu Create/Invector/New Body Struct</b>, you can set for both Humanoid or Generic Rig")]
public vBodyStruct bodyStruct;
public bool showLabels;
[HideInInspector] public List<vBoneTransformSnapping> boneSnappingList = new List<vBoneTransformSnapping>();
private bool bonesIsLoaded;
protected virtual void Reset()
{
LoadBones();
}
#if UNITY_EDITOR
public void NewBodyStruct()
{
vBodyStruct newBodyStruct = ScriptableObject.CreateInstance<vBodyStruct>();
AssetDatabase.CreateAsset(newBodyStruct, "Assets/BodyStruct@" + gameObject.transform.parent.name + ".asset");
AssetDatabase.SaveAssets();
SerializedObject serializedObj = new SerializedObject(this);
this.bodyStruct = newBodyStruct;
serializedObj.ApplyModifiedProperties();
}
#endif
public virtual void LoadBones()
{
var animator = GetComponentInParent<Animator>();
var bones = bodyStruct ? bodyStruct.bones : vBodyStruct.GetHumanBones();
if (bodyStruct)
{
var needToRemove = boneSnappingList.FindAll(_b => !bones.Exists(_b2 => _b2.name.Equals(_b.name)));
for (int i = 0; i < needToRemove.Count; i++)
{
boneSnappingList.Remove(needToRemove[i]);
}
}
if (bones.Count > 0)
{
for (int i = 0; i < bones.Count; i++)
{
Transform bone = null;
if (bones[i].isHuman && animator && animator.isHuman)
{
bone = animator.GetBoneTransform(bones[i].humanBone);
}
else
{
bone = GetBoneByName(bones[i].genericBone);
}
vBoneTransformSnapping b = boneSnappingList.Find(_b => _b.name.Equals(bones[i].name));
if (b == null)
{
b = new vBoneTransformSnapping();
b.name = bones[i].name;
b.bone = bone;
boneSnappingList.Add(b);
}
else
{
b.bone = bone;
}
}
}
boneSnappingList = boneSnappingList
.OrderBy(x => x.bone != null && x.name.ToUpper().Contains("LEFT"))
.ThenBy(x => x.bone != null && x.name.ToUpper().Contains("RIGHT"))
.ToList();
if (!Application.isPlaying)
{
bonesIsLoaded = true;
}
}
protected virtual void Awake()
{
LoadBones();
SnapAll();
}
public virtual void SnapAll()
{
foreach (var bt in boneSnappingList)
{
bt.Snap();
}
}
public virtual Transform GetBone(string name)
{
if (!bonesIsLoaded)
{
LoadBones();
}
var bone = boneSnappingList.Find(b => b.name.Equals(name));
return bone != null ? bone.bone : null;
}
protected virtual Transform GetBoneByName(string name)
{
var animator = GetComponentInParent<Animator>();
if (!animator) return null;
var root = animator.GetBoneTransform(HumanBodyBones.Hips);
if (root == null)
{
root = animator.transform;
}
List<Transform> childrens = root.gameObject.GetComponentsInChildren<Transform>(true).vToList();
Transform t = null;
if (childrens.Count > 0)
{
if (!string.IsNullOrEmpty(name.Trim()))
{
string[] nameSplited = name.Trim().Split(';');
t = childrens.Find(child => ContainsName(nameSplited, child.gameObject.name.Trim()));
}
}
return t;
}
protected virtual bool ContainsName(string[] nameSplited, string targetName)
{
bool contains = false;
for (int i = 0; i < nameSplited.Length; i++)
{
if (targetName.Contains(nameSplited[i]))
{
contains = true;
break;
}
}
return contains;
}
[System.Serializable]
public class vBoneTransformSnapping
{
public string name;
public Transform bone;
public Transform target;
public Orientation orientation;
public UnityEngine.Events.UnityEvent onSnap;
#if UNITY_EDITOR
[SerializeField]
private bool showProperties;
#endif
public void Snap()
{
if (bone && target)
{
if (Application.isPlaying && target.parent != bone)
{
target.parent = bone;
onSnap.Invoke();
}
target.rotation = targetRotation;
target.position = bone.position;
}
}
public enum Orientation
{
Forward, Back, Right, Left, Up, Down
}
public Quaternion targetRotation
{
get
{
Quaternion rot = Quaternion.LookRotation(Vector3.forward);
Vector3 lookAt = Vector3.forward;
if (bone && target && bone.parent)
{
switch (orientation)
{
case Orientation.Back:
lookAt = Vector3.back;
break;
case Orientation.Right:
lookAt = Vector3.right;
break;
case Orientation.Left:
lookAt = Vector3.left;
break;
case Orientation.Up:
lookAt = Vector3.up;
break;
case Orientation.Down:
lookAt = Vector3.down;
break;
}
rot = Quaternion.LookRotation(bone.TransformDirection(lookAt), bone.up);
}
return rot;
}
}
}
}
}