Unit reads external resources

1. Dynamically read files in the editor

In the actual game development, a considerable part of the static data can be placed on the client, so the demand for dynamic reading of these files naturally arises, such as csv, xml, etc. no matter what system is used for Unity development, if you want to achieve this effect, you obviously need to implement the basic functions in the editor first, and then go to various mobile platforms to test, so the first step to read external files is, Obviously, we need to implement this common pumpkin (Editor) on the computer side
The following is an example of reading xml - reading xml files and dynamically generating a class

give an example:

<?xml version="1.0" encoding="UTF-8"?>
<test>
  <name>chenjd</name>
  <blog>http://www.cnblogs.com/murongxiaopifu</blog>
  <organization>Fanyoy</organization>
  <age>25</age>
 </test>

This is the xml file we want to load
Put this file anywhere and read it by specifying the address

public class Test2 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        XElement result = LoadXML("Assets/xml-to-egg/Test.xml");
        Debug.Log(result.ToString());
    }

    private XElement LoadXML(string v)
    {
        XElement xml = XElement.Load(v);
        return xml;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

The reading effect is as follows:

Issues to be considered
1. Nonstandard address. If the address parameters are written as above, the cross platform must be wrong. The problem is that the target folder cannot be found in Unity on the mobile end
2. It uses the traditional method of reading resources on the computer. Instead of using the method provided by unity, it may find the file, but it may not be able to read the contents of the file correctly
The above are the possible reasons for the failure of reading resources in Dongguan. Next, let's take a look at the first problem (about the resource path on different mobile platforms)

2. Resource path problem of mobile platform

We know that if you want to read a file, you naturally need to find it. The following table shows the resource paths in unity and their descriptions

Path in unity3D introduce
Application.dataPath Used to return the file and path of the data file of the program
Application.streamingAssetsPath The cache directory used to return stream data. The return path is a relative path
Application.persistentDataPath It is used to return the path of a persistent data storage directory, where some persistent data files can be stored
Application.temporaryCachePath Returns the cache directory of temporary data
Android platform Specific path
Application.dataPath /data/app/xxx.xxx.apk
Application.streamingAssetsPath jar:file:///data/app/xxx.xxx.apk/!assets
Application.persistentDataPath /data/data/xxx.xxx.xxx/files
Application.temporaryCachePath /data/data/xxx.xxx.xxx/cache
IOS platform Specific path
Application.dataPath Application/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data
Application.streamingAssetsPath xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw
Application.persistentDataPath xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents
Application.temporaryCachePath xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches

Isn't it strange that we know that the default resource file storage path in the Unity editor is Assets. How can there be so many paths?
To solve this problem, let's take a look at the types of resource processing in unit
The resource locations involved in the unit include the following categories ---------- Resources, StreamingAssets, AssetBundle and PersistentDataPath
Resources: it appears as the reserved folder of unity, that is, if the name of the folder you create is the same as this name, the content in it will be unconditionally packaged into the release package during packaging. Its characteristics are as follows:
1. Read only (cannot be modified)
2. The content in the folder will be packaged and integrated into In assets, it is recommended to put some Prefab
3. Main thread loading
4. Read resources load()

StreamingAssets: similar to Resources, it also appears as a reserved folder, but the difference is that the former will be compressed and encrypted, while erStreamingAssets will not. It is mainly used to put some binary files. Its characteristics are as follows
1. Read only and not writable
2. It mainly stores binary files
It can only be read with WWW class

AssetBundle: in short, it is to encapsulate Prefab or binary files into AssetBundle files (also a kind of binary). It should be noted that scripts cannot be updated on the mobile terminal
The features are as follows:
1. Binary type defined by unity
2. It's best to sub assemble prefab into AssetBundle, but what's the matter with the mobile terminal's inability to update the script? As long as the prefab is loaded with local scripts, it is OK
3. Use www to load

persistentDataPath: it looks like a path. In fact, it is readable and writable under this path
Features: the content is readable and writable, but it can only be written or read at runtime. It is impossible to store the data in this path in advance
2. There is no content restriction. You can read binary files from StreamingAsset or read files from AssetBundle to write here
3. The written documents can be viewed on the computer and can also be cleaned up

After introducing these kinds of resource processing, let's give some examples to realize the process of reading external resources

1. Use Resources to read
Create the folder Resources in the unit and put test Copy a copy of XML and read it through Respurces
The code is as follows

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using UnityEngine;

public class Test3 : MonoBehaviour
{
    private string _result;
    // Start is called before the first frame update
    void Start()
    {
        LoadXML("Test");
    }

    private void LoadXML(string v)
    {
        _result = Resources.Load(v).ToString();
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(_result);
    }

    private void OnGUI()
    {
        GUIStyle titlestyle = new GUIStyle();
        titlestyle.fontSize = 20;
        titlestyle.normal.textColor = new Color(0, 1, 1);
        GUI.Label(new Rect(400, 10, 500, 20), _result, titlestyle);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

The drawing results are as follows

2. Using StreamingAssets
Create a StreamingAssets folder and copy a copy of the xml file
Load code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test4 : MonoBehaviour
{
    string _result;
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(LoadXML());
    }
    IEnumerator LoadXML()
    {
        string sPath = Application.streamingAssetsPath + "/Test.xml";
        WWW wWW = new WWW(sPath);
        yield return wWW;
        _result = wWW.text;
    }
    private void OnGUI()
    {
        GUIStyle titlestyle = new GUIStyle();
        titlestyle.fontSize = 20;
        titlestyle.normal.textColor = new Color(0, 1, 1);
        GUI.Label(new Rect(400, 10, 500, 20), _result, titlestyle);
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

As mentioned above, www must be used for loading, otherwise an empty exception error will be reported
Operation results

Leverage AssetBundle
1. Create an empty Prefab, name the Cube, and then create a Cube and pull it to the newly created Prefab
2. Create a new script, exportassetbundles CS (the code comes from the official document) and is saved in the Asset/Editor directory

//Add menu in Unity editor  
[MenuItem("Assets/Build AssetBundle From Selection")]  
static void ExportResourceRGB2()  
{  
    // Open the save panel and get the path selected by the user  
    string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "assetbundle");  
  
    if (path.Length != 0)  
    {  
        // Select the object to save  
        Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);  
        //pack  
        BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows);  
    }  
}  

3. Select the preset Cube and run Build AssetBundle From Selection. A save box will pop up and name it Cube Unity3d (put them on Disk c for testing convenience. In the actual project, we need to put them on the web server for all clients to download updates)
4. Create a new scene Scene1 Unit, put several models on it, and then save
5. Select this scenario and click the previous exportassetbundles CS script, add the function to package the scene, run assets - > build scene, and save it as Scene1 Unity3d (here, it is also placed on the c disk for the convenience of testing)

[MenuItem("Assets/Save Scene")]  
static void ExportScene()  
{  
      // Open the save panel and get the path selected by the user  
    string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "unity3d");  
  
    if (path.Length != 0)  
    {  
        // Select the object to save  
        Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);  
        string[] scenes = {"Assets/scene1.unity"};  
        //pack  
        BuildPipeline.BuildPlayer(scenes,path,BuildTarget.StandaloneWindows,BuildOptions.BuildAdditionalStreamedScenes);  
    }  
}  

a. The save suffix of assetbundle can be assetbundle or unity3d
b. Buildabassetbundle should be packaged separately according to different platforms. The BuildTarget parameter specifies the platform. If not specified, the default weblayer is selected

Load assetbundle

using System;  
using UnityEngine;  
using System.Collections;  
  
public class Load: MonoBehaviour  
{  
    private string BundleURL = "file:///C:/cube.assetbundle";  
    private string SceneURL = "file:///C:/scene1.unity3d";  
  
    void Start()  
    {  
        //BundleURL = "file//"+Application.dataPath+"/cube.assetbundle";  
        Debug.Log(BundleURL);  
        StartCoroutine(DownloadAssetAndScene());  
    }  
  
    IEnumerator DownloadAssetAndScene()  
    {  
        //Download the assetbundle and load the Cube  
        using (WWW asset = new WWW(BundleURL))  
        {  
            yield return asset;  
            AssetBundle bundle = asset.assetBundle;  
            Instantiate(bundle.Load("Cube"));  
            bundle.Unload(false);  
            yield return new WaitForSeconds(5);  
        }  
        //Download scene, load scene  
        using (WWW scene = new WWW(SceneURL))  
        {  
            yield return scene;  
            AssetBundle bundle = scene.assetBundle;  
            Application.LoadLevel("scene1");  
        }  
         
    }  
}  

In this way, you can load external resources or resources on the server

Use the Resources class to load Resources
How can we easily find and access the resource objects we want through the Resources class? For example, use this class's FindObjectsTypeAll to obtain the resource information in the current scene
Signed as follows

public static object[] FindObjectsOfTypeAll(Type type)

It only needs to return a type parameter typr, which is used to search for resources and obtain the type matching the type, and returns an Object array. The Object types it can obtain include all resource types that Unity3d can load, such as game objects, preforms, materials, Mesh, maps, etc. it should be noted that this method consumes a lot of running cost and is not suitable for calling every frame
Example use

private void OnGUI()
    {
        GUILayout.Label("All" + Resources.FindObjectsOfTypeAll(typeof(UnityEngine.Object)));
    }

In addition to this usage, another static method is Load
The method signature is as follows

public static Object Load(string path);
public static Object Load(string path, [NotNull] Type systemTypeInstance);

If the path of the current project object is not the same as the path of the current project object, note that only the path of the current project object can be returned as the second parameter. If the path of the current project object is the same as the path of the current project object

void Start () {
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.Plane);
        Renderer rend = go.GetComponent<Renderer>();
        rend.material.mainTexture = Resources.Load("glass") as Texture;
	}

Using the constructor of WWW class to download resources
The constructor of www has several overloaded versions. Generally, the method with only one string parameter is used. The signature is as follows

public WWWW(string url)

The string parameter indicates that the WWW will download resources from the address provided by the URL. Because this is the constructor of the WWW, once the resource download is completed, the downloaded resource object can be accessed through the class object. In addition to creating a new www class object, this method will also create and send a GET request, and then a data stream will be created and downloaded. At this time, we must wait for the completion of the whole download process, Then you can access the downloaded resource object through the WWW class object. Because the data flow can be conveniently suspended through the yeild keyword, it is very convenient. An example is as follows

public string url = "http://image.earthcam.com/ec_metros/ourcame/fridays.jpg";
	IEnumerator Start () {
        WWW www = new WWW(url);
        yield return www;
        Image s = this.GetComponent<Image>();
        Sprite sprite = Sprite.Create(www.texture,new Rect(0,0,www.texture.width,www.texture.height),new Vector2(0,0));
        s.overrideSprite= sprite ;
        Debug.Log("sucess");
	}

This paper introduces several operations of actually reading external resources in the process of unit development. Of course, there are many other ways.

Tags: Unity

Posted by nbalog on Wed, 25 May 2022 21:11:22 +0300