Tuesday, July 19, 2011

Unity3 and web services

Introduction
We wanted to be able to stream data into our Unity3D environment from our joomla website. I have rewritten the component from the ground-up twice but I thought I would share my design methodology (and technology) with the masses.

The humble backbone through all the iterations has been the WWW class (docs). By sending GET (and later POST) requests to the server, I would serve back information about articles, resources, images and other content. To being with, I wrote a couple extra methods in our joomla server's resource controller that send back the number of resources, the path to files. But this was all plain unformatted text, and one response per attribute - it was tiresome to use. I then moved to creating an XML structured response, and unity would then parse the XML. But because the XML readers required additional code, and I was trying to keep the codebase as lean as possible, I moved to JSON. A nice JSON reader comes prepackaged with the unity3 API for smartfox server - and I had already built a JSON web services API for a google web toolkit client... so the match was made in heaven - I was going to build a JSON web services api.

The result:
Picture! Images dynamically downloaded into Unity based on web services


So what follows is a big chunk of semi-source code of how that is integrated to Unity. The basic flow is:


  • a UnityGameObject starts a coroutene asking for web data
  • Generate request inside unity, and send using WWW class
  • Server receives call, retrieves data from database, creates an encode a PHP object to JSON, sends this as response
  • Unity receives object, decodes using libJSON, calls back original UnityGameObject with data



Example client functions for send request and decode response (C#):


    //Download and add image to billboard
    public static IEnumerator DownloadResource(int id, IHasResourceResult attachpoint)
    {


        WWW request = new WWW(baseURL() + hubCommandBaseUrl + "&task=jsonlist&jtask=detail&rid=" + id.ToString() + hubNoHtmlUrl);
//formats a url that look similar to:
//http://myserver/option=com_resource&task=jsonlist&jtask=detail&rid=1000&no_html=1
            yield return request;


            
            //attach resource callback
            //This is inside a loader help class, and called via a coroutene with a self reference passed as a callback
            //we need to callback the caller and give them the resource          attachpoint.AttachResource(loadFromJSON(request.text.Replace("[","").Replace("]","")));




            yield return null;
       
    }


//decode the JSON data

    private static ResourceResult loadFromJSON(String JSON)
    {
        LitJson.JsonData jResponse = LitJson.JsonMapper.ToObject(JSON);
        ResourceResult result = new ResourceResult();


        result.introtext = Escape((string)jResponse["introtext"]);
        result.id = (int) jResponse["id"];
        result.title = Escape((string)jResponse["title"]);
        result.thumbnail = Escape((string)jResponse["image"]);
        result.document = Escape((string)jResponse["document"]);


        return result;
    }



Example server functions to send response (PHP):

   //encode objects into JSON
function JSONObj($object)
{
$json =  json_encode($object);
$json = preg_replace( "/\"(\d+)\"/", '$1', $json );

return $json;
}
function startList()
{
echo '[ ';
}
function endList()
{
echo ' ]';
}

//called by controller to switch off to correct task
function JSONResponse($task)
{
switch ($task)
{
//switch off to task function
                        default:
                          $this->exampletaskfunction()
}
}

function exampletaskfunction()
{
$database =& JFactory::getDBO();
$id = JRequest::getInt( 'rid', 0 );
$resource = GET_RESOURCE_DATA_FROM_DATABASE()

                                       //make an object to convert into JSON
$obj = array();
$obj['introtext'] = htmlentities($resource->introtext);
$obj['image'] = '';
$obj['title'] = $resource->title;
$obj['id'] = $resource->id;
$obj['fulltext'] = $resource->fulltext? htmlentities($resource->fulltext): '' ;
$obj['document'] = '';


$this->startList();
echo $this->JSONObj($obj)."\n";
$this->endList();


}


Example JSON response data from server:

[ {"introtext":" \n\n\t The primary purpose of this paper is to present the instrumentation plan of a fullâ","image":"2011\/07\/03081\/.thumb\/.thumbfile.1.jpg","title":"SEISMIC RESPONSE OF STRUCTURAL PILE\u2010WHARF DECK CONNECTIONS FOR PORT STRUCTURES ","id":3080,"fulltext":"\t<p>\n\t <\/p>\n<p>\n\t The primary purpose of this paper is to present the instrumentation plan of a fullâ","document":"MjAxMS8wNy8wMzA4MS8ud2Vidmlldy8ud2Vidmlldy5zd2Y="}
 ]

Conclusion
So that shows a basic concept of how to program a web service architecture into unity.

3 comments: