Where do input files go so built games can find them?

Another update:

I have solved the issue that came up about the build not being able to copy the / directory to the projectname_Data directory basically by recreating the project from scratch in a new directory. Apparently some kind of windows permissions stray lock file related problem. Only another good reason to port Unity to Linux…;^) But, I am at a complete loss over figuring out why the UI play mode will use my XML file but the built game executable does not.

I moved the XML file I want to read into a Resources directory in Assets and updated the path for my file read as indicated in my edits to my old message below:

I have code that reads an XML file just how I want it to when I run my project from the Unity UI. But, when I build the game, it seems that the program cannot read the file.
I use the following line to read the file from a subroutine:

	return System.IO.File.ReadAllText(   Application.dataPath
		                               + "/Resources/" + filename);

Where the Resources folder is in Assets. That works fine from the UI. And, the build
generated the projectname_Data folder and put a Resources folder inside it. I copied the XML file into that directory and ran the executable. The executable produces my terrain and surfaces and properly applies colormaps, etc. in the game window. But, it does not read in and apply the XML data as it does in the UI. The log file implies the file is being seen:

Initializing input.
Input initialized.

The above path is correct. But, shortly after that there are somewhat hard to interpret errors:

at (wrapper managed-to-native) UnityEngine.Material:Internal_CreateWithShader (UnityEngine.Material,UnityEngine.Shader)

at UnityEngine.Material…ctor (UnityEngine.Shader shader) [0x00000] in :0

at turtleTrackRender.Start () [0x00000] in :0

(Filename: Line: -1)

Where the Filename: Line: -1) errors continue on for many pages.

If anyone knows what might be going on or can at least give a clue where to look
I’d appreciate it! Since it possibly could be something to do with the XML parsing and not reading the file, for completeness, I’ve included the subroutine I use to read the XML file at the bottom of this message. It uses the standard System.Xml functions.

Here is the top of the log file, up to the start of the errors:

Initialize engine version: 4.2.1f4 (4d30acc925c2)
GfxDevice: creating device client; threaded=1
Version: Direct3D 9.0c [nvd3dum.dll]
Renderer: NVIDIA GeForce GT 430
Vendor: NVIDIA
VRAM: 1999 MB (via DXGI)
Caps: Shader=30 DepthRT=1 NativeDepth=1 NativeShadow=1 DF16=0 DF24=0 INTZ=1 RAWZ=0 NULL=1 RESZ=0 SlowINTZ=0
Begin MonoManager ReloadAssembly
Platform assembly: C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\UnityEngine.dll (this message is harmless)
Loading C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\UnityEngine.dll into Unity Child Domain
Platform assembly: C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\Assembly-CSharp-firstpass.dll (this message is harmless)
Loading C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\Assembly-CSharp-firstpass.dll into Unity Child Domain
Platform assembly: C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\Assembly-CSharp.dll (this message is harmless)
Loading C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\Assembly-CSharp.dll into Unity Child Domain
Platform assembly: C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\Assembly-UnityScript-firstpass.dll (this message is harmless)
Loading C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\Assembly-UnityScript-firstpass.dll into Unity Child Domain

  • Completed reload, in 0.057 seconds
    desktop: 1920x1080 60Hz; virtual: 1920x1080 at 0,0
    Initializing input.
    Input initialized.

(Filename: C:/BuildAgent/work/cac08d8a5e25d4cb/Runtime/ExportGenerated/StandalonePlayer/UnityEngineDebug.cpp Line: 54)

Platform assembly: C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\System.dll (this message is harmless)
Platform assembly: C:\Users\alans\Desktop\TurtleTest\CostaRicaTurtles_Data\Managed\System.Xml.dll (this message is harmless)
at (wrapper managed-to-native) UnityEngine.Material:Internal_CreateWithShader (UnityEngine.Material,UnityEngine.Shader)

at UnityEngine.Material…ctor (UnityEngine.Shader shader) [0x00000] in :0

at turtleTrackRender.Start () [0x00000] in :0

(Filename: Line: -1)

NullReferenceException: Object reference not set to an instance of an object
at turtleTrackRender.Update () [0x00000] in :0

(Filename: Line: -1)

//------------------------------ parseXMLData ---------------------------------

