using UnityEngine; using RR.Common.Interfaces; using RR.Common.Utility; using RR.Game2D.Core.Managers; using RR.Game2D.Behaviour.ScriptableObjects; using RR.Game2D.FX; using UnityEngine.VFX; namespace RR.Game2D.Behaviour { [RequireComponent(typeof(Rigidbody2D), typeof(Collider2D), typeof(Animator))] public class EnemyController : MonoBehaviour, IDamageable { [SerializeField] public EnemySFXAsset sfxAsset; [SerializeField] private EnemyControllerConfiguration config; [Header("FX")] [SerializeField] private VisualEffectAsset stompImpactVFX; [SerializeField] private string stompImpactVFXResourcePath = "VFX/MVP_Impact_VFX"; [SerializeField] private float stompImpactVFXLifetime = 1f; [SerializeField] private MvpVFXSpawnSettings stompImpactVFXSettings = new MvpVFXSpawnSettings { spreadRadiusX = 0.2f, spreadRadiusY = 0.04f }; private Health health; [ReadOnlyInInspector] public float CurrentHealth => health?.CurrentHealth ?? 0; [HideInInspector] public float MaxHealth => health?.MaxHealth ?? 0; private Animator _animator; private Rigidbody2D _rb; private Collider2D _col; private bool _isDead; private GameObject _playerObj; // Аниматор private static readonly int Speed = Animator.StringToHash("Speed"); private void Start() { _rb = GetComponent(); _col = GetComponent(); _animator = GetComponent(); _playerObj = GameManager.Instance.PlayerObj; if (_playerObj == null) { _playerObj = GameObject.FindGameObjectWithTag("Player"); } health = GetComponent(); if (health == null) { health = gameObject.AddComponent(); } health.ResetHealth(); health.SetOwner(this); health.SetMaxHealth(config.maxHealth); } private void FixedUpdate() { if (!_playerObj) return; _playerObj.TryGetComponent(out PlayerController playerController); Vector3 playerCenter = _playerObj.transform.position; if (PlayerInSight() && !playerController.isDeadAtMoment) { Vector3 toPlayer = playerCenter - transform.position; float dist = toPlayer.magnitude + 0.1f; if (dist > 0) { _rb.linearVelocity = new Vector2(config.moveSpeed * toPlayer.normalized.x, 0); } // if (dist > 0) // rb.linearVelocity = toPlayer / dist * moveSpeed; // else // rb.linearVelocity = Vector2.zero; } else { _rb.linearVelocity = Vector2.MoveTowards( _rb.linearVelocity, Vector2.zero, config.acceleration * Time.fixedDeltaTime); } _animator.SetFloat(Speed, Mathf.Abs(_rb.linearVelocity.magnitude)); } private void OnCollisionEnter2D(Collision2D collision) { if (!collision.collider.CompareTag("Player")) return; Rigidbody2D playerRb = collision.collider.GetComponent(); _playerObj.TryGetComponent(out PlayerController playerController); if (playerRb == null) return; Vector2 contactNormal = collision.GetContact(0).normal; if (playerController.isDeadAtMoment) return; if (contactNormal.y < -0.5f) { playerRb.linearVelocity = new Vector2(playerRb.linearVelocity.x, 35f); MvpVFXUtility.Spawn(stompImpactVFX, stompImpactVFXResourcePath, collision.GetContact(0).point, stompImpactVFXLifetime, stompImpactVFXSettings); TakeDamage(MaxHealth); // TakeDamage(1); } else { playerController.TakeDamage(1f); } } private bool PlayerInSight() { if (!_playerObj) return false; Vector3 playerPos = _playerObj.transform.position; Collider2D playerCol = _playerObj.GetComponent(); playerPos.y = playerCol.bounds.center.y; Vector2 eyePos = new(_rb.position.x, _col.bounds.center.y); Vector2 toPlayer = (Vector2)playerPos - eyePos; float dist = toPlayer.magnitude; if (dist > config.viewDistance) return false; Vector2 forward = transform.localEulerAngles.y >= 180f ? Vector2.right : Vector2.left; float cosHalf = Mathf.Cos(0.5f * config.viewAngle * Mathf.Deg2Rad); float dot = Vector2.Dot(forward.normalized, toPlayer / Mathf.Max(dist, 1e-6f)); if (dot < cosHalf) return false; int mask = config.obstacleMask; mask &= ~(1 << gameObject.layer); mask &= ~(1 << _playerObj.layer); Vector2 origin = eyePos + forward.normalized * 0.05f; RaycastHit2D hit = Physics2D.Raycast(origin, toPlayer.normalized, dist, mask); return !hit.collider; } public void TakeDamage(float amount) { health.TakeDamage(amount); } public void Heal(float amount) { health.Heal(amount); } public void Die() { if (_isDead) return; _isDead = true; _animator.Play("Death"); // Destroy(gameObject); } #if UNITY_EDITOR private void OnDrawGizmos() { if (!_playerObj) return; Vector2 origin = new(_rb.position.x, _col.bounds.center.y); Vector2 forward = transform.localEulerAngles.y >= 180f ? Vector2.right : Vector2.left; float half = config.viewAngle * 0.5f; Vector2 edgeA = (Quaternion.Euler(0, 0, +half) * forward); Vector2 edgeB = (Quaternion.Euler(0, 0, -half) * forward); Gizmos.color = Color.yellow; Gizmos.DrawLine(origin, origin + edgeA * config.viewDistance); Gizmos.DrawLine(origin, origin + edgeB * config.viewDistance); Gizmos.DrawWireSphere(origin, config.viewDistance); Vector2 toPlayer = (Vector2)_playerObj.transform.position - origin; if (!(toPlayer.magnitude <= config.viewDistance)) return; Gizmos.color = Color.red; Gizmos.DrawLine(transform.position, _playerObj.transform.position); } #endif } }