Movement Issue - 2d game

Hi all!

I’m having issues with my character’s movement in my 2d game. When I put the ‘jump’ movement, the character jumps only once when I press to go either left or right. The console shows the following errors:

"InvalidOperationException while executing 'performed' callbacks of 'Player1/Move[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)"
InvalidOperationException: Cannot read value of type 'Single' from composite 'UnityEngine.InputSystem.Composites.Vector2Composite' bound to action 'Player1/Move[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d]' (composite is a 'Int32' with value type 'Vector2')
UnityEngine.InputSystem.InputActionState.ReadValue[TValue] (System.Int32 bindingIndex, System.Int32 controlIndex, System.Boolean ignoreComposites) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:2038)
UnityEngine.InputSystem.InputAction+CallbackContext.ReadValue[TValue] () (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputAction.cs:1433)
InputManager.GetPauseInput (UnityEngine.InputSystem.InputAction+CallbackContext callbackContext) (at Assets/DowloadedPackages/MSU/Scripts/Player/InputManager.cs:136)
UnityEngine.Events.InvokableCall`1[T1].Invoke (T1 args0) (at <42a5878ce129403083acccf18e43363f>:0)
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <42a5878ce129403083acccf18e43363f>:0)
UnityEngine.InputSystem.Utilities.DelegateHelpers.InvokeCallbacksSafe[TValue] (UnityEngine.InputSystem.Utilities.InlinedArray`1[System.Action`1[TValue]]& callbacks, TValue argument, System.String callbackName, System.Object context) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Utilities/DelegateHelpers.cs:51)
UnityEngine.InputSystem.LowLevel.<>c__DisplayClass7_0:<set_onUpdate>b__0(NativeInputUpdateType, NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate(NativeInputUpdateType, IntPtr)
InvalidOperationException while executing 'performed' callbacks of 'Player1/Move[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)

Here’s the control mapping:

Here’s the player controller script I’m using (one that has worked before - believe or not):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Class which handles player movement
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour
{
    #region Variables
    #region GameObject references
    [Header("Game Object and Component References")]
    [Tooltip("The Input Manager component used to gather player input.")]
    public InputManager inputManager = null;
    [Tooltip("The Ground Check component used to check whether this player is grounded currently.")]
    public GroundCheck groundCheck = null;
    [Tooltip("The sprite renderer that represents the player.")]
    public SpriteRenderer spriteRenderer = null;
    [Tooltip("The health component attached to the player.")]
    public Health playerHealth;

    // The rigidbody used to move the player (necessary for this component, so not made public)
    private Rigidbody2D playerRigidbody = null;
    #endregion

    #region Getters (primarily from other components)
    #region Directional facing
    /// <summary>
    /// Enum to help determine which direction the player is facing.
    /// </summary>
    public enum PlayerDirection
    {
        Right,
        Left
    }

    // Which way the player is facing right now
    public PlayerDirection facing
    {
        get
        {
            if (horizontalMovementInput > 0)
            {
                return PlayerDirection.Right;
            }
            else if (horizontalMovementInput < 0)
            {
                return PlayerDirection.Left;
            }
            else
            {
                if (spriteRenderer != null && spriteRenderer.flipX == true)
                    return PlayerDirection.Left;
                return PlayerDirection.Right;
            }
        }
    }
    #endregion

    // Whether this player is grounded false if no ground check component assigned
    public bool grounded
    {
        get
        {
            if (groundCheck != null)
            {
                return groundCheck.CheckGrounded();
            }
            else
            {
                return false;
            }
        }
    }

    #region Input from Input Manager
    // The horizontal movement input collected from the input manager
    public float horizontalMovementInput
    {
        get
        {
            if (inputManager != null)
                return inputManager.horizontalMovement;
            else
                return 0;
        }
    }
    // the jump input collected from the input manager
    public bool jumpInput
    {
        get
        {
            if (inputManager != null)
                return inputManager.jumpStarted;
            else
                return false;
        }
    }
    #endregion
    #endregion

    #region Movement Variables
    [Header("Movement Settings")]
    [Tooltip("The speed at which to move the player horizontally")]
    public float movementSpeed = 4.0f;

    [Header("Jump Settings")]
    [Tooltip("The force with which the player jumps.")]
    public float jumpPower = 10.0f;
    [Tooltip("The number of jumps that the player is alowed to make.")]
    public int allowedJumps = 1;
    [Tooltip("The duration that the player spends in the \"jump\" state")]
    public float jumpDuration = 0.1f;
    [Tooltip("The effect to spawn when the player jumps")]
    public GameObject jumpEffect = null;
    [Tooltip("Layers to pass through when moving upwards")]
    public List<string> passThroughLayers = new List<string>();

    // The number of times this player has jumped since being grounded
    private int timesJumped = 0;
    // Whether the player is in the middle of a jump right now
    private bool jumping = false;
    #endregion

    #region Player State Variables
    /// <summary>
    /// Enum used for categorizing the player's state
    /// </summary>
    public enum PlayerState
    {
        Idle,
        Walk,
        Jump,
        Fall,
        Dead
    }

    // The player's current state (walking, idle, jumping, or falling)
    public PlayerState state = PlayerState.Idle;
    #endregion
    #endregion

    #region Functions
    #region GameObject Functions
    /// <summary>
    /// Description:
    /// Standard Unity function called once before the first update
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    private void Start()
    {
        SetupRigidbody();
        SetUpInputManager();
    }

    /// <summary>
    /// Description:
    /// Standard Unity function called once every frame after update
    /// Every frame, process input, move the player, determine which way they should face, and choose which state they are in
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    private void LateUpdate()
    {
        ProcessInput();
        HandleSpriteDirection();
        DetermineState();
    }
    #endregion

    #region Input Handling and Movement Functions
    /// <summary>
    /// Description:
    /// Collects input from the input manager, then calls functions to move the player accordingly
    /// Input: none
    /// Return: void (no return)
    /// </summary>
    private void ProcessInput()
    {
        HandleMovementInput();
        HandleJumpInput();
    }

    /// <summary>
    /// Description:
    /// Handles movement input
    /// Input: none
    /// Return: void (no return)
    /// </summary>
    private void HandleMovementInput()
    {
        Vector2 movementForce = Vector2.zero;
        if (Mathf.Abs(horizontalMovementInput) > 0 && state != PlayerState.Dead)
        {
            movementForce = transform.right * movementSpeed * horizontalMovementInput;
        }
        MovePlayer(movementForce);
    }

    /// <summary>
    /// Description:
    /// Moves the player with a specified force
    /// Input: 
    /// Vector2 movementForce
    /// Return: 
    /// void (no return)
    /// </summary>
    /// <param name="movementForce">The force with which to move the player</param>
    private void MovePlayer(Vector2 movementForce)
    {
        if (grounded && !jumping)
        {
            float horizontalVelocity = movementForce.x;
            float verticalVelocity = 0;
            playerRigidbody.velocity = new Vector2(horizontalVelocity, verticalVelocity);
        }
        else
        {
            float horizontalVelocity = movementForce.x;
            float verticalVelocity = playerRigidbody.velocity.y;
            playerRigidbody.velocity = new Vector2(horizontalVelocity, verticalVelocity);
        }
        if (playerRigidbody.velocity.y > 0)
        {
            foreach (string layerName in passThroughLayers)
            {
                Physics2D.IgnoreLayerCollision(LayerMask.NameToLayer("Player"), LayerMask.NameToLayer(layerName), true);
            } 
        }
        else
        {
            foreach (string layerName in passThroughLayers)
            {
                Physics2D.IgnoreLayerCollision(LayerMask.NameToLayer("Player"), LayerMask.NameToLayer(layerName), false);
            }
        }
    }

    /// <summary>
    /// Description:
    /// Handles jump input
    /// Input:
    /// none
    /// Return:
    /// void (no return)
    /// </summary>
    private void HandleJumpInput()
    {
        if (jumpInput)
        {
            StartCoroutine("Jump", 1.0f);
        }
    }

    /// <summary>
    /// Description:
    /// Coroutine which causes the player to jump.
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    /// <returns>IEnumerator: makes coroutine possible</returns>
    private IEnumerator Jump(float powerMultiplier = 1.0f)
    {
        if (timesJumped < allowedJumps && state != PlayerState.Dead)
        {
            jumping = true;
            float time = 0;
            SpawnJumpEffect();
            playerRigidbody.velocity = new Vector2(playerRigidbody.velocity.x, 0);
            playerRigidbody.AddForce(transform.up * jumpPower * powerMultiplier, ForceMode2D.Impulse);
            timesJumped++;
            while (time < jumpDuration)
            {
                yield return null;
                time += Time.deltaTime;
            }
            jumping = false;
        }
    }

    /// <summary>
    /// Description:
    /// Spawns the effect that occurs when the player jumps
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    private void SpawnJumpEffect()
    {
        if (jumpEffect != null)
        {
            Instantiate(jumpEffect, transform.position, transform.rotation, null);
        }
    }

    /// <summary>
    /// Description:
    /// Bounces the player upwards, refunding jumps.
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    public void Bounce()
    {
        timesJumped = 0;
        if (inputManager.jumpHeld)
        {
            StartCoroutine("Jump", 1.5f);
        }
        else
        {
            StartCoroutine("Jump", 1.0f);
        }
    }

    /// <summary>
    /// Description:
    /// Determines which way the player should be facing, then makes them face in that direction
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    private void HandleSpriteDirection()
    {
        if (spriteRenderer != null)
        {
            if (facing == PlayerDirection.Left)
            {
                spriteRenderer.flipX = true;
            }
            else
            {
                spriteRenderer.flipX = false;
            }
        }
    }
    #endregion

    #region State Functions
    /// <summary>
    /// Description:
    /// Gets and returns the player's current state
    /// Input: 
    /// none
    /// Return: 
    /// PlayerState
    /// </summary>
    /// <returns>PlayerState: The player's current state (idle, walking, jumping, falling</returns>
    private PlayerState GetState()
    {
        return state;
    }

    /// <summary>
    /// Description:
    /// Sets the player's current state
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    /// <param name="newState">The PlayerState to set the current state to</param>
    private void SetState(PlayerState newState)
    {
        state = newState;
    }

    /// <summary>
    /// Description:
    /// Determines which state is appropriate for the player currently
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    private void DetermineState()
    {
        if (playerHealth.currentHealth <= 0)
        {
            SetState(PlayerState.Dead);
        }
        else if (grounded)
        {
            if (playerRigidbody.velocity.magnitude > 0)
            {
                SetState(PlayerState.Walk);
            }
            else
            {
                SetState(PlayerState.Idle);
            }
            if (!jumping)
            {
                timesJumped = 0;
            }
        }
        else
        {
            if (jumping)
            {
                SetState(PlayerState.Jump);
            }
            else
            {
                SetState(PlayerState.Fall);
            }
        }
    }
    #endregion

    /// <summary>
    /// Description:
    /// Sets up the player's rigidbody
    /// Input: 
    /// none
    /// Return: 
    /// void (no return)
    /// </summary>
    private void SetupRigidbody()
    {
        if (playerRigidbody == null)
        {
            playerRigidbody = GetComponent<Rigidbody2D>();
        }
    }

    /// <summary>
    /// Description:
    /// Gets the reference to the input manager
    /// Throws an error if none exists
    /// Input:
    /// none
    /// Return:
    /// void (no return)
    /// </summary>
    private void SetUpInputManager()
    {
        inputManager = InputManager.instance;
        if (inputManager == null)
        {
            Debug.LogError("There is no InputManager set up in the scene for the PlayerController to read from");
        }
    }
    #endregion
}

I’ve followed several posts here, including asking to change action to pass through and biding. Nothing worked! Any help will be extremelly appreciated!

1 Like

The errors mention InputManager.GetPauseInput. InputManager.cs:136, line 136 of InputManager. Also mentioning:

Cannot read value of type 'Single' from composite 'UnityEngine.InputSystem.Composites.Vector2Composite' bound to action 'Player1/Move[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d]' (composite is a 'Int32' with value type 'Vector2')

Which means you’re trying to read Player1/Move’s Vector2 value as a float because you’ve incorrectly bound your pause menu to it and are reading a value there.

1 Like

Thank you so much!