Unity API 返回 Vector3,但值错误

Unity API Returning Vector3s but the Values are Wrong

提问人:Mashimaro7 提问时间:5/24/2023 最后编辑:cocomacMashimaro7 更新时间:5/28/2023 访问量:44

问:

我正在使用 Replicate API 尝试在 Unity 中获取点云。我终于让 API 返回一个 JSON 文件,我很确定它返回了每个点的位置。然而,当我将其转换为 vector3 时,这些值变得异常,每个数字都类似于 6.852349e-43,甚至一些“NaN”,在 API 上它们似乎都是低于 0 的值。vector3

这是它们在 Replicate 页面上的样子,

  "coords": [
    [
      -0.039118800312280655,
      -0.25740715861320496,
      0.28050559759140015
    ],
    [
      0.13176295161247253,
      0.23972494900226593,
      -0.06013050675392151
    ],

然而,当我在 Unity 中将它们转换为 a 时,它们像这样返回,Broken Points我假设我不应该将它们转换为 ?Replicate 的页面显示“坐标”是 (X,Y,Z) 点坐标的 [N x 3] 数组“,经过广泛的谷歌搜索,我不知道 NX3 是什么意思。我试过 , , 而不是 ,但是唯一一个从 JSON 文件中正确分配的,(使用vector3vector3float[,]float[,,]float[][]vector3vector3JsonUtility.FromJson)

这是我调用 api 的脚本,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using SimpleJSON;

public class Prompt
{
    public string prompt;
}

public class PostResponse
{

    public string completed_at;
    public string created_at;
    public string error;
    public string id;
    public Prompt input;
    public string[] logs;
    public string metrics;
    public Output output;
    public string started_at;
    public string status;
    public string version;

}

public class ReplicateAPI : MonoBehaviour
{
    private string API_ENDPOINT = "https://api.replicate.com/v1/predictions";
    private string REPLICATE_API_TOKEN = "r8_DhOE6C7LuA7edPvLh7Io0ukwbmXCLEY450vc3";

    [SerializeField] PointEResponse response;

    public string prompt = "dog";

    [SerializeField] Material material;

    [SerializeField] MeshFilter meshFilter;

    private void Start()
    {
        StartCoroutine(Predict());
    }

    IEnumerator Predict()
    {
        string requestData = $@"{{
    ""version"": ""1a4da7adf0bc84cd786c1df41c02db3097d899f5c159f5fd5814a11117bdf02b"",
    ""input"": {{
        ""prompt"": ""{prompt}"",
        ""output_format"": ""json_file""
    }}
}}";

        UnityWebRequest request = UnityWebRequest.Put(API_ENDPOINT, requestData);
        request.method = "POST";
        request.SetRequestHeader("Authorization", "Token " + REPLICATE_API_TOKEN);
        request.SetRequestHeader("Content-Type", "application/json");


        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.LogError("Error " + request.responseCode + ": " + request.error);
            StartCoroutine(Predict());
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
            PostResponse response = JsonUtility.FromJson<PostResponse>(request.downloadHandler.text);

            StartCoroutine(GetResult(response.id));
        }
    }

    IEnumerator GetResult(string id)
    {
        yield return new WaitForSeconds(5);
        UnityWebRequest request = UnityWebRequest.Get(API_ENDPOINT + "/" + id);
        request.SetRequestHeader("Authorization", "Token " + REPLICATE_API_TOKEN);
        request.SetRequestHeader("Content-Type", "application/json");



        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.LogError("Error " + request.responseCode + ": " + request.error);
            yield break;
        }
        else
        {
            response = JsonUtility.FromJson<PointEResponse>(request.downloadHandler.text);



            Debug.Log(request.downloadHandler.text);
        }

        if (response.status != "succeeded") StartCoroutine(GetResult(id));
        else
        {

            if (response.output.json_file.colors != null && response.output.json_file.colors.Length > 0)
            {

                for (int i = 0; i < response.output.json_file.colors.Length; i++)
                {

                    GameObject g = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                    g.transform.localScale = Vector3.one / 100;
                    g.transform.position = new Vector3(response.output.json_file.coords[i].x, response.output.json_file.coords[i].y, response.output.json_file.coords[i].z);
                    Material m = new Material(material);
                    m.color = new Color(response.output.json_file.colors[i].x, response.output.json_file.colors[i].y, response.output.json_file.colors[i].z);
                    g.GetComponent<MeshRenderer>().material = m;
                }

            }
            else
            {
                StartCoroutine(GetResult(id));
            }
        }
    }

}

“response”类如下所示,

using System;
using UnityEngine;

[Serializable]
public class PointEResponse
{
    public string id;
    public string version;
    public Urls urls;
    public DateTime created_at;
    public DateTime started_at;
    public DateTime completed_at;
    public string source;
    public string status;
    public Prompt input;
    public Output output;
    public object error;
    public string[] logs;
    public Metrics metrics;
}

[Serializable]
public class Output
{
    public JsonFile json_file;
}

[Serializable]
public class JsonFile
{
    public Vector3[] coords;
    public Vector3[] colors;

}

public class Urls
{
    public string get;
    public string cancel;
}

public class Metrics
{
    public float predict_time;
}

其中输出包含每个坐标的坐标和颜色。我假设我不应该将这些转换为 Vector3,但我已经尝试了我所知道的所有其他方法,其中唯一一个甚至返回任何形式的。

如何解决此问题?

json unity-game-engine httpwebrequest

评论

1赞 derHugo 5/24/2023
AFAIK 的意思只是:一个普通数组,其中 -> 我应该只是一个普通数组N x 3float[N * 3]N = amount of points
1赞 Mashimaro7 5/24/2023
@derHugo 好吧,我以为它会是一个 2D 数组,但我尝试将 Vector3 数组换成 float[ , ],但它从未被分配。我尝试了 float[]、float[ , ], float[][],甚至尝试了 Matrix4x4,看看这是否能让我走到任何地方,哈哈,但只有 Vector3 得到了正确的解析......然而,这是不恰当的,因为这些值有时是 NaN
1赞 derHugo 5/24/2023
是的,对不起,没有看到您包含的 JSON 代码段.这在我看来确实很像......问题是这样的不能直接兼容内置的 JsonUtiltiy,它仅限于不支持复杂集合的 Unity 序列化程序float[][]float[][]

答:

2赞 derHugo 5/24/2023 #1

您在此处获取的 JSON 不会直接匹配。Vector3

Afaik a 不能直接 JSON(反)序列化。Vector3

"coords": [
    [
      -0.039118800312280655,
      -0.25740715861320496,
      0.28050559759140015
    ],
    [
      0.13176295161247253,
      0.23972494900226593,
      -0.06013050675392151
    ],

意味着你得到的东西看起来很像

[Serializable]
public class JsonFile
{
    public float[][] coords;

    // assuming same layout for those
    public float[][] colors;
}

我猜,如果以及如何在内存中将其布置成结构,那将是黑魔法。Vector3

但是,内置的 /Unity 的序列化程序也不会支持这种类型的复杂集合!JsonUtility

您宁愿需要第三方库,例如 通过包管理器以的形式提供Newtonsoft Json.NET

相反,你会去例如

using UnityEngine.Scripting;
using Newtonsoft.Json;

...

[Serializable]
public class JsonFile
{
    public Vector3[] coords;
    public Color[] colors;
    
    // using a JsonConstructor you can directly here ensure your given Json data format is 
    // translated in usable types and structure
    // If this attribute is present Newtonsoft will automatically not directly assign the fields but rather use this constructor 
    // The [Preserve] is to ensure this constructor is not stripped of while building since nothing else will be ever using it
    [Preserve]
    [JsonConstructor]
    public JsonFile(float[][] coords, float[][] colors)
    {
        this.coords = new Vector3[coords.GetLength(0)];
        for (var i = 0; i < this.coords.Length; i++)
        {
            this.coords[i] = new Vector3(coords[i][0], coords[i][1], coords[i][2]);
        }

        this.colors = new Color[colors.GetLength(0)];
        for(var i = 0; i < this.colors.Length; i++)
        {
            this.colors[i] = new Color(colors[i][0], colors[i][1], colors[i][2]);
        }
    }
}

然后去

var response = JsonConvert.DeserializeObject<PostResponse>(request.downloadHandler.text);

顺便说一句,因为这是一个结构,你只是简单地做Vector3

g.transform.position = response.output.json_file.coords[i];
...
m.color = nresponse.output.json_file.colors[i];

评论

0赞 Mashimaro7 5/24/2023
好吧,实际上,你给了我一个主意。我使用 SimpleJson 的 JsonNode 直接从 Json 手动分配 vector3。JsonUtility 只是无法正确反序列化它所以,我只是手动分配给它们 JSONNode node = JSON。解析(request.downloadHandler.text);for (int i = 0; i < node[“output”][“json_file”][“coords”].计数;i++) { response.output.json_file.coords[i] = node[“output”][“json_file”][“coords”][i]; response.output.json_file.colors[i] = node[“output”][“json_file”][“colors”][i];
1赞 derHugo 5/24/2023
@Mashimaro7如果你想走这条路,那当然是另一种选择。请注意,这种方式当然可以复制两次解析 JSON 的处理工作(一次使用 JsonUtility,一次使用该 SimpleJson)。如果这些数据(坐标和颜色)实际上是您真正关心的唯一信息,那么这可能是一个不错的选择。当然,仍然需要正确解析那些从 to /float[]Vector3Color
0赞 Mashimaro7 5/24/2023
感谢您的输入,如果您不告诉我 JsonUtility 无法解析 Vector3s,我永远不会尝试过哈哈