Originally published at: http://www.sitepoint.com/web-apis-and-iot-in-unity/
For me, the Internet of Things is most exciting when you look at just how far reaching the capabilities are. There is so much technology out there, with new devices emerging all the time, that can benefit from connectivity to the web. Over the next few months at SitePoint, I’ll be looking at the various possibilities that the Internet of Things can bring, reviewing a different platform or device in each article. In the previous article, we looked at how to pull in data from the Jawbone Up.
This time around, we’ll be bringing IoT data into Unity, a widely used games engine that is used to build everything from iOS and Android games to console and Facebook games. It is also emerging as an engine that’ll be used on the Oculus Rift, Gear VR and more, so it’s a fun one to try pairing with the IoT. Imagine a game that adapts to real world conditions like weather and light, if it suddenly goes dark in your room, the game world suddenly goes dark too!
To start with, we’re going to connect up a Unity scene to a weather API, allowing us to use real-world weather data in our virtual world. Then we’ll connect up a Spark Core to use its light sensor data.
In this article, we’re assuming that you know all about setting up a scene in Unity, with a skybox, terrain and lighting. We’ll be building from a scene that has all of this ready and set up. My code samples will be in C# but it is possible to do the same sorts of things in UnityScript if you’d prefer.
Download the Demo Code
Working demo code for those who’d like to see this in action is available here.
Connecting Up to the Weather
Our first example of bringing in real world data will be setting up a skybox to change texture depending on the weather. Our sky is going to change to reflect the weather in our real world city! We’ll use OpenWeatherMap to pull in the latest weather data into Unity.
In order to control our scene’s Skybox, we’ll attach a script called IoTSkybox
to an Empty Game Object called “SkyboxController”:
Our Skybox Code
In IoTSkybox.c
, we include the following code:
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()); } }
The JSONObject Class
One of the first things we’ll need to do for this code to work, is to add the JSONObject class. To do this, go to the Asset Store in Unity, search for “JSON Object” and you’ll see it available to import into your project:
Install it into your project and you’ll have access to the `JSONObject` class in your code. We should have everything we need for the code above to work, so it’s about time we cover what’s actually happening in that code.
The code explained
We register two public variables for us to define – clearSky
and cloudySky
.
public Material clearSky; public Material cloudySky;
These are the two skybox materials we’ll be able to switch between depending on our weather data. We can then set which materials we want to use in the settings for our SkyboxController:
Next we’ll be using a Coroutine and IEnumerator. To start with, jump down to the Start()
function:
void Start () { StartCoroutine (AdjustSkyToWeather()); }
Here we are starting our Coroutine. A Coroutine allows us to pause the function’s execution whenever we need to. We do that using a yield
statement inside the Coroutine. We’ll be using it to wait for our web API response and to wait before regularly repeating our web call.
We define the IEnumerator function further up in our code, it starts with this line:
IEnumerator AdjustSkyToWeather()
Within it, we surround its contents with a while (true)
statement, this will allow it to loop when the very last yield
is returned.
The main bit where we’re performing our web call is the following group of code:
string weatherUrl = "http://api.openweathermap.org/data/2.5/weather?zip=2000,au"; WWW weatherWWW = new WWW (weatherUrl); yield return weatherWWW;
That makes a web call to http://api.openweathermap.org/data/2.5/weather?zip=2000,au
, which you can adjust to include whichever postcode and country code you’d like (I’ve got 2000
for Sydney and au
for Australia).
We then set up a WWW
object with that URL and then set up a yield
which will return true once it has retrieved the contents at that address. Until then, it pauses this function.
This call returns JSON like so:
{ "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 }
That function returns a JSON string which we can read using weatherWWW.text
. We create a JSONObject
(remember the class we installed from the Unity Store?) from this text:
JSONObject tempData = new JSONObject (weatherWWW.text);
We then create one more JSONObject
to store the JSON data that is nested within the weather
key value. It returns an array, so we focus on the first element in the array and grab the string value of main
, storing it in WeatherType
:
JSONObject weatherDetails = tempData["weather"]; string WeatherType = weatherDetails[0]["main"].str;
Then, if the weather type it returns is “Clear”, we set the skybox to use our clearSky
material. If it is “Clouds” or “Rain”, then we use the cloudySky
material:
if (WeatherType == "Clear") { RenderSettings.skybox = clearSky; } else if (WeatherType == "Clouds" || WeatherType == "Rain") { RenderSettings.skybox = cloudySky; }
Finally, we set our final yield
to wait for 60 seconds, then return true. This will repeat our while
statement every 60 seconds, checking the weather every minute.
yield return new WaitForSeconds(60);
Our results
If we run our Unity scene whilst there are clear skies in the town we’ve chosen (in my case Sydney), it looks like so:
If it is rainy or cloudy, it looks like so: