Unity Debug.Log with multiple arguments

Javascript has neat debugging function console.log that accepts multiple variables which makes easy to compose and modify debug output. It’s easy to do same kind of utility script for the Unity.

public class Console
{
    public static void Log(params object[] a)
    {
        var s =a[0].ToString();
        for ( int i = 1; i < a.Length; i++ ) {
            s += " ";
            s += a[i].ToString();
        }
        Debug.Log(s);
    }
}

Now it’s easy to write debug strings like this

var i = 4;
var a = "the";
Console.Log("Hello", i, a, "World");
// => "Hello 4 the World"

Instead of this crappy string concatenation..

var i = 4;
var a = "the";
Debug.Log("Hello " + i.ToString() + " " + a + "World");
// "Hello 4 theWorld" .. forgot one space :(

Quickstart for SQLite in Unity

I had some troubles setting up SQLite for my Unity project for Android and iOS app. Let’s hope this quickstart helps you faster up to speed with your mobile app.

DISCLAIMER. This example is for OS/X development environment and Android and iOS builds. Never tried this for Windows but I guess installing sqlite3.dll should do the trick.

Step 1. Get wrapper API

First, get the SQLite library by @busta117 from Github: https://github.com/Busta117/SQLiteUnityKit

Step 2. Copy API files on your project

Copy the DataTable.cs and SqliteDatabase.cs somewhere under your projects Assets/Scripts/ folder. If you build also for android, then copy libsqlite3.so in your projects Assets/Plugins/Android/ folder. iOS does not need plugin as it has native support for sqlite.

Step 3. Create default database

This is the database that should contain the tables you need with any default data you may want to have. Default database is used to bootstrap the actual in app database.

Create folder Assets/StreamingAssets/. Then create your default template database with sqlite3.

$ sqlite3 Assets/StreamingAssets/default.db
SQLite version 3.8.8.3 2015-02-25 13:29:11
Enter ".help" for usage hints.
sqlite> create table example (
   ...> name string,
   ...> dummy int
   ...> );
sqlite> .schema example
CREATE TABLE example (
name string,
dummy int
);
sqlite> insert into example values ("hello world", 1);
sqlite> select * from example;
hello world|1
sqlite>.quit
$

Now you have default database and your project should have files like these.

./Assets/Plugins/Android/libsqlite3.so
./Assets/Scripts/SQLiteUnityKit/DataTable.cs
./Assets/Scripts/SQLiteUnityKit/SqliteDatabase.cs
./Assets/StreamingAssets/default.db

Step 4. Database initialization code.

Initialize the database in your main scripts Awake() method. This checks if database already exists and if not, it copies the default db as template.


SqliteDatabase sqlDB;

void Awake() 
{
    string dbPath = System.IO.Path.Combine (Application.persistentDataPath, "game.db");
    var dbTemplatePath = System.IO.Path.Combine(Application.streamingAssetsPath, "default.db");

    if (!System.IO.File.Exists(dbPath)) {
        // game database does not exists, copy default db as template
        if (Application.platform == RuntimePlatform.Android)
        {
            // Must use WWW for streaming asset
            WWW reader = new WWW(dbTemplatePath);
            while ( !reader.isDone) {}
            System.IO.File.WriteAllBytes(dbPath, reader.bytes);
        } else {
            System.IO.File.Copy(dbTemplatePath, dbPath, true);
        }		
    }
    sqlDB = new SqliteDatabase(dbPath);
}

You can use the script execution order setting to ensure that this code is always executed first.

Step 5. Use the database!

API supports normal selects, inserts and updates.

result = sqlDB.ExecuteQuery("SELECT * FROM example");
row = result.Rows[0];
print("name=" + (string)row["name"]);
print("dummy=" + (int)row["dummy"]);

API is simple to use, check detailed documentation from the https://github.com/Busta117/SQLiteUnityKit.

Class Persistence in Unity

Games need to store some persistent data like high scores and progress between the game sessions. Fortunately Unity gives us PlayerPrefs class that is essentially a persistent hash map.

Reading and writing values with PlayerPrefs is as simple as calling get and set.

// saving data
PlayerPrefs.SetInt("foobar", 10);
PlayerPrefs.SetString("something", "foo");
PlayerPrefs.Save();
...
// reading data
if(PlayerPrefs.HasKey("foobar")) {
    int foo = PlayerPrefs.GetInt("foobar");
}

It has its limitations, only strings and numbers can be stored and that makes more complex data lot more difficult to maintain.

