Twitter
Friday
Oct142011

Adventures in iOS Unity

Up until now we've been developing our prototype for the Unity WebPlayer with a thought for the iOS devices. See our blog post about baking down textures. 

This week we decided to sworn the target platform and see how things looked on iOS. 

At first there were some issues with texture format and the bake down code. We also found some problems with getpixels throwing up errors. These didn't take long to fix and pretty soon we had the game game up and running on the simulator. 

Encouraged by this we decided to renew our lapsed iOS Dev status with Apple and build to an iPhone 4. 

The long forgotten process of provisioning profiles and all that goes with it meant a couple of hours of poking things with a stick but finally we got the game to build and run on the device. 

This is where things started to go horribly wrong. 

We use the singleton manager from the unity wiki as a place to store globally accessible variables etc. Turns out that any generic lists  that we held there were not picked up by the game and caused a crash. We fixed this by creating a new script that simply had Static instances of the script and this worked just fine.

A bigger problem was the 4D Array we were using to store all the map data. Now - this was probably a massive waste of memory using something like this - but on a desktop it didn't arise as a problem.

A few hours late we'd converted this 4D Array to a generic list which is probably much more memory efficient but not sure right now about how much of a performance hit it will be to search the list as opposed to directly referencing the index in the array. 

During the conversion we made sure to limit the amount of access needed to this new list and while in the profiler we're seeing some regular spikes, we're pretty sure we can iron them out.

Amazingly this conversion from a 4D array to a list worked first time... 100s of lines of code affected over multiple files. We were floored, but the problems weren't over.

The baking down we do is obviously hitting the iPhones memory pretty hard. Each NPC has it's own unique baked texture, as do the building and all the dropped objects. Sounds expensive but since each NPC has around 7 layers that can be unique it means that each NPC should take one draw call, not 7. The problem is more in the amount of memory being used to generate the textures.

We fudged a few things. Baked less textures for the NPCs. Stopped the baked buildings from being generated and finally got the game to run.

The framerate is far from ideal, hovering around 17fps. With the drawcalls pretty high but for 120 draw calls, 80ish are batched.

Moving forward, are map data structure problem is solved and it's really the backing down for customisation that is an issue. We're still making plans but the current thinking is to use much larger texture pages that contain, say, all of the NPC faces and then instead of backing a new texture we just reposition the UVs to the location of the chosen faces. Same for buildings, same for items.

This method will mean more draw calls on a single object, but since all similar objects use the same material they would be batched. Using our old method 20 NPCs would be 20 draw calls. using the new method the maximum would be 7 total.

We're a little surprised that draw calls are still a huge bottle neck with the iPhone and Unity. Back when we made Doki Doki Bloki we kept everything below 20 and it was fine on an iPhone 3G. We thought the iPhone 4 would have been a lot more forgiving but that assumption was wrong.

Overall we predict about a weeks worth of work to get the game fully working on the iPhone and Web Player at the same time. Not too bad considering the differences in the architectures of the platform. But really - this is more of a cautionary tale for anyone thinking Unity is a pull the switch and it just works solution.

I have to say we're pretty disappointed that the game functioned just fine in the editor and the simulator and not on the device. It probably says more about Apple's iPhone simulator than Unity but the frantic refactoring of our data structures is something we'd rather not face again...

...anyone got an Android phone they can lend to us?? :D

Saturday
Sep242011

Doki Doki Bloki - Soundtrack

No tutorials this weekend as F1 fever has hit Singapore and we're soaking it in! But - we did put up the soundtrack to the iOS puzzle game we made called Doki Doki Bloki.

It's cute, it's bouncy, it's chiptune all the way... hopefully will make you feel happy and retro.

Grab it from http://dokidokigames.bandcamp.com

Have a great weekend!

Sunday
Sep182011

Scripted Cutscenes

This is an unscheduled post, but we couldn't quite keep this to ourselves. Something we've known we've needed for a while was a way to call scripted cutscenes in the prototype. This morning we decided it was time to stop putting it off and start putting it together. 

2 hours later, we had out cutscene manger in place and our first scripted cutscene in the game. Something we would never have expected to happen.

This is really down to all the hard work we've been doing in building a re-usable framework, but it still surprised us how easy it was to put together. 

This isn't going to be a big 'how to' - but we just wanted to share a little of what the code looks like for a cutscene. The scenario his that the player approaches an NPC and the cutscene triggers. In the cutscene the NPC and the player share some lines, turn to a boat, walk to it and jump on. The boat then moves off with the two character onboard. 

And this is the script:

