Sunday, September 14, 2014

Creating array of IBOutlet

Sometimes you want to enable/disable bunch of buttons together or perform other such activities on bunch of UI elements created using Interface Builder.

I recently needed to perform similar operation on bunch of button for iOS game I am working on. For this kind of operation, its easier to create array of IBOutlet which can be created using IBOutletCollection. Following is my code, which create IBOutletCollection of UIButtons and then enable/disbale them together.
@interface MyViewController : UIViewController {
    IBOutletCollection(UIButton) NSArray *buttons;
}
in MyViewController.m, we can use buttons as below.
-(void) enableUI:(BOOL) enable {    
    for( UIButton* button in buttons){
        button.enabled = enable;
    }    
}
Only thing missing now is, connecting UIButtons created using Interface Builder to IBoutletCollection. We need to do this from Interface Builder, process is similar to connecting UI Elements from Interface Builder to IBOutlet.

Following snaps describes the required process. I created three buttons on view.


You can connect IBoutletCollection to UIButton as shown in below snap.


And you can repeat above process to connect more UIButton with IBOutletCollection.

That's it.

Wednesday, September 10, 2014

Matching custom NSObject with isEqual and hash

While working on one multiplayer game for iOS using GameKit, I faced issue while comparing object.

For single player version I was not facing this issue but while running multiplayer version I was facing this issue. After debugging a little I realised condition for comparing object's value is failing and it turned out that I was comparing object's address rather then its value. As for Multiplayer game I was creating object using data from server/host, I needed to compare object's value and not its address.

For objectiveC, if you want to compare object's value using "==" operator, you need to overload isEqual and hash method as shown like below.

-(BOOL)isEqual:(id) other {
    if (other == self)
        return YES;
    
    if (!other || ![other isKindOfClass:[self class]])
        return NO;
    
    Card* otherCard = other;
    return ( mSuit == otherCard.mSuit && mRank == otherCard.mRank);
}

- (NSUInteger)hash {
    NSUInteger hash = 0;
    hash += [[NSNumber numberWithInt:mSuit] hash];
    hash += [[NSNumber numberWithInt:mRank] hash];
    return hash;
}
Note that you need to overload both method together, overloading only one will not work. Once I overloaded above method my multiplayer version started working as normal.

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.