Saturday, July 5, 2014

Creating simple fade animation with Unity3D

I recently used fade animation or opacity animation with one of my Unity game, it was used to fade then earned points during game play. Initially I struggled a bit to achieve opacity animation due to confusion between using Animation or Animator. Look like from Unity version 4.3 we need to use Animator to animate the properties. I was trying to use Animation instead of Animator, was not able to perform opacity animation properly.
In this post I will show, how I accomplished the Fading animation. below is video of final effect and whole process to achieve animation.
First create a initial setup of scene. Following is how my scene looks like.

I wanted to add fading effect to 3D text, which signifies the points earned during game play. Now scene looks like below.

Now we need to add animator component to text.

Now, that we have added animator, we can go to Animation windows and setup our first key.

First key is from where our animation will start, we need to setup second key where animation will end. Create a second key at desired location and move text at appropriate location.

I also wanted to animate opacity with size and location. So lets add new curve for color animation.

Setup the opacity value for color curve for both first key and second key. Following is how my keys looks like.


Now if you press play button or play animation you will be able to see fade animation. Thanks for reading my blog.

Creating global variable in Unity3D

Sometime you want to create Global object that you can access from whole project. Recently while working on my unity game, I also needed the same.

While method is quite simple to achieve the same, but its quite confusing if you are just started with Unity3D just like me. Finally I now understand the process, and in this post I am describing how we can create global variable with Unity3D.

In Unity we work with Scene and when scene is unloaded all its objects are unloaded as well. To make global variable, we need to create a game object in first scene and attach a script to it which prevent it from unloading when scene is unloaded. Now we can access this game object from newly loaded scene and other scenes as well.

Let's start with simple project and create a scene, I called this scene as first scene.

Now we need to create a empty object which we treat as a global gameobject. This object will not be destroyed when first scene is unloaded.


To preserve this gameobject when First scene is unloading, we need to attach a script, I named it MakeGlobal and added following code to it.


using UnityEngine;
using System.Collections;

public class MakeGlobal : MonoBehaviour {
 void Awake () {
  DontDestroyOnLoad (transform.gameObject);
 }
}
By using DontDestroyOnLoad, we ask Unity engine not to destroy this gameobject when its scene is getting unloaded, now we can assign a new script to this global gameobject, which holds global variables.


This script is simple, it just holds a variable which can be accessed from any scene by any game object.

using UnityEngine;
using System.Collections;

public class GlobalVariables : MonoBehaviour {
 public string globalVar;
}

Now we have a GameObject which we can treat as a Global Object and it has a script which holds global variables. But till now we have not used it. To use it we can assign a script to Background element and that assign some value to globalVar variable.



using UnityEngine;
using System.Collections;

public class background : MonoBehaviour {
 // Use this for initialization
 void Start () {
  GameObject globalGO = GameObject.Find("global_gameObject");
  GlobalVariables globalVars  = globalGO.GetComponent();
  globalVars.globalVar = "String from 1st Scene";
 }
}

Now our global variable has value which is assigned by script attached to background object. To see its value on screen I created a 3D Text Object and assigned a simple script which update text object's text to global variable.

using UnityEngine;
using System.Collections;

public class TextUpdate : MonoBehaviour {
 // Update is called once per frame
 void Update () {
  GameObject globalGO = GameObject.Find("global_gameObject");
  GlobalVariables globalVars  = globalGO.GetComponent();
  gameObject.GetComponent ().text = globalVars.globalVar;
 }
}

Now if you run first scene, you can see text is displaying global variables value.



To test if global variable really retaining its value, we should create another scene and destroy first scene. So I created a another scene, which has 3D text object which displays global variables value, using the same TextUpdate script. You will can see it still preservs value from first scene and we can use global variable in second scene as well.


That's all, Thanks for reading my blog.

Saturday, June 28, 2014

Resolving Folder reading issue from SD Card in BB10

There was a tricky bug in Audiobook Reader BB10 version for quite a some time which I was trying to resolve. Issue was that, for some folder in SD card, I was not able to browse it using QDir Qt API or Posix API. Though API was showing the folder, but when I try to find out folder's content, it reports folder as empty.

This was causing serious usability issue with Audiobook Reader application, as user were not able to add book from SD card. I debugged it long and tried to use various approach to resolve the issue but could not find any satisfactory solution.

I read it somewhere it causes issue if SD card was formatted with PC software and issue is resolved if you format SD card with BB10 setting. But this also looks hassle to user.

Finally, I decided to see how directory structure looks like and what is folders permission using SSH to device. When I used linux command like ls and cd, I was able to see folder's content fine. And I decide to use those command to resolve directory browsing issue.

