Unity调用Replicate API返回Vector3值异常问题求助
问题:Unity中使用JsonUtility解析Replicate API点云坐标值异常
我正在使用Replicate API在Unity中获取点云数据,API已成功返回JSON文件,其中coords字段为[N×3]格式的(X,Y,Z)点坐标数组。但使用JsonUtility.FromJson将其转换为Vector3[]后,值出现异常,变为6.852349e-43这类极小值甚至NaN,而API返回的原始值均为接近0的数值。
API返回的JSON示例:
"coords": [ [ -0.039118800312280655, -0.25740715861320496, 0.28050559759140015 ], [ 0.13176295161247253, 0.23972494900226593, -0.06013050675392151 ] ]
我尝试过使用float[,]、float[,,]、float[][]替代Vector3[],但只有Vector3[]能通过JsonUtility完成赋值,其他类型均无法正常解析。以下是我的调用脚本和响应类定义:
调用脚本
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)); } } } }
响应类定义
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; }
解决方案
问题根源在于Unity的JsonUtility不支持直接将二维数组(如[[x,y,z],[x,y,z]])解析为Vector3[]。JsonUtility期望的Vector3格式是带键的对象(如{"x":0,"y":0,"z":0}),而非数组形式,所以直接解析会导致内存值混乱,出现极小值或NaN。
可以通过以下两种方式解决:
方法1:自定义可序列化的三维数组包装类
创建一个可序列化的Float3类来替代数组中的每个三维元素,让JsonUtility能正确解析:
[Serializable] public class Float3 { public float[] values; // 转换为Vector3的方法 public Vector3 ToVector3() { if (values == null || values.Length < 3) return Vector3.zero; return new Vector3(values[0], values[1], values[2]); } } // 修改JsonFile类 [Serializable] public class JsonFile { public Float3[] coords; public Float3[] colors; }
然后在解析完成后,将Float3[]转换为Vector3[]使用:
// 在GetResult的成功分支中 List<Vector3> pointCoords = new List<Vector3>(); List<Color> pointColors = new List<Color>(); foreach (var coord in response.output.json_file.coords) { pointCoords.Add(coord.ToVector3()); } foreach (var color in response.output.json_file.colors) { var rgb = color.ToVector3(); pointColors.Add(new Color(rgb.x, rgb.y, rgb.z)); } // 之后使用pointCoords和pointColors创建点云 for (int i = 0; i < pointCoords.Count; i++) { GameObject g = GameObject.CreatePrimitive(PrimitiveType.Sphere); g.transform.localScale = Vector3.one / 100; g.transform.position = pointCoords[i]; Material m = new Material(material); m.color = pointColors[i]; g.GetComponent<MeshRenderer>().material = m; }
方法2:使用SimpleJSON解析(你已引入该库)
既然项目中已经导入了SimpleJSON,可以用它直接解析二维数组,再手动转换为Vector3:
修改GetResult中的解析逻辑:
else { // 用SimpleJSON解析原始文本 var jsonNode = JSON.Parse(request.downloadHandler.text); // 提取coords和colors数组 var coordsArray = jsonNode["output"]["json_file"]["coords"].AsArray; var colorsArray = jsonNode["output"]["json_file"]["colors"].AsArray; List<Vector3> pointCoords = new List<Vector3>(); List<Color> pointColors = new List<Color>(); foreach (var coordNode in coordsArray) { var coord = coordNode.AsArray; pointCoords.Add(new Vector3(coord[0].AsFloat, coord[1].AsFloat, coord[2].AsFloat)); } foreach (var colorNode in colorsArray) { var color = colorNode.AsArray; pointColors.Add(new Color(color[0].AsFloat, color[1].AsFloat, color[2].AsFloat)); } // 创建点云 for (int i = 0; i < pointCoords.Count; i++) { GameObject g = GameObject.CreatePrimitive(PrimitiveType.Sphere); g.transform.localScale = Vector3.one / 100; g.transform.position = pointCoords[i]; Material m = new Material(material); m.color = pointColors[i]; g.GetComponent<MeshRenderer>().material = m; } }
这种方法不需要修改原有的响应类,直接用SimpleJSON处理更灵活,适合解析数组格式的JSON数据。
内容的提问来源于stack exchange,提问作者Mashimaro7
相关产品推荐
相关产品推荐

