379 lines
12 KiB
C#
379 lines
12 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using UnityEngine.Animations.Rigging;
|
|
using UnityEngine.VFX;
|
|
using System.Collections.Generic;
|
|
|
|
[System.Serializable]
|
|
public class IrisData
|
|
{
|
|
public SkinnedMeshRenderer meshRenderer;
|
|
public int blendShapeIndex;
|
|
public float currentValue;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class BurnableEye
|
|
{
|
|
public Renderer mainRenderer;
|
|
public Renderer eyeballRenderer;
|
|
public float burnLerpValue;
|
|
}
|
|
|
|
public class HallucinationsEye : MonoBehaviour
|
|
{
|
|
public bool IsAlive { get; private set; } = true;
|
|
public bool IsWatching { get; private set; } = false;
|
|
public bool IsOutOfWall { get; private set; } = false;
|
|
|
|
[Header("References")]
|
|
public List<Transform> eyePivots = new List<Transform>();
|
|
public Animator eyeAnimator;
|
|
public AudioSource eyeAudioSource;
|
|
public List<MultiAimConstraint> eyeMultiAimConstraints = new List<MultiAimConstraint>();
|
|
private float targetAimWeight = 1f;
|
|
public float aimWeightChangeSpeed = 2f;
|
|
public RigBuilder eyeRigBuilder;
|
|
public Renderer[] eyeRenderers;
|
|
|
|
[Header("Gaze Settings")]
|
|
public float gazeDistanceThreshold = 8f;
|
|
public float gazeFOV = 90f;
|
|
|
|
[Header("Iris Control")]
|
|
public List<IrisData> irisEyes = new List<IrisData>();
|
|
public float minIrisValue = 0f;
|
|
public float maxIrisValue = 100f;
|
|
public float defaultIrisValue = 70f;
|
|
public float maxIrisDistance = 10f;
|
|
public float irisSmoothSpeed = 5f;
|
|
public float irisLightFactor = 0f;
|
|
public bool isBeingLit = false;
|
|
|
|
[Header("Effects")]
|
|
public VisualEffect smokeEffect;
|
|
private bool IsPlayingSmokeEffect;
|
|
|
|
[Header("Eye Material Burn")]
|
|
public List<BurnableEye> burnableEyes = new List<BurnableEye>();
|
|
public string lerpProperty = "_Lerp";
|
|
private bool isBurning = false;
|
|
|
|
[Header("Debug Visualization")]
|
|
public bool showIrisDebug = true;
|
|
public Vector3 debugOffset = new Vector3(0, 1f, 0);
|
|
|
|
[Header("Audio")]
|
|
public AudioClip[] eyeMovementClips;
|
|
public float audioFadeDuration = 0.5f;
|
|
private Coroutine fadeInCoroutine;
|
|
private Coroutine fadeOutCoroutine;
|
|
private bool shouldPlayAudioClip = true;
|
|
|
|
[Header("Eye Colliders")]
|
|
public Collider[] eyeColliders;
|
|
|
|
private Dictionary<Collider, int> colliderToEyeIndex;
|
|
|
|
private void Awake()
|
|
{
|
|
if (eyeColliders == null || eyeColliders.Length == 0)
|
|
eyeColliders = GetComponentsInChildren<Collider>();
|
|
|
|
colliderToEyeIndex = new Dictionary<Collider, int>();
|
|
for (int i = 0; i < eyeColliders.Length; i++)
|
|
colliderToEyeIndex[eyeColliders[i]] = i;
|
|
}
|
|
|
|
public bool OwnsCollider(Collider col)
|
|
{
|
|
return colliderToEyeIndex != null && colliderToEyeIndex.ContainsKey(col);
|
|
}
|
|
|
|
public bool TryGetEyeIndex(Collider col, out int index)
|
|
{
|
|
if (colliderToEyeIndex != null)
|
|
return colliderToEyeIndex.TryGetValue(col, out index);
|
|
|
|
index = -1;
|
|
return false;
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
eyePivots.Clear();
|
|
foreach (var c in eyeMultiAimConstraints)
|
|
{
|
|
if (c != null && c.data.constrainedObject != null)
|
|
eyePivots.Add(c.data.constrainedObject);
|
|
}
|
|
|
|
foreach (var iris in irisEyes)
|
|
iris.currentValue = defaultIrisValue;
|
|
|
|
StartCoroutine(AfterGameLoaded());
|
|
|
|
if (eyeAnimator == null) eyeAnimator = GetComponent<Animator>();
|
|
if (eyeAudioSource == null) eyeAudioSource = GetComponent<AudioSource>();
|
|
}
|
|
|
|
IEnumerator AfterGameLoaded()
|
|
{
|
|
yield return new WaitUntil(() => LoadManager.GetInstance() != null && !LoadManager.GetInstance().LoadingGame);
|
|
yield return new WaitUntil(() => EyesManager.GetInstance() != null);
|
|
EyesManager.GetInstance().RegisterEye(this);
|
|
SetEyeTarget(PlayerManager.GetInstance().playerHead.transform);
|
|
}
|
|
|
|
private void SetEyeTarget(Transform newTarget)
|
|
{
|
|
if (eyeMultiAimConstraints.Count == 0 || newTarget == null) return;
|
|
foreach (var c in eyeMultiAimConstraints)
|
|
{
|
|
var src = new WeightedTransformArray();
|
|
src.Add(new WeightedTransform(newTarget, 1f));
|
|
c.data.sourceObjects = src;
|
|
}
|
|
eyeRigBuilder?.Build();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
UpdateIris();
|
|
UpdateEyeGaze();
|
|
UpdateAimWeight();
|
|
UpdateBurnVisual();
|
|
}
|
|
|
|
public void CheckPlayerVisibility(Transform player, float fov, float distance)
|
|
{
|
|
if (!IsAlive) return;
|
|
bool visible = false;
|
|
foreach (var col in eyeColliders)
|
|
{
|
|
Vector3 origin = col.transform.position;
|
|
Vector3 toPlayer = player.position - origin;
|
|
float d = toPlayer.magnitude;
|
|
if (d > distance) continue;
|
|
float ang = Vector3.Angle(col.transform.forward, toPlayer.normalized);
|
|
if (ang > fov * 0.5f) continue;
|
|
if (Physics.Raycast(origin, toPlayer.normalized, out RaycastHit hit, distance))
|
|
{
|
|
if (hit.transform == player) { visible = true; break; }
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public void EyeIsWatching()
|
|
{
|
|
eyeAnimator.SetBool("EyeIsFadingIn", true);
|
|
eyeAnimator.SetBool("EyeIsFadingOut", false);
|
|
IsOutOfWall = true; IsWatching = true;
|
|
}
|
|
|
|
public void EyeIsNoLongerWatching()
|
|
{
|
|
eyeAnimator.SetBool("EyeIsFadingIn", false);
|
|
eyeAnimator.SetBool("EyeIsFadingOut", true);
|
|
IsOutOfWall = false; IsWatching = false;
|
|
}
|
|
|
|
public void EvaluateLightingFrom(Transform lightT, float spotAngle, float maxDist)
|
|
{
|
|
if (!CandleController.GetInstance().isCandleOn)
|
|
{
|
|
isBeingLit = false; irisLightFactor = 0f;
|
|
return;
|
|
}
|
|
Vector3 toEye = transform.position - lightT.position;
|
|
float d = toEye.magnitude;
|
|
if (d > maxDist)
|
|
{
|
|
isBeingLit = false; irisLightFactor = 0f;
|
|
return;
|
|
}
|
|
float ang = Vector3.Angle(lightT.forward, toEye.normalized);
|
|
float cf = 1f - Mathf.Clamp01(ang / (spotAngle * 0.5f));
|
|
isBeingLit = cf > 0f;
|
|
irisLightFactor = Mathf.Clamp01(cf);
|
|
}
|
|
|
|
private void UpdateIris()
|
|
{
|
|
if (irisEyes.Count == 0 || EyesManager.GetInstance().player == null) return;
|
|
float target = defaultIrisValue;
|
|
if (isBeingLit)
|
|
{
|
|
float dist = Vector3.Distance(EyesManager.GetInstance().player.position, transform.position);
|
|
float df = 1f - Mathf.Clamp01(dist / maxIrisDistance);
|
|
float inf = irisLightFactor * df;
|
|
target = Mathf.Lerp(maxIrisValue, minIrisValue, inf);
|
|
}
|
|
foreach (var iris in irisEyes)
|
|
{
|
|
iris.currentValue = Mathf.Lerp(iris.currentValue, target, Time.deltaTime * irisSmoothSpeed);
|
|
if (iris.meshRenderer != null && iris.blendShapeIndex >= 0)
|
|
iris.meshRenderer.SetBlendShapeWeight(iris.blendShapeIndex, iris.currentValue);
|
|
}
|
|
}
|
|
|
|
private void UpdateEyeGaze()
|
|
{
|
|
if (eyeMultiAimConstraints.Count == 0 || EyesManager.GetInstance().player == null) return;
|
|
Transform player = EyesManager.GetInstance().player;
|
|
Vector3 toP = player.position - transform.position;
|
|
float d = toP.magnitude;
|
|
float ang = Vector3.Angle(transform.forward, toP.normalized);
|
|
for (int i = 0; i < eyeMultiAimConstraints.Count; i++)
|
|
{
|
|
float tw = (d <= gazeDistanceThreshold && ang <= gazeFOV * 0.5f) ? 1f : 0f;
|
|
eyeMultiAimConstraints[i].weight = Mathf.Lerp(eyeMultiAimConstraints[i].weight, tw, Time.deltaTime * 5f);
|
|
}
|
|
}
|
|
|
|
private void UpdateAimWeight()
|
|
{
|
|
foreach (var c in eyeMultiAimConstraints)
|
|
c.weight = Mathf.Lerp(c.weight, targetAimWeight, Time.deltaTime * aimWeightChangeSpeed);
|
|
}
|
|
|
|
public void UpdateBurnVisual()
|
|
{
|
|
UpdateBurnVisual(0f);
|
|
}
|
|
|
|
public void UpdateBurnVisual(float normalizedBurnProgress, int eyeIndex = -1)
|
|
{
|
|
if (eyeIndex >= 0 && eyeIndex < burnableEyes.Count)
|
|
{
|
|
var eye = burnableEyes[eyeIndex];
|
|
float val = isBurning
|
|
? Mathf.Clamp01(normalizedBurnProgress)
|
|
: Mathf.Lerp(eye.burnLerpValue, 0f, Time.deltaTime * 2f);
|
|
eye.burnLerpValue = val;
|
|
if (eye.mainRenderer != null) eye.mainRenderer.material.SetFloat(lerpProperty, val);
|
|
if (eye.eyeballRenderer != null) eye.eyeballRenderer.material.SetFloat(lerpProperty, val);
|
|
}
|
|
else
|
|
{
|
|
foreach (var eye in burnableEyes)
|
|
{
|
|
float val = isBurning
|
|
? Mathf.Clamp01(normalizedBurnProgress)
|
|
: Mathf.Lerp(eye.burnLerpValue, 0f, Time.deltaTime * 2f);
|
|
eye.burnLerpValue = val;
|
|
if (eye.mainRenderer != null) eye.mainRenderer.material.SetFloat(lerpProperty, val);
|
|
if (eye.eyeballRenderer != null) eye.eyeballRenderer.material.SetFloat(lerpProperty, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetBurningState(bool burning, int eyeIndex = -1)
|
|
{
|
|
if (eyeIndex >= 0 && eyePivots.Count > 1)
|
|
{
|
|
string param = eyeIndex == 0 ? "Eye1IsGettingBurned"
|
|
: eyeIndex == 1 ? "Eye2IsGettingBurned"
|
|
: eyeIndex == 2 ? "Eye3IsGettingBurned" : null;
|
|
if (param != null)
|
|
eyeAnimator.SetBool(param, burning);
|
|
}
|
|
if (eyePivots.Count == 1)
|
|
{
|
|
eyeAnimator.SetBool("EyeIsGettingBurned", burning);
|
|
}
|
|
isBurning = burning;
|
|
targetAimWeight = burning ? 0f : 1f;
|
|
}
|
|
|
|
public void BurnEye()
|
|
{
|
|
if (!IsAlive) return;
|
|
eyeAnimator.SetBool("EyeIsGettingBurned", false);
|
|
eyeAnimator.SetBool("EyeIsDying", true);
|
|
eyeAudioSource.Stop();
|
|
SetSmokeActive(false);
|
|
IsAlive = false;
|
|
IsWatching = false;
|
|
}
|
|
|
|
public void ResetEye()
|
|
{
|
|
IsAlive = true;
|
|
IsWatching = false;
|
|
foreach (var r in eyeRenderers)
|
|
r.enabled = true;
|
|
eyeAnimator.SetBool("EyeIsDying", false);
|
|
switch (eyePivots.Count)
|
|
{
|
|
case 1:
|
|
eyeAnimator.SetBool("EyeIsGettingBurned", false);
|
|
break;
|
|
case 2:
|
|
eyeAnimator.SetBool("Eye1IsGettingBurned", false);
|
|
eyeAnimator.SetBool("Eye2IsGettingBurned", false);
|
|
break;
|
|
case 3:
|
|
eyeAnimator.SetBool("Eye1IsGettingBurned", false);
|
|
eyeAnimator.SetBool("Eye2IsGettingBurned", false);
|
|
eyeAnimator.SetBool("Eye3IsGettingBurned", false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void SetSmokeActive(bool active)
|
|
{
|
|
if (smokeEffect == null) return;
|
|
if (active && !IsPlayingSmokeEffect)
|
|
{
|
|
smokeEffect.Play();
|
|
IsPlayingSmokeEffect = true;
|
|
}
|
|
else if (!active && IsPlayingSmokeEffect)
|
|
{
|
|
smokeEffect.Stop();
|
|
IsPlayingSmokeEffect = false;
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (EyesManager.GetInstance() != null)
|
|
EyesManager.GetInstance().UnregisterEye(this);
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (!IsAlive || eyePivots.Count == 0) return;
|
|
Gizmos.color = IsWatching ? Color.red : Color.yellow;
|
|
foreach (var pivot in eyePivots)
|
|
{
|
|
Vector3 origin = pivot.position;
|
|
Vector3 forward = pivot.forward;
|
|
float halfFOV = gazeFOV * 0.5f;
|
|
float radius = gazeDistanceThreshold;
|
|
int seg = 20;
|
|
for (int i = 0; i <= seg; i++)
|
|
{
|
|
float angle = -halfFOV + i * (gazeFOV / seg);
|
|
Vector3 dir = Quaternion.Euler(0, angle, 0) * forward;
|
|
Gizmos.DrawLine(origin, origin + dir * radius);
|
|
}
|
|
}
|
|
DrawIrisDebugBar();
|
|
}
|
|
|
|
private void DrawIrisDebugBar()
|
|
{
|
|
if (!showIrisDebug || irisEyes.Count == 0) return;
|
|
Color c = Color.Lerp(Color.red, Color.green, irisLightFactor);
|
|
Gizmos.color = c;
|
|
Vector3 pos = transform.position + debugOffset;
|
|
float len = 1f * irisLightFactor;
|
|
Vector3 left = pos - transform.right * 0.5f;
|
|
Vector3 right = left + transform.right * len;
|
|
Gizmos.DrawCube((left + right) * 0.5f, new Vector3(len, 0.1f, 0.01f));
|
|
}
|
|
}
|