Showing posts with label Unity. Show all posts
Showing posts with label Unity. Show all posts

Sunday, February 1, 2015

Unity: Install application into a writable folder on Windows OS with High UAC using Inno Setup

I have a Unity simulation program in which i need to create a Windows installer, I choose Inno Setup because it is free and easy to use. However, my Unity simulation program needs to writes simulation data and log data into the same folder when it is installed. Therefore the default program folder for installation is not preferred, because some users might have the UAC set to high in their Windows OS in which case the write permission in the program folder is not available.

After some thinking and a bit of search online, I found a simple way to do this. The trick it to create the installed directory in the AppData folder (one of its sub folder, e.g., Local, LocalLow, Roaming). This allows Unity simulation program installed there to write its data to the same folder even if user may have UAC set to high.

To do this with the Inno Setup. After generating the *.iss installation script in Inno Setup. Locate the line that starts with "DefaultDirName=". Now change it to "DefaultDirName={localappdata}\{#MyAppName}", Then build the installer. and the default installation path will be from the AppData/Local folder on the user's computer.


Wednesday, January 28, 2015

Unity: Tips for optimizing performance for C#

This post discusses some tips on how to optimize performance in C# Unity programming:

1. Uses foreach() as infrequent as possible. foreach(...) is bad in Unity programming, the foreach(...) loop generates 24 bytes of garbage memory. If you are using a array or list object in C#, then you can use for loop to replace foreach loop. If you are using other collection classes such Dictionary and HashSet, then uses GetEnumerator() from the collection object and while loop to replace it.

2. Uses DateTime.UtcNow to replaces DateTime.Now for tasks such as tracking elapsed time and so on. DateTime.Now consumes more computational resources than DateTime.UtcNow.

3. Overrides OnBecomeInvisible() of the MonoBehavior class to implements logics that hide and stop some proccessing of the game object whenever it becomes invisible (Note that the game object should have a Renderer component for this method handler to be invoked during runtime).

