- Home /
Will calling and instance of classA, from classA, repeatedly, cause a stack overflow?
Suppose I have a class in C#. This class will be within another class, like this:
using UnityEngine; using System.Collections;
public class mainClass : MonoBehaviour {
//the class in question
public class classA{
public float someNum;
//our constructor
public classA(float num){
someNum = num;
}
//the method we use
public void sendNumOnward(){
//modify the number a little
float num2 = someNum + 1.5f;
//send it to a new instance of this class
classA classB = new classA(num2);
//perform the same numeric action in classB and send it on again
classB.sendNumOnward();
}
}
public float starterNum = 0.5f;
void Start () {
//call the class initially
classA initial = new classA(starterNum);
}
}
Will this sort of thing cause a stack overflow? Will the initial instance of classA remain permanently in use, because we're calling an instance of itself from itself repeatedly, or will only the appropriate(called) instance remain each time, while previous iterations will be disposed of by Unity?
(This is a hypothetical question, my actual script has a condition whose fulfillment will stop subsequent calls of the class, and that condition's fulfillment is assured.)
The code I'm trying to modify to use class instances instead of method calling:
using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq;
public class NewNavigation : MonoBehaviour {
//Major issue - we're calcularing ALL the costs at once, of ALL paths. Gotta figure this one out...
List<Vector3> zeroList;
float zeroCost = 0.0f;
GameObject Static;
public LayerMask mask;
public List<GameObject> closedNodes;
public List<Vector3> finalPath;
public List<Vector3> trulyFinalPath;
public float minimalCost;
public Dictionary<float, List<Vector3>> pathDatabase = new Dictionary<float, List<Vector3>>();
public Dictionary<float, string> debugD;
public List<float> allCosts;
Vector3 position;
public Vector3 target;
public bool pathFinding = false;
public bool calculating = false;
public bool secondCalculating = false;
// Use this for initialization
void Start () {
Static = GameObject.FindGameObjectWithTag("STATIC");
zeroList = new List<Vector3>();
closedNodes = new List<GameObject>();
//pathFinding = false;
//pathDatabase = new Dictionary<float, List<Vector3>>();
//calculating = false;
allCosts = new List<float>();
finalPath = new List<Vector3>();
target = transform.position;
trulyFinalPath = new List<Vector3>();
debugD = new Dictionary<float, string>();
}
// Update is called once per frame
void Update () {
position = transform.position;
if(Input.GetMouseButtonDown(0)){
clearDada();
Ray targetRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit targetHit;
//so basically we're casting a ray from the camera onto the "ground", and checking if we touch any ground even
//Were casting the ray we defined, "exporting" the hit point onto targetHit, and we say - we want to hit the ground no matter how far
if(Physics.Raycast(targetRay, out targetHit, Mathf.Infinity)){
target = targetHit.point;
//the next code is for debugging purposes
Vector3 adjPos = adjusted(transform.position);
Vector3 adjTar = adjusted(target);
Vector3 dir = direction(adjPos, adjTar);
Debug.DrawRay(adjPos, dir*5, Color.red, 2.0f);
}
pathFinding = true;
}
if(pathFinding == true){
Debug.Log("Pathfiding");
getToPath(zeroList,zeroCost,transform.position,Static);
}
if(calculating){
Debug.Log("Calculating");
pathCalculating();
}
if(secondCalculating == true){
//debugging
string deb;
debugD.TryGetValue(minimalCost, out deb);
Debug.Log(deb);
if(pathDatabase.ContainsKey(minimalCost)){
Debug.Log("Contains Key");
foreach(KeyValuePair<float, List<Vector3>> pair in pathDatabase){
float key = pair.Key;
if(minimalCost == key){
finalPath = pair.Value;
secondCalculating = false;
}
}
}
//pathDatabase.Clear();
secondCalculating = false;
}
}
void clearDada(){
closedNodes.Clear();
pathDatabase.Clear();
finalPath.Clear();
allCosts.Clear();
}
#region distance method
float Distance(Vector3 locA, Vector3 locB){
float dist = Vector3.Distance(locA, locB);
return dist;
}
#endregion
#region casting direction
Vector3 direction(Vector3 posA, Vector3 posB){
//we want a position of the target, in relative to the position of the origin
//best way to achieve this is via POSITION B - POSITION A = DIRECTION
Vector3 direction = posB - posA;
Vector3 directionR = adjusted(direction);
return directionR;
}
#endregion
#region adjusted position
Vector3 adjusted(Vector3 position){
Vector3 Position = new Vector3(position.x, 0.5f, position.z);
return Position;
}
#endregion
void getToPath(List<Vector3> path, float cost, Vector3 position, GameObject parent){
List<GameObject> parentsList = new List<GameObject>();
parentsList.Add(parent);
float distance = Vector3.Distance(transform.position, target);
Vector3 pos = position;
Vector3 local = adjusted(pos);
//check if we can dray a raw to the target, if there's no obstacles between our position and the target position
if(Physics.Raycast(local, direction(local, target), Mathf.Max(distance, 0.01f)/*, mask*/)){
//we can't draw a ray - there's an obstacle in the way
//we get all Nodes into an array, and check which ones we can use
GameObject[] temp = GameObject.FindGameObjectsWithTag("Node"); /*new GameObject[300];*/
/*temp = GameObject.FindGameObjectsWithTag("Node");*/
List<GameObject> usable = new List<GameObject>();
//we check if our nodes are useful by iterating between them and casting rays to them, and if they can even be used
int counter = temp.Length;
for(int i =0; i < counter; i++){
GameObject Node = temp[i];
if(!closedNodes.Contains(Node)){
Vector3 nodePos = Node.transform.position;
float dist = Vector3.Distance(local, adjusted(nodePos));
if(Physics.Raycast(local, direction(local, nodePos),Mathf.Max(dist, 0.01f)/*, mask*/) && !closedNodes.Contains(Node)){
//we have encountered an obstacle and do nothing
Debug.DrawRay(local, direction(local, nodePos)*2, Color.yellow, 0.8f);
} else {
//there's no obstacle in our way, we add them to the available's list
if(!parentsList.Contains(Node)){
usable.Add(Node);
Debug.DrawRay(local, direction(local, nodePos)*2, Color.cyan, 1.5f);
}
}
}
}
//now we iterate between the nodes in the usable list and "activate" them
int usableCount = usable.Count;
for(int m = 0; m < usableCount; m++){
GameObject node = usable[m];
if(!closedNodes.Contains(node)){
closedNodes.Add(node);
Vector3 adjNod = adjusted(node.transform.position);
//float locDistance = Distance(local, adjNod);
//float localCost = cost + locDistance;
//debugging purposes
//PROBLEM AREA - we aren't casting rays correctly
Debug.DrawRay(local, direction(local, adjNod)*2, Color.blue, 2.0f);
//end debugging
nodeData(path, cost, node, local);
}
}
} else {
//Debug.Break();
//we CAN actually touch the bloody target at this point
//we found a path, we put into our dictionary the whole entire path list AND the corresponding key as the total "cost"
Vector3 adjTarget = adjusted(target);
float finalDist = Distance(local, adjTarget);
float finalCost = cost + finalDist;
List<Vector3> waypoints = new List<Vector3>();
waypoints = path;
waypoints.Add(target);
allCosts.Add(finalCost);
Debug.Log(finalCost.ToString());
//we check if there's already the same key in the dictionary
List<Vector3> checker = new List<Vector3>();
if(pathDatabase.TryGetValue(finalCost,out checker)){
//if there is, we do nothing.
} else {
pathDatabase.Add(finalCost, waypoints);
}
//debugging purposes
Debug.DrawRay(local, direction(local, adjusted(target))*2, Color.green, 2.0f);
//end debugging
calculating = true;
pathFinding = false;
}
}
void nodeData(List<Vector3> path, float cost, GameObject self, Vector3 parentPosition){
//here we extract relevant data from the node we just touched
Transform localTransform = self.transform;
Vector3 location = localTransform.transform.position;
Vector3 adjLoc = adjusted(location);
float Cost = cost;
float locDistance = Distance(adjLoc, parentPosition);
float localCost = Cost + locDistance;
List<Vector3> Path = path;
Path.Add(adjLoc);
getToPath(Path, localCost, location, self);
}
void pathCalculating(){
//we're gonna go through our costs list, and check the smallest cost.
//according to that cost we'll extract the proper waypoint's list from the dictionary
List<float> costs = allCosts;
float minCost;
minCost = costs.Min();
debugD.Add(minCost, "Return OK");
minimalCost = minCost;
secondCalculating = true;
calculating = false;
}
}
The actual methods in question are "getToPath" and "nodeDada". The purpose is to find each available node, and to instantiate a function for each one, and then to transfer that unique data onto the next nodes, etc. This script, however, does it all at once. It causes all of the data to be just clumped up into one big mess and I end up having just one path, which is basically all the paths calculated at once.
Answer by nesis · Feb 04, 2014 at 02:03 AM
You'll never exit the function sendNumOnward() since each call will be waiting on the next call to exit. A stack overflow will occur. Read up on recursion for a better understanding.
I see. Is there a way to then automatically activate the function locally for the specific instant of classA, without having to call it in the above manner and without causing this problem?
To basically create an independant recurring instant of classA, in which the previous instance will always be disposed?
You could use a coroutine or an Invoke() call to defer the actual work of creating the new instance, but that seems like overkill. Do you absolutely have to create new objects like that? What is it you're trying to do?
Im attempting to create a very simple and short (in terms of code) pathfinding system. What this particular class instantacing is trying to achieve is this in a nutshell: For an undeter$$anonymous$$ed, but finite array of different numbers: Iterate through the array, for each number instance classA. Each instance of classA should operate independently from the others, and instantiate a new classA based on the unique number it had started with. This process should be recurring until stopped, when at the end I will have a number of results, each after the same amout of recurring, but each different based on the numbers it had started with. All this should occure almost in parralel. If I can do this, the rest is already done. Basically I need to get a system that creates an N amount of independant recurring branches. This could probably(maybe?) be better achieved through separate scripts, where classA has it
s own update function, which will simply call an instance of classA again from within itself if a boolean is set, byt I need this in one script to better understand what`s going on.
Note that this can not be done with a loop, but this particular set ofoperations is needed for my design. I tried it already with only methods, but I seemed to end up with everything just happening all together and the end results were all the same, and much larger than they needed to be, even though the methods had their own variables.
You didn't really presented a concrete problem here. There are major constraints missing. Recursion always need a break condition. infinite recursion will always sooner or later exhaust your memory capacity.
Also you really should read up on recursion if you want to use it. Your statement is simply wrong because **every** recursion can be replaced by an iterative loop in combination with a stack.
At the moment each of your classA instance is creating one new instance which does the same over and over again. At the moment you don't store the references to all those classes, So even if you add a ter$$anonymous$$ate condition, if sendNumOnward of the initial instance is finished, all sub instances will be garbage collected.
Your code is incomplete and your problem as well. There's no way to "answer" this question without a clear problem.
That`s because this isn`t my code, I presented this code as an example to make it easier to understand exactly the kind of action that I need. Reading lots of code is a huge bummer, so I avoided posting the actual code, and presented this as a hypothetical question. $$anonymous$$y code, if you`re willing to read through this mess' is now in the original post edit.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Question about classes C# 1 Answer
Get GameObject transform inside a custom class 0 Answers
C# Conception - Hide inherited members and functions 1 Answer