This commit is contained in:
2026-06-04 10:42:23 +07:00
parent e7e90790c9
commit 9be2242378
4166 changed files with 53005 additions and 11401 deletions

View File

@@ -0,0 +1,736 @@
using Invector.vEventSystems;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Invector.vCharacterController
{
[vClassHeader("HEAD TRACK", iconName = "headTrackIcon")]
public class vHeadTrack : vMonoBehaviour
{
#region variables
[vEditorToolbar("Settings")]
[vHelpBox("If your character is not looking up/down, try changing the axis", vHelpBoxAttribute.MessageType.Info)]
public Vector3 upDownAxis = Vector3.right;
[Header("Head & Body Weight")]
public float strafeHeadWeight = 0.6f;
public float strafeBodyWeight = 0.6f;
public float aimingHeadWeight = 0.8f;
public float aimingBodyWeight = 0.8f;
public float freeHeadWeight = 0.6f;
public float freeBodyWeight = 0.6f;
[SerializeField] protected float smooth = 10f;
[Header("Default Offsets ")]
[SerializeField] protected Vector2 defaultOffsetSpine;
[SerializeField] protected Vector2 defaultOffsetHead;
[vReadOnly(true)]
public Vector2 offsetSpine;
[vReadOnly(true)]
public Vector2 offsetHead;
[Header("Tracking")]
[Tooltip("Follow the Camera Forward")]
public bool followCamera = true;
public bool _freezeLookPoint = false;
[vHideInInspector("followCamera")]
[Tooltip("Force to follow camera")]
[SerializeField] protected bool _alwaysFollowCamera = false;
public virtual bool alwaysFollowCamera { get { return _alwaysFollowCamera; } set { _alwaysFollowCamera = value; } }
[Tooltip("Ignore the Limits and continue to follow the camera")]
public bool cancelTrackOutOfAngle = true;
[Tooltip("Considerer the head animation forward while tracking, try it to see different results")]
public bool considerHeadAnimationForward;
[Header("Limits")]
[vMinMax(minLimit = -180f, maxLimit = 180f)] public Vector2 horizontalAngleLimit = new Vector2(-100, 100);
[vMinMax(minLimit = -90f, maxLimit = 90f)] public Vector2 verticalAngleLimit = new Vector2(-80, 80);
[vHelpBox("Animations with vAnimatorTag Behavior will ignore the HeadTrack while is being played")]
[Header("Ignore AnimatorTags")]
public List<string> animatorIgnoreTags = new List<string>() { "Attack", "LockMovement", "CustomAction", "IsEquipping", "IgnoreHeadtrack" };
[vEditorToolbar("Bones")]
[vHelpBox("Auto Find Bones using Humanoid")]
public bool autoFindBones = true;
public Transform head;
public List<Transform> spine = new List<Transform>();
[vEditorToolbar("Detection")]
public float updateTargetInteraction = 1;
public float distanceToDetect = 10f;
public LayerMask obstacleLayer = 1 << 0;
[vHelpBox("Gameobjects Tags to detect")]
public List<string> tagsToDetect = new List<string>() { "LookAt" };
[vEditorToolbar("HeadTrack Angles")]
[vHelpBox("Angle between character forward and camera forward")]
[SerializeField, vReadOnly(false)]
protected Vector2 _desiredlookAngle;
[vHelpBox("Angle between head forward and character forward")]
[SerializeField, vReadOnly(false)]
protected Vector2 _currentLookAngle;
[vHelpBox("Angle between head forward and camera forward")]
[SerializeField, vReadOnly(false)]
protected Vector2 _relativeLookAngle;
[vSeparator("Optinal Animator paramenters")]
[vHelpBox("This is an optional to use and update paramenters in the animator using the look angles of the headtrack")]
[Tooltip("When enable this. The headtrack will update the angles relative to the character forward and camera forward")]
public bool useDesiredLookAngle;
[vHideInInspector("useDesiredLookAngle")]
public string desiredLookAngleH = "HorizontalLookAngle";
[vHideInInspector("useDesiredLookAngle")]
public string desiredLookAngleV = "VerticalLookAngle";
[Tooltip("When enable this. The headtrack will update the angles relative to the Head forward and Character forward")]
public bool useCurrentAngle;
[vHideInInspector("useCurrentAngle")]
public string currentAngleH = "CurrentHorizontalLookAngle";
[vHideInInspector("useCurrentAngle")]
public string currentAngleV = "CurrentVerticalLookAngle";
[Tooltip("When enable this. The headtrack will update the angles relative to the character head forward and camera forward")]
public bool useRelativeLookAngle;
[vHideInInspector("useRelativeLookAngle")]
public string relativeLookAngleH ="HorizontalLookAngleRelative";
[vHideInInspector("useRelativeLookAngle")]
public string relativeLookAngleV = "VerticalLookAngleRelative";
internal UnityEvent onInitUpdate = new UnityEvent();
internal UnityEvent onFinishUpdate = new UnityEvent();
internal List<vLookTarget> targetsInArea = new List<vLookTarget>();
public virtual Camera cameraMain { get; set; }
public virtual vLookTarget currentLookTarget { get; set; }
public virtual vLookTarget lastLookTarget { get; set; }
public virtual Quaternion currentLookRotation { get; set; }
public virtual bool ignoreSmooth { get; set; }
protected virtual float yRotation { get; set; }
protected virtual float xRotation { get; set; }
protected virtual float _currentHeadWeight { get; set; }
protected virtual float _currentBodyWeight { get; set; }
protected virtual Animator animator { get; set; }
protected virtual vIAnimatorStateInfoController animatorStateInfo { get; set; }
protected virtual float headHeight { get; set; }
protected virtual Transform simpleTarget { get; set; }
protected virtual Vector3 temporaryLookPoint { get; set; }
protected virtual float temporaryLookTime { get; set; }
protected virtual vHeadTrackSensor sensor { get; set; }
protected virtual float interaction { get; set; }
protected virtual vICharacter vChar { get; set; }
protected virtual Transform forwardReference { get; set; }
protected int currentLookAngleH_Hash = -1;
protected int currentLookAngleV_Hash = -1;
protected int desiredLookAngleH_Hash = -1;
protected int desiredLookAngleV_Hash = -1;
protected int relativeLookAngleH_Hash = -1;
protected int relativeLookAngleV_Hash = -1;
#endregion
public virtual float Smooth
{
get
{
return ignoreSmooth ? 1f : smooth * Time.deltaTime;
}
}
protected virtual Vector3 _currentLocalLookPosition { get; set; }
protected virtual Vector3 _lastLocalLookPosition { get; set; }
public virtual float currentVerticalLookAngle { get => _currentLookAngle.x; protected set => _currentLookAngle.x = value; }
public virtual float currentHorizontalLookAngle { get => _currentLookAngle.y; protected set => _currentLookAngle.y = value; }
public virtual float desiredVerticalLookAngle{ get => _desiredlookAngle.x; protected set => _desiredlookAngle.x = value; }
public virtual float desiredHorizontalLookAngle { get => _desiredlookAngle.y; protected set => _desiredlookAngle.y = value; }
public virtual float relativeVerticalLookAngle{ get => _relativeLookAngle.x; protected set => _relativeLookAngle.x = value; }
public virtual float relativeHorizontalLookAngle { get => _relativeLookAngle.y; protected set => _relativeLookAngle.y = value; }
public virtual bool freezeLookPoint { get => _freezeLookPoint; set => _freezeLookPoint = value; }
public virtual Vector3 currentLookPosition
{
get => freezeLookPoint ? transform.TransformPoint(_lastLocalLookPosition) : transform.TransformPoint(_currentLocalLookPosition);
protected set
{
_currentLocalLookPosition = transform.InverseTransformPoint(value);
if (!freezeLookPoint)
{
_lastLocalLookPosition = _currentLocalLookPosition;
}
}
}
protected virtual void Start()
{
if (!sensor)
{
var sensorObj = new GameObject("HeadTrackSensor");
sensor = sensorObj.AddComponent<vHeadTrackSensor>();
}
// updates the headtrack using the late update of the tpinput so we don't need to create another one
var tpInput = GetComponent<vThirdPersonInput>();
if (tpInput)
{
tpInput.onLateUpdate -= UpdateHeadTrack;
tpInput.onLateUpdate += UpdateHeadTrack;
}
vChar = GetComponent<vICharacter>();
sensor.headTrack = this;
cameraMain = Camera.main;
var layer = LayerMask.NameToLayer("HeadTrack");
sensor.transform.parent = transform;
sensor.gameObject.layer = layer;
sensor.gameObject.tag = transform.tag;
animatorStateInfo = GetComponent<vIAnimatorStateInfoController>();
Init();
}
public virtual void Init()
{
currentLookPosition = GetLookPoint();
_lastLocalLookPosition = _currentLocalLookPosition;
if (animator == null)
{
animator = GetComponentInChildren<Animator>();
}
if (animator.GetValidParameter(currentAngleH, out AnimatorControllerParameter p)) currentLookAngleH_Hash = p.nameHash;
if (animator.GetValidParameter(currentAngleV, out p)) currentLookAngleV_Hash = p.nameHash;
if (animator.GetValidParameter(desiredLookAngleH, out p)) desiredLookAngleH_Hash = p.nameHash;
if (animator.GetValidParameter(desiredLookAngleV, out p)) desiredLookAngleV_Hash = p.nameHash;
if (animator.GetValidParameter(relativeLookAngleH, out p)) relativeLookAngleH_Hash = p.nameHash;
if (animator.GetValidParameter(relativeLookAngleV, out p)) relativeLookAngleV_Hash = p.nameHash;
if (autoFindBones)
{
spine.Clear();
head = animator.GetBoneTransform(HumanBodyBones.Head);
if (head)
{
if (!forwardReference)
{
forwardReference = new GameObject("FWRF").transform;
}
forwardReference.SetParent(head);
forwardReference.transform.localPosition = Vector3.zero;
forwardReference.transform.rotation = transform.rotation;
var hips = animator.GetBoneTransform(HumanBodyBones.Hips);
if (hips)
{
var target = head;
for (int i = 0; i < 4; i++)
{
if (target.parent && target.parent.gameObject != hips.gameObject)
{
spine.Add(target.parent);
target = target.parent;
}
else
{
break;
}
}
}
}
}
if (head)
{
headHeight = Vector3.Distance(transform.position, head.position);
sensor.transform.position = head.transform.position;
}
else
{
headHeight = 1f;
sensor.transform.position = transform.position;
}
if (spine.Count == 0)
{
Debug.Log("Headtrack Spines missing");
}
spine.Reverse();
}
protected virtual Vector3 headPoint { get { return transform.position + (transform.up * headHeight); } }
public virtual void UpdateHeadTrack()
{
if (animator == null || !animator.enabled)
{
return;
}
if (vChar != null && vChar.currentHealth > 0f && animator != null && !vChar.ragdolled)
{
onInitUpdate.Invoke();
if (!freezeLookPoint)
{
currentLookPosition = GetLookPoint();
}
SetLookAtPosition(currentLookPosition, _currentHeadWeight, _currentBodyWeight);
UpdateAngles();
onFinishUpdate.Invoke();
}
}
public virtual void SetLookAtPosition(Vector3 point, float headWeight, float spineWeight)
{
var lookRotation = Quaternion.LookRotation(point - headPoint);
currentLookRotation = lookRotation;
var euler = lookRotation.eulerAngles - transform.rotation.eulerAngles;
var y = NormalizeAngle(euler.y);
var x = NormalizeAngle(euler.x);
var eulerB = considerHeadAnimationForward ? forwardReference.eulerAngles - transform.eulerAngles : Vector3.zero;
currentVerticalLookAngle = Mathf.Clamp(Mathf.Lerp(currentVerticalLookAngle, ((x) - eulerB.NormalizeAngle().x) + Quaternion.Euler(offsetSpine + defaultOffsetSpine).eulerAngles.NormalizeAngle().x, Smooth), verticalAngleLimit.x, verticalAngleLimit.y);
currentHorizontalLookAngle = Mathf.Clamp(Mathf.Lerp(currentHorizontalLookAngle, ((y) - eulerB.NormalizeAngle().y) + Quaternion.Euler(offsetSpine + defaultOffsetSpine).eulerAngles.NormalizeAngle().y, Smooth), horizontalAngleLimit.x, horizontalAngleLimit.y);
var xSpine = NormalizeAngle(currentVerticalLookAngle);
var ySpine = NormalizeAngle(currentHorizontalLookAngle);
foreach (Transform segment in spine)
{
var rotY = Quaternion.AngleAxis((ySpine * spineWeight) / spine.Count, segment.InverseTransformDirection(transform.up));
segment.rotation *= rotY;
var rotX = Quaternion.AngleAxis((xSpine * spineWeight) / spine.Count, segment.InverseTransformDirection(transform.TransformDirection(upDownAxis)));
segment.rotation *= rotX;
}
if (head)
{
var xHead = NormalizeAngle(currentVerticalLookAngle - (xSpine * spineWeight) + Quaternion.Euler(offsetHead + defaultOffsetHead).eulerAngles.NormalizeAngle().x);
var yHead = NormalizeAngle(currentHorizontalLookAngle - (ySpine * spineWeight) + Quaternion.Euler(offsetHead + defaultOffsetHead).eulerAngles.NormalizeAngle().y);
var _rotY = Quaternion.AngleAxis(yHead * headWeight, head.InverseTransformDirection(transform.up));
head.rotation *= _rotY;
var _rotX = Quaternion.AngleAxis(xHead * headWeight, head.InverseTransformDirection(transform.TransformDirection(upDownAxis)));
head.rotation *= _rotX;
}
}
public virtual void UpdateAngles()
{
if (useCurrentAngle)
{
if (currentLookAngleH_Hash != -1) animator.SetFloat(currentLookAngleH_Hash, currentHorizontalLookAngle);
if (currentLookAngleV_Hash != -1) animator.SetFloat(currentLookAngleV_Hash, currentVerticalLookAngle);
}
if (useDesiredLookAngle)
{
if (desiredLookAngleH_Hash != -1) animator.SetFloat(desiredLookAngleH_Hash, desiredHorizontalLookAngle);
if (desiredLookAngleV_Hash != -1) animator.SetFloat(desiredLookAngleV_Hash, desiredVerticalLookAngle);
}
if(useRelativeLookAngle)
{
if (relativeLookAngleH_Hash != -1) animator.SetFloat(relativeLookAngleH_Hash, relativeHorizontalLookAngle);
if (relativeLookAngleV_Hash != -1) animator.SetFloat(relativeLookAngleV_Hash, relativeVerticalLookAngle);
}
}
/// <summary>
/// Rotate the spine using angles
/// </summary>
/// <param name="angleX">Vertical angle</param>
/// <param name="angleY">Horizontal angle</param>
public virtual void RotateSpine(float angleX, float angleY)
{
var xSpine = NormalizeAngle(angleX);
var ySpine = NormalizeAngle(angleY);
foreach (Transform segment in spine)
{
var rotY = Quaternion.AngleAxis((ySpine) / spine.Count, segment.InverseTransformDirection(transform.up));
segment.rotation *= rotY;
var rotX = Quaternion.AngleAxis((xSpine) / spine.Count, segment.InverseTransformDirection(transform.TransformDirection(upDownAxis)));
segment.rotation *= rotX;
}
}
public virtual Vector3 desiredLookDirection { get; protected set; }
public virtual Vector3 LookDirection { get; protected set; }
protected virtual bool lookConditions
{
get
{
if (!cameraMain)
{
cameraMain = Camera.main;
}
return head != null && (followCamera && cameraMain != null) || (!followCamera && (currentLookTarget || simpleTarget)) || temporaryLookTime > 0;
}
}
protected virtual Vector3 GetLookPoint()
{
if (animator == null)
{
return Vector3.zero;
}
var distanceToLook = 100;
if (lookConditions && !IgnoreHeadTrack())
{
desiredLookDirection = transform.forward;
if (temporaryLookTime <= 0)
{
var lookPosition = headPoint + (transform.forward * distanceToLook);
if (followCamera)
{
lookPosition = (cameraMain.transform.position + (cameraMain.transform.forward * distanceToLook));
}
desiredLookDirection = lookPosition - headPoint;
if ((followCamera && !alwaysFollowCamera) || !followCamera)
{
if (simpleTarget != null)
{
desiredLookDirection = simpleTarget.position - headPoint;
if (currentLookTarget && currentLookTarget == lastLookTarget)
{
currentLookTarget.ExitLook(this);
lastLookTarget = null;
}
}
else if (currentLookTarget != null && (currentLookTarget.ignoreHeadTrackAngle || TargetIsOnRange(currentLookTarget.lookPoint - headPoint)) && currentLookTarget.IsVisible(headPoint, obstacleLayer))
{
desiredLookDirection = currentLookTarget.lookPoint - headPoint;
if (currentLookTarget != lastLookTarget)
{
currentLookTarget.EnterLook(this);
lastLookTarget = currentLookTarget;
}
}
else if (currentLookTarget && currentLookTarget == lastLookTarget)
{
currentLookTarget.ExitLook(this);
lastLookTarget = null;
}
}
}
else
{
desiredLookDirection = temporaryLookPoint - headPoint;
temporaryLookTime -= Time.deltaTime;
if (currentLookTarget && currentLookTarget == lastLookTarget)
{
currentLookTarget.ExitLook(this);
lastLookTarget = null;
}
}
var angle = GetTargetAngle(desiredLookDirection);
if (cancelTrackOutOfAngle && (lastLookTarget == null || !lastLookTarget.ignoreHeadTrackAngle))
{
if (TargetIsOnRange(desiredLookDirection))
{
if (animator.GetBool("IsStrafing") && !IsAnimatorTag("Upperbody Pose"))
{
SmoothValues(strafeHeadWeight, strafeBodyWeight, angle.x, angle.y);
}
else if (animator.GetBool("IsStrafing") && IsAnimatorTag("Upperbody Pose"))
{
SmoothValues(aimingHeadWeight, aimingBodyWeight, angle.x, angle.y);
}
else
{
SmoothValues(freeHeadWeight, freeBodyWeight, angle.x, angle.y);
}
}
else
{
SmoothValues();
}
}
else
{
if (animator.GetBool("IsStrafing") && !IsAnimatorTag("Upperbody Pose"))
{
SmoothValues(strafeHeadWeight, strafeBodyWeight, angle.x, angle.y);
}
else if (animator.GetBool("IsStrafing") && IsAnimatorTag("Upperbody Pose"))
{
SmoothValues(aimingHeadWeight, aimingBodyWeight, angle.x, angle.y);
}
else
{
SmoothValues(freeHeadWeight, freeBodyWeight, angle.x, angle.y);
}
}
if (targetsInArea.Count > 1)
{
SortTargets();
}
}
else
{
SmoothValues();
if (targetsInArea.Count > 1)
{
SortTargets();
}
}
var rotA = Quaternion.AngleAxis(yRotation, transform.up);
var rotB = Quaternion.AngleAxis(xRotation, transform.right);
var finalRotation = (rotA * rotB);
var lookDirection = finalRotation * transform.forward;
LookDirection = lookDirection;
_desiredlookAngle = GetTargetAngle(cameraMain.transform.forward);
_relativeLookAngle = -(GetTargetAngle(lookDirection) - _desiredlookAngle);
return headPoint + (lookDirection * distanceToLook);
}
protected virtual Vector2 GetTargetAngle(Vector3 direction)
{
if (direction.magnitude == 0) return Vector2.zero;
var lookRotation = Quaternion.LookRotation(direction, Vector3.up);
var angleResult = lookRotation.eulerAngles - transform.eulerAngles;
return new Vector2(angleResult.NormalizeAngle().x, angleResult.NormalizeAngle().y);
}
protected virtual bool TargetIsOnRange(Vector3 direction)
{
var angle = GetTargetAngle(direction);
return (angle.x >= verticalAngleLimit.x && angle.x <= verticalAngleLimit.y && angle.y >= horizontalAngleLimit.x && angle.y <= horizontalAngleLimit.y);
}
public virtual void SetAlwaysFollowCamera(bool value)
{
alwaysFollowCamera = value;
}
/// <summary>
/// Set vLookTarget
/// </summary>
/// <param name="target"></param>
public virtual void SetLookTarget(vLookTarget target, bool priority = false)
{
if (!targetsInArea.Contains(target))
{
targetsInArea.Add(target);
}
if (priority)
{
currentLookTarget = target;
}
}
/// <summary>
/// Set Simple target
/// </summary>
/// <param name="target"></param>
public virtual void SetLookTarget(Transform target)
{
simpleTarget = target;
}
/// <summary>
/// Set a temporary look point to headtrack
/// </summary>
/// <param name="point">look point</param>
/// <param name="time">time to stay looking</param>
public virtual void SetTemporaryLookPoint(Vector3 point, float time = 1f)
{
temporaryLookPoint = point;
temporaryLookTime = time;
}
public virtual void RemoveLookTarget(vLookTarget target)
{
if (targetsInArea.Contains(target))
{
targetsInArea.Remove(target);
}
if (currentLookTarget == target)
{
currentLookTarget = null;
}
}
public virtual void RemoveLookTarget(Transform target)
{
if (simpleTarget == target)
{
simpleTarget = null;
}
}
/// <summary>
/// Make angle to work with -180 and 180
/// </summary>
/// <param name="angle"></param>
/// <returns></returns>
protected virtual float NormalizeAngle(float angle)
{
if (angle > 180)
{
angle -= 360;
}
else if (angle < -180)
{
angle += 360;
}
return angle;
}
protected virtual void ResetValues()
{
_currentHeadWeight = 0;
_currentBodyWeight = 0;
yRotation = 0;
xRotation = 0;
}
protected virtual void SmoothValues(float _headWeight = 0, float _bodyWeight = 0, float _x = 0, float _y = 0)
{
_currentHeadWeight = Mathf.Lerp(_currentHeadWeight, _headWeight, Smooth);
_currentBodyWeight = Mathf.Lerp(_currentBodyWeight, _bodyWeight, Smooth);
yRotation = Mathf.Lerp(yRotation, _y, Smooth);
xRotation = Mathf.Lerp(xRotation, _x, Smooth);
yRotation = Mathf.Clamp(yRotation, horizontalAngleLimit.x, horizontalAngleLimit.y);
xRotation = Mathf.Clamp(xRotation, verticalAngleLimit.x, verticalAngleLimit.y);
}
protected virtual void SortTargets()
{
interaction += Time.deltaTime;
if (interaction > updateTargetInteraction)
{
interaction -= updateTargetInteraction;
if (targetsInArea == null || targetsInArea.Count < 2)
{
if (targetsInArea != null && targetsInArea.Count > 0)
{
currentLookTarget = targetsInArea[0];
}
return;
}
for (int i = targetsInArea.Count - 1; i >= 0; i--)
{
if (targetsInArea[i] == null)
{
targetsInArea.RemoveAt(i);
}
}
targetsInArea.Sort(delegate (vLookTarget c1, vLookTarget c2)
{
return Vector3.Distance(this.transform.position, c1 != null ? c1.transform.position : Vector3.one * Mathf.Infinity).CompareTo
((Vector3.Distance(this.transform.position, c2 != null ? c2.transform.position : Vector3.one * Mathf.Infinity)));
});
if (targetsInArea.Count > 0)
{
currentLookTarget = targetsInArea[0];
}
}
}
public virtual void OnDetect(Collider other)
{
if (tagsToDetect.Contains(other.gameObject.tag) && other.GetComponent<vLookTarget>() != null)
{
currentLookTarget = other.GetComponent<vLookTarget>();
var headTrack = other.GetComponentInParent<vHeadTrack>();
if (!targetsInArea.Contains(currentLookTarget) && (headTrack == null || headTrack != this))
{
targetsInArea.Add(currentLookTarget);
SortTargets();
currentLookTarget = targetsInArea[0];
}
}
}
public virtual void OnLost(Collider other)
{
if (tagsToDetect.Contains(other.gameObject.tag) && other.GetComponentInParent<vLookTarget>() != null)
{
var _currentLookTarget = other.GetComponentInParent<vLookTarget>();
if (targetsInArea.Contains(_currentLookTarget))
{
targetsInArea.Remove(_currentLookTarget);
if (_currentLookTarget == lastLookTarget)
{
_currentLookTarget.ExitLook(this);
}
}
SortTargets();
if (targetsInArea.Count > 0)
{
currentLookTarget = targetsInArea[0];
}
else
{
currentLookTarget = null;
}
}
}
public virtual bool IgnoreHeadTrack()
{
if (animatorIgnoreTags.Exists(tag => IsAnimatorTag(tag)))
{
return true;
}
return false;
}
public virtual bool IsAnimatorTag(string tag)
{
if (animator == null)
{
return false;
}
if (animatorStateInfo.isValid())
{
if (animatorStateInfo.animatorStateInfos.HasTag(tag))
{
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 61a5d2516d5dbbc40b1cfb5cbf17758a
timeCreated: 1469474947
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using UnityEngine;
namespace Invector.vCharacterController
{
[RequireComponent(typeof(SphereCollider))]
[RequireComponent(typeof(Rigidbody))]
public class vHeadTrackSensor : MonoBehaviour
{
[HideInInspector]
public vHeadTrack headTrack;
public SphereCollider sphere;
void OnDrawGizmos()
{
if (Application.isPlaying && sphere && headTrack)
{
sphere.radius = headTrack.distanceToDetect;
}
}
void Start()
{
var _rigidB = GetComponent<Rigidbody>();
sphere = GetComponent<SphereCollider>();
sphere.isTrigger = true;
_rigidB.useGravity = false;
_rigidB.isKinematic = true;
_rigidB.constraints = RigidbodyConstraints.FreezeAll;
if (headTrack) sphere.radius = headTrack.distanceToDetect;
}
void OnTriggerEnter(Collider other)
{
if (headTrack != null) headTrack.OnDetect(other);
}
void OnTriggerExit(Collider other)
{
if (headTrack != null) headTrack.OnLost(other);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6f8827d96877cdc4bb6f1b8126c504de
timeCreated: 1472314528
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,271 @@
using UnityEngine;
using Invector.vCharacterController;
namespace Invector
{
public class vLookTarget : MonoBehaviour
{
public bool ignoreHeadTrackAngle;
[Header("Set this to assign a different point to look")]
public Transform lookPointTarget;
[Header("Area to check if is visible")]
public Vector3 centerArea = Vector3.zero;
public Vector3 sizeArea = Vector3.one;
public bool useLimitToDetect = true;
public float minDistanceToDetect = 2f;
public VisibleCheckType visibleCheckType;
[Tooltip("use this to turn the object undetectable")]
public bool HideObject;
[System.Serializable]
public class OnLookEvent : UnityEngine.Events.UnityEvent<vHeadTrack> { }
public OnLookEvent onEnterLook, onExitLook;
public enum VisibleCheckType
{
None, SingleCast, BoxCast
}
void OnDrawGizmosSelected()
{
DrawBox();
}
void Start()
{
var layer = LayerMask.NameToLayer("HeadTrack");
gameObject.layer = layer;
}
/// <summary>
/// Point to look
/// </summary>
public Vector3 lookPoint
{
get
{
if (lookPointTarget)
{
return lookPointTarget.position;
}
else
return transform.TransformPoint(centerArea);
}
}
void DrawBox()
{
Gizmos.color = new Color(1, 0, 0, 1f);
Gizmos.DrawSphere(lookPoint, 0.05f);
if (visibleCheckType == VisibleCheckType.BoxCast)
{
var sizeX = transform.lossyScale.x * sizeArea.x;
var sizeY = transform.lossyScale.y * sizeArea.y;
var sizeZ = transform.lossyScale.z * sizeArea.z;
var centerX = transform.lossyScale.x * centerArea.x;
var centerY = transform.lossyScale.y * centerArea.y;
var centerZ = transform.lossyScale.z * centerArea.z;
Matrix4x4 rotationMatrix = Matrix4x4.TRS(transform.position + new Vector3(centerX, centerY, centerZ), transform.rotation, new Vector3(sizeX, sizeY, sizeZ) * 2f);
Gizmos.matrix = rotationMatrix;
Gizmos.color = new Color(0, 1, 0, 0.2f);
Gizmos.DrawCube(Vector3.zero, Vector3.one);
Gizmos.color = new Color(0, 1, 0, 1f);
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
}
else if (visibleCheckType == VisibleCheckType.SingleCast)
{
var point = transform.TransformPoint(centerArea);
Gizmos.color = new Color(0, 1, 0, 1f);
Gizmos.DrawSphere(point, 0.05f);
}
}
internal void EnterLook(vHeadTrack vHeadTrack)
{
onEnterLook.Invoke(vHeadTrack);
}
internal void ExitLook(vHeadTrack vHeadTrack)
{
onExitLook.Invoke(vHeadTrack);
}
}
#region Helper
public static class vLookTargetHelper
{
struct LookPoints
{
public Vector3 frontTopLeft;
public Vector3 frontTopRight;
public Vector3 frontBottomLeft;
public Vector3 frontBottomRight;
public Vector3 backTopLeft;
public Vector3 backTopRight;
public Vector3 backBottomLeft;
public Vector3 backBottomRight;
}
static LookPoints GetLookPoints(vLookTarget lookTarget)
{
LookPoints points = new LookPoints();
var centerArea = lookTarget.centerArea;
var sizeArea = lookTarget.sizeArea;
var lookTransform = lookTarget.transform;
points.frontTopLeft = new Vector3(centerArea.x - sizeArea.x, centerArea.y + sizeArea.y, centerArea.z - sizeArea.z);
points.frontTopRight = new Vector3(centerArea.x + sizeArea.x, centerArea.y + sizeArea.y, centerArea.z - sizeArea.z);
points.frontBottomLeft = new Vector3(centerArea.x - sizeArea.x, centerArea.y - sizeArea.y, centerArea.z - sizeArea.z);
points.frontBottomRight = new Vector3(centerArea.x + sizeArea.x, centerArea.y - sizeArea.y, centerArea.z - sizeArea.z);
points.backTopLeft = new Vector3(centerArea.x - sizeArea.x, centerArea.y + sizeArea.y, centerArea.z + sizeArea.z);
points.backTopRight = new Vector3(centerArea.x + sizeArea.x, centerArea.y + sizeArea.y, centerArea.z + sizeArea.z);
points.backBottomLeft = new Vector3(centerArea.x - sizeArea.x, centerArea.y - sizeArea.y, centerArea.z + sizeArea.z);
points.backBottomRight = new Vector3(centerArea.x + sizeArea.x, centerArea.y - sizeArea.y, centerArea.z + sizeArea.z);
points.frontTopLeft = lookTransform.TransformPoint(points.frontTopLeft);
points.frontTopRight = lookTransform.TransformPoint(points.frontTopRight);
points.frontBottomLeft = lookTransform.TransformPoint(points.frontBottomLeft);
points.frontBottomRight = lookTransform.TransformPoint(points.frontBottomRight);
points.backTopLeft = lookTransform.TransformPoint(points.backTopLeft);
points.backTopRight = lookTransform.TransformPoint(points.backTopRight);
points.backBottomLeft = lookTransform.TransformPoint(points.backBottomLeft);
points.backBottomRight = lookTransform.TransformPoint(points.backBottomRight);
return points;
}
/// <summary>
/// Check if anny corner points of LookTarget area is visible from observer
/// </summary>
/// <param name="lookTarget">principal transform of lookTarget</param>
/// <param name="from">observer point</param>
/// <param name="layerMask">Layer to check</param>
/// <param name="debug">Draw lines </param>
/// <returns></returns>
public static bool IsVisible(this vLookTarget lookTarget, Vector3 from, LayerMask layerMask, bool debug = false)
{
if (lookTarget.HideObject) return false;
if (lookTarget.visibleCheckType == vLookTarget.VisibleCheckType.None)
{
if (lookTarget.useLimitToDetect && Vector3.Distance(from, lookTarget.transform.position) > lookTarget.minDistanceToDetect) return false;
return true;
}
else if (lookTarget.visibleCheckType == vLookTarget.VisibleCheckType.SingleCast)
{
if (lookTarget.useLimitToDetect && Vector3.Distance(from, lookTarget.centerArea) > lookTarget.minDistanceToDetect) return false;
if (CastPoint(from, lookTarget.transform.TransformPoint(lookTarget.centerArea), lookTarget.transform, layerMask, debug)) return true; else return false;
}
else if (lookTarget.visibleCheckType == vLookTarget.VisibleCheckType.BoxCast)
{
if (lookTarget.useLimitToDetect && Vector3.Distance(from, lookTarget.transform.position) > lookTarget.minDistanceToDetect) return false;
LookPoints points = GetLookPoints(lookTarget);
if (CastPoint(from, points.frontTopLeft, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.frontTopRight, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.frontBottomLeft, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.frontBottomRight, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.backTopLeft, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.backTopRight, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.backBottomLeft, lookTarget.transform, layerMask, debug)) return true;
if (CastPoint(from, points.backBottomRight, lookTarget.transform, layerMask, debug)) return true;
}
return false;
}
/// <summary>
/// Check if anny corner points of LookTarget area is visible from observer
/// </summary>
/// <param name="lookTarget">principal transform of lookTarget</param>
/// <param name="from">observer point</param>
/// <param name="debug">Draw lines </param>
/// <returns></returns>
public static bool IsVisible(this vLookTarget lookTarget, Vector3 from, bool debug = false)
{
if (lookTarget.HideObject) return false;
LookPoints points = GetLookPoints(lookTarget);
if (lookTarget.visibleCheckType == vLookTarget.VisibleCheckType.None)
{
return true;
}
else if (lookTarget.visibleCheckType == vLookTarget.VisibleCheckType.SingleCast)
{
if (CastPoint(from, lookTarget.transform.TransformPoint(lookTarget.centerArea), lookTarget.transform, debug)) return true; else return false;
}
else if (lookTarget.visibleCheckType == vLookTarget.VisibleCheckType.BoxCast)
{
if (CastPoint(from, points.frontTopLeft, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.frontTopRight, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.frontBottomLeft, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.frontBottomRight, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.backTopLeft, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.backTopRight, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.backBottomLeft, lookTarget.transform, debug)) return true;
if (CastPoint(from, points.backBottomRight, lookTarget.transform, debug)) return true;
}
return false;
}
static bool CastPoint(Vector3 from, Vector3 point, Transform lookTarget, LayerMask layerMask, bool debug = false)
{
RaycastHit hit;
if (Physics.Linecast(from, point, out hit, layerMask))
{
if (hit.transform != lookTarget.transform)
{
if (debug) Debug.DrawLine(from, hit.point, Color.red);
return false;
}
else
{
if (debug) Debug.DrawLine(from, hit.point, Color.green);
return true;
}
}
if (debug) Debug.DrawLine(from, point, Color.green);
return true;
}
static bool CastPoint(Vector3 from, Vector3 point, Transform lookTarget, bool debug = false)
{
RaycastHit hit;
if (Physics.Linecast(from, point, out hit))
{
if (hit.transform != lookTarget.transform)
{
if (debug) Debug.DrawLine(from, hit.point, Color.red);
return false;
}
else
{
if (debug) Debug.DrawLine(from, hit.point, Color.green);
return true;
}
}
if (debug) Debug.DrawLine(from, point, Color.green);
return true;
}
}
#endregion
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 079af0928a59a2a489c3eb00c58cd2f4
timeCreated: 1469630605
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: