New objects and arrays

I am trying to make an array of Objects but cannot create new fields
“cannot find field” error

so I try to create multidimensional arrays with enums but Unity compains about this not being the best way to reference items and throws out a runtime error. How can I do this, then?

private var newvar = new Object();
newvar.name = "Player";  //this is where I get the first error

private var var2;
var2.name = "player"; //same problem

//new approach

//enums
private var eName = 0;
private var ePrefab = 1;

var playerModel   : Transform;
var numPlayers = 2;

private var var3 = new Array();
var3.length = numPlayers;

for(i=0; i < numPlayers; i++)
{
  var3[i] = new Array();
  var3[i].length = 3;

  var3[i,eName]   = "Player " + i;
  var3[i,ePrefab]  = Istantiate(playerModel,transform.location, transform.rotation);
}

//all this works thus far, but when I do:
var3[0,ePrefab].transform.Translate(i * 2, 0, 0);
//it all goes to hell... This is when it warns me about referencing optimisation

What I am trying to do, is to create a board game where the player can decide at each game how many players join the game. 2 to 8. For each player, a new starting location gets created just off the board and spaced evenly from each other. Then for each one, three or four move pieces is created (depending on difficulty) which then need to be placed on the starting board, evenly spaced.

Because of all these variables, I cannot place the models in the inspector, but need to create it at runtime. Then obviously I need a way to keep track of it.

I started creating this game yesterday and in one day created almost the entire game. All that is left is to crate the movement code (simple go to waypoint movement) and to add the sounds/animations. Today is day two and I have done absolutely nothing due to me trying to figure out how to create objects that work. Spent all my time on JavaScript sites and trying to get this one function to work.

Please help…

TIA

I am not sure what you are doing in the first lines of code. I’d just remove that for now. They certainly will give you runtime errors, which means the remaining part of the script will never be executed because it failed before.

Then you are using invalid syntax for arrays.
The correct syntax is like this:

//enums 
private var eName = 0; 
private var ePrefab = 1; 

var playerModel   : Transform; 
var numPlayers = 2; 

private var var3 = new Array(); 
var3.length = numPlayers; 

for(i=0; i < numPlayers; i++) 
{ 
  var3[i] = new Array(); 
  var3[i].length = 3; 

  var3[i][0]   = "Player " + i; 
  var3[i][ePrefab]  = Instantiate(playerModel, transform.position, transform.rotation); 
  var3[i][ePrefab].transform.Translate(i, 0, 0); 
}

The first lines were merely examples of how I tried to do it. I saw this is the most direct way to create new fields to an object. I still can’t understand why this doesn’t work, though. i used it years ago and it worked perfectly. Well, times change, no?

As for the array syntax. I can’t believe I made such a silly mistake. Thanks for that. I will try that again and see what happens.

Thanks again :smile:

This is because Unity’s Javascript does not support expando objects. Ie. all fields have to be declared at compile time. You could create a helper class with the fields you want and create an instance of that instead of using an Array and constant enumerations:

// Helper class
class PlayerInfo {
   var name : String;
   var prefab : GameObject;
}

var playerModel   : Transform; 
var numPlayers = 2; 

private var var3 = new Array(); 
var3.length = numPlayers; 

for(i=0; i < numPlayers; i++) 
{ 
  var3[i] = new PlayerInfo(); 

  var3[i].name   = "Player " + i; 
  var3[i].prefab  = Instantiate(playerModel, transform.position, transform.rotation); 
  var3[i].prefab.transform.Translate(i, 0, 0); 
}

In the spirit of TIMTOWTDI here’s a version that uses Hashtables:

var playerModel   : Transform; 
var numPlayers = 2; 

private var var3 = new Array(); 
var3.length = numPlayers; 

for(i=0; i < numPlayers; i++) 
{ 
  var3[i] = new Hashtable(); 

  var3[i]["name"]   = "Player " + i; 
  var3[i]["prefab"]  = Instantiate(playerModel, transform.position, transform.rotation); 
  var3[i]["prefab"].transform.Translate(i, 0, 0); 
}

