using System; using UnityEngine; using Code.Input; using Cysharp.Threading.Tasks; namespace Code.Gameplay.Player { public class PlayerMovement : MonoBehaviour { [SerializeField] private Rigidbody2D _rb; [SerializeField] private Transform _groundCheck; [SerializeField] private LayerMask _groundLayer; [SerializeField] private Animator _animator; [SerializeField] private SpriteRenderer _spriteRenderer; [SerializeField] private PlayerHealth _playerHealth; [SerializeField] private float _moveSpeed = 5f; [SerializeField] private float _sprintSpeedMultiplier = 1.8f; [SerializeField] private float _jumpForce = 12f; [SerializeField] private float _groundCheckRadius = 0.2f; [SerializeField] private float _jumpDuration = 0.5f; [SerializeField] private float _sprintPitchMultiplier = 1.2f; [SerializeField] private AudioSource audioSource; private IInputService _input; private bool _isJumping; private float _jumpTimer; private bool _isAttacking; private float _attackEndTime; private RuntimeAnimatorController _idleAnimator; private RuntimeAnimatorController _walkAnimator; private RuntimeAnimatorController _jumpAnimator; private RuntimeAnimatorController _attackAnimator; private AudioClip _jumpClip; private AudioClip _walkClip; private bool _wasMovingLastFrame; public bool isGrounded { get; private set; } public void Construct(IInputService input) { _input = input; _animator.runtimeAnimatorController = null; _idleAnimator = Resources.Load("Animations/penguin_idle_01"); _walkAnimator = Resources.Load("Animations/penguin_walk_01"); _jumpAnimator = Resources.Load("Animations/penguin_jump_01"); _attackAnimator = Resources.Load("Animations/penguin_atack_01"); _jumpClip = Resources.Load("Audio/SFX/JumpSFX"); _walkClip = Resources.Load("Audio/SFX/WalkSFX"); _playerHealth.OnTookDamage += StartBlinking; } private void OnDestroy() { _playerHealth.OnTookDamage -= StartBlinking; } private void Update() { CheckGround(); HandleInput(); } private void FixedUpdate() { Move(); HandleJump(); } private void HandleInput() { switch (_input.JumpPressed) { case true when isGrounded: _isJumping = true; audioSource.PlayOneShot(_jumpClip); _jumpTimer = 0f; _rb.linearVelocity = new Vector2(_rb.linearVelocity.x, _jumpForce); break; case false when _isJumping: { _isJumping = false; if (_rb.linearVelocity.y > 0) _rb.linearVelocity = new Vector2(_rb.linearVelocity.x, _rb.linearVelocity.y * 0.5f); break; } } if (_input.AttackPressed && !_isAttacking && Time.time >= _playerHealth.nextAttackTime) { _isAttacking = true; _attackEndTime = Time.time + 0.3f; _playerHealth?.TryAttack(); } UpdateFacingDirection(); UpdateAnimations(); } private void UpdateFacingDirection() { transform.rotation = _input.MoveInput switch { > 0 => Quaternion.identity, < 0 => Quaternion.Euler(0, -180, 0), _ => transform.rotation }; } private void UpdateAnimations() { if (_isAttacking) { if (_animator.runtimeAnimatorController != _attackAnimator) _animator.runtimeAnimatorController = _attackAnimator; if (Time.time >= _attackEndTime) { _isAttacking = false; } return; } var isMoving = Mathf.Abs(_input.MoveInput) > 0.1f; var isJumping = !isGrounded; if (isJumping) { if (_animator.runtimeAnimatorController != _jumpAnimator) _animator.runtimeAnimatorController = _jumpAnimator; } else if (isMoving) { if (_animator.runtimeAnimatorController != _walkAnimator) _animator.runtimeAnimatorController = _walkAnimator; } else if (_animator.runtimeAnimatorController != _idleAnimator) _animator.runtimeAnimatorController = _idleAnimator; } private void CheckGround() { isGrounded = Physics2D.OverlapCircle(_groundCheck.position, _groundCheckRadius, _groundLayer); } private void Move() { var moveInput = _input.MoveInput; var isMoving = Mathf.Abs(moveInput) > 0.1f; var speed = _input.SprintHeld ? _moveSpeed * _sprintSpeedMultiplier : _moveSpeed; var targetVelocityX = moveInput * speed; _rb.linearVelocity = new Vector2(targetVelocityX, _rb.linearVelocity.y); // Проигрываем звук ходьбы ТОЛЬКО при начале движения if (isMoving && !_wasMovingLastFrame && isGrounded) { if (_walkClip) { audioSource.pitch = _input.SprintHeld ? _sprintPitchMultiplier : 1f; audioSource.PlayOneShot(_walkClip); } } _wasMovingLastFrame = isMoving; } private void HandleJump() { if (!_isJumping || !_input.JumpPressed) return; _jumpTimer += Time.fixedDeltaTime; if (!(_jumpTimer < _jumpDuration && _rb.linearVelocity.y > 0)) { _isJumping = false; } } private async void StartBlinking() { _spriteRenderer.color = new Color(1, 1, 1, 0.5f); // полупрозрачность float blinkDuration = 2f; float blinkInterval = 0.1f; int blinkCount = (int)(blinkDuration / blinkInterval); for (int i = 0; i < blinkCount; i++) { _spriteRenderer.enabled = !_spriteRenderer.enabled; await UniTask.Delay(TimeSpan.FromSeconds(blinkInterval)); } _spriteRenderer.enabled = true; _spriteRenderer.color = Color.white; } private void OnDrawGizmosSelected() { if (_groundCheck == null) return; Gizmos.color = isGrounded ? Color.green : Color.red; Gizmos.DrawWireSphere(_groundCheck.position, _groundCheckRadius); } } }