无法从JSON文件动态加载精灵

时间:2019-02-17 23:01:24

标签: unity3d

我正在构建的Unity项目针对iOS,Android和Windows X64。

问题
在我的一个场景中,我使用JSON文件在运行时动态加载位于Resources文件夹中的某些精灵。我目前遇到的问题是:当我在Unity编辑器中运行游戏时,它的行为符合预期(精灵会动态加载并显示在场景中)。但是,当我在三个平台中的任何一个上(在实际硬件上)运行精灵时,它们不会在场景中加载/显示。不过会加载静态精灵。

设置
该场景是一种级别选择屏幕。对于每个级别,都会显示一个精灵。精灵和显示的精灵数量基于场景开始时读取的JSON文件。这是一个截图,可以给您更好的印象:

enter image description here

在游戏对象的Start回调之一中,我运行代码以读取JSON数据:

var sceneSelectionInfoList = JsonHelper.GetSceneSelectionInfoForLanguage(GameLanguage.German);

到目前为止的JSONHelper类(将JSON.NET用于Asset Store中的Unity资产):

using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static class JsonHelper
{
    private const string SceneDataIndexFilename = "Assets/Resources/SceneData/SceneDataIndex.json";

    // Start is called before the first frame update
    public static List<SceneSelectionInfo> GetSceneSelectionInfoForLanguage(GameLanguage language)
    {
        var sceneSelectionInfoList = new List<SceneSelectionInfo>();

        // Open scene selection index
        var sceneDataIndexEntries = GetSceneDataIndexEntries(SceneDataIndexFilename);

        foreach (var sceneDataIndexEntry in sceneDataIndexEntries)
        {
            Logger.LogInfo(sceneDataIndexEntry.Filename);
            using (var streamReader = new StreamReader(sceneDataIndexEntry.Filename))
            {
                var jsonData = streamReader.ReadToEnd();
                var jObject = JObject.Parse(jsonData);
                var id = jObject.SelectToken("id").ToString();
                var basePath = jObject.SelectToken("basePath").ToString();
                var sceneSelectionImage = basePath + jObject.SelectToken("dragAndDrop.sceneSelectionImage");
                string title = null;

                switch (language)
                {
                    case GameLanguage.English:
                        title = jObject.SelectToken("titleEN").ToString();
                        break;
                    case GameLanguage.French:
                        title = jObject.SelectToken("titleFR").ToString();
                        break;
                    case GameLanguage.SwissGerman:
                        title = jObject.SelectToken("titleSG").ToString();
                        break;
                    case GameLanguage.Spanish:
                        title = jObject.SelectToken("titleES").ToString();
                        break;
                    case GameLanguage.German:
                        title = jObject.SelectToken("titleDE").ToString();
                        break;
                    case GameLanguage.Italian:
                        title = jObject.SelectToken("titleIT").ToString();
                        break;
                }

                var sceneSelectionInfo = new SceneSelectionInfo();
                sceneSelectionInfo.SceneId = id;
                sceneSelectionInfo.SceneSelectionImage = sceneSelectionImage;
                sceneSelectionInfo.Title = title;
                sceneSelectionInfoList.Add(sceneSelectionInfo);
            }
        }

        return sceneSelectionInfoList;
    }

    private static List<SceneDataIndexEntry> GetSceneDataIndexEntries(string sceneDataIndexFilename)
    {
        using (var reader = new StreamReader(sceneDataIndexFilename))
        {
            var jsonData = reader.ReadToEnd();
            Logger.LogInfo(jsonData);
            return JsonConvert.DeserializeObject<List<SceneDataIndexEntry>>(jsonData);
        }
    }
}

仅出于完整性考虑:SceneSelectionInfo类只是一个数据容器(DTO),其中包含一些要传递的值:

public class SceneSelectionInfo
{
    public string SceneId;
    public string SceneSelectionImage;
    public string Title;
}

以下是相对于Unity项目文件夹的JSON文件和精灵的路径:

Sprite路径:
Assets/Resources/SceneData/AfternoonAtTheBeach/DragAndDrop/SceneSelection.png

JSON文件路径:
Assets/Resources/SceneData/AfternoonAtTheBeach/SceneData.json

以下是从JSON文件中摘录的内容(basePathsceneSelectionImage共同构成了要加载的精灵的路径):

{
  "id": "AfternoonAtTheBeach",
  "basePath": "SceneData/AfternoonAtTheBeach/",
  "titleEN": "Afternoon at the beach",
  "titleFR": "Après-midi sur la plage",
  "titleSG": "Namitag am Strand",
  "titleES": "Tarde en la playa",
  "titleDE": "Nachmittag am Strand",
  "titleIT": "Pomeriggio in spiaggia",
  "dragAndDrop": {
    "sceneSelectionImage": "DragAndDrop/SceneSelection",
    "levels": [
      {
        "backgroundImage": "DragAndDrop/Graphics/Level1/Background",
        "items": [
          {
            "image": "DragAndDrop/Graphics/Level1/Ball",
            "dropPosX": -623,

加载精灵的代码(从JSON文件读取路径之后):

  var sprite = Resources.Load<Sprite>(sceneSelectionInfo.SceneSelectionImage);
  swiperItem.GetComponent<SpriteRenderer>().sprite = sprite;

我到目前为止检查的内容

  • 我使用从Assets / Resources目录开始的相对路径引用Sprite,没有文件扩展名(请参见上面的Sprite路径示例)。
  • 我已禁用Unity Cloud Build中的库缓存,以避免旧的构建工件出现问题(因此,每次构建时,我都会进行正确,干净的构建)
  • 我可以在本地构建所有三个平台(Unity称其为“构建成功”)
  • 我正在使用LoadSceneMode.Single(默认)
  • 我在本地和Unity Cloud Build:2018.3.0f2中使用相同的Unity版本

感谢任何提示!

1 个答案:

答案 0 :(得分:1)

这里的两个主要问题是:1)JSON文件的加载方式; 2)管理器对象销毁自身的方式。


对于JSON文件:

您正在使用StreamReader,它是一种C#工具,用于从文件系统读取。这并不是Unity的“资源”系统的工作方式。由于它存在于“资产”目录下,因此您的编辑器可以找到它。这是它运行所在的文件系统中的 where 。进行设备构建时,“资源”目录下的所有内容都会打包到构建中,并且必须必须通过Resources API进行访问。

您在这里有两个选择:您可以将StreamReader的用法替换为Resources.Load<TextAsset>的调用,并确保使用“ SceneData / SceneDataIndex ”(注意:没有文件扩展名),而不是带有“ 资产/资源”的版本。您还可以选择将JSON资产放置在名为“ Assets / StreamingAssets ”的折叠后,然后使用Application.streamingAssetsPathStreamReader加载到该资产。流媒体资源路径允许将其与常规C#文件加载约定一起加载,因为它将被放置在可读的文件系统路径中。

一些注意事项:无论平台如何,Resources API调用必须都使用正斜杠(/)。使用StreamReader之类的基于文件系统的加载程序时,应使用Path.CombinePath.PathSeparator来确保斜杠正确。


现在是场景A的第二次加载。在不知道游戏对象层次结构的确切布局的情况下,很难确定问题出在哪里,但我的假设是您的GameManager脚本位于重要的{ {1}},而您正在使用以下方法销毁它:

GameObject

这将破坏游戏对象,该对象上的所有脚本,及其所有子对象以及它们上的所有脚本。如果将其更改为else if (Instance != this) { Destroy(gameObject); } ,它只会从对象中删除脚本,并保持游戏对象层次结构不变。