Скрипт в Unity, отвечающий на неправильные значения

#c# #android #api #unity3d #game-engine

#c# #Android #API #unity3d #игровой движок

Вопрос:

Я пытался использовать weatherbit.io API для доступа к информации AQI в моем приложении для Android. Скрипт AqiInfoScript используется для доступа к API, а Update AQI скрипт используется для вывода значения.

AqiInfoScript:

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

public class AqiInfoScript : MonoBehaviour
{
    private float timer;
    public float minutesBetweenUpdate;
    private float latitude;
    private float longitude;
    private bool locationInitialized;
    public static string cityName;
    public static double currentAqi;

    private readonly string baseWeatherbitURL = "https://api.weatherbit.io/v2.0/current/airquality?";
    private readonly string key = "*********************";

    public void Begin()
    {
        latitude = GPS.latitude;
        longitude = GPS.longitude;
        locationInitialized = true;

    }
    void Update()
    {
        if (locationInitialized)
        {
            if (timer <= 0)
            {
                StartCoroutine(GetAqi());
                timer = minutesBetweenUpdate * 60;
            }
            else
            {
                timer -= Time.deltaTime;
            }
        }
    }
    private IEnumerator GetAqi()
    {
        string weatherbitURL = baseWeatherbitURL   "lat="   latitude   "amp;lon="   longitude   "amp;key=" 
          key;
        UnityWebRequest aqiInfoRequest = UnityWebRequest.Get(weatherbitURL);

        yield return aqiInfoRequest.SendWebRequest();

        //error
        if (aqiInfoRequest.isNetworkError || aqiInfoRequest.isHttpError)
        {
            Debug.LogError(aqiInfoRequest.error);
            yield break;
        }

        JSONNode aqiInfo = JSON.Parse(aqiInfoRequest.downloadHandler.text);

        cityName = aqiInfo["city_name"];
        currentAqi = aqiInfo["data"]["aqi"];
    }
}
  

Скрипт UpdateAQI

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

public class UpdateAQI : MonoBehaviour
{
    public Text airquality;
    //public Text coordinates;

    private void Update()
    {
        airquality.text = "Current Aqi:  "   AqiInfoScript.currentAqi.ToString();
    }
}
  

Текущий вывод: Текущий AQI: 0
Желаемый результат: Текущий AQI: 129.0000

Комментарии:

1. вы уверены, что Begin вызывается? После currentAqi = aqiInfo["data"]["aqi"]; можете ли вы добавить Debug.Log($"New data available: ${currentAqi}"); ? Вы также пытались явно вызвать agiInfo["data"]["aqi"].AsDouble ? .. вы также не должны публиковать здесь реальные ключи

2. В общем, я бы не делал этого в Update , а скорее управлял событиями. Запустите следующую загрузку после завершения или сбоя. Обновляйте текстовый компонент только в том случае, если были получены новые данные