private static void parseXMLData (string data, out string datetime,
out string ptts, out int pttcnt,
out string[,] records,
out int[,] reccnts, out int ctdcnt,
out int maxrecs, out string ctddpts,
out string[,] profiles) {

// Parse data from the file XML string returned by readXMLFile. Multiple 
 // tracks are separated by turtle nodes in the input XML file (NOTE 05,) and
// further identified by ptt attribute value in each turtle node. Data 
// record nodes for each track are contained within a turtle node segment,
// and have type "pos" or "ctd" for track postion or ctd cast, respectively.
// Data records for all tracks and ctd casts are returned in one 3D array,
// indexed by number of ptt values, maximum number of records for one ptt
// value across of all types of records, and number of record types along
// with a 2D array of the number of records for each track set and ctd cast
// set, plus an array of corresponding turtle ptt IDs, the number of 
// turtles (ptts), the number of ctd casts, the maximum of the number of
// ctd casts or records for all tracks. The general use ctd cast depths
// string and a 2D string array of ctd cast profiles are also returned. An
// optional ctddepths attribute is allowed in record tags to provide 
// individual depth profiles for ctd casts when necessary. The single cast
// depths string provided in the ctddepths node is used to generate the
// depth profile for ctd casts when a profile is not specified. A track is
// an entity that may contain both ctd casts and separate sequential
// position data.

	int pos = 0; int ctd = 1; // array index abstractions
	// Split input XML string records on '

’ into an array:

	string[] rawLines = data.Split('

int rawLength = rawLines.Length;

	// Declare new array same length to ensure long enough (and deal with
	// C# wacky ways regarding initializations and variable scope):

	string[] xmlLines = new string[rawLength]; 
	int xmlLength = 0;
	Regex hdorcm = new Regex("^<[?!]"); // check for header or comment line 
	                                    // (assumes always in first column)
	int i = 0;
	while (i < rawLength) {                   
      Match m = hdorcm.Match(rawLines*);* 
  •     if (!(m.Success)) {*

_ xmlLines[xmlLength++] = rawLines*; // load only XML data lines*_
* } *
* i++; *
* } *

* // Paste XML data lines back together without ’
’ trimming each*

* // element’s leading and trailing whitespace to create a well formed*
* // XML doc string:*

* string xml = “”;*

* for (i = 0; i < xmlLength; i++) {*
_ xml = xml + xmlLines*.Trim();
} *_

// Use System.Xml stuff to parse the XML doc string:

* XmlDocument doc = new XmlDocument();*
* doc.LoadXml(xml);*

* // Get epoch reference datetime string from root node day0 attribute:*

XmlNode idN = doc.SelectSingleNode(“/CostaRicaData”);
* XmlAttributeCollection dt = idN.Attributes;*
* datetime = dt[0].Value;*
* // Get the ctd cast depth list if it exists, assume only one,*
* // get only the first one if more than one:*

* XmlNode ctdnode = doc.SelectSingleNode(“/CostaRicaData/ctddepths”);*
* if (ctdnode != null) {*
* ctddpts = ctdnode.InnerText;*
* } else {*
* ctddpts = “none”;*
* }*
* // Get the list of all turtle nodes:*
* XmlNodeList pttNL = doc.GetElementsByTagName(“turtle”);*

* // Extract ptt attribute values from turtle nodes:*

// NOTE: Apparently all out parameters must be initialized at the top
* // subroutine scope level, not in the scope of lower clause*
* // levels. (Why? It certainly knows about them from the out!)*
* // Once they are initalized, even to zero size in the case of*
* // arrays, then they can updated to correct values in lower*
* // parameter scope levels. So, do all remaining out parameter*
* // initializations here before continuing into if() statement*
* // below to determine their final values.*

* pttcnt = pttNL.Count;*
* ptts = new string[pttcnt];*

* records = new string[0,0,0]; // give sizes 0 here to make out parameter*
* profiles = new string[0,0]; // happy, assign correct sizes in if below*
* reccnts = new int[pttcnt,2]; // only need number of records per ptt per*
* // record type so can initalize here*

* ctdcnt = 0; // assign values 0 here and give correct*
* maxrecs = 0; // values when determined below*
* XmlNodeList ctdlist;*
* XmlNodeList poslist;*

* if (pttcnt > 0) { *

* int posmax = 0; int ctdmax = 0; // local maximum trackers*

* for (i=0; i<pttcnt; i++) {*
* // NOTE: Because of how out parameter scoping works it would be*
* // annoying to figure out an efficient way to size the out*
* // arrays, while keeping track of missing tag attributes,*
* // before running through this entire loop. So, error*
* // checking here doesn’t help protect against errors, just*
* // warns about them, then you die. Maybe someday when I have*
* // more time I’ll worry about it. For now, here’s a pro tip:*
* // missing an instance of ptt attribute in a turtle XML tag*
* // would be a very bad thing!*

* // Get attribute list from current ptt turtle tag node:*

_ XmlAttributeCollection atr = pttNL*.Attributes;*_

* bool gotptt = false; *
* for (int j=0; j<atr.Count; j++) { // one turtle atr but may expand*
* if (atr[j].Name == “ptt”) {*
* // Debug.Log (atr[j].Name + ": "+atr[j].Value);*
_ ptts = atr[j].Value;
gotptt = true;

* if (gotptt) {*

* // Find position records for current ptt attribute turtle node:*

* poslist = doc.SelectNodes(“/CostaRicaData/”*
_ + “turtle[@ptt='” + ptts + “']/”
* + “record[@type="pos"]”);*_

* // Track largest position records count:*

* if (poslist.Count > posmax) {*
* posmax = poslist.Count;*
* }*

* // Find ctd records for current ptt attribute turtle node:*
* ctdlist = doc.SelectNodes(“/CostaRicaData/”*
_ + “turtle[@ptt='” + ptts + “']/”
* + “record[@type="ctd"]”);*_

* // Track largest ctd records count:*

* if (ctdlist.Count > ctdmax) {*
* ctdmax = ctdlist.Count;*
* }*

* // Accumulate ctd records count:*

* ctdcnt = ctdcnt + ctdlist.Count;*

* } else {*
_ ptts = “missing”;
Debug.Log (“Instance " + i + " of turtle does not have a ptt attribute!”);_

* } // end if(gotptt)*

* } // end for (i<pttcnt)*

* // Find the larger of posmax and ctdmax, to return as maxrecs:*

* maxrecs = posmax;*
* if (ctdmax > posmax) {*
* maxrecs = ctdmax;*
* }*

* // With maxrecs initialized can now properly size output arrays:*

* records = new string[pttcnt,maxrecs,2];*
* profiles = new string[pttcnt,maxrecs];*

* // NOTE: You can’t always do something like*
* // you would expect in C#, like:*
* //*
* // XmlNodeList poslist;*
* //*
* // or:*
* //*
* // XmlNodeList posList = new XmlNodeList();*
* //*
* // above the if() statement to enable use beyond the if(), as*
* // the former does not stick for use beyond the if(), and the*
* // latter gives the "Cannot create an instance of the*
* // abstract class or interface" error. Nor can you set a*
* // parameter to an existing list and reuse it. Rather than*
* // setting up abstract classes and such right now, just*
* // regenerate XmlNodelists in a ppt loop again to use below.*

* for (i=0; i<pttcnt; i++) {*

* // Find position records for current ptt attribute turtle node:*

* poslist = doc.SelectNodes(“/CostaRicaData/”*
_ + “turtle[@ptt='” + ptts + “']/”
* + “record[@type="pos"]”);
// Save current position records count:*_

* reccnts[i,pos] = poslist.Count;*

* // Get an enumerator for the position record list:*

* IEnumerator posenum = poslist.GetEnumerator();*
* // Put position records into 3D array segementing by track and type:*

* int j = 0;*
* while (posenum.MoveNext()) {*
*records[i,j,pos] = ((XmlElement)posenum.Current).InnerText; *
* j++;*
* }*

* // Find ctd records for current ptt attribute turtle node:*
* ctdlist = doc.SelectNodes(“/CostaRicaData/”*
_ + “turtle[@ptt='” + ptts + “']/”
* + “record[@type="ctd"]”);
// Save current ctd records count:*_

* reccnts[i,ctd] = ctdlist.Count;*

* // Get an enumerator for the ctd record list:*

* IEnumerator ctdenum = ctdlist.GetEnumerator();*

* // Put ctd records into 3D array segementing by track and type:*

* j = 0;*
* while (ctdenum.MoveNext()) {*

* // Save curent ctd record data string:*

records[i,j,ctd] = ((XmlElement)ctdenum.Current).InnerText;

* // Check if current ctd record has ctddepths attribute:*

* XmlAttributeCollection atr = ((XmlNode)ctdenum.Current).Attributes;*

* bool gotdpt = false; *
* for (int k=0; k<atr.Count; k++) {*
* if (atr[k].Name == “ctddepths”) {*
* //Debug.Log (atr[k].Name + ": "+atr[k].Value);*
* profiles[i,j] = atr[k].Value; *
* gotdpt = true; *
* } *
* }*

* if (!gotdpt) { // then use main profile string for depths *
* //Debug.Log(“Using main profile string for depths”);*
* profiles[i,j] = ctddpts; *
* } *

* j++;*
* }*

* }*

* } else {*

* Debug.Log (“Tag turtle not found in document!”); *
* } // end if (pttcnt > 0)*

* }*

OK…get to answer my own question again! The problems (yes, plural) , beyond the bizzareness of windows access permissions, were indeed related to where files go. But, there was a bit more to it than that. Initially putting my XML files in my own directory and making it relative to Application.dataPath seemed like a good idea. I have lots of data and lots of scripts and keeping them separate is a plus. But, clearly Unity is happier with everything dropped in a Resources directory, so I can live with that, (even though the files don’t get carried over by the build and have to be placed in the Resources directory in the projectname_Data directory manually, after the fact). So, getting XML data into the build was solved before I realized it. There was another issue, that because of sequencing of errors in the log file at first seemed to be related to reading the XML file, but, was actually a problem with the build not being able to find the shader (Particles/Additive) I was assigning to the materials for my LineRenderers, like this:

posLineRend[i,j].material = new Material(Shader.Find("Particles/Additive"));

Although the API says that assigning a built-in shader to a material should be enough for it to carry over in the build, it is not. Apparently the built-in shaders are built-in enough for the UI to find, but, not built-in enough for the build to find…;^) I had to track down the code for the shader (Particle Add.shader) and drop that in my Resources directory as well, and then everything rendered as expected.

Unity only includes the shaders that you specify in the build. You specify which shaders to include in you project, under ProjectSettings/Graphics. In the Inspector, you adjust Size and Elements to include the shaders you want. Then the player will have the shaders. Better than manual edits after the build process no?