Ответы пользователя по тегу Unity
  • Плавное движение и вращение персонажа с помощью acceleration.x?

    K0TlK
    @K0TlK
    Буллю людей.
    Код твой изменять не буду, сам все перепишешь на моем примере.

    Начнем с низов. Есть ввод от игрока. Для этого ввода вводим интерфейс:
    namespace SmoothMovement
    {
        public interface IPlayerInput
        {
            float Acceleration { get; }
        }
    }


    Далее нам нужно сглаживать этот Acceleration, значит вводим еще один интерфейс:
    namespace SmoothMovement
    {
        public interface ISmoothAcceleration
        {
            float Smooth(float acceleration, float input);
        }
    }


    Далее реализуем IPlayerInput:
    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);
            }
        }
    }


    В апдейте присваиваем свойству Acceleration сглаженное значение инпута. Далее само сглаживание:
    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;
            }
        }
    }

    Инжектим IPlayerInput через инспектор, двигаем геймобжект. С вращением делай сам что-нибудь, я не знаю как у тебя там что должно вращаться
    Ответ написан
    Комментировать
  • Плавное заполнение слайдер. Как сделать?

    K0TlK
    @K0TlK
    Буллю людей.
    В корутине заполняешь с течением времени.

    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;
            }
        }
    }
    Ответ написан
  • Почему OnTriggerStay2D работает не сразу?

    K0TlK
    @K0TlK
    Буллю людей.
    Потому что OnTriggerStay вызывается не каждый кадр. Отдели ввод игрока. WakeUp - костыль, которого не должно быть. Вот примерно то, что тебе надо:
    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");
        }
    
    }


    Отдельно все остальное. На входе подписываемся, на выходе отписываемся. Все. Не нужен никакой OnTriggerStay и WakeUp.
    Ответ написан
    8 комментариев
  • Как отследить, что объект находится около точки в 2d на unity?

    K0TlK
    @K0TlK
    Буллю людей.
    Проверяй дистанцию между платформой и точкой
    var distance = (point.position - transform.position).magnitude;

    Vector3.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;
        }
    
    }


    В UpdatePoint я рассчитываю расстояние между точкой и позицией платформы и, если оно меньше максимальной дистанции приближения, то меняю точку, к которой стремится платформа.

    С тем же успехом можно сравнивать позиции платформы и точки, к которой она стремится и, если они равны, менять точку.
    Ответ написан
    Комментировать
  • Как нажимать на кнопки в 3D?

    K0TlK
    @K0TlK
    Буллю людей.
    Ты делаешь рейкаст куда-то вперед от какого-то трансформа, а тебе нужно делать рейкаст в сторону курсора. Camera.ScreenPointToRay возвращает луч, который идет от камеры к месту на экране. Пример:
    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);
    }


    И не надо так использовать тэги. Когда у тебя будет не 4 кнопки, а 40, то в методе OnButtonClick у тебя будет 40 условий? А если ты написал тэг с ошибкой, то будешь искать потом откуда ошибки валят? Выдели эти кнопки отдельно, получай нужный компонент при рейкасте и вызывай метод OnButtonClick уже у отдельного компонента.
    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();
                    }
                }
            }
    
        }
    }


    Также, каждый компонент может иметь метод OnMouseDown, который вызывается, когда игрок нажимает на него. Можно создать компонент кнопки и при нажатии на него будет вызываться ивент, на который можно подписать нужные методы.
    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()
        {
    
        }
    }
    Ответ написан
    Комментировать
  • Почему деньги не вычитаются при покупке из переменной?

    K0TlK
    @K0TlK
    Буллю людей.
    Какой раз ты уже задаешь этот вопрос? Третий? Ты ничему не научишься, если будешь просто копипастить код, который кто-то написал за тебя. Ты где-то нашел код, скопировал его и пытаешься изменить, не понимая того, как все работает.
    Ты и не отнимаешь эти деньги из первого скрипта, ты загружаешь данные из json в BuyCharacter, изменяешь их и сохраняешь.
    Тебе сюда и сюда
    Ответ написан
  • Почему может не реагировать объект на условие нажатия кнопки?

    K0TlK
    @K0TlK
    Буллю людей.
    OnTriggerEnter вызывается один раз, когда объект входит в триггер. Соответственно, все эти проверки выполняются единожды. Т.е. в одном кадре у объекта, вошедшего в триггер должен быть тэг player и должна быть нажата кнопка F. Первое условие может быть и соблюдено, если тэг правильно написан, но чтобы соблюсти второе, нужно одновременно с входом в триггер нажать кнопку F, что сделать сложно. Умники, конечно, посоветуют использовать OnTriggerStay, но не нужно этого делать. Нужно отделить ввод. Получится что-то типа этого:
    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
    }


    В OnTriggerEnter проверяется, присутствует ли компонент Player на объекте. Именно компонент, а не тэг. Не нужно делать проверки по тэгу, т.к. это строки и ты можешь ошибиться в написании этой строки, делай проверки по наличию компонента. Если компонент имеется, то подписываем нужный метод на событие ActionButtonPressed. В OnTriggerExit, соответственно, отписываемся. И в OnDestroy тоже, если объект будет уничтожен. Отписываться нужно всегда.
    И грубый пример PlayerInput ниже:
    public class PlayerInput : MonoBehaviour
    {
        public static event Action ActionButtonPressed;
    
    
        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.F))
            {
                ActionButtonPressed?.Invoke();
            }
        }
    }
    Ответ написан
    Комментировать
  • Как сделать так, чтобы функция вызывалась при изменении переменной в Unity?

    K0TlK
    @K0TlK
    Буллю людей.
    Если ползунок - юнитивский слайдер, то у него есть событие onValueChanged. Подписываешь на это событие метод, который изменяет громкость и все.
    Что-то типа этого:
    [SerializeField] private Slider _slider;
    
    private void OnEnable()
    {
        _slider.onValueChanged.AddListener(ChangeVolume);
    }
    
    private void OnDisable()
    {
        _slider.onValueChanged.RemoveListener(ChangeVolume);
    }
    
    private void ChangeVolume(float amount)
    {
        //изменить громкость на значение amount
    }
    Ответ написан
    1 комментарий
  • Как убрать инверсию при слежке за курсором?

    K0TlK
    @K0TlK
    Буллю людей.
    GGWPKATASI, Еще раз объясню. Когда вы поворачиваете персонажа, вы разворачиваете только персонажа. Спрайт оружия все еще повернут туда, куда и был повернут. Вам нужно оружие тоже флипать. Сейчас покажу пример.

    Есть скрипт игрока.
    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 в зависимости от того, куда смотрит персонаж
            }
    
        }
    }


    Итак. Игрок. Двигается, смотрит в сторону мыши.
    Оружие. Смотрит на мышь.
    Можете потестировать, посмотреть как все работает.
    Главное у Игрока у компонента Rigidbody2D поставить gravity scale на 0.
    Всё это работает, если изначально спрайт игрока смотрит вправо, спрайт оружия смотрит вправо.
    Вот так: 623a14e2ef06f683850837.jpeg
    Мега краткое объяснение в картинках:
    Изначально оружие смотрит туда - 623a164516692952768870.jpeg
    Но когда мы поворачиваем персонажа, то оно тоже поворачивается вот так - 623a1665a5bac426619425.jpeg
    Соответственно, нужно отразить картинку по Y, чтобы ее выровнять - 623a168c06707463491882.jpeg
    Ответ написан
    Комментировать
  • Как сделать затухание объекта в Unity2D через функцию OnTriggerEnter2D?

    K0TlK
    @K0TlK
    Буллю людей.
    Изменять у компонента SpriteRenderer свойство color. У типа Color есть 4 канала - красный, зеленый, синий и альфа (R,G,B,A). А - отвечает за прозрачность. Соответственно new Color(1f, 1f, 1f, 0.5f) будет полупрозрачный спрайт. При входе в триггер - уменьшаем альфа канал, при выходе увеличиваем.
    Ответ написан
    Комментировать
  • Как сделать регулировку громкости с помощью слайдера?

    K0TlK
    @K0TlK
    Буллю людей.
    Вы бы хоть посмотрели в документации, что такое Mathf.Lerp, прежде чем слепо слепо копипастить код из видео: https://docs.unity3d.com/ru/2019.4/ScriptReference.... Вам здесь lerp абсолютно не нужен, ибо, если установить t меньше единицы, то, пытаясь установить громкость на 100(или другое число), у вас она будет меньше 100. А если t будет равно 1, то смысла в линейной интерполяции нет. Mathf.Lerp(-80, 0, volume) замените на просто volume.
    Ответ написан
  • Как вызвать класс из другого скрипта (прикреплены к одному объекту)?

    K0TlK
    @K0TlK
    Буллю людей.
    Stop это не класс, а метод. Если они прикреплены к одному объекту, то можно получить ком>понент через GetComponent, как вы это делаете с rigidbody. В вашем случае это -

    GetComponent<combatPlaye>(), а далее уже у закэшированного компонента вызывать метод, который вам нужен.
    Ответ написан
    7 комментариев
  • Почему у переменной берется 0?

    K0TlK
    @K0TlK
    Буллю людей.
    Это делается через корутины либо через таски. Вот пример через корутину:

    using System.Collections;
    using UnityEngine;
    
    public class Fire : MonoBehaviour
    {
    
        private void Start()
        {
            StartCoroutine(Burn(5f, 1f));
        }
    
        private IEnumerator Burn(float time, float damageInterval)
        {
            while (time > 0)
            {
                time -= damageInterval;
                Debug.Log("ApplyDamage");
                yield return new WaitForSeconds(damageInterval);
            }
    
            yield break;
        }
    }


    В методе старт я стартую корутину. Сама корутина, в моем случае, будет в течение time времени писать лог через каждые damageInterval секунд. yield break нужен для того, чтобы корутина завершилась после выполнения цикла.
    Ответ написан
    Комментировать
  • Где можно изучить Unity лучше чем самый базовый уровень?

    K0TlK
    @K0TlK
    Буллю людей.
    https://learn.unity.com/courses Официальные юнитивские курсы будут получше процентов 80 того, что показывают на YT. По c# есть курс от ulearn можете его пройти: https://ulearn.me/
    Ответ написан
    Комментировать
  • Как сделать статическое движение в сторону?

    K0TlK
    @K0TlK
    Буллю людей.
    Использовать нормальное перемещение, а не через AddForce.

    Используйте velocity у Rigidbody, чтобы задавать конкретную скорость, а не какую-то силу.

    Вот пример:

    using UnityEngine;
    
    [RequireComponent(typeof(Rigidbody2D))]
    public class Bird : MonoBehaviour
    {
        [SerializeField] private float _speed;
        [SerializeField] private float _jumpSpeed;
    
        private Rigidbody2D _rb;
    
        private void Start()
        {
            _rb = GetComponent<Rigidbody2D>();
        }
    
        private void Update()
        {
            Move();
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
        }
    
        private void Jump()
        {
            var velocity = _rb.velocity;
            velocity.y = _jumpSpeed;
            _rb.velocity = velocity;
        }
    
        private void Move()
        {
            var velocity = _rb.velocity;
            velocity.x = _speed;
            _rb.velocity = velocity;
        }
    
    }


    В методе Move устанавливаем скорость по оси Х, тем самым персонаж двигается вправо. В методе Jump устанавливаем скорость по оси Y и персонаж прыгает. Это лишь пример того, как можно делать движение через velocity, реализуйте все под свои нужды. Используя перемещение через velocity, куда проще будет отбалансить игру, просто изменив скорость на нужную, нежели гадать какую силу нужно приложить к объекту, чтобы он прыгал выше.
    Ответ написан
  • Как заблокировать перемещение по Y Particle system?

    K0TlK
    @K0TlK
    Буллю людей.
    Создать отдельный скрипт, который будет перемещать партиклы, навешать его на игрока, не делать партиклы дочерними по отношению к игроку.

    Что-то типа этого:
    public class Particles : MonoBehaviour
    {
        [SerializeField] private ParticleSystem _particle;
    
        private void FixedUpdate()
        {
            MoveParticles();
        }
    
        private void MoveParticles()
        {
            var position = _particle.transform.position;
            position.x = transform.position.x;
            _particle.transform.position = position;
        }
    }


    Помещаем партиклы в инспекторе и все. У меня проект в 2д, так что оси z нет, но если у вас 3д, то по оси z тоже нужно перемещать.
    Ответ написан
    Комментировать