@Timur975787

Почему скрипт работает не корректно?

У меня два игрока, одним я должен управлять стрелочками а другим клавишами WASD.
При старте игроком с галочкой на keyCodeMovement управлять можно только клавишами WASD, а другим игроком без галочки можно управлять клавишами WASD и стрелочками.
Код:
private Rigidbody2D rb;
    public float spedd;
    private float Ypos, Xpos;
    public bool keyCodeMovement = true;// у первого игрока я поставил на true, а у другого на false
    void Start()
    {
        rb = gameObject.GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if (keyCodeMovement == false)
        {
            Vector2 vec = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
            Vector2 velocityVec = vec.normalized * spedd;
            rb.velocity = velocityVec * Time.deltaTime;
        }
        else if (keyCodeMovement == true)
        {
            rb.velocity = new Vector2(Xpos, Ypos) * Time.deltaTime;//Код для красивого передвижение игрока
            if (Input.GetKey(KeyCode.A))
            {
                Xpos = -spedd;
            }
            else if (Input.GetKey(KeyCode.D))
            {
                Xpos = spedd;
            }
            else if (Input.GetKey(KeyCode.W))
            {
                Ypos = spedd;
            }
            else if (Input.GetKey(KeyCode.S))
            {
                Ypos = -spedd;
            }
        }
    }
  • Вопрос задан
  • 61 просмотр
Пригласить эксперта
Ответы на вопрос 2
K0TlK
@K0TlK
Буллю людей.
Во-первых, velocity не нужно умножать на deltaTime. Во-вторых, ты применяешь скорость перед тем, как у тебя инпут обработается(когда keyCodeMovement == false), перемести строчку rb.velocity = ... в самый низ этого условия после обработки инпута. В-третьих, у тебя одновременно может быть нажата только одна кнопка, либо a либо w либо s либо d. else if (Input.GetKey(KeyCode.W)) else здесь убери. Ну и в-четвертых, он работает некорректно, потому что он некорректен изначально.

Можешь сделать то, что я написал выше, а можешь сделать нормально. Есть два путя, для сильных и для слабых. В обоих случаях есть общее. Два интерфейса, которые описывают движение и инпут:
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);
        }
    }
}

и передвижение на стрелочках и на WASD:
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);
            
        }
    }
}


Здесь все тоже просто. В FixedUpdate происходит движение. Метод Init - метод инициализации, после спавна этого игрока, нужно вызывать этот метод и передавать зависимости. Чтобы спавнить этого персонажа нужна фабрика. Вот ее интерфейс:
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;
        }
    }
}


Здесь все тоже предельно просто. Принимает нужные зависимости через конструктор и, при вызове метода Spawn спавнит игрока и инициализирует его. И, чтобы все это было на сцене, нужно где-то это все спавнить:
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();
        }
    }
}

Root в данном случае является точкой входа в сцену, всё, что нужно - поместить его на какой-нибудь объект на сцене и при запуске он заспавнит двух игроков с разным управлением. Нужно лишь поместить в инспекторе префаб и настроить скорости.

Но есть и способ для слабых. Инпут со стрелочек:
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);
        }
    }
}

инпут для WASD:
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);
        }
    }
}


Отличаются они от первой версии тем, что отнаследованы от MonoBehaviour, поэтому их нужно вешать на какой-то объект на сцене. И собственно сам игрок:
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);
        }
    }
}

Здесь все просто. На сцене сделать 2 объекта, навешать на них скрипт Player, вставить нужный инпут в поля в инспекторе, установить каждому нужную скорость. Будет все работать точно так же, как и первый вариант. Благодаря OnValidate я инжекчу интерфейс через испектор. Вот. А ну и еще, у тебя XPos и YPos нулю никогда не будут равны, следовательно, стоять на месте персонаж не может, обнуляй их перед тем, как инпут считывать.
Ответ написан
freeExec
@freeExec
Участник OpenStreetMap
Input.GetAxis("Horizontal") у вас на какие клавиши завязан?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы