JIT exception in Unity iPhone

Hi, I have my project working in the editor, but there is an error when runing on my iPad.
This is the exception:

ExecutionEngineException: Attempting to JIT compile method ‘TerrainData:.ctor ()’ while running with --aot-only.

at TerrainData…cctor () [0x00000] in :0
Rethrow as TypeInitializationException: An exception was thrown by the type initializer for TerrainData

I suspect my use of generics is causing a problem, but since I am new to C#/Unity I am befuddled!

Here is the entire class:

public class TerrainData {
 private static readonly TerrainData instance = new TerrainData();
 
 enum roomWallType { exterior, interior };
 
 //a set of slope prefabs indexed by type then variant
 public List<List<GameObject>> slopePrefabs;
 public List<GameObject> roomWallJoin;  //joiner bits for room walls
 
 public List<GameObject>[] roomWall;
 public List<GameObject>[] roomWin;
 public List<GameObject>[] roomDoor;
 public List<GameObject>[] roomLockDoor;
 public List<GameObject> moveHighlightPrefabs;
 
 //Defines all possible slopes based on height level of four corners (hence 4 dimensional array)
 public MapElement[,,,] slopes; 
 public MapElement[,,,,,,,] wallJoins;
 
 
 //define a slope type by the height level of each corner (only one of the four possible orientations)
 //this data is based on the template graphics (with first corner at x0,y1 in Blender file)
 public int[,] slopeTypes;
 //defined by edge types (external=1, internal=2) on each of four spokes
 public int[,] wallJoinTypes;

 
 
 private TerrainData() {
 slopePrefabs = new  List<List<GameObject>>();
 
 
 roomWallJoin = new List<GameObject>();  //joiner bits for room walls
 roomWall = new List<GameObject>[2];
 roomWin = new List<GameObject>[2];
 roomDoor = new List<GameObject>[2];
 roomLockDoor = new List<GameObject>[2];
 moveHighlightPrefabs = new List<GameObject>();
 
 
 slopes = new MapElement[3,3,3,3];
 wallJoins = new MapElement[3,3,3,3,3,3,3,3];
 //define a slope type by the height level of each corner (only one of the four possible orientations)
 //this data is based on the template graphics (with first corner at x0,y1 in Blender file)
 slopeTypes = new int[,] { //represents height level of each corner
 {0,0,0,0},  //floor type
 {1,1,1,1},  //floor type
 {2,2,2,2},  //floor type
 {1,1,0,0},
 {2,2,1,1},
 {1,0,0,0},
 {2,1,1,1},
 {1,1,0,1},
 {2,2,1,2},
 {1,1,0,2},
 {1,2,0,0},
 {1,2,0,1},
 {2,0,0,0},
 {2,1,0,0},
 {2,1,0,1},
 {2,1,0,2},
 {2,2,0,0},
 {2,2,0,1},
 {2,2,0,2},
 {1,0,1,0},
 {2,1,2,1},
 {2,0,2,0},
 {2,0,1,0},
 {2,0,2,1} };
 
 
 //defined by edge types (external=1, internal=2) on each of four spokes
 wallJoinTypes = new int[,] {
 {0,0,0,0,0,0,1,2},
 {0,0,0,0,0,0,2,1},
 {0,0,0,0,0,0,2,2},
 {0,0,1,2,0,0,2,1},
 {0,0,2,2,0,0,2,2},
 {1,1,1,2,0,0,2,1},
 {1,2,0,0,0,0,2,1},
 {1,2,2,1,1,2,2,1},
 {1,2,2,2,0,0,2,1},
 {1,2,2,2,2,2,2,1},
 {2,1,0,0,0,0,1,2},
 {2,1,1,2,0,0,2,2},
 {2,2,0,0,0,0,2,2},
 {2,2,2,1,0,0,1,2},
 {2,2,2,2,0,0,2,2},
 {2,2,2,2,2,2,2,2} }; 
 }
 
 
 
 
 
 public static TerrainData Instance
 {
 get 
 {
 return instance; 
 }
 }
 
 
 // Terrain\Floors\Template\Template_slope_0000
 public void LoadSlopes() {
 string envName = "Template";
 string slopePathStub = String.Format("Terrain/Floors/{0}/{0}_slope_", envName);
 for (int i=0; i< slopeTypes.GetLength(0); i++) {
 string slopeID = String.Format("{0}{1}{2}{3}", slopeTypes[i,0], slopeTypes[i,1], slopeTypes[i,2], slopeTypes[i,3]);
 string slopeFolder = slopePathStub + slopeID;
 List<GameObject> slopeVariants = new List<GameObject>();
 GameObject slope = null;
 int variantNum = 0;
 while (true) {
 string fileName = slopeFolder + String.Format( "/{0}_slope_{1}_{2}"  , envName, slopeID, variantNum );
 slope = LoadPrefab(fileName);
 if (slope == null) break;
 slopeVariants.Add(slope);
 variantNum++;
 }
 slopePrefabs.Add(slopeVariants);
 }
 }
 
 
 public void LoadMoveHighlights() {
 string pathStub = "Interface/MoveHighlights/MH_";
 for (int i=0; i< slopeTypes.GetLength(0); i++) {
 string slopeID = String.Format("{0}{1}{2}{3}", slopeTypes[i,0], slopeTypes[i,1], slopeTypes[i,2], slopeTypes[i,3]);
 string pathName = pathStub + slopeID;
 GameObject highlight = null;
 highlight = LoadPrefab(pathName);
 moveHighlightPrefabs.Add(highlight);
 }
 } 
 
 
 /* builds four rotations for each slope type in a 4D array, which is then used in the map building
 */
 public void BuildSlopeElements() {
 MapElement slopeElement;
 slopeElement.valid = true;
 for (int i=0; i< slopeTypes.GetLength(0); i++) {
 int c1 = slopeTypes[i,0];
 int c2 = slopeTypes[i,1];
 int c3 = slopeTypes[i,2];
 int c4 = slopeTypes[i,3];
 slopeElement.typeID = i;
 slopeElement.orientation = 0;
 slopes[c1,c2,c3,c4] = slopeElement;
 slopeElement.orientation = 1;
 slopes[c4,c1,c2,c3] = slopeElement;
 slopeElement.orientation = 2;
 slopes[c3,c4,c1,c2] = slopeElement;
 slopeElement.orientation = 3;
 slopes[c2,c3,c4,c1] = slopeElement;
 }
 }
 
 public GameObject GetSlopePrefab( int slopeID, int variant ) {
 //if (variant >= slopePrefabs[slopeID].Count) variant = 0;
 return slopePrefabs[slopeID][variant];
 }

 public GameObject GetWallJoinPrefab( int joinID ) {
 return roomWallJoin[ joinID ];
 } 
 
 public int GetNumVariants( int slopeID ) {
 return slopePrefabs[slopeID].Count;
 }
 
 public int GetNumRoomWallVariants( RoomWallCell.BaseType baseType, RoomWallCell.SubType subType ) {
 switch (subType) {
 case RoomWallCell.SubType.wall:
 return roomWall[(int)baseType-1].Count;
 case RoomWallCell.SubType.window:
 return roomWin[(int)baseType-1].Count;
 case RoomWallCell.SubType.door:
 return roomDoor[(int)baseType-1].Count;
 case RoomWallCell.SubType.lockDoor:
 return roomLockDoor[(int)baseType-1].Count;
 }
 return 1;
 } 
 
 
 public int SetValidSlopeVariant( int slopeID, int variant) {
 if ( variant >= slopePrefabs[slopeID].Count) variant = 0;
 return variant;
 }
 
 
 //will return null if no such object
 private GameObject LoadPrefab(string pathName) {
 GameObject prefab = Resources.Load(pathName) as GameObject;
 //if (prefab == null) {
 //Debug.Log( String.Format("Could not load prefab [{0}]", pathName) );
 //}
 return prefab;
 }
 
 
 public void LoadRoomWalls() {
 string envName = "Template";
 string stub = String.Format("Terrain/Rooms/RoomWalls/{0}", envName);
 GameObject wall = null;
 int variantID;
 string[] wallTypes = {"ext", "int"};
 for (int i=0 ; i<2; i++) {
 //walls
 variantID = 0;
 roomWall *= new List<GameObject>();*

while (true) {
string fname = String.Format( “{0}/{1}wall{2}{3}", stub, envName, wallTypes*, variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
_roomWall.Add( wall );*

variantID ++;
}
//windows
variantID = 0;
roomWin = new List();
while (true) {
string fname = String.Format( “{0}/{1}win{2}{3}", stub, envName, wallTypes*, variantID );*
wall = LoadPrefab( fname );
if (wall == null) break;
roomWin*.Add( wall );*
variantID ++;
}
//doors
variantID = 0;
roomDoor = new List();
while (true) {
string fname = String.Format( "{0}/{1}door{2}{3}”, stub, envName, wallTypes*, variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
_roomDoor.Add( wall );
variantID ++;
}
//locked doors*

variantID = 0;
roomLockDoor = new List();
while (true) {
string fname = String.Format( "{0}/{1}lockdoor{2}{3}”, stub, envName, wallTypes*, variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
_roomLockDoor.Add( wall );
variantID ++;
}
}
}*_

public GameObject GetRoomWall( RoomWallCell.BaseType baseType, RoomWallCell.SubType subType, int variant ) {
//probably should check if valid variant
switch (subType) {
case RoomWallCell.SubType.wall:
return roomWall[(int)baseType-1][variant];
case RoomWallCell.SubType.window:
return roomWin[(int)baseType-1][variant];
case RoomWallCell.SubType.door:
return roomDoor[(int)baseType-1][variant];
case RoomWallCell.SubType.lockDoor:
return roomLockDoor[(int)baseType-1][variant];
}
return null;
}

public void LoadRoomWallJoins() {
string envName = “Template”;
string stub = String.Format(“Terrain/Rooms/RoomWalls/{0}/{0}walljoin”, envName);
for (int i=0; i < wallJoinTypes.GetLength(0); i++ ) {
string ID = “”;
for (int j=0; j<8; j++) {
ID += wallJoinTypes[i,j].ToString();
}
roomWallJoin.Add( LoadPrefab( stub + ID ) );
}

}

public void BuildWallJoinElements() {
MapElement slopeElement;
slopeElement.valid = true;
for (int i=0; i< wallJoinTypes.GetLength(0); i++) {
int c1 = wallJoinTypes[i,0];
int c2 = wallJoinTypes[i,1];
int c3 = wallJoinTypes[i,2];
int c4 = wallJoinTypes[i,3];
int c5 = wallJoinTypes[i,4];
int c6 = wallJoinTypes[i,5];
int c7 = wallJoinTypes[i,6];
int c8 = wallJoinTypes[i,7];
slopeElement.typeID = i;
slopeElement.orientation = 0;
wallJoins[c1,c2,c3,c4,c5,c6,c7,c8] = slopeElement;
slopeElement.orientation = 1;
wallJoins[c7,c8,c1,c2,c3,c4,c5,c6] = slopeElement;
slopeElement.orientation = 2;
wallJoins[c5,c6,c7,c8,c1,c2,c3,c4] = slopeElement;
slopeElement.orientation = 3;
wallJoins[c3,c4,c5,c6,c7,c8,c1,c2] = slopeElement;
}
}

/* load all prefabs and prepare data */
public void MakeData() {
LoadSlopes();
BuildSlopeElements();
LoadRoomWalls();
LoadRoomWallJoins();
BuildWallJoinElements();
LoadMoveHighlights();
}

}

Yup, the JIT error is for sure caused by those multidimensional arrays. I had the same issue with my game. You could probably fix it by using non-dynamic arrays (int) but as a Java guy I was annoyed with how those are treated. Also, when you’re doing so many dimensions (like 8), you probably want to flatten everything into a 1D array for better performance. I made a quick class for doing this.

using System.Collections;

//this focuses on speed, not safety, so don't call a function that
//uses a length bigger than what you passed in
public class FlattenedFloatArray
{
	private float[] array;
	private int[] dimensionMultiples;
	private int[] dimensionLengths;
	private int totalLength;
	
	public FlattenedFloatArray( int[] dimensions )
	{
		dimensionLengths = dimensions;
		
		totalLength = 1;
		for ( int i = 0; i < dimensions.Length; i++ )
		{
			totalLength *= dimensions*;*
  •  }*
    
  •  array = new float[ totalLength ];*
    
  •  dimensionMultiples = new int[ dimensions.Length ];*
    
  •  dimensionMultiples[ dimensions.Length - 1 ] = 1;*
    
  •  for ( int i = dimensions.Length - 2; i >= 0; i-- )*
    
  •  {*
    

dimensionMultiples = dimensions[i+1] * dimensionMultiples[i+1];
* }*
* }*

* public float Get( int i, int j )*
* {*
_ return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] ];_
* }*

* public float Get( int i, int j, int k )*
* {*
_ return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] ];_
* }*

* public float Get( int i, int j, int k, int l )*
* {*
_ return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] ];_
* }*

* public float Get( int i, int j, int k, int l, int m )*
* {*
_ return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] + m * dimensionMultiples[4] ];_
* }*

* public float Get( int[] indices )*
* {*
* int index = 0;*
* for ( int i = 0; i < indices.Length; i++ )*
* {*
index += indices * dimensionMultiples*;*
* }*
* return array[ index ];*
* }*

* public void Set( float v, int i, int j )*
* {*
_ array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] ] = v;
* }*_

* public void Set( float v, int i, int j, int k )*
* {*
_ array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] ] = v;
* }*_

* public void Set( float v, int i, int j, int k, int l )*
* {*
_ array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] ] = v;
* }*_

* public void Set( float v, int i, int j, int k, int l, int m )*
* {*
_ array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] + m * dimensionMultiples[4] ] = v;
* }*_

* public void Set( float v, int[] indices )*
* {*
* int index = 0;*
* for ( int i = 0; i < indices.Length; i++ )*
* {*
index += indices * dimensionMultiples*;*
* }*
* array[ index ] = v;*
* }*

* public int GetTotalLength()*
* {*
* return array.Length;*
* }*

* public int GetDimensionCount()*
* {*
* return dimensionMultiples.Length;*
* }*

* public int GetLength( int dimension )*
* {*
* return dimensionLengths[ dimension ];*
* }*
}