using UnityEngine; /// /// Снаряд ядовитого био-плевка Босса. Летит к игроку, при контакте наносит прямой урон, /// создает расширяющуюся акустическую волну эхолокации и оставляет токсичную кислотную лужу. /// public class AcidProjectile : MonoBehaviour { public float speed = 19f; public float directDamage = 18f; public float maxLifetime = 5f; public bool isMainGlobule = true; private Vector3 moveDirection; private float lifeTimer = 0f; private bool hasImpacted = false; public static AcidProjectile Spawn(Vector3 startPos, Vector3 targetPos, float projSpeed = 19f, float damage = 18f, bool isMain = true, float spreadAngle = 0f) { GameObject go = new GameObject(isMain ? "Boss_Saliva_Globule" : "Boss_Saliva_Droplet"); go.transform.position = startPos; Vector3 dir = (targetPos - startPos); dir.y += 0.22f; // небольшая естественная баллистическая дуга плевка if (spreadAngle > 0.01f) { float pitch = Random.Range(-spreadAngle, spreadAngle); float yaw = Random.Range(-spreadAngle, spreadAngle); dir = Quaternion.Euler(pitch, yaw, 0f) * dir; } Vector3 normDir = dir.normalized; go.transform.rotation = Quaternion.LookRotation(normDir); float baseScale = isMain ? 1f : 0.65f; // 1. Формируем анатомическую вытянутую каплю слюны (а не плоскую кляксу) // Основное каплевидное тело (вытянутое вдоль направления полета Z) GameObject body = GameObject.CreatePrimitive(PrimitiveType.Sphere); body.name = "Saliva_Body"; body.transform.SetParent(go.transform, false); body.transform.localScale = new Vector3(0.08f, 0.08f, 0.34f) * baseScale; body.transform.localPosition = Vector3.zero; // Переднее утолщение капли слюны GameObject head = GameObject.CreatePrimitive(PrimitiveType.Sphere); head.name = "Saliva_Head"; head.transform.SetParent(go.transform, false); head.transform.localScale = new Vector3(0.11f, 0.11f, 0.14f) * baseScale; head.transform.localPosition = new Vector3(0f, 0f, 0.11f * baseScale); // Хвостовая мелкая капелька слизи GameObject tail = GameObject.CreatePrimitive(PrimitiveType.Sphere); tail.name = "Saliva_Tail"; tail.transform.SetParent(go.transform, false); tail.transform.localScale = new Vector3(0.05f, 0.05f, 0.08f) * baseScale; tail.transform.localPosition = new Vector3(0f, 0f, -0.20f * baseScale); Destroy(body.GetComponent()); Destroy(head.GetComponent()); Destroy(tail.GetComponent()); // Органический материал токсичной слюны: влажный, глянцевый, полупрозрачный Material salivaMat = new Material(Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard")); salivaMat.color = new Color(0.35f, 0.95f, 0.2f, 0.85f); salivaMat.EnableKeyword("_EMISSION"); salivaMat.SetColor("_EmissionColor", new Color(0.2f, 0.85f, 0.15f) * 1.3f); if (salivaMat.HasProperty("_Smoothness")) salivaMat.SetFloat("_Smoothness", 0.94f); body.GetComponent().material = salivaMat; head.GetComponent().material = salivaMat; tail.GetComponent().material = salivaMat; // 2. След летящей слюны (TrailRenderer) — нить тягучей слизи за летящим плевком TrailRenderer trail = go.AddComponent(); trail.time = 0.20f; trail.startWidth = 0.09f * baseScale; trail.endWidth = 0.005f; trail.minVertexDistance = 0.04f; Material trailMat = new Material(Shader.Find("Sprites/Default") ?? Shader.Find("Universal Render Pipeline/Unlit")); trailMat.color = new Color(0.4f, 0.95f, 0.25f, 0.7f); trail.material = trailMat; Gradient grad = new Gradient(); grad.SetKeys( new GradientColorKey[] { new GradientColorKey(new Color(0.4f, 1f, 0.25f), 0f), new GradientColorKey(new Color(0.25f, 0.85f, 0.15f), 1f) }, new GradientAlphaKey[] { new GradientAlphaKey(0.75f, 0f), new GradientAlphaKey(0.0f, 1f) } ); trail.colorGradient = grad; // 3. Мягкая подсветка капли в полете GameObject lightObj = new GameObject("SalivaGlow"); lightObj.transform.SetParent(go.transform, false); Light l = lightObj.AddComponent(); l.type = LightType.Point; l.color = new Color(0.35f, 1.0f, 0.25f); l.intensity = isMain ? 0.9f : 0.5f; l.range = isMain ? 2.0f : 1.2f; // 4. Коллайдер-триггер на корневом объекте SphereCollider col = go.AddComponent(); col.isTrigger = true; col.radius = 0.13f * baseScale; AcidProjectile proj = go.AddComponent(); proj.speed = projSpeed; proj.directDamage = damage; proj.isMainGlobule = isMain; proj.moveDirection = normDir; return proj; } private void Update() { if (hasImpacted) return; lifeTimer += Time.deltaTime; if (lifeTimer >= maxLifetime) { Destroy(gameObject); return; } // Перемещение снаряда вперед с естественной баллистикой moveDirection += Vector3.down * (4.0f * Time.deltaTime); transform.position += moveDirection.normalized * (speed * Time.deltaTime); // Поворот по вектору движения, чтобы капля летела кончиком вперед if (moveDirection.sqrMagnitude > 0.001f) { transform.rotation = Quaternion.LookRotation(moveDirection); } // SphereCast вперед для точной регистрации удара на высокой скорости if (Physics.SphereCast(transform.position, 0.12f, moveDirection, out RaycastHit hit, speed * Time.deltaTime, ~0, QueryTriggerInteraction.Ignore)) { OnHit(hit.collider, hit.point); } } private void OnTriggerEnter(Collider other) { if (hasImpacted) return; if (other.GetComponentInParent() != null) return; if (other.name.Contains("Saliva") || other.name.Contains("Acid")) return; OnHit(other, transform.position); } private void OnHit(Collider hitCollider, Vector3 impactPoint) { if (hasImpacted) return; hasImpacted = true; // 1. Урон игроку при прямом попадании PlayerHealth player = hitCollider.GetComponentInParent(); if (player != null) { player.TakeDamage(directDamage); Debug.Log($"[Босс] Плевок слюны попал в игрока! Нанесено {directDamage} урона."); } // 2. Волна эхолокации в месте удара (жидкость ударяется о поверхность и разлетается брызгами) if (EchoTrigger.Instance != null && isMainGlobule) { Vector3 echoPos = impactPoint; if (Physics.Raycast(impactPoint + Vector3.up * 0.5f, Vector3.down, out RaycastHit floorHit, 4f)) { echoPos = floorHit.point + Vector3.up * 0.05f; } EchoTrigger.Instance.TriggerExternalWave(echoPos, 14f, 1.8f); } // 3. Создание органического всплеска слюны на полу if (isMainGlobule) { Vector3 poolPos = impactPoint; if (Physics.Raycast(impactPoint + Vector3.up * 0.5f, Vector3.down, out RaycastHit fHit, 5f)) { poolPos = fHit.point; } AcidPool.Create(poolPos, poolLifetime: 4.5f, poolRadius: 0.85f); } // 4. Звук смачного шлепка слюны о поверхность GameAudioSynthesizer.PlayAcidImpact(); Destroy(gameObject); } }