Following is my code, if someone else is also facing similar issue. First I am escaping special character in filepath, else linux command will not work. Then I am creating "ls" command with sorting and with output which list directory content in absolute file path. Once we have proper command I am using popen API to run command and then using IO api to read command's output.
QStringList BookListModel::getFileList(const QString& dir) {

 QString filePath(dir);
 filePath.replace(" ","\\ ");
 filePath.replace("'","\\'");
 filePath.replace("`","\\`");
 filePath.replace("!","\\!");
 filePath.replace("$","\\$");
 filePath.replace("&","\\&");
 filePath.replace("*","\\*");
 filePath.replace("\"","\\\"");
 filePath.replace("(","\\(");
 filePath.replace(")","\\)");
 filePath.replace("[","\\[");
 filePath.replace("]","\\]");

 qDebug() << filePath;
 QString cmd = QString("ls -1 %1/*").arg(filePath);
 qDebug() << cmd;

 QStringList files;
 char path[1035];
 FILE* fp = popen(cmd.toStdString().c_str(), "r");
 if (fp == NULL) {
  qDebug() << "Failed to run command";
  return files;
 }

 while (fgets(path, sizeof(path)-1, fp) != NULL) {
  QString file = QString(path).trimmed();
  QFileInfo fileInfo(file);
  if (fileInfo.isFile() && FileModel::isSupportedMedia(fileInfo.fileName()) ){
   files << file;
  }
 }

 qDebug() << files;

 pclose(fp);
}
That's all, hope it will be helpful to someone.

Thursday, June 26, 2014

Audiobook Reader for BB10 now got Built for Blackberry status

Recently I upgraded my Audiobook Reader application pro version for BB10.

This version contains many UI related changes and now it is more aligned to Blackberry design guideline and now it is also awarded Built for Blackberry status.

Apart form UI related changes, this version contains two major changes.

Now it Book auto pauses in case it detect phone call activity.

And there was a very critical bug in application which prevents book to be added from SD card. Though issue was related to BB10 OS, I found one work around and now adding books from SD card works as expected.

Full list of features in current version is as below.
  • Allows adding single file or whole folder with book audio data
  • Supports adding custom bookmark
  • Supports browsing mp3 chapter files and play selected mp3 chapter file
  • Supports chapter skip
  • Supports 2 min, 1 min, 30 sec skip option
  • Supports auto pause on call
  • Supports Play/Pause using HW buttons
  • Supports custom cover art download
  • Advance file browser
  • Supports sleep timer
  • Supports shuffle
  • and some more...
Following is demo for current version.


Hope you will like the updates.

Friday, June 13, 2014

Using WorkerScript from QML

As you might know, I am contributing to Ubuntu Touch Calendar core application. Those application are pure QML application with no C++ backend or we aim to avoid it as much as possible. This is done to make sure application are portable across device.

For simple use-case you can just use javascript code as backend logic and it should work fine without any problem. but if you need to some intensive calculation in application then its better to move those calculation to WorkerScript.

While working on one calendar feature, Calculating layout for overlapping events, I also needed to use WorkerScript. But one major problem with WorkerScript is that, it does not have access to QML Engine. So you will not be able to send your Declarative object to worker script to offload intensive task to threads.

In calendar application I am getting Events declarative object from model and I needed to process events time to decide position and size of events in view.

As such I can not send those Events to WorkerScript, but to overcome that limitation, I created temporary Javascript object which has required data like start time, end time and id, which I can use to decide position, size and Id can be used to map temp object to original object from model and rest of logic then can be implemented in WorkerScript.

Code wise using WorkerScript is quite easy. Following code creates WorkerScript object and sends array of events to script.
    WorkerScript {
        id: eventLayoutHelper
        source: "EventLayoutHelper.js"

        onMessage: {
            //recevied process data, which we can use to layout events
            layoutEvents(messageObject.schedules);
        }
    }

    function createEvents() {

        var eventMap = {};
        var allSchs = [];

        var startDate = new Date().midnight();
        var endDate = new Date().endOfDay();
        var items = model.getItems(startDate,endDate);
        for(var i = 0; i < items.length; ++i) {
            var event = items[i];
            var schedule = {"startDateTime": event.startDateTime, "endDateTime": event.endDateTime,"id":event.itemId };
            allSchs.push(schedule);
            eventMap[event.itemId] = event;
        }

        intern.eventMap = eventMap;
        //here we are asking worker script to process all the events
        eventLayoutHelper.sendMessage(allSchs);
    }


Following is script code, it will process these events and will send back location and size information back.
WorkerScript.onMessage = function(events) {

    //do some pre-processing like sorting and converting the data type more comfortable to algorithm
    var allSchs = processEvents(events);

    while( allSchs.length > 0) {
        var sch = allSchs.shift();

        //finds all schedules overlapping with current schedule and remove from original array
        var schs = findOverlappingSchedules(sch, allSchs);

        //insert original schedule first, so array remain sorted
        schs.unshift(sch);

        //send processed events back to GUI thread so we can render it
        WorkerScript.sendMessage({ 'schedules': schs});
    }
}