Skip to content
Snippets Groups Projects
CharacterMovement.cs 10.9 KiB
Newer Older
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Interactions;

//This script handles moving the character on the X axis, both on the ground and in the air.
[RequireComponent( typeof(CharacterGround) )]
[RequireComponent( typeof(Rigidbody2D) )]
public class CharacterMovement : MonoBehaviour {
    private Rigidbody2D body;
    CharacterGround ground;


    [Header( "Character Settings Scriptable Object" )]
    public Character_Settings_SO characterSettingsSO = null;

    //[Header("Movement Stats")]
    //[SerializeField, Range(0f, 20f)][Tooltip("Maximum movement speed")] public float maxSpeed = 10f;
    //[SerializeField, Range(0f, 100f)][Tooltip("How fast to reach max speed")] public float maxAcceleration = 52f;
    //[SerializeField, Range(0f, 100f)][Tooltip("How fast to stop after letting go")] public float maxDeceleration = 52f;
    //[SerializeField, Range(0f, 100f)][Tooltip("How fast to stop when changing direction")] public float maxTurnSpeed = 80f;
    //[SerializeField, Range(0f, 100f)][Tooltip("How fast to reach max speed when in mid-air")] public float maxAirAcceleration;
    //[SerializeField, Range(0f, 100f)][Tooltip("How fast to stop in mid-air when no direction is used")] public float maxAirDeceleration;
    //[SerializeField, Range(0f, 100f)][Tooltip("How fast to stop when changing direction when in mid-air")] public float maxAirTurnSpeed = 80f;
    //[SerializeField][Tooltip("Friction to apply against movement on stick")] private float friction;

    //[Header("Options")]
    //[Tooltip("When false, the charcter will skip acceleration and deceleration and instantly move and stop")] public bool useAcceleration;

    [Header( "Current State" )]
    [XnTools.ReadOnly] public float directionX;
    private Vector2 desiredVelocity;
    [XnTools.ReadOnly] public Vector2 velocity;
    private float   maxSpeedChange;
    private float   acceleration;
    private float   deceleration;
    private float   turnSpeed;
    [XnTools.ReadOnly] public bool onGround;
    [XnTools.ReadOnly] public bool nonZeroHorizontalInput;

    private void Awake() {
        if ( characterSettingsSO == null ) {
            Debug.LogError(
                "You must assign a Character_Settings_SO Scriptable Object to the CharacterMovement script for it to function." );
            enabled = false;
        }
        //Find the character's Rigidbody and ground detection script
        body = GetComponent<Rigidbody2D>();
        ground = GetComponent<CharacterGround>();
    }

    // public void OnMovement(InputAction.CallbackContext context)
    // {
    //     //This is called when you input a direction on a valid input type, such as arrow keys or analogue stick
    //     //The value will read -1 when pressing left, 0 when idle, and 1 when pressing right.
    //     directionX = context.ReadValue<float>();
    // }

