Monday, May 4, 2015

Color Animation using Animator with Unity3D

I created a small demo application to demonstrate how Animation can be done using Animator and State Machine with Unity5. This demo creates a Blinking Animation on Mouse click.

Following is final output of this demo.


Below post shows, how above animation can be created.

-> To begin, First create a simple cube and apply a material on the cube. Setup should look like below.

-> Then using "Add Component", add Animator to this cube.

-> Once Animator is added, open Animation window using "Window" -> "Animation", We will need to use while performing the animation.


Animator performs animation using State Machine and this state machine should have default state. Animator will transit to this default State when object is created. In Animator, Animation clip is considered as a State, so lets add a Animation clip and name it default.


-> Our default state will not have any animation, it should show just initial color. Let's just add one frame with current color. Like below.


-> Now we have default state, let's add another state which will perform "Blinking" animation.



-> Now we should add frames, so let's add color property and frames to animate color.




After adding these frames color should animate like below.



-> Now we have Blinking animation state, we are now ready to create a State Machine. Open Animator window by clicking on "Animator" object, it should look like below.

-> We should now add a Parameter which will be used to Trigger animation. On "Animator" window add new boolean parameter and name it "blink".



Following video shows how to create transition and how to add condition to those transition.


-> Now we have state machine with transition and condition, but we want Blink Animation to terminate once its finished. Let's open "Blink Animation" and add Animation event line shown in below image.


This will prompt you to select function to be invoked when Animation frame is reached. You can select function from attached Script.


Following video shows the same process.



-> Finally, We also need to add a script to Cube object. Following is code for the script. In script we are setting "blink" transition parameter of Animator to true on mouse click. While StopAnimation sets "blink" parameter to false when animation event is called.

using UnityEngine;
using System.Collections;

public class MyAnimation : MonoBehaviour {

 private Animator animator;

 void Awake() {
  animator = GetComponent ();
 }

 void OnMouseUp() {
  startAnimation ();
 }

 public void startAnimation() {
  animator.SetBool ("Blink", true);
 }

 public void stopAnimation() {
  animator.SetBool ("Blink", false);
 }
}

And now if run project and click cube, you should see blink animation as per demo video. That's it hope this post will be helpful.

Sunday, April 12, 2015

Handling Drag and Drop with Unity 3D

Drag and Drop is quite useful and we need to implement this quite often in our project. While working on one of my Unity Project, I required to implement the same.

There are some options but I found code described in this post quite simple to use with my project. In this post I will describe how I achieved the same.

So, Let's first create a C# script, I named it Drag.cs and added following code to it. This script will enable touch interaction to added GameObject and will move object along with touch. Also add BoxCollider to GameObject, so we can detect collision.

using UnityEngine;
using System.Collections;
 
public class Drag : MonoBehaviour {
    private Plane plane;
    private Vector3 v3Offset;


    void OnMouseDown() {
 plane.SetNormalAndPosition
             (Camera.main.transform.forward, transform.position);
 Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
 float dist;
 plane.Raycast (ray, out dist);
 v3Offset = transform.position - ray.GetPoint (dist);         
    }

    void OnMouseUp() {
    }

    void OnMouseDrag() {
 Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
 float dist;
 plane.Raycast (ray, out dist);
 Vector3 v3Pos = ray.GetPoint (dist);
 transform.position = v3Pos + v3Offset;  
    }
}
Now, suppose we have another GameObject and we want to detect when GameObject we just created with above script, is dropped on it. To detect drop easily let's first add RigidBody to this GameObject and BoxCollider. After adding BoxCollider also check isTrigger is true. After doing this we need to add C# script to it which will handle drop. Let's name it to Drop.cs and add below code to it.

Below script observe the Collision. When collision happens it checks which object is collided with it. If collided object is Drag object then do something.
using UnityEngine;
using System.Collections;

public class Drop : MonoBehaviour {


    void OnCollisionEnter2D (Collision2D other) {
    }
 
    void OnCollisionExit2D(Collision2D other){
    }
 
