Статьи

Веб-API и IoT в Unity

Для меня Интернет вещей является наиболее захватывающим, когда вы смотрите на то, насколько далеко достигают возможности. Существует так много технологий, с появлением новых устройств, которые могут выиграть от подключения к сети. В течение следующих нескольких месяцев в SitePoint я буду изучать различные возможности, которые может принести Интернет вещей, рассматривая различные платформы или устройства в каждой статье. В предыдущей статье мы рассмотрели, как получить данные из Jawbone Up .

На этот раз мы будем вводить данные IoT в Unity, широко используемый игровой движок, который используется для создания всего, от игр для iOS и Android до консольных игр и игр для Facebook. Кроме того, он становится двигателем, который будет использоваться в Oculus Rift, Gear VR и других, поэтому очень интересно попробовать соединиться с IoT. Представьте себе игру, которая адаптируется к реальным условиям мира, таким как погода и свет, если в вашей комнате вдруг темнеет, игровой мир тоже внезапно потемнеет!

Для начала мы собираемся подключить сцену Unity к API погоды, что позволит нам использовать реальные погодные данные в нашем виртуальном мире. Затем мы подключим Spark Core, чтобы использовать данные датчика освещенности.

В этой статье мы предполагаем, что вы знаете все о настройке сцены в Unity с использованием скайбокса, ландшафта и освещения. Мы будем строить из сцены, которая имеет все это готово и настроено. Мои примеры кода будут на C #, но вы можете делать то же самое в UnityScript, если хотите.

Загрузите демонстрационный код

Рабочий демонстрационный код для тех, кто хотел бы увидеть это в действии, доступен здесь .

Подключение к погоде

Наш первый пример ввода реальных данных будет настройка скайбокса для изменения текстуры в зависимости от погоды. Наше небо изменится, чтобы отразить погоду в нашем реальном городе мира! Мы будем использовать OpenWeatherMap для загрузки последних данных о погоде в Unity.

Чтобы управлять IoTSkybox нашей сцены, мы прикрепим скрипт с именем IoTSkybox к IoTSkybox объекту под названием «SkyboxController»:

Новый контроллер Skybox

Наш код Skybox

В IoTSkybox.c мы включаем следующий код:

 using UnityEngine; using System.Collections; public class IoTSkybox : MonoBehaviour { public Material clearSky; public Material cloudySky; IEnumerator AdjustSkyToWeather() { while (true) { string weatherUrl = "http://api.openweathermap.org/data/2.5/weather?zip=2000,au"; WWW weatherWWW = new WWW (weatherUrl); yield return weatherWWW; JSONObject tempData = new JSONObject (weatherWWW.text); JSONObject weatherDetails = tempData["weather"]; string WeatherType = weatherDetails[0]["main"].str; if (WeatherType == "Clear") { RenderSettings.skybox = clearSky; } else if (WeatherType == "Clouds" || WeatherType == "Rain") { RenderSettings.skybox = cloudySky; } yield return new WaitForSeconds(60); } } void Start () { StartCoroutine (AdjustSkyToWeather()); } } 

Класс JSONObject

Первое, что нам нужно сделать, чтобы этот код работал, — это добавить класс JSONObject . Для этого перейдите в Asset Store в Unity, найдите «JSON Object», и вы увидите, что он доступен для импорта в ваш проект:

Скачать класс объектов JSON

Установите его в свой проект, и у вас будет доступ к классу `JSONObject` в вашем коде. У нас должно быть все, что нам нужно для работы кода, приведенного выше, поэтому пришло время рассказать о том, что на самом деле происходит в этом коде.

Код объяснил

Мы зарегистрировали две общедоступные переменные, которые мы должны определить — clearSky и cloudySky .

 public Material clearSky; public Material cloudySky; 

Это два материала скайбокса, между которыми мы сможем переключаться в зависимости от наших погодных данных. Затем мы можем установить, какие материалы мы хотим использовать в настройках нашего SkyboxController:

IoT Skybox Настройка

Далее мы будем использовать сопрограмму и IEnumerator. Для начала перейдите к функции Start() :

 void Start () { StartCoroutine (AdjustSkyToWeather()); } 

Здесь мы начинаем нашу сопрограмму. Coroutine позволяет нам приостанавливать выполнение функции всякий раз, когда нам нужно. Мы делаем это с помощью оператора yield в Coroutine. Мы будем использовать его, чтобы дождаться ответа нашего веб-API и дождаться регулярного повторения нашего веб-вызова.

Мы определяем функцию IEnumerator далее в нашем коде, она начинается с этой строки:

 IEnumerator AdjustSkyToWeather() 

Внутри него мы окружаем его содержимое оператором while (true) , это позволит ему зацикливаться при возвращении самого последнего yield .

