- Home /
2D array values are not updating or pointers are using old instance in Javascript (fixed?)
I have a puzzle game I am creating that features a grid based on the values of a 2D array. The player can then place special orbs that modify the values in the grid squares (0 is a blank orb, 1 is fire orb, 2 is water orb, 3 is lightning orb, 4 is earth orb, 9 is a blank space for an orb to be placed). This works perfectly. The problem I'm having is when I try to reset or change the level, the values are not being reset or properly updated. If I place a special orb in a grid space and change the level, the sprite resets but it retains the special orb value in that position. I'm guessing the issue is with how I'm trying to update the array values for the new level?
Here is my script that is in charge of the array data and the for loops. The GUI button script at the bottom has the current level change code. All of the grid spaces have the "gridspace" tag so they can be deleted before the new level is created.
//These are variables needed for the level creation portion of the game to work
var obj_BlankSpot : Transform;
var obj_PlacedBlankOrb : Transform;
var currentOrb : int; // sets up the currently selected orb variable for use with other scripts
static var gridH : int; //used for grid height
static var gridW : int; //used for grid width
static var r : int; //used in the grid creation for loops
static var c : int; //used in the grid creation for loops
var destroyGrid : GameObject[]; // used to destroy puzzle grid game objects when a level is reset or loaded
//This will create a 2D array that will hold some spots and data for various squares
//9 represents a blank square where a player can place an orb
//0 represent a square filled with a blank orb
var startX : int = -1; // this is the left hand side of the puzzle grid
var startY : int = 1; // this is the top of the puzzle grid
var gridArray = [
[9, 0, 9, 0],
[9, 9, 0, 9],
[9, 9, 9, 9],
[0, 0, 9, 0]];
function Start()
{
///This will end up placing the correct objects in the level based on gridArray
gridW = gridArray[0].length; //gets the width of the gridArray for the for loops
gridH = gridArray.length; //gets the height of the gridArray for the for loops
for(r = 0; r < gridW; r++)
{
for(c = 0; c < gridH; c++)
{
var currentX = startX + (r*.96); // this moves newsprites right for each new column being created
var currentY = startY - (c*.96); // this moves new sprites down for each new row being created
// 1 pixel seems to be .01 unity units. 100 pixels would be 1 unit
// r and c are reversed in the if statements so the squares are placed left to right
if(gridArray[c][r] == 9)
{
Instantiate(obj_BlankSpot, Vector3 (currentX, currentY, 0), Quaternion.identity); //this object is a blank square where a player can place an or
}
else if(gridArray[c][r] == 0)
{
Instantiate(obj_PlacedBlankOrb, Vector3 (currentX, currentY, 0), Quaternion.identity); // this object is a square that hold a blank orb that can be broken
}
}
}
}
//Make these buttons fully reset the level and not just the graphics of the sprites but all the data as well
function OnGUI()
{
// Make a background box
GUI.Box (Rect (10,10,100,90), "Menu");
// Make the first button. If it is pressed, the board will be cleared and level 2 will be loaded
if (GUI.Button (Rect (20,40,80,20), "Level 2"))
{
destroyGrid = GameObject.FindGameObjectsWithTag ("gridspace"); // adds all puzzle grid pieces to the destroyGrid array
for(var o = 0 ; o < destroyGrid.length ; o ++) // for all entries in the destroyGrid array
{
Destroy(destroyGrid[o]); //destroys the current grid piece
}
// sets up a test grid for level 2
gridArray = [
[9, 0, 9, 0, 9],
[9, 9, 0, 9, 9],
[9, 0, 9, 0, 9],
[9, 9, 9, 9, 9],
[0, 0, 9, 0, 9]];
Start(); // runs the start function again to create the new grid
}
}
I know that's a chunk of code to sort through, so I took a few screenshots in case those help explain the problem. The first screenshot shows the level 1 4x4 grid with an orb placed in the top left. The second screenshot shows that level 2 has a 5x5 grid but I can't place orbs in the the spot that the lightning orb was placed in level 1 or in the 5th row and column, which weren't present in level 1. That makes me think I'm doing something wrong when I try to update the array for the new level.
///////////////////////////////////////////////////////////////////////////
////////// UPDATE: Blank Spot Script /////////
///////////////////////////////////////////////////////////////////////////
Here is a copy of the other script in which I modify gridArray from the code above. I cut out a large portion of the code for this example simply to save space since it just consisted of more if statements using the same code. A few things to note is that the gridArrayScript variable links to the script above so that I can access variables from that. xCoord and yCoord in the Awake() function save the r and c values at the time of creation so that each instance of the prefab will remember where it's positioned in the grid. When the game object is clicked on with the mouse, it uses xCoord and yCoord to access its position in gridArray and change the value of that spot. Of course at this point, it looks like the objects are still pulling from the original gridArray instead of the new one in level 2. How would I go about fixing that?
//selectedOrbScript : scr_gridArray makes it so that I can access scr_gridArray's variables like currentOrb
//the sprite values make it so that the blank spot can change into various orbs based on what the player has selected
var gridArrayScript : scr_gridArray;
gridArrayScript.currentOrb = 0; // sets the value of currentOrb to 0 at the beginning of each level when it is placed
var xCoord : int;
var yCoord : int;
var fireSprite : Sprite;
var waterSprite : Sprite;
var lightningSprite : Sprite;
var earthSprite : Sprite;
function Awake()
{
xCoord = gridArrayScript.r; //sets xCoord = to the c value in the loops for the 2D array
yCoord = gridArrayScript.c; //sets yCoord = to the r value in the loops for the 2D array
}
/* Orb data codes for if checks in the mouse click function
0 = blank orb
1 = fire orb
11 = fire in empty square
13 = fire and lightning in empty square
2 = water orb
22 = water in empty square
3 = lightning orb
33 = lightning in empty square
4 = earth orb
9 = empty square
*/
function OnMouseDown ()
{
if(gridArrayScript.gridArray[yCoord][xCoord] == 9 ||
gridArrayScript.gridArray[yCoord][xCoord] == 11 ||
gridArrayScript.gridArray[yCoord][xCoord] == 13 ||
gridArrayScript.gridArray[yCoord][xCoord] == 22 ||
gridArrayScript.gridArray[yCoord][xCoord] == 33) // checks to see if the spot has no orb before executing any code
{
//in the if statements xCoord and yCoord are reversed due to the way the grid is created
////////////////////////////////
//Fire Orb Code
///////////////////////////////
if(gridArrayScript.currentOrb == 1) //execute this is fire orb is currently selected
{
GetComponent(SpriteRenderer).sprite = fireSprite; //changes the sprite to a fire orb
if (gridArrayScript.gridArray[yCoord][xCoord] == 33 ||
gridArrayScript.gridArray[yCoord][xCoord] == 13 ) // checks to see if the square has lightning or fire and lightning in it
{
print ("You broke an orb"); ////////////////// remove this line when testing is done and replace it with a pop up messages, level reset, and broken orb sprite
}
else //if the spot doesn't have lightning in it to break the fire orb as it's placed, execute the code
{
gridArrayScript.gridArray[yCoord][xCoord] = 1; // changes the data for this square to fire orb
// Executes fire orb code going to the right or x+
if ((xCoord+1) < gridArrayScript.gridW) //checks to see if there is a grid space to the right
{
if (gridArrayScript.gridArray[yCoord][xCoord+1] == 0 ||
gridArrayScript.gridArray[yCoord][xCoord+1] == 3) //checks to see if the object is a blank orb or lightning orb
{
print ("You broke an orb"); ////////////////// remove this line when testing is done and replace it with a pop up messages, level reset, and broken orb sprite
}
else if (gridArrayScript.gridArray[yCoord][xCoord+1] == 9) // checks to see if this is an empty square
{
gridArrayScript.gridArray[yCoord][xCoord+1] = 11; // changes the data for this square to be fire an empty square
}
else if (gridArrayScript.gridArray[yCoord][xCoord+1] == 33) // checks to see if this is an empty square with lightning
{
gridArrayScript.gridArray[yCoord][xCoord+1] = 13; // changes the data for this square to be fire and lightning in an empty square
}
if ((xCoord+2) < gridArrayScript.gridW) //checks to see if there is a grid space 2 spaces to the right
{
if(gridArrayScript.gridArray[yCoord][xCoord+1] != 4) // checks to see if there was an earth orb in the previous square to block this
{
if (gridArrayScript.gridArray[yCoord][xCoord+2] == 0 ||
gridArrayScript.gridArray[yCoord][xCoord+2] == 3) //checks to see if the object is a blank orb or lightning orb
{
print ("You broke an orb"); ////////////////// remove this line when testing is done and replace it with a pop up messages, level reset, and broken orb sprite
}
else if (gridArrayScript.gridArray[yCoord][xCoord+2] == 9) // checks to see if this is an empty square
{
gridArrayScript.gridArray[yCoord][xCoord+2] = 11; // changes the data for this square to be fire an empty square
}
else if (gridArrayScript.gridArray[yCoord][xCoord+2] == 33) // checks to see if this is an empty square with lightning
{
gridArrayScript.gridArray[yCoord][xCoord+2] = 13; // changes the data for this square to be fire and lightning in an empty square
}
}
}
}
}
}
//more similar code for different circumstances
}
}
///////////////////////////////////////////////////////////////////////////
/////////////////////////////2ND UPDATE://////////////////////////
///////////////////////////////////////////////////////////////////////////
So I tried out NoseKills debugging process just to double check the values of the array after it had supposedly been updated. As shown above, level 1's array has the following set up:
[9, 0, 9, 0],
[9, 9, 0, 9],
[9, 9, 9, 9],
[0, 0, 9, 0]];
I placed fire orbs in all of the blank spots which changed it to:
[1, 0, 1, 0],
[1, 1, 0, 1],
[1, 1, 1, 1],
[0, 0, 1, 0]];
After I press the "level 2 button" the array should be reset to:
[9, 0, 9, 0, 9],
[9, 9, 0, 9, 9],
[9, 0, 9, 0, 9],
[9, 9, 9, 9, 9],
[0, 0, 9, 0, 9]];
However it's not changing at all after I used the print out that NoseKills suggested. All of the 9's are still 1's, and the print out isn't even acknowledging that there is a 5th row and column. The highest space on the grid that the printout shows after level 2 was loaded is 3,3 instead of 4,4 like it should be. The second script is still pulling the original array data instead of the new one. Is there any easy to way to fix that or something I should include in my second scripts OnMouseDown or Awake functions?
///////////////////////////////////////////////////////////////////////////
//////////////////////Final Update?//////////////////////////////
///////////////////////////////////////////////////////////////////////////
So I was able to fiddle with things enough that I think I may have stumbled across a fix. From the limited testing that I've done, it looks like the array data is updating properly, even though I'm not 100% sure why this worked and the other did not. With the following code the game starts out with nothing, and pressing the level buttons load the appropriate puzzle grid and wipes away the old data. My only issue at the moment is that I can't declare my 2D array as blank without popping up a bunch of errors in that script or one of the ones that point to it. To bypass that I just threw in a value that has no meaning just to make it work. Here's the new code.
var startX : int; // used to track the left side of the puzzle grid
var startY : int; // used to track the top of the puzzle grid
//this is just a placeholder to declare the 2D array so that there aren't errors, creates a blank level
static var gridArray = [[99]];
levelCounter = 0; //starts the level counter at 0
function Start()
{
//puts all grid spaces into the destroyGrid array so they can be destroyed before the level is recreated
destroyGrid = GameObject.FindGameObjectsWithTag ("gridspace");
for(var o = 0 ; o < destroyGrid.length ; o ++)//cycles through all the grid spaces
{
Destroy(destroyGrid[o]); //destroys the current grid space
}
//This will create a 2D array that will hold some spots and data for various squares based on the level
//9 represents a blank square where a player can place an orb
//0 represent a square filled with a blank orb
if(levelCounter == 1) //if level 1 is selected
{
startX = -1; // this is the left hand side of the puzzle grid
startY = 1; // this is the top of the puzzle grid
gridArray = [
[9, 0, 9, 0],
[9, 9, 0, 9],
[9, 9, 9, 9],
[0, 0, 9, 0]];
}
else if(levelCounter == 2) //if level 2 is selected
{
startX = -2; // this is the left hand side of the puzzle grid
startY = 1; // this is the top of the puzzle grid
gridArray = [
[9, 0, 9, 0, 9],
[9, 9, 0, 9, 9],
[9, 0, 9, 0, 9],
[9, 9, 9, 9, 9],
[0, 0, 9, 0, 9]];
}
///This will end up placing the correct objects in the level based on gridArray
gridW = gridArray[0].length; //gets the width of the gridArray for the for loops
gridH = gridArray.length; //gets the height of the gridArray for the for loops
for(r = 0; r < gridW; r++)
{
for(c = 0; c < gridH; c++)
{
var currentX = startX + (r*.96); // this moves newsprites right for each new column being created
var currentY = startY - (c*.96); // this moves new sprites down for each new row being created
// 1 pixel seems to be .01 unity units. 100 pixels would be 1 unit
// r and c are reversed in the if statements so the squares are placed left to right
if(gridArray[c][r] == 9)
{
Instantiate(obj_BlankSpot, Vector3 (currentX, currentY, 0), Quaternion.identity); //this object is a blank square where a player can place an or
}
else if(gridArray[c][r] == 0)
{
Instantiate(obj_PlacedBlankOrb, Vector3 (currentX, currentY, 0), Quaternion.identity); // this object is a square that hold a blank orb that can be broken
}
}
}
}
Do you pass gridArray to some other script to be manipulated by player controls? Do you pass the new grid to that scrip also or is that other script still trying to modify the old grid perhaps? $$anonymous$$ight be useful to put some Debug.Log()s in place to see what actually is stored in the array you are trying to add orbs to in level 2.
Actually yeah, the script that I have set up to execute the code for placing orbs into the blank spots ends up accessing gridArray so it can change the value/data of that spot in order to react with the rest of the board.
For example, the blank spot in the top left corner is [0,0] starts with a value of 9 (indicates an empty spot). If I place a fire orb there (value of 1) it would change [0,0] to a value of 1. You're thinking that even those I'm setting the gridArray to have new values, that the old values are still being stored and accessed somewhere? I'll see if I can test that
Yeah. Could you post code of how you are using gridArray in your other scripts?
You have to realise that when yo make the Level 2 gridArray, that's a completely new array instance. If you passed gridArray to other scripts in Level1, make sure to pass the newly created gridArray to all those scripts in Level 2 again.
Alright, i included a portion of the code from the other script that accesses and modifies gridArray. If you have questions about the game mechanics in order for it to make sense, let me know, but it should give you an idea of what I'm doing with gridArray. Would I want to create a variable that represents gridArrayScript.gridArray and have it be set to the new array each time On$$anonymous$$ouseDown is used? Would that make it reference the new instance of the array?
I also did a bit more research last night based on what you said and I came across http://davidwalsh.name/empty-array which explains the "creating a new instance of the array" situation that it looks like I might be having. I did try adding a gridArray.length = 0; line right in the "level 2" button code right before I added the new values to gridArray to see if that helped, but it kept giving me an error saying that .length was read only.
Based on the new code you posted, it doesn't look like the problem is what i suspected.
I was suspecting a situation like this:
Script 1
var gridArray = [
[9, 0, 9, 0],
[9, 9, 0, 9],
[9, 9, 9, 9],
[0, 0, 9, 0]];
// you pass the gridArray to some other script
script2.setArray(gridArray);
// this doesn't update the array we already passed to script 2
gridArray = [
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]];
// now the scripts have different arrays
// gridArrayInScript2 is still full of 0's and 9's and it's a different array than this one
Script 2
var gridArrayInScript2;
function setArray(var arrayPassedFromScript1)
{
gridArrayInScript2 = arrayPassedFromScript1;
}
function On$$anonymous$$ouseDown ()
{
// do stuff to gridArrayInScript2 ...
}
If you are always accessing gridArray stright gridArrayScript.gridArray
there is no way this can happen.
Now my suggestion would be to just add more Debug.Log (or print) to see what's happening. For example to be sure what's in the array you could put this in the beginning of On$$anonymous$$ouseDown()
for(int r = 0; r < gridArrayScript.gridArray.Length; r++)
{
for(int c = 0; c < gridArrayScript.gridArray[r].Length; c++)
{
print("Value at " + r + "," + c + " is " + gridArrayScript.gridArray[r][c]);
}
}
Answer by floydtherooster · Sep 26, 2014 at 06:18 PM
I may have found a fix for the issue, although I'm not sure why it works and the original method did not. It seems to be working, at least for the testing that I've done so far. I'm going to post my new code above but I don't really want to put it as an answer until I'm 100% sure or understand why it's working.