And then there’s one more option. I noticed that your only extra information you store is the name of the player. As it happens, GameObjects in Unity do have a name property, so why not just use that?

var playerModel   : Transform; 
var numPlayers = 2; 

private var var3 = new Array(); 
var3.length = numPlayers; 

for(i=0; i < numPlayers; i++) 
{ 
  var3[i]  = Instantiate(playerModel, transform.position, transform.rotation); 
  var3[i].name   = "Player " + i; 
  var3[i].transform.Translate(i, 0, 0); 
}

The code I gave above was not the code I am using. It was only an example of what I am doing. I am indeed saving more info than just the name, but it was my intention to use the game object’s name field for that purpose.

As for the hash table, that looks a whole lot easier than creating enums. I am not a real fan of creating global vars that serve no real purpose and enums, in my opinion, serve no other purpose other than readability and maintainbability. Hash tables take care of that problem. I have never before in my life worked with hash table so this was something completely new for me.

The class option is what i will be using. I have been doing c coding for ages now so I am familiar with classes. I haven’t done JavaScript for ages now and when I did I did amazing stuff that never before required me to make a class since I just built stuff by adding new fields when needed. I never knew one could define classes in javascript… seems I have a lot of catching up to do.

Anyways, thanks for informing me of that. This bit of info will come in most handy indeed! :smile: :smile:

The hashtable solution is more flexible than the class solution, but as everything is stored in a hash, the performance is a bit slower. (But I doubt that would be noticeable.)

One more solution is to create a script that contains the information and attach it to the PlayerPrefab object. This is really a variation of the class example given above, except that the info is attached to the game object instead of the other way around.

// PlayerInfoScript.js

// 'name' is inherited from MonoBehaviur..
var someInfo: String;
var score: Float;

… and then in the instantiation script:
(Note that the playerModel variable is now of type PlayerInfoScript. You still drag the player prefab onto that variable. Just make sure it has a PlayerInfoScript attached to it.)

var playerModel   : PlayerInfoScript; 
var numPlayers = 2; 

private var var3 = new Array(); 
var3.length = numPlayers; 

for(i=0; i < numPlayers; i++) 
{ 
  var3[i]  = Instantiate(playerModel, transform.position, transform.rotation); 
  var3[i].name   = "Player " + i; 
  var3[i].transform.Translate(i, 0, 0); 
  var3[i].someInfo="This is some Info for player " +i; 
  var3[i].score = 0.0; 
}

edit: The advantage of the above solution, is that during development, you will be able to view the parameters in the inspector of the cloned player objects during play mode.

Hello again.

What does this runtime error mean: ArgumentException: invokeAttr ?
i am now including my actual code so you can see exactly what I am doing.
If I want an array inside a class, I assume I declare it as such:

var fieldName   : Array;

and because it is inside a class, I do not have to call the
fieldName = new Array() function because it will already have allocated memory for it. To play it safe, i simply didn’t give it a type and declare it with the new Array call in code anyways. I still can’t see why my first class works and my second one doesn’t…

Anyway, here is my script:

// PC vars
var players             :   int = 2;    //number of players this game
var maxPieces           :   int = 3;    //number of player pieces
var playerModel         :   Transform;

//board segment vars
var segments            : int;          //number of segments board is devided into
var block2Instantiate   : Transform;    //define prefab to use as entry/exit collision detection
var homeBaseBoard       : Transform;    //image players stand on at start
private var waypoints   = new Array();  //define movement markers and locations

//home base vars
private var homeBase            = new Array();

class playerPiece
{
var piece               : Transform;
var currentWaypoint     : int;
var nextWaypoint        : int;
}

class homeBaseVars
{
//var name;
var board               : Transform;
var player;
var firstWaypoint       : int;
}

//random possible deletable vars
var textarea            : GUIText;

function initHomeBase(base, rotation)
{
//    textarea.text = "Rotation: " + rotation * base * (segments/players);
    
    homeBase[base] = new homeBaseVars();
    
    homeBase[base].board = Instantiate(homeBaseBoard, transform.position, transform.rotation);
    homeBase[base].board.name = "Player" + base;
    homeBase[base].board.transform.Rotate(0,rotation * base * (segments / players),0);

    homeBase[base].player = new Array();
    homeBase[base].player.length = maxPieces;
    
    for(i=0; i < maxPieces; i++)
    {
    //get rotation from board, rotate player piece by that much, translate along it's z axis and then rotate 180 degrees to point back
    //game board size = constant and homebase prefab base object is at 0,0,0 with the actual base object place at edge of board.
    //thus the prefab's transform.translate value is always 0,0,0 so to place the pieces on the board for now, I will translate the
    //pieces to where the board is. Once all code works, I will do this a better way like trying to find the translate value of the board
    //itself, not the prefab.
    
    //Note: I have seen the variable "childcount" but I have not seen a "child" method to access any of the children.
    //NOTE: Need to figure out how to access children
    
        homeBase[base].player[i] = new playerPiece(); //ERROR: ArgumentException: invokeAttr
        homeBase[base].player[i].piece = Instantiate(playerModel,homeBase[base].board.transform.position, homeBase[base].board.transform.rotation);
        homeBase[base].player[i].piece.transform.Rotate(0,rotation,0);
        homeBase[base].player[i].piece.transform.Translate(0,0,2);
        homeBase[base].player[i].piece.transform.Rotate(0,180,0);
    }
}

//place collision prefab on start of every board segment to act as onEnterBlock trigger
function initMarkers()
{
    var newSegment : Transform;
    var RotateBy = 360 / segments;    
        
    for (i=0; i< segments; i++)
    {        
        newSegment = Instantiate (block2Instantiate, transform.position, transform.rotation);
        newSegment.transform.Rotate(0,i * RotateBy,0);
        
        waypoints.push(newSegment);
    }
    
    for (i=0; i < players; i++)
    {
        initHomeBase(i, RotateBy);
    }
}


function Start ()
{
    if (players < 2) players = 2;               //always at least 2 opponents to pit against each other
    if (segments == 0) segments = 16;           //just to have some segments
    while (segments % players > 0) segments++;  //equal segment count per player

    homeBase.length = players;
     
    initMarkers();
}

Please help…

Telling what the error means is a bit hard without knowing which line caused it. Can you post the line number given in the error message?

The ‘var varname: Array’ part of your decalration only tells the compiler that you are going to store an Array in the variable. It does not initialise it, so unless you do a ‘varname = new Array()’ somewhere, you will get a NullReference exception when trying to access the variable.

As for accessing the children of a transform, you just treat the transform as a collection object.

You can loop through all children of a transform like this:

for (var child : Transform in transform) {
   child.Translate(0,1,0);
}

… and access individual children by using the index operator on the transform:

var child_seven = transform[7];

Ahh I saw it… There’s a comment in the code saying //ERROR: … :slight_smile:

It looks like you have more than one thing called “playerPiece”. What happens if you rename the playerPiece class to something else?

will find out when i get back from work.

I can’t imagine having more than one item called that since I started from scratch, deleted my code and started on that one. This file is the only script in my project…

I will try to rename it when I get home and see if that works.

I never realized the child transforms can be accessed via index…That makes like real simple and will cause a few major changes in code!!!

LOL I never realized I knew so little of what I knew so much of

(I think I just found my new signature…)

Tried renaming the class to something else and still got exactly the same error. I then actually defined the var as an Array in the class definition but it made no difference.

I tried something new.
i created a global var of type array and instantiated the player pieces into that. This worked perfectly woth no runtime errors and showed the models on screen.

I then tried to assign the fields from the global vaar to the one inside the class but got the same error as before.

i then tried to assign the entire array to the class after the loop and it worked fine until runtime when it says it can’t find the variable.

I then tried to assign the instatiated class to a var instead of an array and then to simply push it into the class variable but now I get an error:
MissingMethodException: The best match for method push has some invalid parameter

All this has led me to believe that the problem lies in defining an array in a class. Perhaps because it doesn’t have a fixed size it cannot be used? Could this be right?

I am thinking the simplest way to solve this is to not make the player pieces a variable but rather a constant. I intended to change the amount of player avatars per level depending on the difficulty level selected. Perhaps I should discard the difficulty levels and just hard code the amount of play pieces.

This sounds a bit drastic. Surely there must be a way to build a game to your own requirements and not the other way around. I have faith in Unity that the problem lies not with it but with me. Unfortunately I am a bit stumped.

It seems the only way I am going to be able to do this is to do what you suggested earlier and attach a script to the homeBase prefab with a global array attached to that to say how many pieces it has.

I would prefer not to do it that way, to be honest (personal preference) so if you could tell me what the error is with my code I would greatly appreciate it.

Can you post the full script you are using right now.

// PC vars
var players             :   int = 2;    //number of players this game
var maxPieces           :   int = 3;    //number of player pieces
var playerModel         :   Transform;

//board segment vars
var segments            : int;          //number of segments board is devided into
var block2Instantiate   : Transform;    //define prefab to use as entry/exit collision detection
var homeBaseBoard       : Transform;    //image players stand on at start
private var waypoints   = new Array();  //define movement markers and locations

//home base vars
private var homeBase            = new Array();

class playPiece
{
var piece               : Transform;
var currentWaypoint     : int;
var nextWaypoint        : int;
}

class homeBaseVars
{
//var name;
var board               : Transform;
var avatar              : Array;
var firstWaypoint       : int;
}

//random possible deletable vars
var textarea            : GUIText;

function initHomeBase(base, rotation)
{
//    textarea.text = "Rotation: " + rotation * base * (segments/players);
    
    homeBase[base] = new homeBaseVars();
    
    homeBase[base].board = Instantiate(homeBaseBoard, transform.position, transform.rotation);
    homeBase[base].board.name = "Player" + base;
    homeBase[base].board.transform.Rotate(0,rotation * base * (segments / players),0);

    homeBase[base].avatar = new Array();
    homeBase[base].avatar.length = maxPieces;
    
    var tempPiece : playPiece;
    
    for(i=0; i < maxPieces; i++)
    {
    //get rotation from board, rotate player piece by that much, translate along it's z axis and then rotate 180 degrees to point back
    //game board size = constant and homebase prefab base object is at 0,0,0 with the actual base object place at edge of board.
    //thus the prefab's transform.translate value is always 0,0,0 so to place the pieces on the board for now, I will translate the
    //pieces to where the board is. Once all code works, I will do this a better way like trying to find the translate value of the board
    //itself, not the prefab.
    
    //Note: I have seen the variable "childcount" but I have not seen a "child" method to access any of the children.
    //NOTE: Need to figure out how to access children
    
        tempPiece = new playPiece();
  
        tempPiece.piece = Instantiate(playerModel,homeBase[base].board.transform.position, homeBase[base].board.transform.rotation);
        tempPiece.piece.transform.Rotate(0,rotation,0);
        tempPiece.piece.transform.Translate(0,0,2);
        tempPiece.piece.transform.Rotate(0,180,0);
  
          homeBase[base].avatar.push(tempPiece);        //Error: The best method for push has some invalid parameter
//        homeBase[base].avatar[i] = tempPiece;        //ERROR: ArgumentException: invokeAttr
//        homeBase[base].avatar[i] = tempPlayPiece[i]; //ERROR: ArgumentException: invokeAttr
//        homeBase[base].avatar[i].piece = Instantiate(playerModel,homeBase[base].board.transform.position, homeBase[base].board.transform.rotation);
//        homeBase[base].avatar[i].piece.transform.Rotate(0,rotation,0);
//        homeBase[base].avatar[i].piece.transform.Translate(0,0,2);
//        homeBase[base].avatar[i].piece.transform.Rotate(0,180,0);
    }
    textarea.text = "avatar 2: " + homeBase[base].avatar.length;
}

//place collision prefab on start of every board segment to act as onEnterBlock trigger
function initMarkers()
{
    var newSegment : Transform;
    var RotateBy = 360 / segments;    
        
    for (i=0; i< segments; i++)
    {        
        newSegment = Instantiate (block2Instantiate, transform.position, transform.rotation);
        newSegment.transform.Rotate(0,i * RotateBy,0);
        
        waypoints.push(newSegment);
    }
    
    for (i=0; i < players; i++)
    {
        initHomeBase(i, RotateBy);
    }
}


function Start ()
{
    if (players < 2) players = 2;               //always at least 2 opponents to pit against each other
    if (segments == 0) segments = 16;           //just to have some segments
    while (segments % players > 0) segments++;  //equal segment count per player

    homeBase.length = players;
     
    initMarkers();
}

I have marked the error locations and the errors I receive. Fingers crossed

// PC vars 
var players             :   int = 2;    //number of players this game 
var maxPieces           :   int = 3;    //number of player pieces 
var playerModel         :   Transform; 

//board segment vars 
var segments            : int;          //number of segments board is devided into 
var block2Instantiate   : Transform;    //define prefab to use as entry/exit collision detection 
var homeBaseBoard       : Transform;    //image players stand on at start 
private var waypoints   = new Array();  //define movement markers and locations 

//home base vars 
private var homeBase            = new Array(); 

class playPiece 
{ 
var piece               : Transform; 
var currentWaypoint     : int; 
var nextWaypoint        : int; 
} 

class homeBaseVars 
{ 
//var name; 
var board               : Transform; 
var avatar              : Array; 
var firstWaypoint       : int; 
} 

//random possible deletable vars 
var textarea            : GUIText; 

function initHomeBase(base, rotation) 
{ 
//    textarea.text = "Rotation: " + rotation * base * (segments/players); 
    
    homeBase[base] = new homeBaseVars(); 
    
    homeBase[base].board = Instantiate(homeBaseBoard, transform.position, transform.rotation); 
    homeBase[base].board.name = "Player" + base; 
    homeBase[base].board.transform.Rotate(0,rotation * base * (segments / players),0); 

    homeBase[base].avatar = new Array(); 
    homeBase[base].avatar.length = maxPieces; 
    
    var tempPiece : playPiece; 
    
    for(i=0; i < maxPieces; i++) 
    { 
    //get rotation from board, rotate player piece by that much, translate along it's z axis and then rotate 180 degrees to point back 
    //game board size = constant and homebase prefab base object is at 0,0,0 with the actual base object place at edge of board. 
    //thus the prefab's transform.translate value is always 0,0,0 so to place the pieces on the board for now, I will translate the 
    //pieces to where the board is. Once all code works, I will do this a better way like trying to find the translate value of the board 
    //itself, not the prefab. 
    
    //Note: I have seen the variable "childcount" but I have not seen a "child" method to access any of the children. 
    //NOTE: Need to figure out how to access children 
    
        tempPiece = new playPiece(); 
  	
  	
        tempPiece.piece = Instantiate(playerModel,homeBase[base].board.transform.position, homeBase[base].board.transform.rotation); 
        tempPiece.piece.transform.Rotate(0,rotation,0); 
        tempPiece.piece.transform.Translate(0,0,2); 
        tempPiece.piece.transform.Rotate(0,180,0); 

  	var currentHomeBase : homeBaseVars = homeBase[base];
  	
	currentHomeBase.avatar.push(tempPiece);        //Error: The best method for push has some invalid parameter 
        currentHomeBase.avatar[i] = tempPiece;        //ERROR: ArgumentException: invokeAttr 
//        currentHomeBase.avatar[i] = tempPlayPiece[i]; //ERROR: ArgumentException: invokeAttr 
        currentHomeBase.avatar[i].piece = Instantiate(playerModel,currentHomeBase.board.transform.position, currentHomeBase.board.transform.rotation); 
//        homeBase[base].avatar[i].piece.transform.Rotate(0,rotation,0); 
//        homeBase[base].avatar[i].piece.transform.Translate(0,0,2); 
//        homeBase[base].avatar[i].piece.transform.Rotate(0,180,0); 
    } 
    textarea.text = "avatar 2: " + homeBase[base].avatar.length; 
} 

//place collision prefab on start of every board segment to act as onEnterBlock trigger 
function initMarkers() 
{ 
    var newSegment : Transform; 
    var RotateBy = 360 / segments;    
        
    for (i=0; i< segments; i++) 
    {        
        newSegment = Instantiate (block2Instantiate, transform.position, transform.rotation); 
        newSegment.transform.Rotate(0,i * RotateBy,0); 
        
        waypoints.push(newSegment); 
    } 
    
    for (i=0; i < players; i++) 
    { 
        initHomeBase(i, RotateBy); 
    } 
} 


function Start () 
{ 
    if (players < 2) players = 2;               //always at least 2 opponents to pit against each other 
    if (segments == 0) segments = 16;           //just to have some segments 
    while (segments % players > 0) segments++;  //equal segment count per player 

    homeBase.length = players; 
      
    initMarkers(); 
}

I changed the code above to use static typing instead of dynamic typing. Going with static typing is generally better since it gives you errors at compile time instead of run time and it is faster-

var currentHomeBase : homeBaseVars = homeBase[base];
currentHomeBase.avatar.push(tempPiece);

So now it runs the script without producing any exceptions.

I was thinking about it tonight. I think I SHOULD attach the vars to the prefabs directly. I didn’t want to do it this way out of personal preference, but i figure this is the way unity was made to work so why not?

I replaced my script with the one you gave me above and I still get a runtime error at this line:

        currentHomeBase.avatar[i].piece = Instantiate(playerModel,currentHomeBase.board.transform.position, currentHomeBase.board.transform.rotation);

The error I get is this:

NullReferenceException: Object reference not set to an instance of an object

WAIT WAIT WAIT;
I commented out the line

        currentHomeBase.avatar[i] = tempPiece;

cause it seemed like a duplicate of the push call just above it. I then commented out the push instead and it all works perfectly now. Now the question arises, should I use this or attach the vars to the prefabs directly?

I knwo it probably wouldn’t make much difference now that it all waorks, but what would you suggest is the best approach?

oh and tahnks once again for helping me fix this script. i will go over it in detail to see what changes you had to make. Thanks once more!

I mean, ultimately you should do how you like it best, but i really think it will be cleaner, easier to understand and probably run faster to make seperate scripts for object behaviours and the script that instantiates it.

'Spose so.

Like I said, this is the way Unity was built to work. Rather than forcing Unity to work in a way I am used to, I should rather take advantage of the features it offers, right?

I had a look at the coding you did for me and did some experiments. By not definding the currentHomeBase var as a static typed var, I get all sorts of errors. the problem with the script i got from you is, when the script has executed, it returns a count of 6 iterations (as it should for 2 players with 3 pieces apiece) but in the inspector there are 12 player pieces. each piece has a duplicate.

It seems I will have to “unlearn all that I have learned. Yes. that I must” and start coding FOR unity and not simply do my coding in Unity… Arrrg… you know what I mean…

Well, back to the drawing board…

Anyway, tomorrow I am going to start again and this time do it the way it was supposed to be done. Let’s see if this works out better this time… :smile: