private void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.CompareTag("Player"))
{
_isPlayerInside= true;
}
}
private void OnTriggerEnter2D(Collider2D coll)
{
if (coll.TryGetComponent(out Player player) _isPlayerInside = true;
}
private void OnTriggerEnter2D(Collider2D coll) { if (coll.TryGetComponent(out Player player)) _isPlayerInside = true; }
using UnityEngine;
namespace BehaviourTree
{
public interface IMovementInput
{
Vector3 Direction { get; }
}
}
using UnityEngine;
namespace BehaviourTree
{
[RequireComponent(typeof(Rigidbody))]
public class Bot : MonoBehaviour
{
[SerializeField] private MonoBehaviour _botInput = null;
[SerializeField] private float _speed = 10f;
private Rigidbody _rb;
private IMovementInput BotInput => (IMovementInput) _botInput;
private void OnValidate()
{
if (_botInput is IMovementInput) return;
Debug.LogError($"{nameof(_botInput)} should implement {nameof(IMovementInput)}");
_botInput = null;
}
private void Awake()
{
_rb = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
var velocity = new Vector3(BotInput.Direction.x, 0, BotInput.Direction.z) * _speed;
_rb.velocity = velocity;
}
public void LookAt(Vector3 direction)
{
var rotation = Quaternion.LookRotation(direction);
transform.rotation = rotation;
}
}
}
using UnityEngine;
namespace BehaviourTree
{
public class BotInput : MonoBehaviour, IMovementInput
{
public Vector3 Direction { get; set; }
}
}
using UnityEngine;
namespace BehaviourTree
{
public interface ITarget
{
Vector3 Position { get; }
}
}
using UnityEngine;
namespace BehaviourTree
{
public class SomeTarget : MonoBehaviour, ITarget
{
public Vector3 Position => transform.position;
}
}
using BehaviorDesigner.Runtime;
namespace BehaviourTree.Brain
{
public class SharedBotInput : SharedVariable<BotInput>
{
public static implicit operator SharedBotInput(BotInput input) => new SharedBotInput {Value = input};
}
}
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using UnityEngine;
namespace BehaviourTree.Brain
{
public class SetRandomDirection : Action
{
public SharedVector3 Direction;
public override TaskStatus OnUpdate()
{
Direction.Value = Vector3.Scale(Random.insideUnitSphere.normalized, new Vector3(1, 0, 1));
return TaskStatus.Success;
}
}
}
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
namespace BehaviourTree.Brain
{
public class SetInput : Action
{
public SharedBotInput SharedInput;
public SharedVector3 Input;
public override TaskStatus OnUpdate()
{
SharedInput.Value.Direction = Input.Value;
return TaskStatus.Success;
}
}
}
using BehaviorDesigner.Runtime.Tasks;
namespace BehaviourTree.Brain
{
public class LookAtDirection : Action
{
public SharedBotInput Direction;
public Bot Bot;
public override TaskStatus OnUpdate()
{
Bot.LookAt(Direction.Value.Direction);
return TaskStatus.Success;
}
}
}
using BehaviorDesigner.Runtime;
namespace BehaviourTree.Brain
{
public class SharedTarget : SharedVariable<SomeTarget>
{
public static implicit operator SharedTarget(SomeTarget target) => new SharedTarget {Value = target};
}
}
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using UnityEngine;
namespace BehaviourTree.Brain
{
public class SetDirectionToTarget : Action
{
public SomeTarget Target;
public Bot Origin;
public SharedVector3 Direction;
public override TaskStatus OnUpdate()
{
var direction = (Target.Position - Origin.transform.position).normalized;
Direction.Value = new Vector3(direction.x, 0, direction.z);
return TaskStatus.Success;
}
}
}
namespace BehaviourTree.Brain
{
public interface IEye
{
bool InSight(ITarget target);
}
}
using UnityEngine;
namespace BehaviourTree.Brain
{
public class Eye : IEye
{
private readonly float _fov;
private readonly Transform _origin;
public Eye(float fov, Transform origin)
{
_fov = fov;
_origin = origin;
}
public bool InSight(ITarget target)
{
var direction = target.Position - _origin.position;
var angle = Mathf.Acos(Vector3.Dot(_origin.forward.normalized, direction.normalized)) * Mathf.Rad2Deg;
return angle <= _fov;
}
}
}
соответственно, нет точного понимания, когда именно произойдет изменение свойства Interactable
public class SomeCanvas : MonoBehaviour
{
public event Action<bool> ActiveStateChanged;
[SerializeField] private CanvasGroup _canvasGroup;
public void Activate()
{
SetActiveState(true);
}
public void Deactivate()
{
SetActiveState(false);
}
private void SetActiveState(bool state)
{
if (_canvasGroup.interactable == state) return;
_canvasGroup.interactable = state;
ActiveStateChanged?.Invoke(state);
}
}
public class NotSomeCanvas : MonoBehaviour
{
[SerializeField] private SomeCanvas _someCanvas;
private void OnEnable()
{
_someCanvas.ActiveStateChanged += LogActiveState;
}
private void OnDisable()
{
_someCanvas.ActiveStateChanged -= LogActiveState;
}
private void LogActiveState(bool state)
{
print($"Current state - {state}");
}
}
using UnityEngine;
namespace Movement
{
public interface IMovementInput
{
Vector2 Direction { get; }
}
public interface IMovement
{
void Move(Vector2 direction);
}
}
using UnityEngine;
namespace Movement
{
public class Movement : IMovement
{
private readonly Rigidbody2D _origin;
private readonly float _speed;
public Movement(Rigidbody2D origin, float speed)
{
_origin = origin;
_speed = speed;
}
public void Move(Vector2 direction)
{
if (direction == Vector2.zero) return;
_origin.velocity = direction * _speed;
}
}
}
using UnityEngine;
namespace Movement.ForStrongGigaChad
{
public class Arrows : IMovementInput
{
public Vector2 Direction => GetDirection();
private Vector2 GetDirection()
{
var x = 0f;
var y = 0f;
if (Input.GetKey(KeyCode.LeftArrow))
{
x = -1f;
} else if (Input.GetKey(KeyCode.RightArrow))
{
x = 1f;
}
if (Input.GetKey(KeyCode.UpArrow))
{
y = 1f;
} else if (Input.GetKey(KeyCode.DownArrow))
{
y = -1f;
}
return new Vector2(x, y);
}
}
}
using UnityEngine;
namespace Movement.ForStrongGigaChad
{
public class WASDArrows : IMovementInput
{
public Vector2 Direction => GetDirection();
private Vector2 GetDirection()
{
var x = Input.GetAxis("Horizontal");
var y = Input.GetAxis("Vertical");
return new Vector2(x, y);
}
}
}
using UnityEngine;
namespace Movement.ForStrongGigaChad
{
[RequireComponent(typeof(Rigidbody2D))]
public class Player : MonoBehaviour
{
private float _speed;
private IMovementInput _input;
private IMovement _movement;
public void Init(IMovementInput input, float speed)
{
_input = input;
_speed = speed;
var rb = GetComponent<Rigidbody2D>();
_movement = new Movement(
rb,
_speed);
}
private void FixedUpdate()
{
_movement.Move(_input.Direction);
}
}
}
using UnityEngine;
namespace Movement.ForStrongGigaChad
{
public interface IFactory<out TObject>
where TObject : MonoBehaviour
{
TObject Spawn();
}
}
using UnityEngine;
namespace Movement.ForStrongGigaChad
{
public class PlayerFactory : IFactory<Player>
{
private readonly IMovementInput _input;
private readonly float _speed;
private readonly Player _prefab;
public PlayerFactory(IMovementInput input, Player prefab, float speed)
{
_input = input;
_prefab = prefab;
_speed = speed;
}
public Player Spawn()
{
var obj = Object.Instantiate(_prefab);
obj.Init(_input, _speed);
return obj;
}
}
}
using UnityEngine;
namespace Movement.ForStrongGigaChad
{
public class Root : MonoBehaviour
{
[SerializeField] private Player _playerPrefab;
[SerializeField] private float _firstPlayerSpeed = 10f;
[SerializeField] private float _secondPlayerSpeed = 10f;
private void Awake()
{
var playerOne = new PlayerFactory(
new Arrows(),
_playerPrefab,
_firstPlayerSpeed)
.Spawn();
var player = new PlayerFactory(
new WASDArrows(),
_playerPrefab,
_secondPlayerSpeed)
.Spawn();
}
}
}
using UnityEngine;
namespace Movement.ForWeak
{
public class Arrows : MonoBehaviour, IMovementInput
{
public Vector2 Direction => GetDirection();
private Vector2 GetDirection()
{
var x = 0f;
var y = 0f;
if (Input.GetKey(KeyCode.LeftArrow))
{
x = -1f;
} else if (Input.GetKey(KeyCode.RightArrow))
{
x = 1f;
}
if (Input.GetKey(KeyCode.UpArrow))
{
y = 1f;
} else if (Input.GetKey(KeyCode.DownArrow))
{
y = -1f;
}
return new Vector2(x, y);
}
}
}
using UnityEngine;
namespace Movement.ForWeak
{
public class WASDArrows : MonoBehaviour, IMovementInput
{
public Vector2 Direction => GetDirection();
private Vector2 GetDirection()
{
var x = Input.GetAxis("Horizontal");
var y = Input.GetAxis("Vertical");
return new Vector2(x, y);
}
}
}
using UnityEngine;
namespace Movement.ForWeak
{
public class Player : MonoBehaviour
{
[SerializeField] private MonoBehaviour _input = null;
[SerializeField] private float _speed = 10f;
private IMovement _movement;
private IMovementInput Input => (IMovementInput) _input;
private void OnValidate()
{
if (_input is IMovementInput) return;
Debug.LogError($"{nameof(_input)} should implement {nameof(IMovementInput)}");
_input = null;
}
private void Awake()
{
var rb = GetComponent<Rigidbody2D>();
_movement = new Movement(
rb,
_speed);
}
private void FixedUpdate()
{
_movement.Move(Input.Direction);
}
}
}
Что я не так делаю с интерфейсами?
namespace Health
{
public interface IHealth
{
void Lose(int amount);
void Restore(int amount);
}
public interface IMutable<out T>
{
T Current { get; }
}
public interface IFinal
{
event Action Over;
}
}
using System;
using UnityEngine;
namespace Health
{
public class Health : IHealth, IFinal, IMutable<int>
{
public event Action Over;
private readonly int _max;
private const int Min = 0;
public Health(int max)
{
_max = max;
Current = _max;
}
public int Current { get; private set; }
public void Lose(int amount)
{
SetCurrent(Current - amount);
}
public void Restore(int amount)
{
SetCurrent(Current + amount);
}
private void SetCurrent(int amount)
{
Current = Mathf.Clamp(amount, Min, _max);
if (Current == Min) Over?.Invoke();
}
}
}
namespace Health
{
public interface IDamageable
{
void ApplyDamage(int amount);
}
}
using UnityEngine;
namespace Health
{
public class Knight : MonoBehaviour, IDamageable
{
[SerializeField] private int _maxHealth = 100;
private Health _health;
private void Awake()
{
_health = new Health(_maxHealth);
}
private void OnEnable()
{
_health.Over += Die;
}
private void OnDisable()
{
_health.Over -= Die;
}
public void ApplyDamage(int amount)
{
_health.Lose(amount);
Debug.Log($"Damaged, hp left - {_health.Current}");
}
private void Die()
{
Debug.Log("Died");
Destroy(gameObject);
}
}
}
using UnityEngine;
namespace Health
{
public class Enemy : MonoBehaviour
{
[SerializeField] private int _damage = 50;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.TryGetComponent(out IDamageable damageable))
{
damageable.ApplyDamage(_damage);
}
}
}
}
namespace SmoothMovement
{
public interface IPlayerInput
{
float Acceleration { get; }
}
}
namespace SmoothMovement
{
public interface ISmoothAcceleration
{
float Smooth(float acceleration, float input);
}
}
using UnityEngine;
namespace SmoothMovement
{
public class MobileInput : MonoBehaviour, IPlayerInput
{
public float Acceleration { get; private set; }
[SerializeField] private float _minAcceleration = -1f;
[SerializeField] private float _maxAcceleration = 1f;
[SerializeField] private float _smoothMultiplier = 5f;
[Range(0, 1)] [SerializeField] private float _fadingSpeed = 0.01f;
private ISmoothAcceleration _smoothing;
private void Awake()
{
_smoothing = new SmoothedAcceleration(_minAcceleration, _maxAcceleration, _smoothMultiplier, _fadingSpeed);
}
private void Update()
{
Acceleration = _smoothing.Smooth(Acceleration, Input.acceleration.x);
}
}
}
using UnityEngine;
namespace SmoothMovement
{
public class SmoothedAcceleration : ISmoothAcceleration
{
private readonly float _multiplier;
private readonly float _minValue;
private readonly float _maxValue;
private readonly float _fadingSpeed;
public SmoothedAcceleration(float minValue, float maxValue, float multiplier, float fadeSpeed)
{
_minValue = minValue;
_maxValue = maxValue;
_multiplier = multiplier;
_fadingSpeed = fadeSpeed;
}
public float Smooth(float acceleration, float input)
{
if (input == 0)
{
acceleration = Mathf.Lerp(acceleration, 0, _fadingSpeed);
return acceleration;
}
acceleration += input * _multiplier * Time.deltaTime;
acceleration = Mathf.Clamp(acceleration, _minValue, _maxValue);
return acceleration;
}
}
}
using UnityEngine;
namespace SmoothMovement
{
public class TestMovement : MonoBehaviour
{
[SerializeField] private MonoBehaviour _input = null;
[SerializeField] private float _speed = 10f;
private IPlayerInput Input => (IPlayerInput)_input;
private void OnValidate()
{
if (_input is IPlayerInput) return;
Debug.LogError($"{nameof(_input)} should implement {nameof(IPlayerInput)}");
_input = null;
}
private void FixedUpdate()
{
Move(Input.Acceleration);
}
private void Move(float direction)
{
if (direction == 0) return;
var position = transform.position;
position.x += direction * _speed * Time.deltaTime;
transform.position = position;
}
}
}
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class SliderFilling : MonoBehaviour
{
[SerializeField] private Slider _slider = null;
[SerializeField] private float _fillTime = 3f;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(FillValue(0.5f));
}
}
private IEnumerator FillValue(float value)
{
var estimateTime = 0f;
while(estimateTime < _fillTime)
{
estimateTime += Time.deltaTime;
_slider.value = Mathf.Lerp(0, value, estimateTime / _fillTime);
yield return null;
}
}
}
using System;
using System.Collections.Generic;
namespace Assets.Scripts.Experience
{
public class Leveling
{
public event Action<LevelingData> Updated;
private Dictionary<int, int> _levelToExprerience = new Dictionary<int, int>()
{
{0, 100 },
{1, 500 },
{2, 1000 },
{3, 2000 }
};
public int CurrentLevel { get; private set; } = 0;
public int CurrentExperience { get; private set; } = 0;
public int ExperienceToLevelUp => _levelToExprerience[CurrentLevel];
public void AddExperience(int amount)
{
IncreaseExperience(amount);
UpdateData();
}
private void IncreaseExperience(int amount)
{
if (amount < 0) throw new ArgumentOutOfRangeException(nameof(amount) + " can't be less than 0");
if (CurrentExperience + amount >= ExperienceToLevelUp)
{
var expLeft = CurrentExperience + amount - ExperienceToLevelUp;
LevelUp();
IncreaseExperience(expLeft);
return;
}
CurrentExperience += amount;
}
private void LevelUp()
{
CurrentLevel++;
ResetExperience();
}
private void ResetExperience()
{
CurrentExperience = 0;
}
private void UpdateData()
{
var data = new LevelingData(CurrentLevel, CurrentExperience, ExperienceToLevelUp);
Updated?.Invoke(data);
}
}
public struct LevelingData
{
public LevelingData(int level, int experience, int experienceToLevelUp)
{
Level = level;
Experience = experience;
ExperienceToLevelUp = experienceToLevelUp;
}
public int Level { get; private set; }
public int Experience { get; private set; }
public int ExperienceToLevelUp { get; private set; }
}
}
using UnityEngine;
using UnityEngine.UI;
namespace Assets.Scripts.Experience
{
public class Test : MonoBehaviour
{
[SerializeField] private Text _experience = null;
[SerializeField] private Text _experienceToLevelUp = null;
[SerializeField] private Text _level = null;
private Leveling _leveling = new Leveling();
private void OnEnable()
{
_leveling.Updated += UpdateUI;
}
private void OnDisable()
{
_leveling.Updated -= UpdateUI;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
_leveling.AddExperience(100);
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
_leveling.AddExperience(300);
}
if (Input.GetKeyDown(KeyCode.LeftControl))
{
_leveling.AddExperience(-100);
}
}
private void UpdateUI(LevelingData data)
{
_experience.text = data.Experience.ToString();
_experienceToLevelUp.text = data.ExperienceToLevelUp.ToString();
_level.text = data.Level.ToString();
}
}
}
public class KeyboardInput : MonoBehaviour, IPlayerInput
{
public event Action ActionButtonPressed;
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
ActionButtonPressed?.Invoke();
}
}
}
public interface IPlayerInput
{
public event Action ActionButtonPressed;
}
[RequireComponent(typeof(Collider2D))]
public class DialogueTrigger : MonoBehaviour
{
[SerializeField] private KeyboardInput _input;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.TryGetComponent(out Player player))
{
_input.ActionButtonPressed += StartDialogue;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.TryGetComponent(out Player player))
{
_input.ActionButtonPressed -= StartDialogue;
EndDialogue();
}
}
private void StartDialogue()
{
Debug.Log("DialogueStarted");
}
private void EndDialogue()
{
Debug.Log("DialogueEnded");
}
}
var distance = (point.position - transform.position).magnitude;
public class Example : MonoBehaviour
{
[SerializeField] private Transform _pointA, _pointB = null;
[SerializeField] private float _speed = 10f;
[SerializeField] private float _closeDistance = 0.2f;
private Transform _currentPoint;
private void Start()
{
_currentPoint = _pointA;
}
private void FixedUpdate()
{
MoveTo(_currentPoint.position);
}
private void MoveTo(Vector3 position)
{
var nextPosition = transform.position;
var delta = (position - transform.position).normalized;
delta *= _speed * Time.deltaTime;
nextPosition += delta;
transform.position = nextPosition;
UpdatePoint();
}
private void UpdatePoint()
{
var distance = (_currentPoint.position - transform.position).magnitude;
if (distance <= _closeDistance)
{
SwitchPoint();
}
}
private void SwitchPoint()
{
_currentPoint = _currentPoint == _pointA ? _pointB : _pointA;
}
}
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity))
{
Debug.DrawRay(hit.point, hit.normal * 10, Color.red, 10f);
}
public interface IButton
{
public void OnClick();
}
public class YellowButton : IButton
{
public void OnClick()
{
Debug.Log("Yellow");
}
}
public class Example : MonoBehaviour
{
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity))
{
if(hit.transform.TryGetComponent(out IButton button))
{
button.OnClick();
}
}
}
}
}
public class Button : MonoBehaviour
{
public event Action ButtonPressed;
private void OnMouseDown()
{
ButtonPressed?.Invoke();
}
}
public class ButtonHandler : MonoBehaviour
{
[SerializeField] private Button _button;
private void OnEnable()
{
_button.ButtonPressed += DoStuff;
}
private void OnDisable()
{
_button.ButtonPressed -= DoStuff;
}
private void DoStuff()
{
}
}
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent(out Player player))
{
PlayerInput.ActionButtonPressed += DoStuff;
}
}
private void OnTriggerExit(Collider other)
{
if (other.TryGetComponent(out Player player))
{
PlayerInput.ActionButtonPressed -= DoStuff;
}
}
private void DoStuff()
{
//Do stuff
}
public class PlayerInput : MonoBehaviour
{
public static event Action ActionButtonPressed;
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
ActionButtonPressed?.Invoke();
}
}
}
[SerializeField] private Slider _slider;
private void OnEnable()
{
_slider.onValueChanged.AddListener(ChangeVolume);
}
private void OnDisable()
{
_slider.onValueChanged.RemoveListener(ChangeVolume);
}
private void ChangeVolume(float amount)
{
//изменить громкость на значение amount
}
using UnityEngine;
namespace Assets.Scripts.Rotation
{
[RequireComponent(typeof(Rigidbody2D))] // Нужен, чтобы компонент Rigidbody2D всегда был на объекте, к которому прикреплен данный скрипт
public class Player : MonoBehaviour
{
[SerializeField] private float _maxSpeed; //[SerializeField] позволяет видеть private поля в инспекторе
[SerializeField] private float _speed;
[Range(0f, 1f)]
[SerializeField] private float _slowDownSpeed; //Range ограничивает между 0 и 1 в данном случае
[SerializeField] private Gun _gun;
private Vector2 _movementDirection;
private Rigidbody2D _rb;
private void Start()
{
_rb = GetComponent<Rigidbody2D>(); // Получаем компонент, если получаете компонент таким образом, обязательно нужно делать [RequireComponent(typeof(НазваниеКомпонента))]
}
private void Update()
{
var horizontal = Input.GetAxis("Horizontal");
var vertical = Input.GetAxis("Vertical");
_movementDirection = new Vector2(horizontal, vertical);
}
private void FixedUpdate() // все действия с физикой в FixedUpdate
{
if (_movementDirection == Vector2.zero) SlowDown(_slowDownSpeed);
Move(_movementDirection, _speed);
Flip();
}
private void Flip()
{
var vector = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position; //берем вектор направления от нашей позиции до позиции мыши в мировых координатах
if (vector.x < 0) transform.eulerAngles = new Vector3(0, 180); // Если он левее, то поворачиваем объект налево
if (vector.x >= 0) transform.eulerAngles = new Vector2(0, 0);// или направо, если он правее
_gun.Flip(vector.x);// поворачиваем оружие
}
private void Move(Vector2 direction, float speed)
{
var velocity = _rb.velocity;
var x = velocity.x + direction.x * speed;// определяем новую скорость
var y = velocity.y + direction.y * speed;
velocity.x = Mathf.Clamp(x, -_maxSpeed, _maxSpeed); //ограничиваем скорость
velocity.y = Mathf.Clamp(y, -_maxSpeed, _maxSpeed);
_rb.velocity = velocity; // присваиваем скорость
}
private void SlowDown(float speed) // замедляем объект
{
var velocity = _rb.velocity;
velocity.x = Mathf.Lerp(velocity.x, 0, speed);
velocity.y = Mathf.Lerp(_rb.velocity.y, 0, speed);
_rb.velocity = velocity;
}
}
}
using UnityEngine;
namespace Assets.Scripts.Rotation
{
[RequireComponent(typeof(SpriteRenderer))]
public class Gun : MonoBehaviour
{
private SpriteRenderer _spriteRenderer;
private void Start()
{
_spriteRenderer = GetComponent<SpriteRenderer>();
}
private void Update()
{
var difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
var rotation = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotation); // ваш код поворота оружия
}
public void Flip(float direction)
{
_spriteRenderer.flipY = direction < 0; // отражаем спрайт по оси Y в зависимости от того, куда смотрит персонаж
}
}
}