    private void Update() {
        // Check for Xnput button presses
        directionX = 0;
        if ( Xnput.GetButton( Xnput.eButton.left ) ) directionX -= 1;
        if ( Xnput.GetButton( Xnput.eButton.right ) ) directionX += 1;

        //Used to flip the character's sprite when she changes direction
        //Also tells us that we are currently pressing a direction button
        if ( directionX != 0 ) {
            transform.localScale = new Vector3( directionX > 0 ? 1 : -1, 1, 1 );
            nonZeroHorizontalInput = true;
        } else { nonZeroHorizontalInput = false; }

        //Calculate's the character's desired velocity - which is the direction you are facing, multiplied by the character's maximum speed
        //Friction is not used in this game
        // // Then WHY is was the friction code still on the next line?!? - JGB 2023-03-14
        desiredVelocity = new Vector2( directionX, 0f ) *
                          Mathf.Max( characterSettingsSO.maxSpeed,
                              0 ); // - characterSettingsSO.friction, 0f);
        //Fixed update runs in sync with Unity's physics engine

        //Get Kit's current ground status from her ground script
        onGround = ground.GetOnGround();

        //Get the Rigidbody's current velocity
        velocity = body.velocity;

        //Calculate movement, depending on whether "Instant Movement" has been checked
        if ( characterSettingsSO.useAcceleration ) { RunWithAcceleration(); } else {
            if ( onGround ) { RunWithoutAcceleration(); } else { RunWithAcceleration(); }

        //Update the Rigidbody with this new velocity
        body.velocity = velocity;
        //Set our acceleration, deceleration, and turn speed stats, based on whether we're on the ground on in the air

        acceleration = onGround ? characterSettingsSO.maxAcceleration : characterSettingsSO.maxAirAcceleration;
        deceleration = onGround ? characterSettingsSO.maxDeceleration : characterSettingsSO.maxAirDeceleration;
        turnSpeed = onGround ? characterSettingsSO.maxTurnSpeed : characterSettingsSO.maxAirTurnSpeed;

            //If the sign (i.e. positive or negative) of our input direction doesn't match our movement, it means we're turning around and so should use the turn speed stat.
            // if ( Mathf.Sign( directionX ) != Mathf.Sign( velocity.x ) ) { // This was a really slow way to do this. - JGB 2023-03-14
            if ( directionX * velocity.x < 0 ) { // This does the same thing without two function calls
                maxSpeedChange = turnSpeed * Time.deltaTime;
                //If they match, it means we're simply running along and so should use the acceleration stat
                maxSpeedChange = acceleration * Time.deltaTime;
            }
            //And if we're not pressing a direction at all, use the deceleration stat
            maxSpeedChange = deceleration * Time.deltaTime;
        }

        //Move our velocity towards the desired velocity, at the rate of the number calculated above
        velocity.x = Mathf.MoveTowards( velocity.x, desiredVelocity.x, maxSpeedChange );
        //If we're not using acceleration and deceleration, just send our desired velocity (direction * max speed) to the Rigidbody
        velocity.x = desiredVelocity.x;

#if UNITY_EDITOR

[CustomEditor( typeof(CharacterMovement) )]
public class CharacterMovement_Editor : Editor {
    private const float lineThickness = 2;

    private CharacterMovement cMove;
    private CapsuleCollider2D cC2D;
    private CharacterGround cGround;
    private void OnEnable() {
        cMove = (CharacterMovement) target;
        cC2D = cMove.GetComponent<CapsuleCollider2D>();
        cGround = cMove.GetComponent<CharacterGround>();
    private void OnSceneGUI() {
        if ( cMove                     == null ) return;
        if ( cMove.characterSettingsSO == null ) return;
        if (cC2D == null) return;
        if (cGround == null) return;

        Character_Settings_SO csso = cMove.characterSettingsSO;

        // Adjust the CapsuleCollider2D based on settings in csso
        Vector2 size = Vector2.zero;
        size.x = csso.colliderSettings.width;
        size.y = csso.colliderSettings.height;
        cC2D.size = size;

        Vector2 offset = Vector2.zero;
        offset.y = csso.colliderSettings.height * 0.5f;
        cC2D.offset = offset;

        // Adjust the CharacterGround settings based on csso
        cGround.raycastOffsetHeight = new Vector3(0, csso.colliderSettings.groundRaycastDepth * 0.5f, 0);
        cGround.raycastOffsetWidth = new Vector3(csso.colliderSettings.groundRaycastWidth * 0.5f, 0, 0);
        cGround.groundLength = csso.colliderSettings.groundRaycastDepth;
        cGround.groundLayers = csso.colliderSettings.groundLayers;

        // Show the ground raycasts
        Handles.matrix = Matrix4x4.Translate( cMove.transform.position );
        Handles.color = Color.green;
        // Draw bottom circle of collider (This is drawn because the collider outline doesn't show up well
        Handles.DrawWireDisc(
            new Vector3( 0, csso.colliderSettings.width * 0.5f, 0 ),
            Vector3.back,
            csso.colliderSettings.width * 0.5f,
            lineThickness );
        // Draw bottom circle of collider (This is drawn because the collider outline doesn't show up well
        Handles.DrawWireDisc(
            new Vector3( 0, csso.colliderSettings.height - csso.colliderSettings.width * 0.5f, 0 ),
            Vector3.back,
            csso.colliderSettings.width * 0.5f,
            lineThickness );
        // Ground raycasts are drawn by the CharacterGround script

        // if ( !csso.showJumpLine ) return;
        // if ( csso.jumpSettingsType == Character_Settings_SO.eJumpSettingsType.GMTK_GameMakersToolKit ) return;
        // if ( csso.jumpLinePoints   == null ) cMove.characterSettingsSO.CalculateJumpLine();
        //
        // GUIStyle labelStyle = new GUIStyle( EditorStyles.foldoutHeader );
        // labelStyle.imagePosition = ImagePosition.TextOnly; // NOTE: This didn't seem to do anything.
        // labelStyle.richText = true;
        //
        // Handles.matrix = Matrix4x4.Translate( cJump.transform.position );
        // Handles.color = Color.green;
        // Handles.DrawAAPolyLine( 4, csso.jumpLinePoints );
        // Vector3 tVec;
        // Vector3[] jSME = csso.jumpStartMidEndPoints;
        // if ( jSME != null && jSME.Length == 3 ) {
        //     Vector3 offset = Vector3.up * 0.2f;
        //     Handles.DrawDottedLine( jSME[0] + offset, jSME[2] + offset, dashSize );
        //     tVec = ( jSME[0] + jSME[2] ) / 2f + offset * 4 + Vector3.left * 0.4f;
        //     Handles.Label( tVec, $"<b>Dist: {csso.maxJumpDistHeight.x:0.##}</b>", labelStyle );
        //     tVec = jSME[1];
        //     tVec.y = 0;
        //     Handles.DrawDottedLine( tVec, jSME[1], dashSize );
        //     tVec = ( tVec + jSME[1] ) / 2f + Vector3.left * 0.4f;
        //     Handles.Label( tVec, $"<b>Height: {csso.maxJumpDistHeight.y:0.##}</b>", labelStyle );
        // }
        //
        // if ( csso.jumpSettingsVariableHeight.useVariableJumpHeight ) {
        //     Handles.color = Color.magenta;
        //     Handles.DrawAAPolyLine( 8, csso.minJumpLinePoints.ToArray() );
        //     if ( csso.minJumpStartMidEndPoints        != null &&
        //          csso.minJumpStartMidEndPoints.Length == 3 ) {
        //         tVec = csso.minJumpStartMidEndPoints[0] + Vector3.down * 0.25f;
        //         Handles.Label( tVec,
        //             $"<b>Min: Ht: {csso.minJumpDistHeight.y:0.##}   Dst: {csso.minJumpDistHeight.x:0.##}" +
        //             $"   tApex: {csso.minTimeApexFull.x:0.##}   tFull: {csso.minTimeApexFull.y:0.##}</b>",
        //             labelStyle );
        //     }
        // }

    }
}