Search Unity

Bug Code caused a StackOverflowException

Discussion in 'Scripting' started by KaiserApple, Nov 10, 2021.

  1. KaiserApple

    KaiserApple

    Joined:
    Sep 12, 2021
    Posts:
    31
    Hello!
    I am in some need of help, as I believe that part of my code caused a StackOverflowException. Since I am still a newbie I don't know what I means, but I believe that this is the script where it's located:
    (By the way, I believe that the faulty code is in the IsCrouching bool)
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerMovement : MonoBehaviour
    6. {
    7.     public float speedChangePerSecond = 0.1f;
    8.     public Animator animator;
    9.     public float PlayerSpeed = 3f;
    10.     public Transform player;
    11.     public PolygonCollider2D bc2ds;
    12.     public PolygonCollider2D bc2dc;
    13.     [SerializeField] LayerMask platformsLayerMask;
    14.  
    15.     private string currentState;
    16.  
    17.     const string RUN_SLOW = "Player_Walk_Slow";
    18.     const string RUN_SLOWMEDIUM = "Player_Walk_SlowMedium";
    19.     const string RUN_MEDIUM = "Player_Walk_Medium";
    20.     const string RUN_MEDIUMFAST = "Player_Walk_MediumFast";
    21.     const string RUN_FAST = "Player_Walk_Fast";
    22.     const string CROUCH_SLOW = "Player_Crouch_Slow";
    23.     const string CROUCH_SLOWMEDIUM = "Player_Crouch_SlowMedium";
    24.     const string CROUCH_MEDIUM = "Player_Crouch_Medium";
    25.     const string CROUCH_MEDIUMFAST = "Player_Crouch_MediumFast";
    26.     const string CROUCH_FAST = "Player_Crouch_Fast";
    27.     const string JUMP = "Player_Jump";
    28.  
    29.     void ChangeAnimationState(string newState)
    30.     {
    31.         animator.Play(newState);
    32.  
    33.         if (currentState == newState) return;
    34.  
    35.         currentState = newState;
    36.     }
    37.     private void Awake()
    38.     {
    39.         PolygonCollider2D[] hboxes = GetComponents<PolygonCollider2D>();
    40.        
    41.         rb = transform.GetComponent<Rigidbody2D>();
    42.         bc2ds = hboxes[0];
    43.         bc2dc = hboxes[1];
    44.     }
    45.    
    46.     public Rigidbody2D rb;
    47.    
    48.     void Update()
    49.     {
    50.        
    51.         if(IsGrounded() && Input.GetKeyDown(KeyCode.Space))
    52.         {
    53.             float jumpVelocity = 10f;
    54.             rb.velocity = Vector2.up * jumpVelocity;
    55.         }
    56.         Movement();
    57.     }
    58.  
    59.     public bool IsCrouching()
    60.     {
    61.         if(IsGrounded())
    62.         {
    63.             if (Input.GetKey(KeyCode.DownArrow))
    64.             {
    65.                 bc2ds.enabled = false;
    66.                 bc2dc.enabled = true;
    67.                 return true;
    68.             }
    69.             else if (Input.GetKeyUp(KeyCode.DownArrow))
    70.             {
    71.                 bc2ds.enabled = true;
    72.                 bc2dc.enabled = false;
    73.                 return false;
    74.             }
    75.             else
    76.             {
    77.                 return false;
    78.             }
    79.         }
    80.         else
    81.         {
    82.             ChangeAnimationState(JUMP);
    83.             bc2ds.enabled = true;
    84.             bc2dc.enabled = false;
    85.             return false;
    86.         }
    87.     }
    88.     public bool IsGrounded()
    89.     {
    90.         RaycastHit2D rch2ds = Physics2D.BoxCast(bc2ds.bounds.center, bc2ds.bounds.size, 0f, Vector2.down, .01f, platformsLayerMask);
    91.         RaycastHit2D rch2dc = Physics2D.BoxCast(bc2dc.bounds.center, bc2dc.bounds.size, 0f, Vector2.down, .01f, platformsLayerMask);
    92.         Debug.Log(rch2ds.collider);
    93.  
    94.         if (rch2ds.collider == null && IsCrouching() == false)
    95.         {
    96.             ChangeAnimationState(JUMP);
    97.         }
    98.        
    99.        
    100.        
    101.         if (IsCrouching() == true)
    102.         {
    103.             return rch2dc.collider != null;
    104.         }
    105.         else
    106.         {
    107.             return rch2ds.collider != null;
    108.         }
    109.     }
    110.  
    111.     private void Movement()
    112.     {
    113.         rb.velocity = new Vector2(PlayerSpeed, rb.velocity.y);
    114.         if (PlayerSpeed < 13)
    115.         {
    116.             PlayerSpeed += speedChangePerSecond * Time.deltaTime;
    117.         }
    118.        
    119.  
    120.         if (IsGrounded())
    121.         {
    122.             if (IsCrouching() == false)
    123.             {
    124.                 //Animations for walking/running
    125.  
    126.                 switch (PlayerSpeed)
    127.                 {
    128.                     case < 5f:
    129.                         ChangeAnimationState(RUN_SLOW);
    130.                         break;
    131.                     case < 7f:
    132.                         ChangeAnimationState(RUN_SLOWMEDIUM);
    133.                         break;
    134.                     case < 9f:
    135.                         ChangeAnimationState(RUN_MEDIUM);
    136.                         break;
    137.                     case < 11f:
    138.                         ChangeAnimationState(RUN_MEDIUMFAST);
    139.                         break;
    140.                     case <= 13f:
    141.                         ChangeAnimationState(RUN_FAST);
    142.                         break;
    143.                 }
    144.             }
    145.             else
    146.             {
    147.                 //Animations for crouching
    148.  
    149.                 switch (PlayerSpeed)
    150.                 {
    151.                     case < 5f:
    152.                         ChangeAnimationState(CROUCH_SLOW);
    153.                         break;
    154.                     case < 7f:
    155.                         ChangeAnimationState(CROUCH_SLOWMEDIUM);
    156.                         break;
    157.                     case < 9f:
    158.                         ChangeAnimationState(CROUCH_MEDIUM);
    159.                         break;
    160.                     case < 11f:
    161.                         ChangeAnimationState(CROUCH_MEDIUMFAST);
    162.                         break;
    163.                     case <= 13f:
    164.                         ChangeAnimationState(CROUCH_FAST);
    165.                         break;
    166.                 }
    167.             }
    168.         }
    169.     }
    170. }
    171.  
    I don't completely know where the problem is, but I need some help fixing it nonetheless. Thanks.
     
  2. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,648
    IsGrounded() calls IsCrouching(), which calls IsGrounded(). This results in an infinite loop as they keep calling eachother before exiting. I suggest not actually "doing" anything in your getter functions like those, as really they should only be directly returning the condition, not doing extra work in the background.
     
  3. KaiserApple

    KaiserApple

    Joined:
    Sep 12, 2021
    Posts:
    31
    Thank you. These functions are kind of important for animation and collider stuff, so I do not think that removing them entirely is the best idea. Is there an alternative to using the IsGrounded and IsCrouching bools?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    I do like you breaking those things up, but I think you want to lift the logic up and out of there, as @RadRedPanda suggests.

    IsGrounded and IsCrouching really should only be booleans, nothing more.

    Any cross-interaction with them should kinda live above and outside of them.

    Check out this controller:

    https://forum.unity.com/threads/a-basic-first-person-character-controller-for-prototyping.1169491/

    See how it processes the movement starting around line 240-ish or so.

    IsGrounded is just a bool, but it uses the vScale variable to indicate your current crouch state.
     
    KaiserApple likes this.
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    Whether a Collider or all Colliders are touching the ground is already available without having to perform spatial queries like BoxCast etc.

    Take a look at this demonstration video which uses this and my 2D Physics examples repository linked in the description to find out how to use it. It's not only faster but means you don't need any special queries or set-up at all.


    In the end, it comes down to this single link to perform the grounded check here.

    Even if you don't use this, you can avoid those BoxCast calls and just cast the collider itself rather than use the bounding box of it (which is only useful for a non-rotated box anyway) by just calling Collider2D.Cast on the Collider. It's worth spending a little time just browsing the API; it's amazing what you'll discover.
     
    KaiserApple and Kurt-Dekker like this.