синхрон шгов с шейдером
This commit is contained in:
parent
9245409fc9
commit
7fba61f7a0
@ -80,9 +80,9 @@ Material:
|
|||||||
- _BaseColor: {r: 0.015, g: 0.015, b: 0.02, a: 1}
|
- _BaseColor: {r: 0.015, g: 0.015, b: 0.02, a: 1}
|
||||||
- _EchoColor: {r: 4, g: 3.026, b: 1.049, a: 1}
|
- _EchoColor: {r: 4, g: 3.026, b: 1.049, a: 1}
|
||||||
- _EchoCoreColor: {r: 7.5, g: 5.5, b: 2.2, a: 1}
|
- _EchoCoreColor: {r: 7.5, g: 5.5, b: 2.2, a: 1}
|
||||||
- _EchoPosition1: {r: 1.5618706, g: 1.0799999, b: -34.510273, a: 0}
|
- _EchoPosition1: {r: 2.9648588, g: 1.0799999, b: -29.737446, a: 0}
|
||||||
- _EchoPosition2: {r: -1.1804235, g: 1.0799999, b: -35.782673, a: 0}
|
- _EchoPosition2: {r: 3.705747, g: 1.0799999, b: -28.43126, a: 0}
|
||||||
- _EchoPosition3: {r: -0.93470556, g: 1.0799999, b: -34.72166, a: 0}
|
- _EchoPosition3: {r: 7.0987725, g: 1.0799999, b: -9.685643, a: 0}
|
||||||
- _WaveCenter: {r: 0, g: 0, b: 0, a: 0}
|
- _WaveCenter: {r: 0, g: 0, b: 0, a: 0}
|
||||||
m_BuildTextureStacks: []
|
m_BuildTextureStacks: []
|
||||||
m_AllowLocking: 1
|
m_AllowLocking: 1
|
||||||
|
|||||||
Binary file not shown.
@ -6,7 +6,28 @@ public class EchoTrigger : MonoBehaviour
|
|||||||
[Header("Связь с шейдером")]
|
[Header("Связь с шейдером")]
|
||||||
public Material echoMaterial;
|
public Material echoMaterial;
|
||||||
|
|
||||||
[Header("Настройки шагов")]
|
[Header("Синхронизация с шагами персонажа")]
|
||||||
|
[Tooltip("Включить синхронизацию волн эхолокации с анимацией шагов")]
|
||||||
|
public bool syncWithAnimation = true;
|
||||||
|
public Animator playerAnimator;
|
||||||
|
public CharacterController characterController;
|
||||||
|
[Tooltip("Позиционировать волну строго на кости наступающей ноги (L/R)")]
|
||||||
|
public bool spawnAtFootBone = true;
|
||||||
|
public Transform leftFootBone;
|
||||||
|
public Transform rightFootBone;
|
||||||
|
|
||||||
|
[Header("Фазы касания земли в анимации (0.0 - 1.0)")]
|
||||||
|
[Tooltip("Фаза шага левой ногой при обычной ходьбе")]
|
||||||
|
[Range(0f, 1f)] public float leftFootPhaseWalk = 0.08f;
|
||||||
|
[Tooltip("Фаза шага правой ногой при обычной ходьбе")]
|
||||||
|
[Range(0f, 1f)] public float rightFootPhaseWalk = 0.55f;
|
||||||
|
[Tooltip("Фаза шага левой ногой при спринте")]
|
||||||
|
[Range(0f, 1f)] public float leftFootPhaseSprint = 0.12f;
|
||||||
|
[Tooltip("Фаза шага правой ногой при спринте")]
|
||||||
|
[Range(0f, 1f)] public float rightFootPhaseSprint = 0.62f;
|
||||||
|
|
||||||
|
[Header("Резервный режим (по дистанции)")]
|
||||||
|
[Tooltip("Используется только если аниматор не найден или выключена синхронизация")]
|
||||||
public float stepDistance = 1.5f;
|
public float stepDistance = 1.5f;
|
||||||
public float waveSpeed = 15f;
|
public float waveSpeed = 15f;
|
||||||
|
|
||||||
@ -20,11 +41,19 @@ public class EchoTrigger : MonoBehaviour
|
|||||||
public float walkIntensity = 1.0f; // Обычный свет для шага
|
public float walkIntensity = 1.0f; // Обычный свет для шага
|
||||||
public float runIntensity = 3.0f; // Очень яркая вспышка для бега
|
public float runIntensity = 3.0f; // Очень яркая вспышка для бега
|
||||||
|
|
||||||
|
[Header("Звуки шагов (Опционально)")]
|
||||||
|
public AudioSource audioSource;
|
||||||
|
public AudioClip[] footstepSounds;
|
||||||
|
[Range(0f, 1f)] public float footstepVolume = 0.6f;
|
||||||
|
|
||||||
|
[Header("Реакция мутантов")]
|
||||||
|
public bool alertMutants = true;
|
||||||
|
|
||||||
// Пул из 3 одновременных волн
|
// Пул из 3 одновременных волн
|
||||||
private const int MAX_WAVES = 3;
|
private const int MAX_WAVES = 3;
|
||||||
private float[] waveRadii = new float[MAX_WAVES];
|
private float[] waveRadii = new float[MAX_WAVES];
|
||||||
private float[] waveMaxRadii = new float[MAX_WAVES];
|
private float[] waveMaxRadii = new float[MAX_WAVES];
|
||||||
private float[] waveIntensities = new float[MAX_WAVES]; // Память для яркости каждой волны
|
private float[] waveIntensities = new float[MAX_WAVES];
|
||||||
private bool[] waveActive = new bool[MAX_WAVES];
|
private bool[] waveActive = new bool[MAX_WAVES];
|
||||||
|
|
||||||
public static EchoTrigger Instance;
|
public static EchoTrigger Instance;
|
||||||
@ -32,6 +61,11 @@ public class EchoTrigger : MonoBehaviour
|
|||||||
private Vector3 lastPosition;
|
private Vector3 lastPosition;
|
||||||
private float distanceMoved = 0f;
|
private float distanceMoved = 0f;
|
||||||
|
|
||||||
|
private bool wasMoving = false;
|
||||||
|
private int lastLeftStepCycle = int.MinValue;
|
||||||
|
private int lastRightStepCycle = int.MinValue;
|
||||||
|
private PlayerNoise playerNoise;
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
if (Instance == null) Instance = this;
|
if (Instance == null) Instance = this;
|
||||||
@ -48,12 +82,116 @@ public class EchoTrigger : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastPosition = transform.position;
|
lastPosition = transform.position;
|
||||||
|
|
||||||
|
if (characterController == null)
|
||||||
|
characterController = GetComponent<CharacterController>();
|
||||||
|
|
||||||
|
if (playerAnimator == null)
|
||||||
|
playerAnimator = GetComponentInChildren<Animator>();
|
||||||
|
|
||||||
|
if (playerNoise == null)
|
||||||
|
playerNoise = GetComponent<PlayerNoise>();
|
||||||
|
|
||||||
|
if (audioSource == null)
|
||||||
|
audioSource = GetComponent<AudioSource>();
|
||||||
|
|
||||||
|
FindFootBones();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FindFootBones()
|
||||||
|
{
|
||||||
|
if (playerAnimator == null) return;
|
||||||
|
|
||||||
|
if (playerAnimator.isHuman)
|
||||||
|
{
|
||||||
|
if (leftFootBone == null)
|
||||||
|
leftFootBone = playerAnimator.GetBoneTransform(HumanBodyBones.LeftFoot);
|
||||||
|
if (rightFootBone == null)
|
||||||
|
rightFootBone = playerAnimator.GetBoneTransform(HumanBodyBones.RightFoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Резервный поиск по имени, если аватар Generic или кости не замаплены
|
||||||
|
if (leftFootBone == null || rightFootBone == null)
|
||||||
|
{
|
||||||
|
Transform[] allChildren = playerAnimator.GetComponentsInChildren<Transform>();
|
||||||
|
foreach (var t in allChildren)
|
||||||
|
{
|
||||||
|
string lower = t.name.ToLower();
|
||||||
|
if (leftFootBone == null && (lower.Contains("l_foot") || lower.Contains("foot_l") || lower.Contains("leftfoot")))
|
||||||
|
leftFootBone = t;
|
||||||
|
if (rightFootBone == null && (lower.Contains("r_foot") || lower.Contains("foot_r") || lower.Contains("rightfoot")))
|
||||||
|
rightFootBone = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
if (echoMaterial == null) return;
|
HandleStepDetection();
|
||||||
|
UpdateWaveShaderProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleStepDetection()
|
||||||
|
{
|
||||||
|
if (syncWithAnimation && playerAnimator != null)
|
||||||
|
{
|
||||||
|
HandleAnimationSteps();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleDistanceSteps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleAnimationSteps()
|
||||||
|
{
|
||||||
|
AnimatorStateInfo stateInfo = playerAnimator.GetCurrentAnimatorStateInfo(0);
|
||||||
|
bool isMoving = playerAnimator.GetBool("IsMoving");
|
||||||
|
float animSpeed = playerAnimator.GetFloat("Speed");
|
||||||
|
bool isGrounded = characterController == null || characterController.isGrounded;
|
||||||
|
|
||||||
|
// Синхронизируем шаги только когда персонаж движется по земле в режиме перемещения
|
||||||
|
if (isMoving && animSpeed > 0.1f && isGrounded && stateInfo.IsName("Locomotion"))
|
||||||
|
{
|
||||||
|
float speedRatio = Mathf.InverseLerp(1.5f, 6.0f, animSpeed);
|
||||||
|
float targetLeftPhase = Mathf.Lerp(leftFootPhaseWalk, leftFootPhaseSprint, speedRatio);
|
||||||
|
float targetRightPhase = Mathf.Lerp(rightFootPhaseWalk, rightFootPhaseSprint, speedRatio);
|
||||||
|
|
||||||
|
float currTime = stateInfo.normalizedTime;
|
||||||
|
|
||||||
|
if (!wasMoving)
|
||||||
|
{
|
||||||
|
// При начале движения инициализируем циклы текущим положением анимации,
|
||||||
|
// чтобы не было фантомного шага из простоя
|
||||||
|
lastLeftStepCycle = Mathf.FloorToInt(currTime - targetLeftPhase);
|
||||||
|
lastRightStepCycle = Mathf.FloorToInt(currTime - targetRightPhase);
|
||||||
|
wasMoving = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int leftCycle = Mathf.FloorToInt(currTime - targetLeftPhase);
|
||||||
|
if (leftCycle > lastLeftStepCycle)
|
||||||
|
{
|
||||||
|
lastLeftStepCycle = leftCycle;
|
||||||
|
TriggerFootstep(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rightCycle = Mathf.FloorToInt(currTime - targetRightPhase);
|
||||||
|
if (rightCycle > lastRightStepCycle)
|
||||||
|
{
|
||||||
|
lastRightStepCycle = rightCycle;
|
||||||
|
TriggerFootstep(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wasMoving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleDistanceSteps()
|
||||||
|
{
|
||||||
Vector3 currentPos = new Vector3(transform.position.x, 0, transform.position.z);
|
Vector3 currentPos = new Vector3(transform.position.x, 0, transform.position.z);
|
||||||
Vector3 lastPos = new Vector3(lastPosition.x, 0, lastPosition.z);
|
Vector3 lastPos = new Vector3(lastPosition.x, 0, lastPosition.z);
|
||||||
|
|
||||||
@ -63,8 +201,94 @@ public class EchoTrigger : MonoBehaviour
|
|||||||
if (distanceMoved >= stepDistance)
|
if (distanceMoved >= stepDistance)
|
||||||
{
|
{
|
||||||
distanceMoved = 0f;
|
distanceMoved = 0f;
|
||||||
TriggerStepWave();
|
TriggerFootstep(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerStepWave()
|
||||||
|
{
|
||||||
|
TriggerFootstep(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerFootstep(bool isLeftFoot)
|
||||||
|
{
|
||||||
|
Vector3 spawnPos = transform.position;
|
||||||
|
|
||||||
|
if (spawnAtFootBone)
|
||||||
|
{
|
||||||
|
Transform foot = isLeftFoot ? leftFootBone : rightFootBone;
|
||||||
|
if (foot != null)
|
||||||
|
{
|
||||||
|
spawnPos = foot.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязываем высоту волны точно к уровню пола под персонажем
|
||||||
|
if (characterController != null)
|
||||||
|
{
|
||||||
|
float floorY = transform.position.y + characterController.center.y - (characterController.height * 0.5f);
|
||||||
|
spawnPos.y = floorY + 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Определяем режим передвижения (Sneak / Walk / Run)
|
||||||
|
var keyboard = Keyboard.current;
|
||||||
|
bool isRunning = keyboard != null && keyboard.leftShiftKey.isPressed;
|
||||||
|
bool isSneaking = keyboard != null && (keyboard.leftCtrlKey.isPressed || keyboard.cKey.isPressed);
|
||||||
|
|
||||||
|
if (playerAnimator != null)
|
||||||
|
{
|
||||||
|
float speedVal = playerAnimator.GetFloat("Speed");
|
||||||
|
if (speedVal > 4.5f) isRunning = true;
|
||||||
|
else if (speedVal > 0f && speedVal < 2.0f && !isRunning) isSneaking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float targetMax = walkRadius;
|
||||||
|
float targetIntensity = walkIntensity;
|
||||||
|
|
||||||
|
if (isRunning)
|
||||||
|
{
|
||||||
|
targetMax = runRadius;
|
||||||
|
targetIntensity = runIntensity;
|
||||||
|
}
|
||||||
|
else if (isSneaking)
|
||||||
|
{
|
||||||
|
targetMax = sneakRadius;
|
||||||
|
targetIntensity = sneakIntensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запуск световой волны эхолокации в шейдере
|
||||||
|
TriggerExternalWave(spawnPos, targetMax, targetIntensity);
|
||||||
|
|
||||||
|
// Воспроизведение звука шага (если задан)
|
||||||
|
if (audioSource != null && footstepSounds != null && footstepSounds.Length > 0)
|
||||||
|
{
|
||||||
|
AudioClip clip = footstepSounds[Random.Range(0, footstepSounds.Length)];
|
||||||
|
if (clip != null)
|
||||||
|
{
|
||||||
|
float vol = isSneaking ? footstepVolume * 0.4f : (isRunning ? footstepVolume * 1.3f : footstepVolume);
|
||||||
|
audioSource.PlayOneShot(clip, vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Синхронизация с PlayerNoise или прямое оповещение мутантов
|
||||||
|
if (playerNoise != null)
|
||||||
|
{
|
||||||
|
playerNoise.EmitFootstep(spawnPos, targetMax);
|
||||||
|
}
|
||||||
|
else if (alertMutants)
|
||||||
|
{
|
||||||
|
Collider[] colliders = Physics.OverlapSphere(spawnPos, targetMax);
|
||||||
|
foreach (Collider hit in colliders)
|
||||||
|
{
|
||||||
|
BlindMutantAI mutant = hit.GetComponent<BlindMutantAI>();
|
||||||
|
if (mutant != null) mutant.HearSound(spawnPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateWaveShaderProgress()
|
||||||
|
{
|
||||||
|
if (echoMaterial == null) return;
|
||||||
|
|
||||||
for (int i = 0; i < MAX_WAVES; i++)
|
for (int i = 0; i < MAX_WAVES; i++)
|
||||||
{
|
{
|
||||||
@ -101,30 +325,10 @@ public class EchoTrigger : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriggerStepWave()
|
|
||||||
{
|
|
||||||
var keyboard = Keyboard.current;
|
|
||||||
if (keyboard == null) return;
|
|
||||||
|
|
||||||
float targetMax = walkRadius;
|
|
||||||
float targetIntensity = walkIntensity;
|
|
||||||
|
|
||||||
if (keyboard.leftShiftKey.isPressed)
|
|
||||||
{
|
|
||||||
targetMax = runRadius;
|
|
||||||
targetIntensity = runIntensity;
|
|
||||||
}
|
|
||||||
else if (keyboard.leftCtrlKey.isPressed)
|
|
||||||
{
|
|
||||||
targetMax = sneakRadius;
|
|
||||||
targetIntensity = sneakIntensity;
|
|
||||||
}
|
|
||||||
|
|
||||||
TriggerExternalWave(transform.position, targetMax, targetIntensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TriggerExternalWave(Vector3 position, float targetMax, float targetIntensity)
|
public void TriggerExternalWave(Vector3 position, float targetMax, float targetIntensity)
|
||||||
{
|
{
|
||||||
|
if (echoMaterial == null) return;
|
||||||
|
|
||||||
// Ищем свободный слот
|
// Ищем свободный слот
|
||||||
int waveIndex = -1;
|
int waveIndex = -1;
|
||||||
for (int i = 0; i < MAX_WAVES; i++)
|
for (int i = 0; i < MAX_WAVES; i++)
|
||||||
|
|||||||
@ -149,6 +149,14 @@ public static class CharacterSetup
|
|||||||
EditorUtility.SetDirty(pg);
|
EditorUtility.SetDirty(pg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var echo = player.GetComponent<EchoTrigger>();
|
||||||
|
if (echo != null)
|
||||||
|
{
|
||||||
|
echo.playerAnimator = animator;
|
||||||
|
echo.FindFootBones();
|
||||||
|
EditorUtility.SetDirty(echo);
|
||||||
|
}
|
||||||
|
|
||||||
// 9. Проверяем CameraFollow на главной камере
|
// 9. Проверяем CameraFollow на главной камере
|
||||||
if (Camera.main != null)
|
if (Camera.main != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
|
|
||||||
public class PlayerNoise : MonoBehaviour
|
public class PlayerNoise : MonoBehaviour
|
||||||
@ -14,8 +14,17 @@ public class PlayerNoise : MonoBehaviour
|
|||||||
|
|
||||||
public GameObject wavePrefab;
|
public GameObject wavePrefab;
|
||||||
|
|
||||||
|
[Header("Синхронизация")]
|
||||||
|
[Tooltip("Если включено и на объекте есть EchoTrigger, генерация шума шагов синхронизируется с анимацией персонажа через EchoTrigger")]
|
||||||
|
public bool syncWithEchoTrigger = true;
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
|
if (syncWithEchoTrigger && GetComponent<EchoTrigger>() != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Keyboard.current == null) return;
|
if (Keyboard.current == null) return;
|
||||||
|
|
||||||
bool isMoving = Keyboard.current.wKey.isPressed ||
|
bool isMoving = Keyboard.current.wKey.isPressed ||
|
||||||
@ -38,25 +47,33 @@ public class PlayerNoise : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmitStepNoise()
|
public void EmitFootstep(Vector3 footPosition, float radius)
|
||||||
{
|
{
|
||||||
float currentRadius = walkRadius;
|
|
||||||
|
|
||||||
if (Keyboard.current.leftShiftKey.isPressed) currentRadius = runRadius;
|
|
||||||
else if (Keyboard.current.leftCtrlKey.isPressed || Keyboard.current.cKey.isPressed) currentRadius = sneakRadius;
|
|
||||||
|
|
||||||
if (wavePrefab != null)
|
if (wavePrefab != null)
|
||||||
{
|
{
|
||||||
Vector3 footPosition = transform.position - new Vector3(0, transform.localScale.y / 2, 0);
|
|
||||||
Instantiate(wavePrefab, footPosition, Quaternion.identity);
|
Instantiate(wavePrefab, footPosition, Quaternion.identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collider[] colliders = Physics.OverlapSphere(transform.position, currentRadius);
|
Collider[] colliders = Physics.OverlapSphere(footPosition, radius);
|
||||||
|
|
||||||
foreach (Collider hit in colliders)
|
foreach (Collider hit in colliders)
|
||||||
{
|
{
|
||||||
BlindMutantAI mutant = hit.GetComponent<BlindMutantAI>();
|
BlindMutantAI mutant = hit.GetComponent<BlindMutantAI>();
|
||||||
if (mutant != null) mutant.HearSound(transform.position);
|
if (mutant != null) mutant.HearSound(footPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EmitStepNoise()
|
||||||
|
{
|
||||||
|
float currentRadius = walkRadius;
|
||||||
|
|
||||||
|
if (Keyboard.current != null)
|
||||||
|
{
|
||||||
|
if (Keyboard.current.leftShiftKey.isPressed) currentRadius = runRadius;
|
||||||
|
else if (Keyboard.current.leftCtrlKey.isPressed || Keyboard.current.cKey.isPressed) currentRadius = sneakRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 footPosition = transform.position - new Vector3(0, transform.localScale.y / 2, 0);
|
||||||
|
EmitFootstep(footPosition, currentRadius);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user