    void OnTriggerStay2D (Collider2D other) {
        if( other.gameObject.tag == "Drag" ) {
            // Object dropped, handle the drop
        } 
    }
} 
That's pretty much it, hope this helps.

Sunday, April 5, 2015

Sprite with Text in Unity3D

I am working on one game where I wanted to show text over sprite. With new Unity UI System, it's quite easy to create button as sprite and text over it.

But if you want to create a traditional sprite and add text to it, this post describes the same.

Basic idea is to add 3D text component to sprite, but if you have tried it then you might know after adding 3D text component we need to move it to same sorting order as sprite. Also we need to layout text properly to set alignment.

Before we begin, I assume you know how to create Sprite, how to create 3D text component and make it a Prefab object. If not you can refer this article to know how to make Prefab object.

Finally, following is my script which I attached to Sprite. Once added to Sprite, It will create 3D text component from provided Prefab object and add the same to Sprite.
using UnityEngine;
using System.Collections;
 
public class TextScript : MonoBehaviour {
 
 public string text;
 public GameObject textPrefab;
    
 void Start() {
  GameObject text = Instantiate(textPrefab) as GameObject;
  text.transform.parent = gameObject.transform;
  text.transform.rotation = gameObject.transform.rotation;

  TextMesh textInstance = text.GetComponent();
  textInstance.text = text;

  Renderer textRenderer = text.GetComponent ();
  Renderer thisRenderer = GetComponent ();

  textRenderer.sortingLayerID = thisRenderer.sortingLayerID;
  textRenderer.sortingOrder = thisRenderer.sortingOrder+1;
 
  Bounds textBounds = textRenderer.bounds;
  Bounds parentBounds = thisRenderer.bounds;
  
  float x = gameObject.transform.position.x 
                     - (textBounds.size.x/2); 
  float y = gameObject.transform.position.y 
                     + (textBounds.size.y/2);
  textInstance.transform.position = 
                     new Vector3(x, y, parentBounds.center.z + 1);
 }
}
That's it, you can add this script to any component and add text over it.

Sunday, January 25, 2015

Notification on property change in iOS SDK

I have used QML quite a lot in my projects and there is one quite nice feature in QML that makes implementation quite easy. The feature is Property change notification.

Apple iOS SDK also similar mechanism named Key-Value Observing mechanism. Here is Apple's documentation for the same.

Following is simple code which demonstrate how we can use Key-Value Observer API to get notification on property changes. First we need to register observer and in which property observer is interested in. Code shows, class "self" is interested in property change event from propertyOwner for "isPlaying" property.
[propertyOwner addObserver:self
    forKeyPath:@"isPlaying"
       options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
       context:NULL];
Now, we have registered observer for property change, we need to implement method which will be called in case of property is changed. Following code shows the same. We need to check which object sent event and for which property, then we can take appropriate action.
- (void) observeValueForKeyPath:(NSString *)path ofObject:(id) object change:(NSDictionary *) change context:(void *)context
{
    // this method is used for all observations, so you need to make sure
    // you are responding to the right one.
    if (object == propertyOwner && [path isEqualToString:@"isPlaying"]) {
        // now we know which property is modified
    }
}
When we are done and don't need notification any more we can remove observer. Below is code for the same. It's safe to use try and catch as we might get exception if we try to remove observer when none is registered.
    @try{
        [propertyOwner removeObserver:self forKeyPath:@"isPlaying"];
    }@catch(id anException){
        //do nothing, obviously it wasn't attached because an exception was thrown
    }
Now whenever "propertyOwner" changes's "isPlaying" property then "self" will get notification and "observeValueForKeyPath" method will get called.

Sunday, December 28, 2014

Showing Remote Image using ImageView in BB10 Cascades

While working on my BB10 App update, I required to show Remote Image using ImageView BB10 cascades API. ImageView or Image component by default does not support loading image from URL on internet. I implemented a custom component which serve the purpose and also easily integrated with ImageView.

Following is implementation for the same, I hope it will be useful to someone.

Let's start by showing how my custom component works. Following code shows how to use ImageDownloader custom component along with ImageView to display Grid of images from internet. Data Model contains two kind of URL, url for low resolution image and url for high resolution image.
import my.library 1.0

