193 lines
8.5 KiB
C#
193 lines
8.5 KiB
C#
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// Снаряд ядовитого био-плевка Босса. Летит к игроку, при контакте наносит прямой урон,
|
||
/// создает расширяющуюся акустическую волну эхолокации и оставляет токсичную кислотную лужу.
|
||
/// </summary>
|
||
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<Collider>());
|
||
Destroy(head.GetComponent<Collider>());
|
||
Destroy(tail.GetComponent<Collider>());
|
||
|
||
// Органический материал токсичной слюны: влажный, глянцевый, полупрозрачный
|
||
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<Renderer>().material = salivaMat;
|
||
head.GetComponent<Renderer>().material = salivaMat;
|
||
tail.GetComponent<Renderer>().material = salivaMat;
|
||
|
||
// 2. След летящей слюны (TrailRenderer) — нить тягучей слизи за летящим плевком
|
||
TrailRenderer trail = go.AddComponent<TrailRenderer>();
|
||
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<Light>();
|
||
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<SphereCollider>();
|
||
col.isTrigger = true;
|
||
col.radius = 0.13f * baseScale;
|
||
|
||
AcidProjectile proj = go.AddComponent<AcidProjectile>();
|
||
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<TitanBossAI>() != 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<PlayerHealth>();
|
||
if (player != null)
|
||
{
|
||
player.TakeDamage(directDamage);
|
||
Debug.Log($"<color=green>[Босс] Плевок слюны попал в игрока! Нанесено {directDamage} урона.</color>");
|
||
}
|
||
|
||
// 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);
|
||
}
|
||
}
|