Основным моментом, когда мы выполняем наш веб-вызов, является следующая группа кода:

 string weatherUrl = "http://api.openweathermap.org/data/2.5/weather?zip=2000,au"; WWW weatherWWW = new WWW (weatherUrl); yield return weatherWWW; 

Это делает веб-вызов http://api.openweathermap.org/data/2.5/weather?zip=2000,au , который можно настроить для включения любого почтового индекса и кода страны, которые вы хотите (у меня есть 2000 для Сидней и au для Австралии).

Затем мы устанавливаем объект WWW с этим URL, а затем устанавливаем yield который будет возвращать true, как только он получит содержимое по этому адресу. До тех пор он приостанавливает эту функцию.

Этот вызов возвращает JSON примерно так:

 { "coord": { "lon": 151.2, "lat": -33.86 }, "sys": { "message": 0.0609, "country": "AU", "sunrise": 1430339337, "sunset": 1430378154 }, "weather": [ { "id": 801, "main": "Clouds", "description": "few clouds", "icon": "02d" } ], "base": "stations", "main": { "temp": 291.487, "temp_min": 291.487, "temp_max": 291.487, "pressure": 1038.32, "sea_level": 1044.67, "grnd_level": 1038.32, "humidity": 89 }, "wind": { "speed": 3.26, "deg": 133.502 }, "clouds": { "all": 24 }, "dt": 1430354026, "id": 0, "name": "Millers Point", "cod": 200 } 

Эта функция возвращает строку JSON, которую мы можем прочитать с помощью weatherWWW.text . Мы создаем JSONObject (помните класс, который мы установили из Unity Store?) Из этого текста:

 JSONObject tempData = new JSONObject (weatherWWW.text); 

Затем мы создаем еще один JSONObject для хранения данных JSON, которые вложены в значение ключа weather . Он возвращает массив, поэтому мы фокусируемся на первом элементе в массиве и берем строковое значение main , сохраняя его в WeatherType :

 JSONObject weatherDetails = tempData["weather"]; string WeatherType = weatherDetails[0]["main"].str; 

Затем, если тип погоды, который он возвращает — «Очистить», мы устанавливаем скайбокс для использования нашего материала clearSky . Если это «Облака» или «Дождь», то мы используем материал cloudySky :

 if (WeatherType == "Clear") { RenderSettings.skybox = clearSky; } else if (WeatherType == "Clouds" || WeatherType == "Rain") { RenderSettings.skybox = cloudySky; } 

Наконец, мы устанавливаем нашу конечную yield на ожидание 60 секунд, а затем возвращаем true. Это будет повторять нашу инструкцию while каждые 60 секунд, проверяя погоду каждую минуту.

 yield return new WaitForSeconds(60); 

Наши результаты

Если мы запустим нашу сцену Unity, пока в выбранном нами городе (в моем случае Сиднее) ясное небо, это будет выглядеть так:

Наше ясное небо

Если это дождливо или облачно, это выглядит так:

Наше серое небо

Ввод в датчики с Spark Core

Мы можем расширить эту идею дальше в мир Интернета вещей, внедрив микроконтроллер, такой как Spark Core или Arduino . В этой статье я буду использовать Spark Core, поскольку он довольно прост в использовании и имеет собственный облачный API, который делает это действительно простым. Если у вас другое устройство, такая же техника будет возможна, вам просто нужно будет направить его на веб-сервис, показывающий уровни этого устройства.

Я не буду вдаваться в подробности того, как использовать Spark Core и настроить его. Если вы новичок в Spark Core, у команды Spark есть краткий обзор здесь — http://docs.spark.io .

Я установил Spark Core с датчиком освещенности следующим образом:

Эскиз Spark Core

Мы можем подключить данные из этого Spark Core к нашей сцене Unity.

Соединяя наши уровни света с нашим миром Единства

Давайте выведем наши данные Spark Core на сцену. Мы собираемся контролировать уровень света от нашего искусственного солнца, используя уровень света вокруг нашего Искрового Ядра!

Мы начнем с добавления нового скрипта с именем IoTLight.c к нашему объекту Directional Light в сцене. В этом скрипте мы добавим следующее:

 using UnityEngine; using System.Collections; public class IoTLight : MonoBehaviour { public string token = "42fu524tdfey45bd2c650ce12f45dfg3453s124g"; public string deviceId = "j23tsd8j3ns893k2g3932n3u"; public Light sceneLight; IEnumerator AdjustLightWithSensor() { while (true) { string lightUrl = "https://api.spark.io/v1/devices/" + deviceId + "/light?access_token=" + token; WWW lightWWW = new WWW (lightUrl); yield return lightWWW; JSONObject lightData = new JSONObject (lightWWW.text); float light = lightData ["result"].n; sceneLight.intensity = light / 255; yield return new WaitForSeconds (10); } } void Start () { sceneLight = GetComponent<Light>(); StartCoroutine (AdjustLightWithSensor()); } } 