What we can do is to write simple utility that can be used to serialize classes to strings that can then be read and written with PlayerPrefs. SerializerUtil is static class with two methods to Load and Write object. In case loading fails it returns default value of the data, usually null.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public static class SerializerUtil {
	static BinaryFormatter bf = new BinaryFormatter ();
	
	public static T LoadObject<T>(string key)
	{
		if (!PlayerPrefs.HasKey(key))
			return default(T);
		
		try {	
			string tmp = PlayerPrefs.GetString(key);
			MemoryStream dataStream = new MemoryStream(Convert.FromBase64String(tmp));
			return (T)bf.Deserialize(dataStream);
			
		} catch (Exception e) {
			Debug.Log("Failed to read "+ key+ " err:" + e.Message);
			return default(T);
		}				
	}
	
	public static void SaveObject<T>(string key, T dataObject)
	{
		MemoryStream memoryStream = new MemoryStream ();
		bf.Serialize (memoryStream, dataObject);
		string tmp = Convert.ToBase64String (memoryStream.ToArray ());
		PlayerPrefs.SetString ( key, tmp);
	}
}

To load and save your classes, declare them as Serializable.

[Serializable]
public class PlayerData {
	public int points;
	public string name; 		
}

You can then easily store instances of the class.

var data = new PlayerData();
data.points = 50;
data.name = "Teemu";

SerializerUtil.SaveObject("player1", data);

Reading classes is also simple

PlayerData data;
data = SerializerUtil.LoadObject<PlayerData>("player1");
// data.points is 50
// data.name is "Teemu"

Classes can be also more complicated, you can add member functions and also exclude some member variables from serialization. It’s also possible to define member function that will be called after serialization, it’s good way to init class after deserialization from disk.

[Serializable]
public class PlayerData : IDeserializationCallback {
	public int points;
	public string name;
    
	// serialization ignores this member variable
	[NonSerialized] Dictionary<int, int> progress = new Dictionary<int, int>();

	// constructor is called only on new instances
	public PlayerData() {
		points = 0;
		name = "Anon";
	}
	
	// serializable class can have member methods as usual
	public bool ValidName() 
	{
		return name.Trim().Length > 4; 
	}

	// Called only on deserialized classes
	void IDeserializationCallback.OnDeserialization(System.Object sender) 
	{
	    // do your init stuff here
	    progress = new Dictionary<int, int>();	
	}
}

Caveats

This serialization does not support versioning. You can not read the stored instance anymore if you change the class members as the LoadObject will fail to deserialize the data.

You need to add following environment variable to force Mono runtime to use reflection instead JIT. Otherwise the serialization will fail on iOS devices. Do this before doing any loading or saving of classes.

void Awake() {
    Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
    ...

Lightweight CSV reader for Unity

Managing game data in Unity scene objects can get really painful especially when more than one people needs to edit the same thing. It’s usually better to have some data in CSV file where it can be controlled centrally.

When facing this problem I couldn’t find CSV reader for Unity that would have been exactly what I need. i.e. not be very buggy, huge or require new assembly dependencies.
So here is very simple CSV reader for Unity. It is bare bones but still robust enough parse quoted text and comma’s inside text. Reader assumes that csv files are in Resources folder of your Unity project.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class CSVReader
{
	static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
	static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";
	static char[] TRIM_CHARS = { '\"' };

	public static List<Dictionary<string, object>> Read(string file)
	{
		var list = new List<Dictionary<string, object>>();
		TextAsset data = Resources.Load (file) as TextAsset;

		var lines = Regex.Split (data.text, LINE_SPLIT_RE);

		if(lines.Length <= 1) return list;

		var header = Regex.Split(lines[0], SPLIT_RE);
		for(var i=1; i < lines.Length; i++) {

			var values = Regex.Split(lines[i], SPLIT_RE);
			if(values.Length == 0 ||values[0] == "") continue;

			var entry = new Dictionary<string, object>();
			for(var j=0; j < header.Length && j < values.Length; j++ ) {
				string value = values[j];
				value = value.TrimStart(TRIM_CHARS).TrimEnd(TRIM_CHARS).Replace("\\", "");
				object finalvalue = value;
				int n;
				float f;
				if(int.TryParse(value, out n)) {
					finalvalue = n;
				} else if (float.TryParse(value, out f)) {
					finalvalue = f;
				}
				entry[header[j]] = finalvalue;
			}
			list.Add (entry);
		}
		return list;
	}
}

Drop this in your Scripts folder as CSVReader.cs and make folder Resources under your Assets folder. Put there any CSV files you want to use.

Example CSV file example.csv in Resources folder.

name,age,speed,description
cat,2,4.5,"cat stalks, jumps and meows"
dog,2,5.5,dog barks
fish,1,1.1,fish swims

How to use

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Main : MonoBehaviour {

	void Awake() {

		List<Dictionary<string,object>> data = CSVReader.Read ("example");

		for(var i=0; i < data.Count; i++) {
			print ("name " + data[i]["name"] + " " +
			       "age " + data[i]["age"] + " " +
			       "speed " + data[i]["speed"] + " " +
			       "desc " + data[i]["description"]);
		}

	}

	// Use this for initialization
	void Start () {
	}

	// Update is called once per frame
	void Update () {

	}
}

See full example code in Github: https://github.com/tikonen/blog/tree/master/csvreader