IEnumerator mainCutscene()
{
	lookTargetObjectPos = MrShaw.transform.position;
	StartCoroutine("lookTarget");
	
	ddsl.moveObjToObj(myCam, GameObject.Find("MRSHAWcamera1"), 3f);

	ddsl.rotateObjToObj(player,MrShaw,0.5f);

	ui.showPanel("textBox");

	textBox.setName("Mr. Shaw");
	textBox.setTextCS("Yar *name*. You ready to go to coast town?");
	ddsl.playAnimObj(MrShaw, "TalkMain1");

	while(textBox.displayingText) yield return new WaitForEndOfFrame();

	textBox.setName(globals.instance.playerDB.playerName);
	textBox.setTextCS("Lets go!");
	ddsl.playAnimObj(player, "TalkYes");

	while(textBox.displayingText) yield return new WaitForEndOfFrame();

	ui.hidePanel("textBox");		

	ddsl.moveObjToObj(myCam, GameObject.Find("MRSHAWcamera2"), 3f);

	ddsl.rotateObjToObj(player,boat,0.5f);
	yield return new WaitForSeconds(0.25f);

	ddsl.rotateObjToObj(MrShaw,boat,0.5f);
	yield return new WaitForSeconds(0.25f);
	
	ddsl.moveObjToObj(player, GameObject.Find("MRSHAWPlayerboatNode"),1.5f);
	ddsl.playAnimObj(player, "Run");
	yield return new WaitForSeconds(0.25f);

	ddsl.moveObjToObj(MrShaw, GameObject.Find("MRSHAWShawboatNode"),1.5f);
	ddsl.playAnimObj(MrShaw, "Run");
	yield return new WaitForSeconds(0.25f);
	
	ddsl.playAnimObj(player, "Jump");
	yield return new WaitForSeconds(0.25f);

	ddsl.playAnimObj(MrShaw, "Jump");
	yield return new WaitForSeconds(1f);
	
	player.transform.parent = boat.transform;
	MrShaw.transform.parent = boat.transform;
	
	ddsl.moveObjToObj(myCam, GameObject.Find("MRSHAWcamera3"), 3f);
	ddsl.moveObjToObj(boat, GameObject.Find("MRSHAWBoatNode1"), 6f);
	
	yield return new WaitForSeconds(5f);
	
	Messenger.Broadcast("cameraFade", 0.5f, 1f);
}	

 

Going through the code you should get some idea of how easy it is to set up little story scenes. Here we're manipulating four actors, the Player, 'Mr. Shaw', the Boat, and the Camera. These actors then use nodes in the scene to set them in the right position for the cutscene. 

Obviously there's a lot of backend code in there. All the ddsl calls are to our scripting language we built into Unity and we have code that handles text boxes and the movement of UI elements. But we hope this will give you a little inspiration if you're thinking about how to add simple scripted cutscene to your game. 

Saturday
Sep172011

Using a Server to keep Game Time in Sync

I haxored your social game loser!A fundamental part of any social game is the use of time as a mechanic to keep the player coming back.

Be it harvesting crops in 1 hour or waiting for your energy to restore, time is important - and more importantly it can be hacked. 

Witness the 10 year old girl at Defcon this year who used a simple date time exploits to hack social games on iOS and Android. 

So what can we do to make sure this doesn't happen to us?

Here at Doki Doki Games we use the same server that runs our php code and MySQL databases as a time server.

Whenever the game runs we pull the time from the server.

We also update the time every 60 seconds and whenever the player does anything time related in the prototype we pull the time again.

All time calculations are also done on the sever. If the player sets something 'growing', when it comes to harvesting the time is validated with the time in the database and the time on the server.

To get the time manager working in your code you need an empty GameObject running the time manager code in Unity and a server that runs the php code to pull the time. 

Here's our Unity time manager code:

using UnityEngine;
using System.Collections;
using System;

public class timeController : MonoBehaviour 
{	
	// Server address where the time php is located
	
	string serverTimeURL = "http://<your server>/getTime.php?";
	public Double second = 0;
	
	// Locally stored game time
	static DateTime gameTime;

	void Start () 
	{
		// We set two coroutines going. One for the server time
		StartCoroutine(getServerTime(true));
		// and one to locally increment the time until the server
		// pulls the server time
		StartCoroutine(maintainGameTime());
	}
	
	IEnumerator getServerTime(bool maintain) 
	{
		// call the php on the server and wait for a return
		WWW getWWW;
		getWWW = new WWW (serverTimeURL);
		yield return getWWW;
		
		// We store the server time in a global so it can be
		// accessed by any script. You don't have to do this.
		globals.instance.serverTime = getWWW.text;

		// This pulls the time form the Server every 60 seconds
		
		if(maintain)
		{
			yield return new WaitForSeconds(60f);
			second = 0;
			StartCoroutine(getServerTime(true));
		}
	}
		
	IEnumerator maintainGameTime()
	{	
		while(1==1)
		{
			// game time is a local copy of the sever time that we
			// increment in Unity. Used by the game for countdowns
			// on 'grown' items etc.
			
			gameTime = gameTime.AddSeconds(1);
			yield return new WaitForSeconds(1f);
		}
	}
}

Now for getTime.php:

	// We use Singapore because there's no daylight
	// saving so we never lose or gain an hour
	// all calculations based of of this timezone are
	// relative meaning that you never see the actual 'time'
	
	$timezone = new DateTimeZone( "Asia/Singapore" ); 
	$date = new DateTime(); 

	$date->setTimezone( $timezone );
	$strDate = 	$date->format('Y-m-d\TH:i:s.uP'); 

	echo $strDate;

And that is pretty much all you need to get a basic server based time manager up and running in Unity. 

But back to that 10 year old hacker for a second. Is this code really all that secure? I mean we are asking the server for the time and returning the date/time in a readable text format with no validation.

In theory, since all the code that sets time in the database is run on the server the worst that can happen is the time looking wrong in the game. All the important mechanics of making sure things happen when they should won't be affected by any interception of the data.

However, like John Cusack in Grosse Point Blank we like our code to have its back against the wall and looking out for hitmen, so in the next installment we'll get into encyprting your sever communication (at both ends) so no one, not even Ms. CyFi herself will be able to fool us. 

Saturday
Sep102011

Baking down Textures for Character Customization

Welcome to the first of many little how to’s from Doki Doki Games. Today we’re going to talk about how we do our character customization for our prototype game.

All the NPCs in our prototype are based on the same base mesh that looks like this. 

The art style for the game is very cute so we are able to get away with the same base mesh for both male and female characters with the main difference being the hair model that we attached to the head.

In our prototype we allow the player to customize their character by choosing the following:

  • Skin Tone
  • Face Detail
  • Top
  • Pants
  • Skirt
  • Shoes
  • Hands
  • Hair Color
  • Hair Model 

To do this we divide up the UV’s for the base NPC mesh into distinct regions. 

Note: The Head, Hair, and Skirt are separate models with the hair and the skirt not show attached to the model in this scene.

Using this zoned UV setup we can dynamically copy new texture into distinct regions of the texture without disrupting any of the other customization. For Example the player can change their pants without affecting any of the other clothing.

Let’s see this in the prototype (NOTE: ALL TEXTURES ARE TEMP AND FOR PROTOTYPE PURPOSES ONLY!):

This is the current player character setup as loaded in from the player DB.

We now change the face without effecting the rest of the customization...

...and now we changed the top.

Side Note: All our item preview icons are generated inside Unity automatically for speed of UI development. This is a cool bit of code that snap shots items, color swobs, etc then builds a single texture page per item group with an accompanying text file that holds all the meta-data. All the UI code needs to know is the name of the item to display and it shows up on the browser as an icon. Ultimately all these icons will be replaced by a some better graphics. All the UI artist has do is draw over the created texture page and the new icons are in the game! Magic!

For our character customization to work we have to build the base texture for an NPC on the fly in the game. So let’s look at the code behind it. First we need to setup a few things:

	public Texture2D skinTone;
	public Texture2D faceDetailLayer;
	public Texture2D bodyTopLayer;
	public Texture2D bodyPantLayer;
	public Texture2D bodyShoeLayer;
	public Texture2D bodySkirtLayer;
	public Texture2D hairDetailLayer;
	public Texture2D hairColorLayer;

	public Texture2D texMerge;
	
	GameObject npcBody;
	GameObject npcFace;	
	GameObject npcSkirt;
	GameObject npcHair;	
	
	Material myMat;

We have a number of Texture2D’s that are used in the bake down. These are the individual clothes, skin etc. These textures are only as big as the UV space they have assigned.

We also need to find the objects that make up our base NPC. These are the Body, Face, Skirt and Hair. These textures in our prototype are resources that are loaded based on the player configuration stored in the playerDB. This means every change the player makes is automatically stored in a MySQL database on our server. We’ll get to how we store that in a later tutorial.

The last thing we do in the startup is create a material called myMat.