Код объяснил

Приведенный выше код структурирован так же, как и в нашем предыдущем примере, однако мы подключаемся к Spark Core API и вместо этого изменяем настройки нашего объекта Light.

Я объясню основные отличия. В нашей функции Start() мы указываем наш скрипт на наш Направленный свет, используя код:

 sceneLight = GetComponent<Light>(); 

Это позволит нам сослаться на солнце нашего Единства.

Затем в нашем Coroutine выполняется наш WWW вызов Spark Core API с использованием идентификатора устройства и вашего токена доступа. Вы можете получить оба из [build.spark.io] (https://build.spark.io) (тот же URL-адрес, по которому вы можете перейти, чтобы поместить код в Spark Core). Мы определяем их в начале нашего кода IoTLight .

 string lightUrl = "https://api.spark.io/v1/devices/" + deviceId + "/light?access_token=" + token; WWW lightWWW = new WWW (lightUrl); yield return lightWWW; 

Возвращаемый объект JSON будет выглядеть так:

 { "cmd": "VarReturn", "name": "light", "result": 20, "coreInfo": { "last_app": "", "last_heard": "2015-04-30T05:05:15.039Z", "connected": true, "last_handshake_at": "2015-04-30T04:06:06.250Z", "deviceID": "j23tsd8j3ns893k2g3932n3u" } } 

Мы читаем данные и превращаем их в JSONObject и получаем числовое значение для самого значения освещения (это значение будет от 0 до 255), которое является частью result нашего JSON. Затем мы устанавливаем нашу интенсивность света. Интенсивность света должна быть от 0 до 1, поэтому мы делим наше значение света на 255.

 JSONObject lightData = new JSONObject (lightWWW.text); float light = lightData ["result"].n; sceneLight.intensity = light / 255; 

Наш код Spark Core

На этот раз, поскольку мы работаем с нашим собственным аппаратным обеспечением (Spark Core), а не сторонним API, нам также потребуется настроить код для этого. Зайдите на build.spark.io и создайте новое приложение. Я назвал мой «Читатель света»:

Spark Core настроен

Код для нашего Spark Core устанавливает переменную light которая доступна для вызовов API через "light" . Мы подключаем вывод A0 на Spark Core для считывания аналоговых значений с датчика освещенности в контуре, сопоставляя их значениям от 0 до 255:

 double light = 0.0; void setup() { Spark.variable("light", &light, DOUBLE); pinMode(A0, INPUT); } void loop() { int lightReading = 0; double lightVoltage = 0.0; lightReading = analogRead(A0); lightVoltage = map(lightReading, 0, 1023, 0, 255); lightVoltage = constrain(lightVoltage, 0, 255); light = lightVoltage; } 

Наш мир освещает в действии

К этому моменту все должно выглядеть хорошо! Если у нас есть Spark Core, настроенный и работающий рядом с нашей сценой Unity, то мы должны увидеть уровень света от нашего Солнца, соответствующий уровню света вокруг Spark Core.

Когда наш датчик света видит много света, наш мир светится красиво и ярко:

Яркий свет

Когда датчик света видит меньше света, наш мир тоже становится темным:

Темный свет

Попробуйте несколько вариантов!

Вот немного бонусного кода для тех, кто очень увлечен. Если вы подключите датчик температуры к Spark Core, вы можете изменить оттенок неба, используя что-то вроде этого:

 IEnumerator AdjustSkyToTemp() { while (true) { string tempUrl = "https://api.spark.io/v1/devices/" + deviceId + "/temperature?access_token=" + token; WWW tempWWW = new WWW (tempUrl); yield return tempWWW; JSONObject tempData = new JSONObject (tempWWW.text); temp = tempData["result"].n; Color skyColor = new Color(0.0F, 0.0F, 0.0F, 0.3F); if (temp >= 25) { skyColor = new Color(0.0F, 0.3F, 0.1F, 0.3F); } else if (temp < 25) { skyColor = new Color(0.5F, 0.5F, 0.5F, 0.3F); } RenderSettings.skybox.SetColor("_Tint", skyColor); yield return new WaitForSeconds(10); } } 

Вывод

Сбор данных из веб-API и устройств IoT, таких как Spark Core, может привнести в ваши сцены Unity совершенно новый интересный элемент! Используя эту базовую концепцию, вы можете подключить целый ряд различных API и наборов данных к вашим объектам Unity. Эксперимент! Веселитесь с этим!