Для меня Интернет вещей является наиболее захватывающим, когда вы смотрите на то, насколько далеко достигают возможности. Существует так много технологий, с появлением новых устройств, которые могут выиграть от подключения к сети. В течение следующих нескольких месяцев в 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
В 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», и вы увидите, что он доступен для импорта в ваш проект:
Установите его в свой проект, и у вас будет доступ к классу `JSONObject` в вашем коде. У нас должно быть все, что нам нужно для работы кода, приведенного выше, поэтому пришло время рассказать о том, что на самом деле происходит в этом коде.
Код объяснил
Мы зарегистрировали две общедоступные переменные, которые мы должны определить — clearSky
и cloudySky
.
public Material clearSky; public Material cloudySky;
Это два материала скайбокса, между которыми мы сможем переключаться в зависимости от наших погодных данных. Затем мы можем установить, какие материалы мы хотим использовать в настройках нашего SkyboxController:
Далее мы будем использовать сопрограмму и 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 к нашей сцене 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 устанавливает переменную 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. Эксперимент! Веселитесь с этим!