Monday, January 31, 2011

Spaceship + Spacebox = Awesome


A screenshot of my spacebox generator, a Toon freighter model and a Crease fullscreen shader, which adds the outline to the silhouette of the ship.

UnityWeb - Why does it exist?

The latest version of UnityWeb now works with iOS, Mac, PC, and WebPlayer builds of Unity3D games.

If you haven't done a lot of work with HTTP, you are probably wondering, "Why is UnityWeb needed?".

Here is a contrived, yet realistic scenario which demonstrates something you can do with UnityWeb, but is impossible with the WWW class.

Posit: Your game needs to download an avatar image for each player and use it as a texture. A player can update their avatar image, so you must periodically check to see if the image has changed.

Using the Unity3D WWW class, you might do something like this:
for each member
download avatar image
update avatar texture

If you have 50 avatars in one game, you will download 50 different images. Every 10 minutes or so, you re-download the images.

Using UnityWeb, you can do something better.
for each member
download avatar image
if image has changed, update avatar texture
UnityWeb will only redownload a URL if it has changed since you last downloaded it. UnityWeb uses the magic of Etags to make this work. To make this type of caching happen, you simply add:
request.useCache = true;
This results in faster response times, less web traffic, and you could poll much quicker than every 10 minutes, as only changes will be downloaded.

Sunday, January 30, 2011

My last Game Jam...

The Global Game Jam event in Perth is about to finish, and at least for the foreseeable future, I believe it will be my last.

If I remember correctly, I think I've run about 8 GameJam events over the last three years. It is time for me to do something else.

Perlin Game Sound Track.

A procedural ambient soundtrack for my space game. Generated using a Perlin driven sequencer.

You can never have too much lens flare.


I am sure it is the most underused effect in the industry.

Saturday, January 29, 2011

Procedural Space Skybox for Unity3D.



Space is awesome. Especially when it is generated using Perlin noise, and some cool shaders. You can try it out over here.

Friday, January 28, 2011

The Unity3D Animation Window

Some Tips we've learned the hard way.

1. You can hover over the time axis and use the scroll wheel to scale time, and fit your entire curve in one window.

2. Using the scroll wheel anywhere else will scale time AND space in a uniform manner.

3. The bottom left drop box in the animation window will let you display animated values only, which helps when searching for a curve.

4. You don't edit an animation by selecting it as an asset, you need to select the game object it is attached to in order to edit it.

5. In the top right, next to name of the game object you are editing, you can see a drop down which lets you pick which animation to work on.

Wednesday, January 26, 2011

UnityWeb 0.1

The Unity3D WWW alternative is now half baked and ready for preliminary tasting.

I've just added support for HTTP redirects, and some safer exception handling. Oh, it supports chunked transfer encoding too!

Update: ARRGH seems like more work is needed for it can be used in a WebPlayer. It works with iOS however. :-(

Music Post #1

I've been creating music since I discovered Scream Tracker 3 in 1995. I moved to Impulse Tracker soon after, finally ending up with Buzz Tracker on Windows.

After a long hiatus, I'm now back producing purely with the iPad, and it's amazing selection of audio software, which can inter-operate quite well.

This track came together in the early hours of this morning. It is pretty raw, and produced in a live situation. No postprocessing or arranging, it is all done on the fly.

Late night oddity by replicator

Tuesday, January 25, 2011

Error Reporting from your Unity3D game.

This is something I use to capture errors and log-information from games that are out in the wild, and report them back home to my server.

using UnityEngine;
using System.Collections;


public class ErrorReporter : MonoBehaviour
{

string debugText = "";
string postURL = "";

void Awake ()
{
Application.RegisterLogCallback (new Application.LogCallback (CaptureLog));
}

IEnumerator SendDebugToServer ()
{
WWW www = new WWW (postURL, System.Text.ASCIIEncoding.ASCII.GetBytes (debugText));
yield return www;
}

void CaptureLog (string condition, string stacktrace, LogType type)
{
string sep = "------------------------------------------------------------------------------\r\n";
debugText = sep + type.ToString () + " " + Time.realtimeSinceStartup.ToString () + "\r\n" + condition + "\r\n" + stacktrace + debugText;
if (type == LogType.Exception) {
StartCoroutine (SendDebugToServer ());
if (!Application.isEditor) {
SomethingReallyBadHappened ();
}
}
}

void SomethingReallyBadHappened ()
{
//NB: Try and recover or fail gracefully here.
}

}


On the server side, you could use a very simple piece of PHP to receive the request and write it to a file.


$data = file_get_contents('php://input');
$file = "reports/report_" . time() . ".txt";

$fp = fopen($file, "w") or die("Couldn't open $file for writing!");
fwrite($fp, $data) or die("Couldn't write values to file!");

fclose($fp);
echo "Saved to $file successfully!";

Free Unity3D Pie Menu System!

I accidentally released my Pie Menu System for free on the awesome Unity asset store!

Oops.

Pie Menu's are awesome for adding context sensitive actions to your game world, and this particular package really shows the beautiful architecture behind Unity, and why it is great for developers. I've resubmitted the package with a token price... but in the meantime you can get it for FREE! :-) If you use it, let me know!

