I'm having some trouble understanding why my variable has a null value.
Here's my class constructor:
private GraphNode current;
private GraphNode goal;
private GraphNode start;
private List<GraphNode> path;
private List<GraphNode> origin;
public Graph()
{
current = new GraphNode(0, 0);
goal = new GraphNode(0, 0);
start = new GraphNode(0, 0);
path = new List<GraphNode>();
origin = new List<GraphNode>();
}
Defenition of method SetPathing:
public void SetPathing(int mouseX, int mouseY)
{
if (Contains((int)mouseX / 32, (int)mouseY / 32))
{
goal = GetNode((int)mouseX / 32, (int)mouseY / 32);
current = goal;
path.Add(current);
while ((current.X != start.X) && (current.X != start.X))
{
current = origin.Where(keyValuePair => (keyValuePair.Key.X == current.X) && (keyValuePair.Key.X == current.Y)).Select(keyValuePair => keyValuePair.Value).LastOrDefault();
path.Add(current);
}
}
}
When I break on the start of the while-loop in SetPathing I see the following info in the locals screen:
current null PathFinding.GraphNode
goal {PathFinding.GraphNode} PathFinding.GraphNode
X 0 int
x 0 int
Y 5 int
y 5 int
How is that possible after having clearly assigned the reference value of goal to current?
I'm probably missing something stupid here, but I haven't found it after looking for two hours. No asynchronous going on here.
EDIT: Didn't want to be overtly verbose with my initial question, here's some extra details, my apologies. Here are the details of Contains, GetNode and GetNodeIndex. Nothing fancy, I'm an amateur.
public int GetNodeIndex(int x, int y)
{
// Find the node index in the list based on x and y
// Return -1 if not found
return nodes.FindIndex(n => (n.X == x) && (n.Y == y));
}
public GraphNode GetNode(int x, int y)
{
// Find the node in the list based on x and y
// Return null if not in list
int nodeIndex = GetNodeIndex(x, y);
if (nodeIndex != -1)
{
return nodes[nodeIndex];
}
return null;
}
public bool Contains(int x, int y)
{
// Check if the returned value is not -1
if (GetNodeIndex(x, y) != -1)
{
return true;
}
}
The usecase is litterally just along the lines of the following:
using System;
namespace Graphs
{
class Program
{
static void Main()
{
Graph graph = new Graph(20, 20);
graph.SetPathing(Mouse.GetState().X, Mouse.GetState().Y);
}
}
}
Let's dissect your code here.
goal = GetNode((int)mouseX / 32, (int)mouseY / 32);
Here, you've set the variable goal to point to a GraphNode object. So, for the sake of simplicity, we'll call it A. At this point your values look like this:
goal -> A
current -> null
Next, you do this:
current = goal;
And now, your values look like this:
goal -> A
current -> A
Later during your while loop, you call this:
current = origin.Where(keyValuePair => (keyValuePair.Key.X == current.X) && (keyValuePair.Key.X == current.Y)).Select(keyValuePair => keyValuePair.Value).LastOrDefault();
For the sake of simplicity, we'll call the result of this where clause B. So, now your values look like this:
goal -> A
current -> B
All that you have done here is change current to point to a new value.
You showed the code for the default Graph constructor,
public Graph()
not the one being used.
Graph graph = new Graph(20, 20);
Is there any difference between them?
Also, are you sure your code is in sync with the assembly? i.e. from your description it seems your break point is on this line
while ((current.X != start.X) && (current.X != start.X))
Are you sure when you were debugging, it was indeed executing only upto here when the debugger stopped, or it could have already executed the filtering code two lines down?
I'm not sure how this is possible, but the answer appears to be that this is in fact not the first iteration of the While-loop.
I had my break point set to the loop, and the results I posted above were from the first hit of that break point. However, after adding a variable that acted as an iteration counter it turned out this was in fact NOT the first iteration, but the third one. Following this discovery I went on to improve the code in the while loop to prevent current being set to null.
I'm not sure how this could happen. I'm using Visual Studio 2013 Express for Desktop. Never encountered anything like this before.
Related
This question already has an answer here:
c# calling method with array parameters [closed]
(1 answer)
Closed 1 year ago.
My code looks like this:
void Update()
{
if (Input.GetKeyDown(KeyCode.G) && questCount == 6)
{
Score(sumFinal);
}
}
public int Score(int sumFinal)
{
sumFinal = dCount - rCount;
return sumFinal;
}
Visual Studio tells me sumFinal doesn't exist in the current context.
How can I return sumFinal within Update() successfully? Am I understanding parameters correctly?
Additional Question:
Below further down the Score() function, I set text to certain values:
public int Score(int sumFinal)
{
sumFinal = dCount - rCount;
return sumFinal;
if (sumFinal == 5 && questCount >= 5)
{
labelText.text = "results";
}
}
When calling this function in Update(), is there a way I can run both the if statement and the sumFinal subtraction equation separately? In short, how do I segment the function, and call only specific parts of it? Should the return type be set to void?
Visual Studio tells me sumFinal doesn't exist in the current context. How can I return sumFinal within Update() successfully? Am I understanding parameters correctly?
The method Score looks great! However, when you call it Score(sumFinal); you need to make sure that you're passing a value to the method. In your case you may have forgotten to have a field with the value at the top of the class.
is there a way I can run both the if statement and the sumFinal subtraction equation separately?
Sure! What you could do, since you don't actually give Score any value when you call it, you can remove that parameter and just return the value that it calculates.
The we could separate updating the text to another method called UpdateTextBox() that takes the result of Score() to determine if it should change the text or not
public class YourUnityClass : MOnoBehaviour
{
int questCount = 0;
int sumFinal = 0; // make sure that if you need to access this variable between methods that this field exists
int dCount = 0;
int rCount = 0;
public TextMesh labelText;
void Update()
{
if (Input.GetKeyDown(KeyCode.G) && questCount == 6)
{
// calculate and save the score
sumFinal = Score();
// check the score, and update the text if necessary
UpdateTextBox(sumFinal);
}
}
public int Score()
{
// calculate the score
return dCount - rCount;
}
public void UpdateTextBox(int sumFinal)
{
// check to see if the score is high enough to change
// the text
if (sumFinal == 5 && questCount >= 5)
{
labelText.text = "results";
}
}
}
I want to add text to the class created in my code.
My problem is that it does not recognize the text and is not added.
In the created class, there are 3 sections, which are filled in 3 parts in order, but the main problem is that the text is not added in class c1.set1
This code is executed in Unity.
Also coded with Visual 2017.
I hope I have made the problem clear.
using System;
[Serializable]
public class c1
{
public string set1;
public string set2;
public string set3;
}
public class stringToArray : MonoBehaviour
{
public string[] S1;
public c1[] c1_item;
public int x;
// Start is called before the first frame update
void Start()
{
S1 = new string[20];// It works properly
while (x<10)
{
S1[x]= string.Format("ok "+x,x);
x++;
}
c1_item = new c1[20];
while (x < 10)
{
//S1[x] = string.Format("ok " + x, x); // It works properly
c1_item[x].set1 = string.Format("ok "+x , x); // It does not work properly
x++;
}
}
}
Does anyone know what to do?
Right, so your problem is that you haven't actually created the c1 objects to store your values, you've only created the c1_items array to hold the referencese to the objects.
void Start() {
// 'string' is (treated as) a value type, does not need to be initialised
S1 = new string[20];// It works properly
while (x < 10) {
S1[x] = string.Format("ok " + x, x);
x++;
}
x = 0; // reset x
// !! CAUSE: 'c1' is a reference type (class), it only holds references and not actual objects
c1_item = new c1[20];
while (x < 10) {
// !! FIX: MUST add the object first
if (c1_item[x] == null) c1_item[x] = new c1();
c1_item[x].set1 = string.Format("ok " + x, x); // It does not work properly
x++;
}
}
What can be tricky is that you've set c1_item as public, which means that Unity will instantiate and manage all the required objects within the array. The issue here is that you wipe that out and generate your own array (new c1[]), which means you also have to be the one to add the objects.
(Of note, if your c1 was a struct instead of a class this wouldn't have come up)
first of all using UnityEngine; might be helpful so don't always delete it
you need also using System.Collections; and using System.Collections.Generic; for the usage of lists and arrays.
I recommend that you initiate S1 before the start I mean in the awake method.
public int x = 0; // doesn't hurt to give it a value
you also need the x to be positive yes ? because it will be the index in a list of string so use range and test the x before working with it ,example :
[Range(0,20)]
public int x = 0;
Or
void Start()
{
if(x>=0){
//do code
}
}
Now why your code line doesn't work
c1_item[x].set1 = string.Format("ok "+x , x); // It does not work properly
I recommend using this
if(c1_item.Length >= x){
c1 newC1 = c1_item[x];
newC1.set1 = string.Format("ok "+x , x);}
I have method with one argument which is instance of my created class - Chessfield which contain three tables - one of integers and two of bools.
In the method I have also List of Chessfield which is the return object of my method.
I am modifying chessfield (so the object from reference) and add it on list List.Add(Chessfield) several times (one time after each change).
In the end return object (so list contain several object of Chessfield) all instances are the same unrespecting my changes !
I have read similar topic and try to put 'ref' before argument and the place where I proceed this method.
Without success also I tried to create instance of Chessfield inside the method and assign to it Chessfield object from reference, then making changes on internal created object.
How I can solve it ? In the end I need to receive list of objects which each is a little bit modified from original one (from reference).
Have a nice day !!
p.s. If the code will be helpful then I cut and paste general idea code.
EDIT:
Joel undrestand me Good ! The difference is that inside object are tables and it makes the problem more complex, because I do few changes in these tables in compare to this original object. To be more clear I paste my code:
public class Chessfield
{
public int[] pieces = new int[64];
public bool[] blacks = new bool[64];
public bool[] whites = new bool[64];
public Chessfield(int[] pieces, bool[] blacks, bool[] whites)
{
this.pieces = pieces;
this.blacks = blacks;
this.whites = whites;
}
public Chessfield()
{
}
}
And method look like this:
static public List<Chessfield> MakeAllMovesForWhites(Chessfield chessfieldModel)
{
List<Chessfield> listOfPossibleMoves = new List<Chessfield>(); // list containing chessfields with changed position of figures
int indexOfCurrentPosition = 0; //start with field 0 (most top left)
foreach (bool singleEnemyChecker in chessfieldModel.whites) //iterate all fields, table of whites contain information if white field stand on the field (true, otherwise false),
{
if (singleEnemyChecker == true) //so algorithm will proceed only fields with white figure
{
int kindOfPiece = chessfieldModel.pieces[indexOfCurrentPosition]; // (table pieces contain information which kind of figure stand on particular field 0 -> empty, 1 -> soldier, 2-> tower, 3 -> horse etc...
switch (kindOfPiece)// (based on figure at field it is going to predict all possible moves
{
case 2: // tower case
if (indexOfCurrentPosition % 8 != 0) // check if the field is not most left, otherwise leave
{
int localIndexIterator = indexOfCurrentPosition; //localIndex iterate all possible moves in left direction
while (localIndexIterator % 8 != 0) // checking if tower is standing on the most left field
{
localIndexIterator = localIndexIterator - 1; //iterate all possible moves of tower for left direction
if (chessfieldModel.pieces[localIndexIterator] == 0) //if there are no figures on checking field proceed:
{
chessfieldModel.pieces[indexOfCurrentPosition] = 0; // erase tower from original position
chessfieldModel.whites[indexOfCurrentPosition] = false; // and mark that white tower is not standing there anymore
chessfieldModel.pieces[localIndexIterator] = 2; // put tower on new place
chessfieldModel.whites[localIndexIterator] = true; // and mark that on new place there is white figure
listOfPossibleMoves.Add(chessfieldModel); // here I add changed object of chessfield to list
chessfieldModel.pieces[indexOfCurrentPosition] = 2; // here I come back to original chessfield
chessfieldModel.whites[indexOfCurrentPosition] = true;
chessfieldModel.pieces[localIndexIterator] = 0;
chessfieldModel.whites[localIndexIterator] = false;
}
else //if there is figure at checking field
break; //leave this case
}
}
if (indexOfCurrentPosition % 8 != 7) // right direction case
{
// here is similar code to the sample above
}
if (indexOfCurrentPosition / 8 != 0) //top direction case
{
// here is similar code to the sample above
}
if (indexOfCurrentPosition / 8 != 7) //bottom direction case
{
// here is similar code to the sample above
}
break;
// here are another figures horse and so on...
}
}
indexOfCurrentPosition++; // go to next field...
}
return listOfPossibleMoves; //return list of changed chessfields
}
and here I call method
Logic.MakeAllMovesForWhites(currentChessfield);
I understand what is the problem. And Joel - you are explaining very good ! (y) Thank you.
My first attemp to solve it was (before I ask here):
Chessfield abc = new Chessfield();
abc = chessfieldModel;
abc.pieces[indexOfCurrentPosition] = 0;
abc.whites[indexOfCurrentPosition] = true;
abc.pieces[localIndexIterator] = 2;
abc.whites[localIndexIterator] = false;
listOfPossibleMoves.Add(abc);
Fail. I tried this in every case (create for every figure and every direction). By the way there are 33 different cases how figure can move in chess, so I had this piece of code above in this 33 places (but sometimes I put different things to tables..). But figure like tower can move left for 1,2,3,4,5,6,7 fields if there are not pieces on the left.. and this is problem that I must to create always new instance and I do not know how, because I must to create unique instance, change it a bit, and add to list.. always unique, but in different cases.
Also I have tried your solution Joel, but the problem is that I need to do some changes to original chessfield (4 rows always, but different changes for different figures).
But I tried to create a new instance, add it to list, and then change it while is already on list. Doesn't work and logic is not proper even then.
listOfPossibleMoves.Add(new Chessfield() { pieces = chessfieldModel.pieces, blacks = chessfieldModel.blacks, whites = chessfieldModel.whites });
listOfPossibleMoves[listOfPossibleMoves.Count - 1].pieces[indexOfCurrentPosition] = 0;
listOfPossibleMoves[listOfPossibleMoves.Count - 1].whites[indexOfCurrentPosition] = false;
listOfPossibleMoves[listOfPossibleMoves.Count - 1].pieces[localIndexIterator] = 2;
listOfPossibleMoves[listOfPossibleMoves.Count - 1].whites[localIndexIterator] = true;
EDIT: So maybe coming back to my first method, but how I can create unique name for objects creating in the same place ? May you recommend some technique or what I can do in this situation ?
Thanks Joel and all :)
Have a nice day (or night) everybody !
It would really help to see code in the question, but it sounds like you're doing something like this:
public class ChessField
{
public bool b1;
public bool b2;
public int i1;
}
public List<ChessField> Method(ChessField c)
{
var result = new List<ChessField>();
for (int i = 0;i<3;i++)
{
c.i1 = i;
result.Add(c);
}
return result;
}
Here's the problem: you are adding the same object to the list. result[0] refers to the same object instance as result[1] refers to the same object instance as result[2] refers to the same object instance as c. Changing a property in c changes it everywhere else in this code, because they all are variables for the same object in memory. If you need the objects to be different, you must do something to create new object instances, like this:
public List<ChessField> Method(ChessField c)
{
var result = new List<ChessField>();
for (int i = 0;i<3;i++)
{
result.Add(new ChessField() {b1 = c.b1, b2 = c.b2, i1 = i});
}
return result;
}
or my preferred style would do this:
public IEnumerable<ChessField> Method(ChessField c)
{
return Enumerable.Range(0, 3)
.Select(i => new ChessField() {b1 = c.b1, b2 = c.b2, i1 = i});
}
I call c++/cli method from c# in this method:
bool SetProperty(Element element, Node referencePoint, List<Materializer> materializers, List<ulong> properties)
{
// Loop over STLs
for (int i = 0; i < materializers.Count; i++)
{
Materializer materializer = materializers[i];
PentalTreeNode pentalTreeRoot = pentalTreeDatasets[i].top;
if (materializer.IsPointInside(referencePoint.X, referencePoint.Y, referencePoint.Z, pentalTreeRoot))
{
element.PropertyId = properties[i];
return true;
};
}
return false;
}
C++/cli method is this:
bool IsPointInside(double x, double y, double z, PentalTreeNode ^root)
{
int intersectionCount = 0;
Math3d::M3d rayPoints[2], intersectionPoint;
rayPoints[0].set(x,y,z);
rayPoints[1].set(x,y,1.0e6);
if(_box->IsContainingPoint(x,y,z))
{
intersectionCount=CountIntersects(x,y,z,root);
return (intersectionCount%2!=0);
}
}
What is wrong, because c++/cli method doesn't return always the same result?
How to pin or marshal?
Method in c++/cli(Maybe this not ok?):
int CountIntersects(double x, double y, double z, PentalTreeNode ^root)
{
Math3d::M3d rayPoints[2], intersectionPoint;
rayPoints[0].set(x,y,z);
rayPoints[1].set(x,y,1.0e6);
if(!root)
return 0;
else
{
int special = CountIntersects(x,y,z,root->special);
if (x <= root->xMax && x >= root->xMin && y <= root->yMax && y >= root->yMin)
{
if( _stlMesh->IsRayIntersectsPoly(root->index, rayPoints, intersectionPoint))
{
return (1 + special);
}
else
return special;
}
else
{
if (y>root->yMax)
{
return (CountIntersects(x,y,z,root->top)+special);
}
else if(y<root->yMin)
{
return (CountIntersects(x,y,z,root->bottom)+special);
}
else if(x<root->xMin)
{
return (CountIntersects(x,y,z,root->left)+special);
}
else if(x>root->xMax)
{
return (CountIntersects(x,y,z,root->right)+special);
}
else
return special;
}
}
}
if( _stlMesh->IsRayIntersectsPoly(root->index, rayPoints, intersectionPoint))
There's one possible flaw in this particular statement, you've never initialized intersectionPoint. C++ lets you get away with this, it doesn't have anything similar to C#'s definite assignment rules. It isn't 100% clear whether that's the real problem, the variable might be passed by reference.
In the Debug build, such an uninitialized variable will have a predictable value. Something you can easily see in the debugger when you switch it to hexadecimal display mode. Fields in this struct or class will contain the value 0xcccccccc, a value that's apt to generate nonsensical results or crash your code with an access violation. In the Release build, the /RTC option isn't turned on and you'll get entirely random values in the variable.
Which corresponds very well with the description of your problem, so high odds that this is indeed the problem. Be sure to use the debugger to find problems like this, you can easily see the value of local variables with the Autos debugger window as you single-step through the code.
You are not calling a C++ method! You are calling a C++/CLI method. And therefore it is normal .NET code and it is always passed correctly. There is no need to pin or marshal anything in C# im this case!
If it returns not the expected values, you should try to find the problem in you C++/CLI project.
I'm about ready to bang my head against the wall
I have a class called Map which has a dictionary called tiles.
class Map
{
public Dictionary<Location, Tile> tiles = new Dictionary<Location, Tile>();
public Size mapSize;
public Map(Size size)
{
this.mapSize = size;
}
//etc...
I fill this dictionary temporarily to test some things..
public void FillTemp(Dictionary<int, Item> itemInfo)
{
Random r = new Random();
for(int i =0; i < mapSize.Width; i++)
{
for(int j=0; j<mapSize.Height; j++)
{
Location temp = new Location(i, j, 0);
int rint = r.Next(0, (itemInfo.Count - 1));
Tile t = new Tile(new Item(rint, rint));
tiles[temp] = t;
}
}
}
and in my main program code
Map m = new Map(10, 10);
m.FillTemp(iInfo);
Tile t = m.GetTile(new Location(2, 2, 0)); //The problem line
now, if I add a breakpoint in my code, I can clearly see that my instance (m) of the map class is filled with pairs via the function above, but when I try to access a value with the GetTile function:
public Tile GetTile(Location location)
{
if(this.tiles.ContainsKey(location))
{
return this.tiles[location];
}
else
{
return null;
}
}
it ALWAYS returns null. Again, if I view inside the Map object and find the Location key where x=2,y=2,z=0 , I clearly see the value being a Tile that FillTemp generated..
Why is it doing this? I've had no problems with a Dictionary such as this so far. I have no idea why it's returning null. and again, when debugging, I can CLEARLY see that the Map instance contains the Location key it says it does not...
very frustrating.
Any clues? Need any more info?
Help would be greatly appreciated :)
You don't show what 'Location' is but this is normal behavior if it is a class: objects are tested for Equality by comparing the references. So different instances of Location will always be unequal, even if their content is the same.
The quickest fix is to override Equals() and GetHashCode() for the Location class. And then it is a good idea to (re)design it as an immutable class (or maybe immutable struct).
Henk is correct; when you test to see if two objects are equal in .Net, you're actually asking if "reference x is pointing to the same object as reference y".
So, by default:
Location a = new Location(2, 2, 0);
Location b = new Location(2, 2, 0);
Location c = a;
bool notEqual = ( a == b ); // false
bool equal = ( a == c ); // true
To get around this, you need to override the equality methods for your Location object to compare the values for equality - the body of your Equals method, for example, might end us as something like:
return (this.x == that.x && this.y == that.y && this.z == that.z);
Thank you everyone! I really appreciate it!
I made Location into a value type and now it's working just fine.
I'm sorry for not posting the whole code, but I didn't feel it was necessary and assumed that the reader could assume that location was a simple class with x,y,z int values.
I learned many new things because of all of you and my understanding of this (generally) wonderful language is greater because of it :o)
Take care,