3. 1. Функция Begin уже присутствовала в GPS скрипте как aqiInfoScript.Begin(); 2. Я добавил Debug. Журнал 3. Также явно вызванный agiInfo["data"]["aqi"] , и все они представили тот же результат, что и до 4. Прошу прощения за ключ, я боролся с этим уже несколько дней, я совершенно забыл о ключе 5. Что касается вашего второго запроса, я сначала хотел убедиться, что я действительно получаю данные перед очисткой кода, но этого не происходит даже с самыми простыми программами:(

4. Но на всякий случай это означает, что это не имеет никакого отношения к вашему второму скрипту, но ошибка уже заключается в данных, которые вы получаете от API… вы на 100% уверены, что в ключах для currentAqi = aqiInfo["data"]["aqi"]; нет опечатки? Имеет ли полученный json точное совпадение имен полей? Не могли бы вы показать нам результат Debug.Log(aqiInfoRequest.downloadHandler.text); ?

5. Ниже приведена ссылка на weatherbit. домашняя страница ввода-вывода, которая содержит образец файла JSON https://www.weatherbit.io/api/airquality-current#:~:text=This Air Quality API returns,(USA and EU only) , я проверял это снова и снова. aqi Находится под data классом. Нет совпадающих имен полей.

Ответ №1:

Проблема

Как я вижу, API возвращает для вашего запроса, например для lat=42, lon=-7 следующего JSON

 {
    "data":[
               {
                   "mold_level":1,
                   "aqi":54,
                   "pm10":7.71189,
                   "co":298.738,
                   "o3":115.871,
                   "predominant_pollen_type":"Molds",
                   "so2":0.952743,
                   "pollen_level_tree":1,
                   "pollen_level_weed":1,
                   "no2":0.233282,
                   "pm25":6.7908,
                   "pollen_level_grass":1
               }
           ],
    "city_name":"Pías",
    "lon":-7,
    "timezone":"Europe/Madrid",
    "lat":42,
    "country_code":"ES",
    "state_code":"55"
}
  

Как вы можете видеть, "data" на самом деле это массив. Вы рассматриваете его как единственное значение.

За кулисами

К сожалению, используемая вами библиотека SmipleJson довольно подвержена ошибкам, потому что она не выдает никаких исключений из-за опечаток, а скорее молчаливо завершает работу и использует значение по умолчанию.

Вы можете увидеть это, например, в

 public override JSONNode this[string aKey]
{
    get
    {
        if (m_Dict.ContainsKey(aKey))
            return m_Dict[aKey];
        else
            return new JSONLazyCreator(this, aKey);
    }
    ....
}
  

и

 public static implicit operator double(JSONNode d)
{
    return (d == null) ? 0 : d.AsDouble;
}
  

Решение

Скорее это должно быть

 cityName = aqiInfo["city_name"];
currentAqi = aqiInfo["data"][0]["aqi"].AsDouble;

Debug.Log($"cityName = {cityName}ncurrentAqi = {currentAqi}");
  

В общем, я бы предложил использовать JsonUtility.FromJson и скорее реализовать требуемую структуру данных на c #, например:

 [Serializable]
public class Data   
{
    public int mold_level;
    public double aqi;
    public double pm10;
    public double co;
    public double o3;
    public string predominant_pollen_type;
    public double so2;
    public int pollen_level_tree;
    public int pollen_level_weed;
    public double no2;
    public double pm25;
    public int pollen_level_grass;
}

[Serializable]
public class Root    
{
    public List<Data> data;
    public string city_name;
    public int lon;
    public string timezone;
    public int lat;
    public string country_code;
    public string state_code;
}
  

и тогда вы бы использовали

 var aqiInfo = JsonUtility.FromJson<Root>(aqiInfoRequest.downloadHandler.text);

cityName = aqiInfo.city_name;
currentAqi = aqiInfo.data[0].aqi;

Debug.Log($"cityName = {cityName}ncurrentAqi = {currentAqi}");
  

Для меня, как сказано с фиктивными значениями lat=42, lon=-7 , оба раза он выводится, как ожидалось

 cityName = Pías
currentAqi = 54
UnityEngine.Debug:Log(Object)
<GetAqi>d__18:MoveNext() (at Assets/Example.cs:109)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
  

Комментарии:

1. Я использовал ваш точный GetAqi пример сценария в Start и просто жестко запрограммировал, lat=42; и lon=-7; если я это сделаю Debug.Log($"cityName = {cityName}ncurrentAqi = {currentAqi}"); , я получу ожидаемый результат печати.. независимо от того, какую из двух библиотек json я использую.

2. Большое вам спасибо @derHugo . Приложение по-прежнему возвращает, Current Aqi: 0 но теперь я попробую JSONUtility и реализую его таким образом. У меня есть одно сомнение, которое может быть или не быть уместным здесь, но weatherbit. io имеет что-то вроде Все параметры должны быть предоставлены API качества воздуха в качестве параметров строки запроса. может ли это быть какой-то проблемой?

3. Извините, что спрашиваю об этом, но что означает «в начале»? должен ли я поместить свой код в void start()? В этот момент я паникую!

4. Я использовал Start , поскольку это вызывается автоматически при запуске приложения… Просто сделал это для тестирования .. если вы говорите, что куда-то звоните Begin , тогда все должно быть в порядке… вы вообще получаете журнал отладки из процедуры загрузки? .. Возможно, ваша процедура никогда не запускается

5. Да, я вызываю Begin , как только получу значения lat / lon из GPS скрипта. Я протестировал и обнаружил, что он точно фиксирует значения широты / lon. Я попытаюсь поместить свой Coroutine в Begin сам и посмотреть, станет ли он лучше. Нет, я не получаю никаких файлов отладки, поскольку я вручную создаю APK и отправляю его на свой телефон. Есть предложения с вашей стороны? Может быть, я должен уничтожить все это и начать сверху.