In the startup of each NPC/Player object we setup the texture we’ll use to bake down the individual textures, assign it to myMat and let each object know to use that material.

	texMerge = new Texture2D (512, 512, TextureFormat.ARGB32, true);
	texMerge.mipMapBias = globals.instance.mipBias;
	npcFace = GameObject.Find(this.name+"/face");
	npcBody = GameObject.Find(this.name+"/body");
	npcHair = GameObject.Find(this.name+"/hair");
	npcSkirt = GameObject.Find(this.name+"/skirt");
		
	myMat = npcBody.renderer.material;
	myMat.shader = Shader.Find("VertexLit");
	myMat.SetColor("_SpecColor", Color.black);
	myMat.SetColor("_Emission", new Color(0.18f,0.18f,0.18f,1f));

	npcFace.renderer.material = myMat;
	npcHair.renderer.material = myMat;
	npcSkirt.renderer.material = myMat;

Note: the mipBias is set by a global so that it can be tweaked for all objects using the bake down textures.

Now we get to the code that bakes down the texture. 

	public void mergeTex()
	{
		ddsl.copyColour(skinTone,texMerge,0,0,512,512);
		
		if(bodyTopLayer != null)
			ddsl.copyTexture(bodyTopLayer, texMerge,
			256,256,256,256);
		
		if(bodyPantLayer != null)		
			ddsl.copyTexture(bodyPantLayer, texMerge,
			0,0,128,256);
		
		if(bodyShoeLayer != null)
			ddsl.copyTexture(bodyShoeLayer, texMerge,
			384,128,128,128);
		
		if(bodySkirtLayer != null)
			ddsl.copyTexture(bodySkirtLayer, texMerge,
			384,0,128,128);
		else
			npcSkirt.renderer.enabled = false;
		
		if(hairColorLayer != null)
			ddsl.copyTexture(hairColorLayer, texMerge,
			256,0,128,128);
		
		if(faceDetailLayer != null)
			ddsl.copyTexture(faceDetailLayer, texMerge,
			0,256,256,256);
		
		myMat.mainTexture = texMerge;		
	}	

We are using a call here to our scripting language (DDSL) to do the texture copying. The code for this is below.

The first thing we copy is the skin. This covers the entire texture and uses no alpha channel. After the skin we sequentially copy all the clothing onto the texture.

This code does the whole NPC body. If we are just affecting a single part of the texture we can wipe out the UV space by copying the base skin tone again to that region and then just copy that required element to that space, for example just the pant texture. 

Note that we do a little error checking to make sure that a texture is assigned before we copy.

Finally here is the code from the DDSL library that handles the texture copying. Notice that we can do a color copy using either a texture as a swob or by directly defining a color we want to use. 

	public void copyTexture(Texture2D source, 
	Texture2D destination, int sourcePosX, 
	int sourcePosY, int destPosX, int destPosY, int amountX, 
	int amountY)
	{
		cols1 = destination.GetPixels();
		cols2 = source.GetPixels(sourcePosX, sourcePosY, amountX, amountY);
		
		count = 0;
		offJump = destination.width - amountX;
		
		offset = destination.width * destPosY + destPosX;
		
		for(int i = 0; i < cols2.Length; ++i)
		{	
			if(cols2[i].a > 0.1) 
			{
				cols1[i+offset] = cols2[i];
			}
			count++;

			if(count == amountX)
			{
				count = 0;
				offset+=offJump;
			}
		}

		destination.SetPixels(cols1);
		destination.Apply();
	}
	public void copyColour(Texture2D source, 
	Texture2D destination, int destPosX, 
	int destPosY, int amountX, int amountY)
	{
		cols1 = destination.GetPixels();
		cols2 = source.GetPixels(0, 0, 1, 1);
		
		count = 0;
		offJump = destination.width - amountX;
		
		offset = destination.width * destPosY + destPosX;
		
		for(int i = 0; i < cols1.Length; ++i)
		{	
			cols1[i+offset] = cols2[0];
			count++;

			if(count == amountX)
			{
				count = 0;
				offset+=offJump;
			}
		}

		destination.SetPixels(cols1);
		destination.Apply();
	}

	
	public void copyColour(Color myCol, 
	Texture2D destination, int destPosX, 
	int destPosY, int amountX, int amountY)
	{
		cols1 = destination.GetPixels();
		
		count = 0;
		offJump = destination.width - amountX;
		
		offset = destination.width * destPosY + destPosX;
		
		for(int i = 0; i < cols1.Length; ++i)
		{	
			cols1[i+offset] = myCol;
			count++;

			if(count == amountX)
			{
				count = 0;
				offset+=offJump;
			}
		}

		destination.SetPixels(cols1);
		destination.Apply();
	}
	

Building textures like this on the fly doesn’t need to just be for NPCs. We use this also for the buildings in the game to allow the player to customize them and have everything on a single texture/material. 

If you have any question or suggestions on how to improve this code and process, please share with us and our readers in the comments. We’d love to hear from you!