Friday, January 21, 2011

How to have an awesome office.


Install a TRON poster. (The other posters are just placeholders for more TRON posters when we get them.)

Unity3D Coroutines.

One of the great things about Unity3D, is the ability to turn the component Start method into a coroutine, simply by changing the return type of Start() to an IEnumerator.

In the example below, the Start method is changed into a coroutine which turns on it's particle emitter, waits for T seconds, then turns it off. Without coroutines, this would be much messier.

using UnityEngine;
using System.Collections;

public class ParticleBurst : MonoBehaviour {
public float T = 1;

IEnumerator Start() {
particleEmitter.emit = true;
yield return new WaitForSeconds(T);
particleEmitter.emit = false;
}

}

The Unity3D Asset Pipeline.

One of the great things about Unity3D, is the programmable content pipeline. I wrote this script so that when I add a texture named "x_SPECULAR.png" to my project, it looks for a texture named "x_DIFFUSE.png", and then copies the specular texture into the alpha channel of the diffuse texture (so that it can be used in a Unity3D specular shader). This automates what would usually be a tedious Photoshop task.

using UnityEngine;
using UnityEditor;
using System.Collections;

class ModifyTextures : AssetPostprocessor
{

void OnPostprocessTexture (Texture2D texture)
{
if (assetPath.Contains ("_SPECULAR")) {
var diffusePath = assetPath.Replace ("_SPECULAR", "_DIFFUSE");
var bytes = System.IO.File.ReadAllBytes (diffusePath);
var diffuse = new Texture2D (texture.width, texture.height);
diffuse.LoadImage (bytes);
var colors = diffuse.GetPixels ();
var specular = texture.GetPixels ();
if (colors.Length == specular.Length) {
Debug.LogWarning (assetPath + ": Adding specular values to diffuse map alpha.");
for (var i = 0; i < colors.Length; i++) {
colors[i].a = (specular[i].r + specular[i].g + specular[i].b) / 3;
}
diffuse.SetPixels (colors);
diffuse.Apply ();
System.IO.File.WriteAllBytes (diffusePath, diffuse.EncodeToPNG ());
AssetDatabase.ImportAsset (diffusePath);
AssetDatabase.DeleteAsset(assetPath);
} else {
Debug.LogWarning (assetPath + ": specular size must match diffuse size.");
}
return;
}
}
}

Tuesday, January 18, 2011

More Unity3D WWW BADNESS!

Dear Unity Technologies,

yield return www;
try {
var bundle = www.assetBundle;
} catch {
//surely if the bundle load fails, I can catch an exception here?
}


No, you can't. Bundle load fail = silent failure, unless you want to PARSE THE ERROR LOG!

Another reason to dump the WWW class and let us do it properly. Please, can give us the ability to load assets from byte streams, and avoid this WWW crock?

Saturday, January 15, 2011

Planet Shader #2


Possibly slightly more correct lighting. The rim light is now only applied in the direction of the sun, rather than being purely based on view position.
Shader "Planet" {
Properties {
_MainTex ("Diffuse(RGB) Spec(A)", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
_RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
_RimPower ("Rim Power", Range(0,8.0)) = 3.0
_SpecColor ("Specular Color", Color) = (0.5,0.5,0.5,1)
_Shininess ("Shininess", Range (0.01, 1)) = 0.078125

}

SubShader {
Tags { "RenderType" = "Opaque" }

CGPROGRAM
#pragma surface surf Planet

float _Shininess;
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;

half4 LightingPlanet (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
half3 h = normalize (lightDir + viewDir);
half diff = max (0, dot (s.Normal, lightDir));
float nh = max (0, dot (s.Normal, h));
float spec = pow (nh, 48.0);
half rim = ((1 - (dot (normalize(viewDir), (s.Normal)))) + ((dot (normalize(lightDir), (s.Normal)))));
half4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec * s.Alpha * _Shininess * _SpecColor) * (atten * 2);
c.rgb = c.rgb * (pow (rim, _RimPower) * _RimColor.rgb);
return c;
}

struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
float3 worldRefl; INTERNAL_DATA
};

void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
o.Alpha = tex2D (_MainTex, IN.uv_MainTex).a;
}
ENDCG

}
Fallback "Diffuse"
}