4. Create and Destroy object using Instantiate and Destroy during runtime can be expensive and slow down the frame rate. Apart from putting these operation in StartCoroutine, one workaround is creating a set of game objects at the beginning of the game and store them in a collection such as a Queue. Then instead of calling Instantiate during the game play, call the Dequeue() on the Queue to return a copy of the already created game object and invokes its SetActive(true) method. And instead of calling Destroy, call the Enqueue() on the Queue to store the game object back to the queue (Remember to call the game object's SetActive(false)). This can be a performance hit

5. For a very large generic list of objects in stored in a collection, performance will be gained when a number of collections is used to store the objects (each of them store one of its property, for example). In my personal experience, I used to have List<Employee> collection object in Unity game, where the number of Employee stored in the list is close to 9 millions. Trying to add or remove Employee object from this List<Employee> collection becomes extremely slow. In the end, what I did was to designed a generic collection class, CustomizedList, resembling List<Employee>. The Employee class can be something like the following:

class Employee
{
  public int Age;
  public int YearsOfExperience;
  public float Salary;
  public string Occupation;
  public string ID;
  public Employee()
  {
      ID = Guid.NewGuid();
  }

   public override int GetHashCode()
   {
       return ID.GetHashCode();
   }

    public override bool Equals(Object rhs)
    {
       if(rhs is Employee)
       {
            Employee rhs1=rhs as Employee;
            return ID=rhs1.ID;
        }
        return false;
    }
}

and my generic class looks something like this (Note that this is a demo class):

class CustomizedList
{
  public List<int> Age = new List<int>();
  public List<int> YearsOfExperience = new List<int>();
  public List<float> Salary = new List<float>();
  public List<string> Occupation = new List<string>();
  public List<string> ID= new List<string>();

  public void Add(Employee e)
  {
     Age.Add(e.Age);
     YearsOfExperience.Add(e.YearsOfExperience);
     Salary.Add(e.Salary);
     Occupation.Add(e.Occupation);
     ID.Add(e.ID);
  }
 
   public Employee this[int index]
   {
         Employee e=new Employee();
         e.ID = ID[index];
         e.Age=Age[index];
         e.Salary =Salary[index];
         e.Occupation=Occupation[index];
         e.YearsOfExperience=YearsOfExperience[index];

         return e;
   }

    public void RemoveAt(int index)
    {
          Age.RemoveAt(index);
          ID.RemoveAt(index);
          Salary.RemoveAt(index);
          Occupation.RemoveAt(index);
          YearsOfExperience.RemoveAt(index);
    }

    public int IndexOf(Employee e)
    {
          return ID.IndexOf(e.ID);
    }
 
     public void Remove(Employee e)
     {
           int index = IndexOf(e);
           RemoveAt(index);
     }

     public int Count
     {
           get
           {
                 return ID.Count;
           }
     }

     public Employee Iterate()
     {
            for(int i=0; i < Count; ++i)
            {
                 yield return this[i];
            }
     }
}

The basic concept is very simple decomposes the Employee objects into its properties, each of which is stored in a separate list when Add(Employee e) method is called. However, CustomizedList has tremendous increase in performance when compare to the usage of List<Employee> when the number of items stored is extremely large. Of course when processing such a large collection, StartCoroutine is required as a time slice method for long processing (please refers to http://czcodezone.blogspot.com/2015/01/unity-use-startcoroutine-to-perform.html).

Thursday, January 22, 2015

Unity: Use PersistentDictionary and ESENT to store large collection object in Unity

This post shows how to add ESENT to Unity game and how to use PersistentDictionary in ESENT to store a very large collection of objects in Unity game.

Firstly, we need to download ESENT and make it available to Unity game development, double-click any C# script in the Unity project panel to bring up the Visual Studio, right-click the project in the Visual Studio and select "Managed NuGet Packages...". In the "Manage NuGet Packages ...", key in "ESENT" to search for the Extensible Storage Engine, and click the "Install" button for the first result (which is "ManagedEsent 1.6" in my case) coming up. This will install the ESENT references for development in Visual Studio.

Next we need to make ESENT libraries available to Unity. To this end, create a new folder under "Assets" in the Unity project panel (e.g., name it "References"). Now go to the "Packages\ManagedEsent.1.6\lib\net20" folder in your Unity project folder, and drag the "Esent.Collections.dll" and "Esent.Interop.dll" into the "References" folder in the Unity project folder. Upon this, Unity will now recognize the ESENT library and you can use it in C#. Below is a C# code snippet which shows how to uses a PersistentDictionary object from ESENT to store a large number of objects.

using Microsoft.Isam.Esent.Collections.Generic;
using UnityEngine;

public class Employee
{
 public string ID;
 public override string ToString()
 {
  return ID;
 }
 public static Employee ParseEmployee(string serializedContent)
 {
  Employee person = new Employee();
  person.ID = serializedContent;
  return person;
 }
}
public class EmployeePool 
{
 private string mID;
 protected PersistentDictionary<string, string> mData = null;

 public EmployeePool(string id)
 {
  mID = id;
  
 }

 public void Add(Employee person)
 {
  if (mData == null)
  {
   CreatePersistentDictionary<string, string>(ref mData, mID);
  }
  mData[person.ID] = person.ToString();
 }

 public void Clear()
 {
  CreatePersistentDictionary<string, string>(ref mData, mID);   
 }

 private static void CreatePersistentDictionary<T, V>(ref PersistentDictionary<T, V> current, string directory)
    where T : IComparable<T>
 {
  string full_directory_path = Path.Combine(Application.persistentDataPath, directory);

  if (PersistentDictionaryFile.Exists(full_directory_path))
  {
   PersistentDictionaryFile.DeleteFiles(full_directory_path);
  }

  current = new PersistentDictionary<T, V>(full_directory_path);
 }

 private static void DeletePersistentDictionary<T, V>(ref PersistentDictionary<T, V> current, string directory)
  where T: IComparable<T>
 {
  string full_directory_path = Path.Combine(Application.persistentDataPath, directory);

  if (PersistentDictionaryFile.Exists(full_directory_path))
  {
   PersistentDictionaryFile.DeleteFiles(full_directory_path);
  }
  current=null;
 }

 public int Count
 {
  get
  {
   if (mData == null)
   {
    return 0;
   }
   return mData.Count;
  }
 }

 public void Remove(Employee e)
 {
  mData.Remove(e.ID);
 }

 public Employee this[string index]
 {
  get
  {
   if (mData.ContainsKey(index))
   {
    return Employee.ParseEmployee(mData[index]);
   }
   return null;
  }
 }

 public IEnumerable<Employee> Enumerator
 {
  get
  {
   foreach (string key in mData.Keys)
   {
    yield return this[key];
   }
  }
 }
}

As PersistentDictionary is quite peculiar about the key type and the value type, in this particular case, each of the Employee object is given a id which is a string and serves as the key for the PersistentDictionary as it has a IComparable interface already. the Employee object itself is serialize into a string to be store (the serialization can be a JSON string, e.g.), the Enumerator function allows each Employee object in the collection to be iterated.

One thing to note is the CreatePersistentDictionary method, the method is implemented such that whenever a collection is created with an existing mID, the old data stores in the database with that mID as name will be wiped out. This is intended to let user starts with an empty database when clear is called (all when the constructor is called).

Wednesday, January 21, 2015

Unity: Use StartCoroutine to perform long time processing

This post shows how to use StartCoroutine to perform long time processing. Below is a simple basic code for calling long processing function (which i named LongProcessingFunction()) in a C# script attached to game object in a scene.


void Update()
{
  int iterationCount1 = 10000000;
  int iterationCount2 = 10000;
  StartCoroutine(LongProcessingFunction(iterationCount1, iterationCount2));
}

private IEnumerator LongProcessingFunction(int count1, int count2)
{
  yield return new WaitForEndOfFrame();
  for(int i = 0; i < count1; ++i)
  {
     for(int j=0; j < count2; ++j)
  {
     Debug.Log("processing ... "+i+" and "+j);
  }
  }
}

If you run the above code, your game will probably hang or suspend for a while during which you cannot click and interact with anything in your game scene. This makes one wonder why the coroutine prevents the running of the game. The important point to note, though, is that calling StartCoroutine(LongProcessingFunction()) does not really start a new thread or a parallel task, as in .NET. By calling StartCoroutine on LongProcessingFunction(), as soon as the "yield return new WaitForEndOfFrame()" line in the LongProcessingFunction, the unity game engine resumes temporally jump out of the LongProcessingFunction() execution, and runs a few frames before resumes the execution of the LongProcessingFunction() from the point it left off earlier. The execution will continue until the next time the "yield return ..." is encountered again in the LongProcessingFunction.

What this means is the execution of LongProcessingFunction is on the same thread as the calling function Update(). Therefore, as long as "yield return ..." is not encountered, the execution will continue on the LongProcessingFunction() until it is completed, then unity game engine resume the execution of frames again. Unfortunately, this means that during the time in which LongProcessingFunction is processing, the graphics of the game becomes unresponsive, which is what causes the "hanging" of the game.

Below is the code I designed to get around this problem, not elegant, but it gets the job done for me.

private bool isInLongProcessing;
private int mLoop1;
private int mLoop2;

void Start()
{
  isInLongProcessing = false;
}

void Update()
{
  int iterationCount1 = 10000000;
  int iterationCount2 = 10000;
  if(isInLongProcessing)
  {
    //do something here during long processing time
    return;
  }
  else
  {
   if(Input.GetKey(KeyCode.A))
   {
  StartCoroutine(LongProcessingFunction(iterationCount1, iterationCount2));
   }
   else
   {
      //do something here during normal time
   }
  }
}

void OnGUI()
{
  if(isInLongProcessing)
  {
    string message = string.Format("Long Processing: {0} {1}", mLoop1, mLoop2);
    GUI.Label(new Rect(0, 0, Screen.width, 20), message);
  }
}

private IEnumerator LongProcessingFunction(int count1, int count2)
{
  yield return new WaitForEndOfFrame();
  isInLongProcessing = true;
  DateTime currentTimeTick = DateTime.Now;
  DateTime intervalTimeTick = currentTimeTick;
  for(mLoop1 = 0; mLoop1 < count1; ++mLoop1)
  {
     for(int mLoop2=0; mLoop2 < count2; ++mLoop2)
  {
  currentTimeTick = DateTime.Now;
  TimeSpan ts = currentTimeTick - intervalTimeTick;
  if(ts.TotalMilliseconds >= 50)
  {
    intervalTimeTick = currentTimeTick;
    yield return null;
  }
  }
  }
  isInLongProcessing = false;
}

My idea is very simple, the code in LongProcessingFunction uses "currentTimeTick" and "intervalTimeTick" to keep track the amount has been elapsed since the last time "yield return null" is called, when the elapsed time is around 50 milliseconds, the "yield return null" is called, which trigger the unity game engine to temporarily leaves the LongProcessingFunction and go to process one frame and then return to the point it left off in the LongProcessingFunction (The "yield return null" basically tolds the unity game engine to come back to LongProcessingFunction after one frame execution). In this way, the game graphics will have a frame rate of roughly 20 frames per seconds, which is bearable, since one wants to spend as much time in the LongProcessingFunction as possible, so that is can be completed asap. The three variables "isInLongProcessing", "mLoop1" and "mLoop2" can be used to display processing progress. and signal the Update() whether it is still doing long  processing (I uses A key press to trigger the long processing, just for demo purpose).

Unity: Prevent 3D Text from always appearing on top

The default shader used by 3D Text make it always appear on top, which is not desirable, for example, when the 3D text attached to a game character go behind a wall or build, the text will still be showing. There is already a well-explained tutorial (see the link below) that shows how to incorporate a shader for 3D text to get around this issue:

http://wiki.unity3d.com/index.php?title=3DText

The problem is for a newbie like me, I had a difficult time to figure out how to generate a font texture for the font material mentioned in that tutorial, on which to apply the shader. Therefore, this tutorial documents on how i managed to find the solution. Firstly, go to unity asset store:

https://www.assetstore.unity3d.com/

Key in "font" in the search box, download and import a free font asset from the asset store into your project, for example, the following one will do:

https://www.assetstore.unity3d.com/en/#!/content/4235

After the font asset has been imported, select the font asset folder under "Assets" in the project panel, and select the font you would like to generate the font texture and material. In the inspector panel, change the "Character" field from dynamic to Unicode, click the gear button at the upper-right of the inspector panel and select "Create Editable Copy", as shown in the figure below:




This will generate the font texture and font material at the same time, as shown in the figure below:




 Remember to change the "shader" property of the font material in the inspector panel to "GUI/3D Text", Now you can change the font material and font in the 3D text component to match with the font material and font you just created. Below is the result of applying the shader in a project i am working on:






Tuesday, January 20, 2015

Unity: Load a file in the build resources folder

In unity, we may want to allow user to have access to the content of a particular file which they can modify and incorporate back into the game. The simple way is to let user create it (or let the game program to create it during the runtime) in the "Resources" folder under the data folder of the game build, which can then be loaded subsequently in the game. However, we may also want to provides user with a default version at the beginning so that they can modify based on the original version. This can be simply solved by having the original version of the file stored in a created folder name "Resources" under the Assets folder in the game development project. When the game is built, the contents in this "Resources" folder will be packaged into the game. When the game start, we can have a script to check whether a version of the file already exists in the "Resources" folder under the data folder of the game build, if not, the default version can be loaded from the game package and saved into that folder. Subsequently the user can then modify the file directly and have it loaded in the game. Below shows a demo script for this concept (in this case, the particular file is ReadMe.txt)

void Start()
{
 string dirPath = Path.Combine(Application.dataPath, "Resources");
 string filepath = Path.Combine(dirPath, "ReadMe.txt");

 if (!File.Exists(filepath))
 {
  TextAsset ta = (TextAsset)Resources.Load("ReadMe");
  using (StreamWriter writer = new StreamWriter(filepath))
  {
   writer.WriteLine(ta.text);
  }
 }

 if (File.Exists(filepath))
 {
  using (StreamReader reader = new StreamReader(filepath))
  {
   mReadMeText = reader.ReadToEnd();
  }
 }
}

Monday, January 19, 2015

Unity: Move the camera in a RTS scene with speed proportional to current zoom level

This is a solution I designed to solve issue with the camera movement that fails to adjust to the camera's current zoom level. Start by attaching a C# script to the main camera object in a scene. The following codes assumes that user expect hold down and drag the right mouse to move the camera around a scene. However, due to the zoom level, if the camera is moving at the same speed at different zoom levels when user drag the mouse, the movement will appear awkward in that when the zoom in, the user feels the camera moves too fast. On the other hand, when zoom out, the user feels the camera moves too slow. I solved the following by incorporating the field of view into the movement of the camera. That is, when zoom in, the field of view is small, and when zoom out, the field of view is large. Therefore, with the same amount of mouse drag, the camera moves slower when zoom in and moves faster when zoom out. However, from the player's point of view, it is as if the camera is moving at the same speed, and the overall effect is much better. Below is the code in the C# script attached to the main camera, which user can
  • Use A and W to zoom in and out, 
  • Use mouse scroll to move the camera up and down
  • Hold down Alt key and hold down and drag the right mouse to rotate camera up/down, left/right.
  • Hold down and drag the right mouse to move in the x and z direction in the RTS scene

public float ScrollSpeed = 10f;
public float DragSpeed = 6f;
public float RotateSpeed = 25;
public float RotateAmount = 25;

void LateUpdate()
{
  ZoomCamera(Input.GetKey(KeyCode.A), Input.GetKey(KeyCode.W));
  RotateCamera();
  MoveCamera();
}

public void ZoomCamera(bool isZoomIn, bool isZoomOut)
{
 float zoomDirection = 0f;
 if (isZoomIn) zoomDirection = 1f;
 else if (isZoomOut) zoomDirection = -1f;
 camera.fieldOfView -= zoomDirection * Time.deltaTime * ScrollSpeed;
}

private void RotateCamera()
{
 Vector3 origin = transform.eulerAngles;
 Vector3 destination = origin;

 //detect rotation amount if ALT is being held and the Right mouse button is down
 if ((Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) && Input.GetMouseButton(1))
 {
  destination.x -= Input.GetAxis("Mouse Y") * RotateAmount;
  destination.y += Input.GetAxis("Mouse X") * RotateAmount;
 }

 //if a change in position is detected perform the necessary update
 if (destination != origin)
 {
  transform.eulerAngles = Vector3.MoveTowards(origin, destination, Time.deltaTime * RotateSpeed);
 }
}

private void MoveCamera()
{
 float xpos = Input.mousePosition.x;
 float ypos = Input.mousePosition.y;
 Vector3 movement = new Vector3(0, 0, 0);

 float fov = camera.fieldOfView;
 bool isAltKeyDown = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
 if (!isAltKeyDown && Input.GetMouseButton(1))
 {
  movement.x -= Input.GetAxis("Mouse X") * DragSpeed;
  movement.z -= Input.GetAxis("Mouse Y") * DragSpeed;
 }

 movement = camera.transform.TransformDirection(movement);
 movement.y = 0;
 movement.y -= ScrollSpeed * Input.GetAxis("Mouse ScrollWheel");

 Vector3 origin = transform.position;
 Vector3 destination = origin;
 destination.x += movement.x;
 destination.y += movement.y;
 destination.z += movement.z;

 if (origin != destination)
 {
  transform.position = Vector3.MoveTowards(origin, destination, Time.deltaTime * DragSpeed * fov);
 }
}

Unity: Orbit a camera around any point in a scene

To orbit a camera around any point in a scene, Create a C# script and attach it to the main camera in your scene. In the script's LateUpdate() method, put the following codes. The code enables user to orbit the camera around a target point (which is Vector3.zero in the demo code, but you can set it to any point, even point that is moving) when he holds down the Ctrl key and hold down and drag the mouse.

public float RotateAmount = 15f;

void LateUpdate()
{
   OrbitCamera();
}

public void OrbitCamera()
{
 bool isCtrlKeyDown = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
 if (isCtrlKeyDown && Input.GetMouseButton(0))
 {
  Vector3 target = Vector3.zero; //this is the center of the scene, you can use any point here
  float y_rotate = Input.GetAxis("Mouse X") * RotateAmount;
  float x_rotate = Input.GetAxis("Mouse Y") * RotateAmount;
  OrbitCamera(target, y_rotate, x_rotate);
 }
}

public void OrbitCamera(Vector3 target, float y_rotate, float x_rotate)
{
 Vector3 angles = transform.eulerAngles;
 angles.z = 0;
 transform.eulerAngles = angles;
 transform.RotateAround(target, Vector3.up, y_rotate);
 transform.RotateAround(target, Vector3.left, x_rotate);

 transform.LookAt(target);
}

Thursday, December 4, 2014

Unity: Collision Detection

Collision detection in Unity is extremely simple. For each game object that the player character will collide with, add a collider (e.g., by selecting Component->Physics->Box Collider), next enable the "Is Trigger" property in the game object's "Box Collider" section. We can set a tag to these game objects so that they can recognized by their tag, e.g. a tag like "enemy". Now in the MonoBehavior C# script attached to the player character, add the following method:

void OnTriggerEnter(Collider hitCollider)
{
 if("enemy" == hitCollider.tag)
 {
  GameObject enemy=hitCollider.gameObject;
  this.Treasures.Add(hitCollider.GetComponent<Treasure>());
  Destroy(enemy);
 }
}

The method above destroy any "enemy" the player character collides with and steal its Treasure item.

Unity: Horizontal Flow Layout

The following code snippet uses the GUILayout to conveniently make a horizontal flow layout that contains 10 labels.

void OnGUI()
{
  GUILayout.BeginArea(new Rect(0, 0, Screen.width / 4, 32));
  GUILayout.BeginHorizontal();

  for(int i=0; i < 10; ++i)
  {
 GUILayout.Label(i.ToString());
  }

  GUILayout.EndHorizontal();
  GUILayout.EndArea();
}


Unity: Implement a Timer

This post shows how to create a timer in Unity similar to the one used in WinForms or WPF. To create a timer in a Unity game, create a C# script and attached to a empty game object, The content of the C# looks like the following:

using UnityEngine;
using System.Collections;

public class Timer : MonoBehaviour {
 public float timerInterval=1.0f; //timer interval in seconds

 // Call this when the scene is loaded before the Start()
 private void Awake()
 {
  StartCoroutine (TimerCoroutine ());
 }

 private IEnumerator TimerCoroutine()
 {
  while(true)
  {
   Timer_OnTicked();
   yield return new WaitForSeconds(timerInterval);
  }
 }

 private void Timer_OnTicked()
 {
  //TODO: put your implementation here
 }

 // Use this for initialization
 void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {
 
 }
}

As can be seen above, the implementation starts the timer when the Unity scene containing the game object is loaded (i.e. in the Awake() method), this is done via the StartCoroutine() method which starts the co routine TimerCoroutine(). The TimerCoroutine() contains an infinite loop which updates based on the timerInterval. The actual update is to be implemented inside the Timer_OnTicked() handler.

Unity: Mouse Event Handling

To handle mouse event on a game object, we can write a C# script attached to the game object, in which the MonoBehavior derived class implements the following event handlers:

void OnMouseEnter(): handler triggered when the mouse moves over the game object
void OnMouseExit(): handler triggered when the mouse moves outside the game object
void OnMouseDown(): handler triggered when the mouse button is down on the game object
void OnMouseUp(): handler triggered when the mouse button is up on the game object

The above methods allow the game object to identify when the mouse is over it, or when the mouse is pressed and released over it, and behave accordingly (e.g., by change to highlight color, etc). Remember to apply a Component->Physics->Box Collider on the game object otherwise the above events will not be fired.

There are a set of methods from the Input class which returns a flag indicating whether a mouse button is pressed, for example:

Input.GetMouseButton(0) : return true if the left mouse button is pressed down
Input.GetMouseButton(1) : return true if the right mouse button is pressed down

There are also a set of methods from the Input class which returns the distance moves by the mouse, for example:

float Input.GetAxis("Mouse X") : return distance on screen for a the mouse moves left or right (positive or negative, respectively)
float Input.GetAxis("Mouse Y") : return distance on screen for a the mouse moves upward (positive) or downward (negative)
Vector3 Input.mousePosition: return the screen coordinate of the mouse position
float Input.GetAxis("Mouse ScrollWheel"): return the extent of mouse wheel scrolling


Wednesday, December 3, 2014

Unity: MiniMap

This post shows an example of a simple implementation for minimap in Unity game. The complete source of the project can be downloaded from:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Minimap.zip

Firstly, we want to make sure that the player character's 3D model will not be rendered in the minimap, therefore we need to exclude the layer to which the player character belong to. The easiest way is to create a new layer and set the player character to be on that layer. Let's name that layer "No Map" layer)

Next, create a camera in the "Hierarchy" view by selecting "Create->Camera". Name the camera "MiniMap Camera", and set the following properties in its "Inspector" view:

1. Clear Flags: Depth Only
2. Culling Mask: Mixed (by excluding "No Map" layer in selected layers so that the player character 3D model will not be rendered in the minimap)
3. Projection: Orthographic
4. Depth: 1

Next, create a C# script named "MiniMapGenerator.cs" and attached it to the "MiniMap Camera" game object in the "Hierarchy" view. Update its content as follows:

using UnityEngine;
using System.Collections;

public class MiniMapGenerator : MonoBehaviour {

 public float camHeight=10.0f;
 public float camDistance=10.0f;

 public GameObject target; 

 public Texture2D marker;

 public bool freezeRotation=true;

 // Use this for initialization
 void Start () {
  Vector3 angles = transform.eulerAngles;
  angles.x = 90;
  angles.y = target.transform.transform.eulerAngles.y;
  transform.eulerAngles = angles;
  Draw ();
 }
 
 // Update is called once per frame
 void Update () {
  transform.position = new Vector3 (target.transform.position.x, 
                                 target.transform.position.y + camHeight,
                                 target.transform.position.z);

  camera.orthographicSize = camDistance;

  if(freezeRotation)
  {
   Vector3 angles=transform.eulerAngles;
   angles.y=target.transform.transform.eulerAngles.y;
   transform.eulerAngles=angles;
  }


  Draw ();
 }

 void Draw()
 {
  float minimap_width=Screen.width * 0.3f;
  float minimap_height=Screen.height * 0.3f;
  float xOffset = 10.0f;
  float yOffset = 10.0f;
  
  float minimap_left = Screen.width - minimap_width - xOffset;
  float minimap_bottom = Screen.height -  minimap_height - yOffset;
  
  camera.pixelRect = new Rect (minimap_left, minimap_bottom, minimap_width, minimap_height);
 }

 void OnGUI()
 {
  if(marker != null)
  {
   Vector3 markerPos = camera.WorldToViewportPoint(target.transform.position);
   float x = (camera.pixelRect.xMin+camera.pixelRect.xMax) * markerPos.x;
   float y = Screen.height - (camera.pixelRect.yMin + camera.pixelRect.yMax) * markerPos.y;
   GUI.DrawTexture(new Rect(x-marker.width * 0.5f, y-marker.height * 0.5f, marker.width, marker.height), marker, ScaleMode.StretchToFill);
  }
 }
}

The code sets the position and orientation of the "MiniMap Camera" to follow the "target" game object (we will set the "target" game object to the player character later) in the Update() method. The Draw() method determines the location and size of the minimap frame on the game screen.

Since we already exclude the player character 3D model from showing up in the minimap camera (i.e., the culling mask settings earlier), we need to add a marker to the minimap frame to indicate the location of the player character in the minimap. The OnGUI() use the DrawTexture on the marker texture to display a marker representing the player character.

Once the code for the "MiniMapGenerator.cs" is completed and attached to the "MiniMap Camera", we need to assign "target" and "marker" to it. With the "MiniMap Camera" selected in the "Hierarchy" view, drag the player character from "Hierarchy" view to the "target" attribute of the "MiniMapGenerator.cs" in the "Inspector" view. Do the same by dragging a texture for the marker to the "marker" attribute of the component. That is it.

Unity: Spherical Camera

This post discuss a simple implementation of a spherical camera in Unity Game. The complete project source can be downloaded from:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Spherical-Camera.zip

 The spherical camera has the following functionality:

1. When the user presses down the SHIFT+UP, the camera will zoom in to its focus point (i.e. a target point). The camera will zoom out while looking at the target point when user presses down the SHIFT+DOWN keys

2. When the user presses down the LEFT or RIGHT arrow key, the camera will rotate horizontally around the target point.

3. When the user presses down the UP or DOWN arrow key, the camera will rotate vertically around the target point over a range of minimum and maximum angles.

4. When the user hold down the right mouse button and drag on the terrain simultaneously, the target point will move in the same direction, and the camera move accordingly following the direction of the mouse drag motion.

To implement this, first lets create an empty game object named "target", which will act as the the focus/target point of the spherical camera (to make it more visible, a spherical game object is attached as a child to this "target" game object).

Next create a C# script and name it "SphericalCamera.cs". Update its content as shown below:

using UnityEngine;
using System.Collections;

public class SphericalCamera : MonoBehaviour {
 private float initialFOV;

 public float minZoomLimit=0.1f;
 public float maxZoomLimit=10;

 public int distance=100;
 public GameObject target = null;

 private float xRotate;
 private float yRotate;

 public float xRotateSpeed=3.0f;
 public float yRotateSpeed=1.5f;

 public float maxYRotate=90.0f;
 public float minYRotate=0.0f;

 public float xMoveSpeed=100.0f;
 public float zMoveSpeed=100.0f;

 // Use this for initialization
 void Start () {
  Vector3 angles = transform.eulerAngles;
  xRotate = angles.x;
  yRotate = angles.y;

  initialFOV = camera.fieldOfView;

  transform.position = new Vector3 (0, 0, -distance) + target.transform.position;
 }
 
 // LateUpdate is called once at the end of a frame
 void LateUpdate () {
  if(Input.GetKey(KeyCode.RightShift) || Input.GetKey(KeyCode.LeftShift))
  {
   float zoom = camera.fieldOfView - Input.GetAxis ("Vertical") * yRotateSpeed;
   if(zoom >= initialFOV / maxZoomLimit && zoom <= initialFOV / minZoomLimit)
   {
    camera.fieldOfView -= Input.GetAxis ("Vertical") * yRotateSpeed;
   }
  }
  else
  {
   xRotate += Input.GetAxis ("Horizontal") * xRotateSpeed;
   yRotate += Input.GetAxis ("Vertical") * yRotateSpeed;

   if(yRotate > 360) yRotate-=360;
   if(yRotate < -360) yRotate+=360;

   yRotate=Mathf.Clamp(yRotate, minYRotate, maxYRotate);
  }

  if(Input.GetMouseButton(1))
  {
   float target_x = Input.GetAxis ("Mouse X") * xMoveSpeed;
   float target_z = Input.GetAxis ("Mouse Y") * zMoveSpeed;
   Vector3 movement = transform.transformDirection(new Vector3(target_x, 0, target_z));
   movement.y = 0.0f;

   
   target.transform.Translate(movement);
  }

  var rotation = Quaternion.Euler (yRotate, xRotate, 0);
  var position = rotation * (new Vector3 (0, 0, -distance)) + target.transform.position;

  transform.rotation = rotation;
  transform.position = position;
 }
}


Now attach the "SphericalCamera.cs" to the "Main Camera" game object. Next with the "Main Camera" selected in the "Hierarchy" view, drag the "target" game object from the "Hierarchy" view to drop it in the "target" attribute of the "SphericalCamera.cs" in the "Inspector" view of "Main Camera". That is it.

The zooming is performed when the SHIFT key is pressed together with the UP or DOWN arrow key (i.e. KeyCode.LeftShift / KeyCode.RightShift), the change is the zooming is calculated as

Input.GetAxis("Vertical") * yRotateSpeed;

The code is fairly easy to understand once the following methods are understood:

  • Input.GetAxis("Vertical") returns the duration / extent to which the UP or DOWN arrow key is pressed.
  • Input.GetAxis("Horizontal") return the duration / extent to which the LEFT or RIGHT arrow key is pressed
  • Input.GetAxis("Mouse X") return the duration / extent to which the mouse is moved in direction x
  • Input.GetAxis("Mouse Y") return the duration / extent to which the mouse is moved in the direction y
  • Input.GetMouseButton(1) return the boolean value indicating whether the right mouse button has been pressed down.



Tuesday, December 2, 2014

Unity: Switch among cameras

Below is the link to a simple demo of how to switch among different cameras in a Unity game:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Switch-Camera.zip

Firstly, create two more cameras by selecting "Create->Camera" in the "Hierarchy" panel, name them cam1 and cam2. Change their transform position and rotation in the "Inspector" panel, so that they point to different locations in the scene. Now we have 3 cameras: Main Camera (created by default), cam1, and cam2.

Now create a C# script "CameraSwitch.cs" and update its content as follows:

using UnityEngine;
using System.Collections;

public class CameraSwitch : MonoBehaviour {
 public string[] shortcuts;
 public Camera[] cameras;

 // Use this for initialization
 void Start () {

 }
 
 // Update is called once per frame
 void Update () {
  for(int i=0; i < shortcuts.Length; ++i)
  {
   if(Input.GetKeyUp (shortcuts[i]))
   {
    SwitchCamera(i);
   }
  }
 }

 void SwitchCamera(int index)
 {
  for (int i=0; i < cameras.Length; ++i) 
  {
   if(i==index)
   {
    cameras[i].GetComponent<AudioListener>().enabled=true;
    cameras[i].camera.enabled=true;
   }
   else
   {
    cameras[i].GetComponent<AudioListener>().enabled=false;
    cameras[i].camera.enabled=false;
   }
  }
 }
}

Now create an empty game object named "Switchboard" by selecting "Game Object->Create Empty" in the menu. Attach the "CameraSwitch.cs" to it.

Select cam1 in the "Hierarchy" panel and uncheck its "camera" and "Audio Listener" in the "Inspector" panel. Do the same for cam2. This will make the "Main Camera" visible by default when the game is launched.

Select the "Switchboard" game object in the "Hierarchy" panel, and set the size of "shortcuts" and "cameras" in the "CameraSwitch" to 3 in the "Inspector" panel. Now enter "1", "2", "3" in the "shortcuts" array elements and drag the 3 camera objects from the "Hierarchy" panel into the "cameras" array elements in the "Inspector" panel. That is it. When the game is run, by press one of the keys: "1", "2" or "3", the camera will be switched to one of them.

The code is self-explained, whenever, a user press one of the "shortcuts" keys, only the corresponding camera will be enabled while the other two are disabled.

Unity: Camera ZoomIn and ZoomOut

Below is the link to a simple piece of codes which shows how to make the camera in Unity to zoom in and zoom out:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.ZoomIn-ZoomOut.zip

Firstly create a C# Script named "TelescopicView" and update its codes as shown below:

using UnityEngine;
using System.Collections;

public class TelescopicView : MonoBehaviour {
 public int zoomInSpeed=100;
 public int zoomOutSpeed=100;
 public int zoomLevel=2;
 private float initFOV;

 // Use this for initialization
 void Start () {
  initFOV = Camera.main.fieldOfView;
 }
 
 // Update is called once per frame
 void Update () {
  if(Input.GetKey(KeyCode.Mouse0))
  {
   ZoomIn();
  }
  else
  {
   ZoomOut();
  }
 }

 void ZoomIn()
 {
  if(Mathf.Abs(Camera.main.fieldOfView - initFOV / zoomLevel) < 0.5f)
  {
   Camera.main.fieldOfView=initFOV / zoomLevel;
  }
  else if(Camera.main.fieldOfView-(Time.deltaTime * zoomInSpeed) >= initFOV / zoomLevel)
  {
   Camera.main.fieldOfView -= Time.deltaTime * zoomInSpeed;
  }
 }

 void ZoomOut()
 {
  if(Mathf.Abs(Camera.main.fieldOfView - initFOV) < 0.5f)
  {
   Camera.main.fieldOfView=initFOV;
  }
  else if(Camera.main.fieldOfView + (Time.deltaTime * zoomOutSpeed) <= initFOV)
  {
   Camera.main.fieldOfView += Time.deltaTime * zoomOutSpeed;
  }
 }
}

Now attach the "TelescopicView" script to the camera object in the "Hierarchy" panel (e.g., the "Main Camera"). Also with the "Main Camera" selected in the "Hierarchy" panel, select "Component->Camera Control->Camera Look" to add the Camera Look component to the "Main Camera". That's it.

The codes are self-explained, basically the techniques rely on changing the field of view of the camera by decreasing it (zoom in) or increasing it (zoom out). The zoom in happens when the user holds down the left mouse (i.e. KeyCode.Mouse0).

Unity: Screen Capture in Game

Below is the link to a simple piece of exercise which shows how to do a screen capture in a Unity game.

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Screen-Capture.zip

Firstly, create a C# script "ScreenTexture.cs" and update its content as follows:

using UnityEngine;
using System.Collections;

public class ScreenTexture : MonoBehaviour {
 public int photoWidth = 50;
 public int photoHeight = 50;
 public int thumbScale=75;
 private int frameWidth;
 private int frameHeight;
 private int screenWidth;
 private int screenHeight;
 public int borderWidth=2;
 public Color borderColor = Color.white;
 private Texture2D texture;
 private Texture2D border;
 private bool shoot = false;

 // Use this for initialization
 void Start () {
  screenWidth = Screen.width;
  screenHeight = Screen.height;
  frameWidth = Mathf.RoundToInt(screenWidth * 0.01f * photoWidth);
  frameHeight = Mathf.RoundToInt(screenHeight * 0.01f * photoHeight);

  texture = new Texture2D (frameWidth, frameHeight, TextureFormat.RGB24, false);
  border = new Texture2D (1, 1, TextureFormat.ARGB32, false);
  border.SetPixel (0, 0, borderColor);
  border.Apply ();
 }
 
 // Update is called once per frame
 void Update () {
  if (Input.GetKeyUp (KeyCode.Mouse0)) 
  {
   StartCoroutine(ScreenCapture());
  }
 }

 IEnumerator ScreenCapture()
 {
  yield return new WaitForEndOfFrame();
  texture.ReadPixels(new Rect(
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   frameWidth,
   frameHeight), 0, 0);
  texture.Apply();
  shoot=true;
 }

 void OnGUI()
 {
  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   frameWidth,
   borderWidth), border, ScaleMode.StretchToFill);

  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f + frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   borderWidth,
   frameHeight), border, ScaleMode.StretchToFill);

  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f + frameHeight * 0.5f,
   frameWidth,
   borderWidth), border, ScaleMode.StretchToFill);

  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   borderWidth,
   frameHeight), border, ScaleMode.StretchToFill);

  if (shoot) 
  {
   GUI.DrawTexture(new Rect(10, 10, frameWidth * 0.01f * thumbScale, frameHeight * 0.01f * thumbScale), texture, ScaleMode.StretchToFill);
  }
 }
}

Now attach the script to the "Main Camera" in the "Hierarchy" panel in your unity project. Also with the "Main Camera" selected in the "Hierarchy" panel, select "Component->Camera Control->Camera Look" to add the Camera Look component to the "Main Camera". That's it.

The script works as follows. When user click the left mouse, the ScreenCapture() method is runned in a separate thread. In the ScreenCapture method, the texture object is updated (this texture object can be thought of as a in-memory image which holds the screen capture, the actual capturing is done by Texture2D.ReadsPixel() method), and the shoot flag is set to true. In the OnGUI, a frame is drawn at the center of the game screen indicating the area which will be capture, and if the shoot flag is true, then the texture (which now contains the screen capture) will be rendered.