ListView {
  id: listView
  
  layout: GridListLayout {}
  dataModel: model;
  
  listItemComponents: [
      ListItemComponent {                        
          ImageView {
              id: imgView
              imageSource: "default_image.jpg"  
                                        
              attachedObjects: [
                  ImageDownloader {
                      id: imageLoader
                      url: ListItemData.mediumImage
                      onImageChanged: {                 
                          if (imageLoader.isVaid) {
                              imgView.image = imageLoader.image
                          }
                      }
                  }
              ]
          }
      }
  ]
}
ImageDownloader component is available in QML because we imported "my.library". We can make any C++ code available to QML by registering it to QML System, folloiwing snippet shows how we can register C++ component to QML system.

#include "ImageDownloader.h"

int main(int argc, char **argv) 
{
 Application app(argc, argv);

 qmlRegisterType<ImageDownloader>("my.library",1, 0, "ImageDownloader");

 QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app);

 AbstractPane *root = qml->createRootObject();
 app.setScene(root);

 return Application::exec();
}
Now that custom component is ready to be used with QML, let's see how its implemented. Below if header file for ImageDownloader class. We are defining few properties like url, image and isValid. By setting "url" we can initiate download of image, when "image" download is finished downloaded image can be accessed by using "image" property. We can check if image is valid or not by checking "isValid" property. we are also defining few signal like "urlChanged" and "imageChanged", which are emited when url is changed or image is downloaded. And we are using QNetworkAccessManager to download image from internet.
#ifndef IMAGEDOWNLOADER_H_
#define IMAGEDOWNLOADER_H_

#include 
#include 

class QNetworkAccessManager;

class ImageDownloader: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QVariant image READ image NOTIFY imageChanged)
    Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
    Q_PROPERTY(bool isVaid READ isValid CONSTANT);
public:
    ImageDownloader( QObject* parent = 0);
    virtual ~ImageDownloader();

signals:
    void urlChanged();
    void imageChanged();

private slots:
    QString url() const;
    void setUrl( const QString& url);

    QVariant image() const;
    bool isValid() const;

    void startDownload();
    void onReplyFinished();

private:
    QNetworkAccessManager mNetManager;
    QString mImageUrl;
    bb::cascades::Image mImage;
    bool mIsValid;
};

#endif /* IMAGEDOWNLOADER_H_ */
Implementation is also quite simple, let's see how its implemented. On URL change, we are initiating the image download using QNetworkAccessManager. When download is finished, we are reading image data in to buffer and creating Image using BB10 Image API.
#include "ImageDownloader.h"

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>

ImageDownloader::ImageDownloader(QObject* parent):
    QObject(parent), mIsValid(false) {}

ImageDownloader::~ImageDownloader() {}

QString ImageDownloader::url() const {
    return mImageUrl;
}

void ImageDownloader::setUrl( const QString& url)
{
    if(url != mImageUrl) {
        mImageUrl = url;
        mIsValid = false;
        mImage = bb::cascades::Image();
        emit urlChanged();
        emit imageChanged();
        startDownload();
    }
}

QVariant ImageDownloader::image() const {
    return QVariant::fromValue(mImage);
}

bool ImageDownloader::isValid() const {
    return mIsValid;
}

void ImageDownloader::startDownload() {
    QNetworkRequest request(mImageUrl);
    QNetworkReply* reply = mNetManager.get(request);
    connect(reply, SIGNAL(finished()), this, SLOT(onReplyFinished()));
}

void ImageDownloader::onReplyFinished() {
    QNetworkReply* reply = qobject_cast(sender());
    QString response;
    if (reply) {
        if (reply->error() == QNetworkReply::NoError) {
            const int available = reply->bytesAvailable();
            if (available > 0) {
                const QByteArray data(reply->readAll());
                mImage = bb::cascades::Image(data);
                mIsValid = true;
                emit imageChanged();
            }
        }
        reply->deleteLater();
    }
}
That's all we have to do to do display remote image in BB10, hope you liked it.