Thursday, January 13, 2011

Unity3D 3.0 Planet Shader


Update: This is another planet shader, with more physical fidelity.
Shader "Planet" {
Properties {
_MainTex ("Diffuse(RGB) Spec(A)", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
_RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
_RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
_SpecColor ("Specular Color", Color) = (0.5,0.5,0.5,1)
_Shininess ("Shininess", Range (0.01, 1)) = 0.078125
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM

#pragma surface surf SimpleSpecular
float _Shininess;

half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
half3 h = normalize (lightDir + viewDir);
half diff = max (0, dot (s.Normal, lightDir));
float nh = max (0, dot (s.Normal, h));
float spec = pow (nh, 48.0);
half4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec * s.Alpha * _Shininess * _SpecColor) * (atten * 2);
c.a = s.Alpha;
return c;
}

struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;

void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
o.Alpha = tex2D (_MainTex, IN.uv_MainTex).a;
}
ENDCG
}
Fallback "Diffuse"
}

Wednesday, January 12, 2011

Unity3D WWW Replacement.

Due to my rage at the stupid WWW class in Unity3D, I have started coding a replacement. This is needed, because Unity3D on iOS or Web devices will not use System.Net.WebRequest due to security concerns. RAAARGH!

This is a WIP Unity3D WWW Replacement class, which works like a real HTTP class should. It is not entirely usable, however it will evolve and get better. At least it has been started.

Store objects in PlayerPrefs.

Usage:
var myObject = new MyClass();
Prefs.Save<MyClass>("my object", myObject);
var anotherObject = Prefs.Load<MyClass>("my object")


or something like that...

using UnityEngine;
using System.Collections;
using System.Xml.Serialization;
using System.IO;


public class Prefs
{

public static void Save<T> (string name, T instance)
{
XmlSerializer serializer = new XmlSerializer (typeof(T));
using (var ms = new MemoryStream ()) {
serializer.Serialize (ms, instance);
PlayerPrefs.SetString (name, System.Text.ASCIIEncoding.ASCII.GetString (ms.ToArray ()));
}
}

public static T Load<T> (string name)
{
if(!PlayerPrefs.HasKey(name)) return default(T);
XmlSerializer serializer = new XmlSerializer (typeof(T));
T instance;
using (var ms = new MemoryStream (System.Text.ASCIIEncoding.ASCII.GetBytes (PlayerPrefs.GetString (name)))) {
instance = (T)serializer.Deserialize (ms);
}
return instance;
}

}

Tuesday, January 11, 2011

Asynchronous Unity3D Components.

If you're using async callbacks in .Net, you need a safe way to get those callbacks to modify things inside your Unity engine.

Inherit from this class, then use ScheduleCallback to schedule an anonymous function which will run in the main Unity thread. Problem solved!

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



public class AsyncComponent : MonoBehaviour
{
public delegate void Anonymous ();
List callbacks = new List ();

public virtual void Update ()
{
Anonymous[] c;
lock(this) {
c = callbacks.ToArray();
callbacks.Clear();
}
foreach(var i in c) i();
}

public void ScheduleCallback(Anonymous fn) {
lock(this) {
callbacks.Add(fn);
}
}
}

Thursday, January 06, 2011

Unity3D - WWW support is broken.

Every now and then, I come across something so abysmally stupid that I... I just have to whinge about it to the planet.

Dear Unity Technologies,

The WWW class in Unity3D is rubbish. It is missing a critical feature, that is, the ability to add or modify headers on an outgoing GET web request. Without this feature, any non-trivial usage of HTTP is IMPOSSIBLE. Try it, and you get this:
Error when creating request. GET request with custom headers is not supported.
It would have taken longer to write the code to raise this exception than to actually code the functionality, so WHY IS IT MISSING!?!?! And it's been like this for A LONG TIME SO FIX IT ALREADY!

please.

/rant.

PS: While you're adding the above code, please expose the HTTP STATUS CODE so that I can actually do REAL HTTP WORK, like for instance. distinguish between a 200 response and a 404 response? THAT WOULD BE VERY HANDY!!@#!@#!

PPS: BTW, there is no need to do a DNS query every time a web request is made. It makes things run like treacle up a hill. You can cache things like that. Honestly, did an intern/monkey write this?

PPPS: If you fancy, please, send me the relevant code and I'll do the job.

PPPPS: You took too long. http://code.google.com/p/unityweb/source/checkout

Thanks.

Tuesday, January 04, 2011

Pony Expired.

I arrived back in the office this morning. Someone killed our pony.


RIP Pink Pony. You will be avenged!

Popular Posts