Greetings to all unity ninjas!
This part we'll make a POST request from a unity client to the node server. We haven't made a database, yet, so I'll make an array for now.
Start with server-side. In app.js
, remember that we had a single enemy object, this part, I'll make an array and populate with some enemy.
let enemies = [
{
"id": 0,
"name": "orc",
"health": 100,
"attack": 25
},
{
"id": 1,
"name": "wolf",
"health": 110,
"attack": 25
}
];
Next, tell express to support JSON-encoded bodies.
app.use(express.json());
Next, let's make a post method to receive a request from unity client.
app.post('/enemy/create', (req, res) => {
let newEnemy = {
"id": req.body.id,
"name": req.body.name,
"health": req.body.health,
"attack": req.body.attack
};
enemies.push(newEnemy);
console.log(enemies);
res.send(enemies);
});
And I'll modify my get method to fetch my enemies
array. Now my app.js
file looks like this:
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.send('Hello Unity Developers!');
});
let enemies = [
{
"id": 0,
"name": "orc",
"health": 100,
"attack": 25
},
{
"id": 1,
"name": "wolf",
"health": 110,
"attack": 25
}
];
app.get('/enemy', (req, res) => {
res.send(enemies);
});
app.post('/enemy/create', (req, res) => {
let newEnemy = {
"id": req.body.id,
"name": req.body.name,
"health": req.body.health,
"attack": req.body.attack
};
enemies.push(newEnemy);
console.log(enemies);
res.send(enemies);
});
app.listen(3000, () => console.log('started and listening.'));
Open a terminal, start node and it's time to visit unity client.
node app.js
Let's make an api call, I'll use postman.
Don't forget to set header and body to application/json
.
Now make an enemy json string based on the enemy schema and hit to send.
Seems our enemies growing :)
Last part, we have successfully received a json object and displayed it to screen with unity ui elements. But this part our structure has changed a bit. I've added id
property to the enemy and my response now not an object but an array.
First, add id
property to Enemy
class in unity.
public class Enemy
{
public int id;
public string name;
public int health;
public int attack;
}
Yeah, that was easy! But keep in mind, JsonUtility
does not work with {get; set;}
.
Now the important part! If your task is to send a json array and parse in unity, there is no out of the box solution with JsonUtility
, unfortunately. Don't worry, after a couple googling and experimenting, I found a workaround, for a detailed explanation, take a look here and here.
For example, If your response is like this, then JsonUtility
won't work:
[
{
//item
},
{
//item
},
{
//item
}
]
Instead, we'll make it a single key and an array object as a value:
{
"result": [{//item},{//item},{//item}]
}
This way, we have a one list of items and this structure works with JsonUtility
.
To make this trick possible we'll make a static class named JsonHelper
. Let's create it in the project folder.
using UnityEngine;
using System.Collections.Generic;
public static class JsonHelper
{
public static List<T> FromJson<T>(string json)
{
Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(json);
return wrapper.result;
}
[System.Serializable]
private class Wrapper<T>
{
public List<T> result;
}
}
JsonHelper
class has a generic FromJson
method that returns a list of any class we're going to make. Here key part is the Wrapper
class that has a result list that stores the values from. In our case, when we get an array of json objects from server, we have to modify our response string like this:
{
"result": [
//enemy,
//enemy
]
}
We have discussed our schema and made a helper class to make it work, let's write some code!
First, mark the Enemy
class as Serializable
, so unity will be able to convert to json. More explanation here.
[System.Serializable]
public class Enemy
{
public int id;
public string name;
public int health;
public int attack;
}
Second, open ClientApi
script and make a post method. It will take two parameters, post url and an enemy object to feed in unity's post method.
public IEnumerator Post(string url, Enemy enemy)
{
var jsonData = JsonUtility.ToJson(enemy);
Debug.Log(jsonData);
using(UnityWebRequest www = UnityWebRequest.Post(url, jsonData))
{
}
}
We are converting an Enemy
object to a json string with JsonUtility
.
Next, configure our request's content-type
.
public IEnumerator Post(string url, Enemy enemy)
{
var jsonData = JsonUtility.ToJson(enemy);
Debug.Log(jsonData);
using(UnityWebRequest www = UnityWebRequest.Post(url, jsonData))
{
www.SetRequestHeader("content-type", "application/json");
www.uploadHandler.contentType = "application/json";
www.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
yield return www.SendWebRequest();
}
}
We have set our method to send a json object and to a url endpoint.
Remember that, we send back our enemies as a response after request. So, let's make a list from the response, then display to console. Now, time to handle response end error.
// handle the result
var result = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);
result = "{\"result\":" + result + "}";
var resultEnemyList = JsonHelper.FromJson<Enemy>(result);
foreach (var item in resultEnemyList)
{
Debug.Log(item.name);
}
This is the part that we have discussed above. As soon as I get data, I modified it to be able to work with JsonHelper
.
Then just display to console each element's name
to test.
public IEnumerator Post(string url, Enemy enemy)
{
var jsonData = JsonUtility.ToJson(enemy);
Debug.Log(jsonData);
using(UnityWebRequest www = UnityWebRequest.Post(url, jsonData))
{
www.SetRequestHeader("content-type", "application/json");
www.uploadHandler.contentType = "application/json";
www.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
yield return www.SendWebRequest();
if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
if (www.isDone)
{
// handle the result
var result = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);
result = "{\"result\":" + result + "}";
var resultEnemyList = JsonHelper.FromJson<Enemy>(result);
foreach (var item in resultEnemyList)
{
Debug.Log(item.name);
}
}
else
{
//handle the problem
Debug.Log("Error! data couldn't get.");
}
}
}
}
To test, define a string variable for post url, make an Enemy
object in Start
and switch back to unity.
public string getUrl = "localhost:3000/enemy";
public string postUrl = "localhost:3000/enemy/create";
void Start()
{
var enemy = new Enemy(){
id = 100,
name = "Balrog",
health = 1000,
attack = 2500
};
StartCoroutine(Post(postUrl, enemy));
}
Time to hit Play!
Check console.log on terminal too.
Good job, our unity ninja skills improved a bit more!!πππππππππππ
Looks like we have successfully sent data from unity client, received from the server and responded with updated data ππΎ
Grab the code here
Next part we'll post with the ui element's help and to handle data in unity, we'll use Scriptable Objects.
Until the next part, cheers!
Top comments (2)
Hey mate, excellent post! About Unity, check this wrapper for the UnityWebRequest to use Promises instead of Coroutines github.com/proyecto26/RestClient
Best!
Thanks!
That's a pretty good wrapper!