Во-первых, 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 нулю никогда не будут равны, следовательно, стоять на месте персонаж не может, обнуляй их перед тем, как инпут считывать.