Stack Overflow Exception on recursive functions
Hello, I am currently working on a mystery box system for skins in my videogame, but I keep getting this error when trying to generate a random skin which hasn't been repeated: StackOverflowException UnityEngine.Random.Range (System.Int32 min, System.Int32 max) (at C:/buildslave/unity/build/Runtime/Export/Random/Random.bindings.cs:48)
I apologize for some of the comments in Spanish
1.-My code has a random function which generates a random number between 0 and 100 when the "buy skin" button is pressed on the UI, then depending in which ranges of numbers the number lands the rarity of the skin is decided (e.g if the random number is 30, it lands on the rarity of a common skin, which is between 0 and 49).
2.-Then, once again a random number is generated but between 1 and 7, and with that number, the specific skin from the category is chosen. (For example, if "3" is generated, skin number 3 from the common category is chosen).
3.-I save in player prefs as an artificial array of sorts the skins and an artificial bool (int 1 or 0) if this skin has been unlocked, 1 is an unlocked skin and 0 is a locked skin. These numbers are stored in a local array at the beginning of the code which is called arraySkinsViejas[ ].
4.-After the random skin is generated the repeatChecker() function is called, which checks if the index i of arraySkinsViejas has been unlocked (is == 1) and if it has been unlocked before the randomSkin function is called again, and this loop continues until a new skin which hasn't been stored before in player prefs is generated.
5.-Once a skin that hasn't been unlocked is generated, guardarSkinGanada( ) stores it in player prefs for future use, and this skin index is later instantiated to show the skin the player obtained in the mystery box.
I would extremely appreciate help with this error since it has haunted me for days and I can't seem to find a way to resolve it, here is all the code.
using UnityEngine; using UnityEngine.UI; using System.Collections; using System;
public class mystery_box1 : MonoBehaviour { //currency management private int monedas_finales;
//Skins
private int rand_class;
private int rand_skin;
private int skinGanada;
public Sprite[] skinLibrary = new Sprite[30];
private int[] arraySkinsViejas = new int[30];
private bool common;
private bool rare;
private bool veryRare;
private bool legendary;
public GameObject objetoSkinGanada;
//texto cantidad de monedas
public Text texto_n;
public Text texto_b;
//objeto de la caja y la transicion
public GameObject caja;
public GameObject fade;
//sprites de caja abierta y caja cerrada
public Sprite open_box;
public Sprite closed_box;
private Image imageComponent;
private Image imageSkinGanada;
public GameObject blanco;
public GameObject congrats;
private bool congrat;
void Start() {
skinGanada = 0;
congrat = false;
common = false;
rare = false;
veryRare = false;
legendary = false;
rand_class = 0;
//aqui va a llegar la cantidad de monedas del juego
texto_n.text = "" + PlayerPrefs.GetInt("monedasfinales");
texto_b.text = "" + PlayerPrefs.GetInt("monedasfinales");
imageComponent = caja.GetComponent<Image>();
imageSkinGanada = objetoSkinGanada.GetComponent<Image>();
for (int i = 0; i < 30; i++)
{
arraySkinsViejas[i] = PlayerPrefs.GetInt("skin" + i.ToString());
}
}
void Update()
{
monedas_finales = PlayerPrefs.GetInt("monedasfinales");
if (Input.GetMouseButtonDown(0) && congrat == false)
{
blanco.GetComponent<Image>().color = new Color(255, 255, 255, 0);
congrats.GetComponent<Text>().color = new Color(0, 0, 0, 0);
congrats.GetComponent<Text>().color = new Color(0, 0, 0, 0);
imageSkinGanada.color = new Color(0,0,0,0);
cajaCerrada();
stopCongratulations();
}
}
/* _ _____ _ _
| | / ____| | (_)
| |__ _ _ _ _| (___ | | ___ _ __
| '_ \| | | | | | |\___ \| |/ / | '_ \
| |_) | |_| | |_| |____) | <| | | | |
|_.__/ \__,_|\__, |_____/|_|\_\_|_| |_|
__/ |
|___/ */
public void comprarSkin()
{
//revisar si hay monedas suficientes para realizar la compra e iniciar secuencia de mystery box
//cero para testear
//cero para testear
//cero para testear
if (monedas_finales >= 0){
//inicia compra, guardamos lista de skins de memoria para agregarle la nueva despues primero que nada
for (int i = 0; i < 30; i++){
arraySkinsViejas[i] = PlayerPrefs.GetInt("skin" + i.ToString());
}
//inicia logica de skins
randomSkin();
//termina logica de skins
//bool que no permite presionar para apagar transicion
Invoke("startCongratulations", 0);
//bool que si permite presionar para apagar transicion
Invoke("stopCongratulations", 3);
//animacion de transicion a blanco
fade.GetComponent<Animation>().Play();
//agitamos camara
InvokeRepeating("agitarCaja", 0, .005f);
//frenamos camara
Invoke("frenarcamara", 1);
//cambiamos a caja abierta
Invoke("cajaAbierta", 1);
//animacion de transicion a blanco, va a durar el segundo que dice el invoke anterior
fade.GetComponent<Animation>().Play();
//que aparezaca texto de congrats
Invoke("congratsAppear", 1.25f);
//restamos a la cuenta total de monedas el costo del pago (30)
//no se restan para testear
//no se restan para testear
//no se restan para testear
PlayerPrefs.SetInt("monedasfinales", PlayerPrefs.GetInt("monedasfinales") - 0);
texto_n.text = "" + PlayerPrefs.GetInt("monedasfinales");
texto_b.text = "" + PlayerPrefs.GetInt("monedasfinales");
}
}
/*
_ _ _____ _
| | (_) | __ \ | |
___| | ___ _ __ | | | | __ _| |_ __ _
/ __| |/ / | '_ \| | | |/ _` | __/ _` |
\__ \ <| | | | | |__| | (_| | || (_| |
|___/_|\_\_|_| |_|_____/ \__,_|\__\__,_|
*/
private void randomSkin() {
//generamos el numero random de la categoria de skin
rand_class = UnityEngine.Random.Range(0, 100);
//common
if (rand_class >= 0 && rand_class <= 39) { common = true; }
//rare
else if (rand_class >= 40 && rand_class <= 69) { rare = true; }
//very rare
else if (rand_class >= 70 && rand_class <= 89) { veryRare = true; }
//legendary
else if (rand_class >= 90 && rand_class <= 100) { legendary = true; }
//skins especificas
//skins especificas
//skins especificas
//common
if (common == true)
{
int randomint;
randomint = UnityEngine.Random.Range(0, 7);
if (randomint == 0) { skinGanada = 1; }
if (randomint == 1) { skinGanada = 2; }
if (randomint == 2) { skinGanada = 3; }
if (randomint == 3) { skinGanada = 4; }
if (randomint == 4) { skinGanada = 5; }
if (randomint == 5) { skinGanada = 6; }
if (randomint == 6) { skinGanada = 7; }
if (randomint == 7) { skinGanada = 8; }
}
//rare
if (rare == true)
{
int randomint;
randomint = UnityEngine.Random.Range(0, 7);
if (randomint == 0) { skinGanada = 9; }
if (randomint == 1) { skinGanada = 10; }
if (randomint == 2) { skinGanada = 11; }
if (randomint == 3) { skinGanada = 12; }
if (randomint == 4) { skinGanada = 13; }
if (randomint == 5) { skinGanada = 14; }
if (randomint == 6) { skinGanada = 15; }
if (randomint == 7) { skinGanada = 16; }
}
//very rare
if (veryRare == true)
{
int randomint;
randomint = UnityEngine.Random.Range(0, 7);
if (randomint == 0) { skinGanada = 17; }
if (randomint == 1) { skinGanada = 18; }
if (randomint == 2) { skinGanada = 19; }
if (randomint == 3) { skinGanada = 20; }
if (randomint == 4) { skinGanada = 21; }
if (randomint == 5) { skinGanada = 22; }
if (randomint == 6) { skinGanada = 23; }
if (randomint == 7) { skinGanada = 24; }
}
//legendary
if (legendary == true)
{
int randomint;
randomint = UnityEngine.Random.Range(0, 5);
if (randomint == 0) { skinGanada = 25; }
if (randomint == 1) { skinGanada = 26; }
if (randomint == 2) { skinGanada = 27; }
if (randomint == 3) { skinGanada = 28; }
if (randomint == 4) { skinGanada = 29; }
if (randomint == 5) { skinGanada = 30; }
}
repeatChecker();
}
private void repeatChecker() {
if (arraySkinsViejas[skinGanada] == 1) {
randomSkin();
}
else if (arraySkinsViejas[skinGanada] == 0) {
Debug.Log(skinGanada);
guardarSkinGanada();
}
}
private void guardarSkinGanada()
{
for (int i = 0; i < 30; i++){
PlayerPrefs.SetInt("skin" + i.ToString(), arraySkinsViejas[i]);
}
PlayerPrefs.Save();
}
/* _ _
(_) | |
__ ___ ___ _ _ __ _| |___
\ \ / / / __| | | |/ _` | / __|
\ V /| \__ \ |_| | (_| | \__ \
\_/ |_|___/\__,_|\__,_|_|___/ */
private void startCongratulations()
{
congrat = true;
}
private void stopCongratulations()
{
congrat = false;
}
//se agita la caja hasta que frenarcamara lo detenga
void agitarCaja()
{
float posX;
float posY;
posX = UnityEngine.Random.value;
posY = UnityEngine.Random.value;
caja.transform.position = new Vector2(posX * .1f, posY * .1f);
}
//detiene el que se agite la caja
void frenarcamara()
{
CancelInvoke("agitarCaja");
caja.transform.position = new Vector2(0, 0);
}
void cajaAbierta()
{
imageComponent.sprite = open_box;
caja.transform.localScale = new Vector2(3, 14);
caja.transform.position = new Vector2(-.4f, caja.transform.position.y);
blanco.GetComponent<Image>().color = new Color(255, 255, 255, .75f);
}
void congratsAppear()
{
congrats.GetComponent<Text>().color = new Color(0, 0, 0, 1);
imageSkinGanada.sprite = skinLibrary[skinGanada];
imageSkinGanada.color = new Color(255, 255, 255, 1);
}
void cajaCerrada()
{
imageComponent.sprite = closed_box;
caja.transform.position = new Vector2(0, 0);
caja.transform.localScale = new Vector2(1, 5);
}
}
Answer by andrew-lukasik · Aug 02, 2020 at 02:33 PM
randomSkin
calls repeatChecker
, which then in turn can call randomSkin
again. This can easily cause infinite loop resulting in StackOverflowException
being thrown.
To resolve this - always write your code in a way that makes infinite loops impossible
don't ever allow methods to call themselves
when some kind of loop is needed - use
for
(orforeach
) and shy away fromwhile
PS: naming your methods with upper-case letters wont hurt either