Saturday, October 30, 2010

A Unity Shader LOD Script.

using UnityEngine;
using System.Collections;

public class LOD : MonoBehaviour
{
//distances where LOD transitions occur.
public float[] lodLevels = new float[] { 1000f, 800f, 600f, 400f, 200f, 100f, 50f, 10f };

//These are the builtin LOD values for Unity shaders.
int[] shaderLOD = new int[] { 600, 500, 400, 300, 250, 200, 150, 100 };

void Start ()
{
StartCoroutine ("UpdateLOD");
}

void SetLOD (int LOD)
{
foreach (var i in renderer.sharedMaterials) {
i.shader.maximumLOD = shaderLOD[LOD];
}
}

IEnumerator UpdateLOD ()
{
var period = Random.value;
var cam = Camera.main.transform;
float[] levels = new float[lodLevels.Length];
int i = 0;
foreach (var level in lodLevels) {
levels[i++] = level * level;
}
while (true) {
var distance = (transform.position - cam.position).sqrMagnitude;
var LOD = levels.Length - 1;
foreach (var level in levels) {
if (distance > level) {
SetLOD (LOD);
break;
}
LOD--;
}
// make LOD checking non deterministic. avoids stampeding LOD calls?
yield return new WaitForSeconds (period);
}
}

}

Dubious Value of the Social Network.

I've been slowly extricating myself from various social networks for a while now. I've found I get a lot more things done.

I do like to stay on the bleeding edge of technology, in front of the game, so to speak. Being disconnected has not hurt this at all, in my opinion. I've not lost any business opportunities, friends or missed out on some new, important, game-changing tech...

I think the only things I'm missing out on are the latest LOL-cat or some other random funny waste of time. Or some random interesting but ultimately worthless blog post.



The above image is an advert targeting software developers, it is not a paid advert I have place on this site. It's message is clear.

What are social networks good for? Exploitation and making money. They're great for selling unmitigated garbage to the masses, where you watch them gulp it down in micro-payment sized portions and then beg for some more. If you have a product to sell, use the social networks. If you want to achieve things, leave them far behind, or exploit them for what they are.

Friday, October 29, 2010

Generic Manager Component

We've found ourselves using this pattern across a number of projects.
using UnityEngine;
using System.Collections;
 
public class Manager : MonoBehaviour {
 
static public T Instance<T>() where T : Component {
T component;
var g = GameObject.Find("/" + typeof(T).ToString());
if(g == null) {
g = new GameObject(typeof(T).ToString());
component = g.AddComponent<T>();
DontDestroyOnLoad(g);
} else {
component = g.GetComponent<T>();
}
return component;
}
 
}
It is used to create components which persist across level loads, without needing to create GameObjects to hold those components in the editor. It solves the problem where you may need to load levels in a specific order to make sure your persistent game objects are available in the level you wish to test.

Eg, you may have a MusicMixer component which will continue to play music across Application.LoadLevel calls. Instead of creating the MusicMixer in every scene, (or in one scene and marking it with DontDestryOnLoad) you can access it via:
Manager.Instance<MusicMixer>();
And if it does not exist in the level already, it will be created and returned, and also used across subsequent references to the MusicMixer.

Unity3D Mesh Combining Tip

You don't need to use the CombineChildren component anymore. It appears that anything marked as static is now automatically batched.

Excellent.

Monday, October 25, 2010

Generic Thread Safe RingQueue in C#

using System;
using System.Threading;



public class RingQueue
{
private int size;
private int read = 0;
private int write = 0;
private int count = 0;
private T[] objects;
private AutoResetEvent addEvent;
private AutoResetEvent popEvent;

public RingQueue (int size)
{
addEvent = new AutoResetEvent (false);
popEvent = new AutoResetEvent (false);
this.size = size;
objects = new T[size + 1];
}

public bool Empty {
get {
lock (this) {
return (read == write) && (count == 0);
}
}
}

public bool Full {
get {
lock (this) {
return (read == write) && (count > 0);
}
}
}

public void Add (T item)
{
lock (popEvent) {
if (Full) {
popEvent.WaitOne ();
}
objects[write] = item;
count++;
write = (write + 1) % size;
addEvent.Set ();
}

}

public T Pop ()
{
lock (addEvent) {
if (Empty) {
addEvent.WaitOne ();
}
T item = objects[read];
count--;
read = (read + 1) % size;
popEvent.Set ();
return item;
}
}
}

Saturday, October 23, 2010

Proposed Shader for Unity3D

I want to build an fairly high level shader. Here are initial thoughts on features or passes the shader should have.

0. Vertex Colours (RGB)
1. Color Map (RGB Color, A Illumination)
2. Normal Map
3. Specular Map (RGB Color, A Shininess)
5. Environment Cube Map

The specular map is used for environment mapping, in that the Alpha channel shows which parts of the model will be shiny, and therefore reflect the environment. The RGB channels in the specular map are used to tint the colour of the reflection. The reflected texture should follow the distortions created by the normal map.

Vertex colours are used to tint the model, and will be used for fake ambient lighting effects.

Fallback Shader

The shader should fallback to using passes 0,1,2 and 3, Then fallback to 0,1 and 2 and finally fallback to 0 and 1 as quality is reduced.

Thursday, October 21, 2010

RMS in Perth - A Summary.

I recently attended a talk by Richard Stallman, at the Hyatt Hotel in Perth.

A highlight of the night was when fellow Python Enthusiast Brad (from Rockethands) was stopped by Stallman as he arrived late to the talk.

You can read an excellent summary over here.

The best part of the talk was question time. Hearing Stallman adeptly defend his views was quite inspiring.

Wednesday, October 20, 2010

Be an Indie Game Developer in 9 Steps.


  1. Make sure you have more ideas than talent.

  2. Hate the big publishers and anything mainstream.

  3. Join mailing lists with other Indie developers.

  4. Blog and write about awesome games you will make one day.

  5. Whine about scarcity of funding and lack of government handouts.

  6. When you eventually make "something", whine about the lack of sales.

  7. Whine about about the App Store being full of shovel-ware.

  8. Watch other developers make money. Call them sell-outs. They don't deserve the "Indie" badge.

  9. Rinse and Repeat.

Tuesday, October 19, 2010

RMS

Went and saw RMS speak tonight.

Does RMS ever get tired of playing Saint IGNUcius? Imagine telling the same joke for decades; surely it eventually gets a bit stale.

Monday, October 18, 2010

Custom Coroutines in Unity3D.

I had a whim today. In my Python green threading framework (Fibra), it is possible to run a section of code inside the green thread function in another OS level thread. This lets you do fun things like break out of the main thread when waiting for IO, or doing something that might block the rest of the green threads.

In Unity3D-speak, a green thread is called a Coroutine. After doing some rather ugly things with threads today, I figured things could be much cleaner if I could simply execute certain parts of a Coroutine outside the Unity3D main loop. It is possible, and I've done it. Here is the code! :-)

This is the test MonoBehaviour script. Notice that it inherits from Fibra, and uses StartGreenThread to execute methods that look like real Unity3D Coroutines. Inside the Coroutine, a FiBranch instance is yielded, which is the signal for the scheduler to run the next section of the Coroutine in the thread pool.

using UnityEngine;
using System.Collections;
using System.Threading;

public class FibraTest : Fibra {

public IEnumerator Busy() {
Debug.Log("Before Continue.");
yield return new FiContinue();
Debug.Log("After Continue.");
yield return new FiContinue();
Debug.Log("Finished.");
yield return new FiSleep(3);
Debug.Log("Ok really finished. Well with this CPU anyway:" + Thread.CurrentThread.ManagedThreadId.ToString());
yield return new FiBranch();
Debug.Log("I'm in another thread:" + Thread.CurrentThread.ManagedThreadId.ToString());
Thread.Sleep(1000);
yield return new FiContinue();
Debug.Log("Done:" + Thread.CurrentThread.ManagedThreadId.ToString());

}

// Use this for initialization
void Start () {
StartGreenThread("Busy");
}

// Update is called once per frame
public new void Update () {
base.Update();
}
}



This is the Fibra class, which implements the green threading magic.

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


public abstract class FInstruction
{
public abstract bool Execute (Fibra scheduler, IEnumerator task);
}

public class FiContinue : FInstruction
{
public override bool Execute (Fibra scheduler, IEnumerator task)
{
return true;
}
}

public class FiBranch : FInstruction
{
public override bool Execute (Fibra scheduler, IEnumerator task)
{
ThreadPool.QueueUserWorkItem(delegate(object state) {
bool removeTask = false;
try {
if (task.MoveNext ()) {
var fi = task.Current as FInstruction;
removeTask = !fi.Execute (scheduler, task);
} else
removeTask = true;
} catch(Exception e) {
// Do something in here!
}
if(!removeTask) {
scheduler.StartGreenThread(task);
}
});
return false;
}
}

public class FiSleep : FInstruction
{
float seconds;
public FiSleep (float seconds)
{
this.seconds = seconds;
}

IEnumerator Sleep (Fibra scheduler, IEnumerator task)
{
yield return new WaitForSeconds (seconds);
scheduler.StartGreenThread (task);
}

public override bool Execute (Fibra scheduler, IEnumerator task)
{
scheduler.StartCoroutine (Sleep (scheduler, task));
return false;
}
}

public class Fibra : MonoBehaviour
{
static List<IEnumerator> tasks = new List<IEnumerator> ();
static Dictionary<string, IEnumerator> names = new Dictionary<string, IEnumerator> ();


public void StartGreenThread (string taskName, params object[] args)
{
var method = this.GetType ().GetMethod (taskName);
if (args.Length == 0)
args = null;
var task = method.Invoke (this, args) as IEnumerator;
names.Add (taskName, task);
StartGreenThread (task);
}

public void StopGreenThread (string taskName)
{
var task = names[taskName];
Fibra.tasks.Remove (task);
names.Remove (taskName);
}

public void StartGreenThread (IEnumerator task)
{
lock(Fibra.tasks) {
Fibra.tasks.Add (task);
}
}

public void Update ()
{
for (int i = 0; i < Fibra.tasks.Count; i++) {
bool removeTask = false;
var task = tasks[i];
if (task.MoveNext ()) {
var fi = task.Current as FInstruction;
removeTask = !fi.Execute (this, task);
} else
removeTask = true;
if (removeTask)
tasks.RemoveAt (i--);
}
}

}


The above class uses a double dispatch mechanism, which makes it a piece of cake to add your own custom schedule instructions. Simply inherit from FInstruction, and write your own Execute method. Return true if you want the green thread to be rescheduled, or false if you don't.

Thursday, October 07, 2010

ZeroConf Ubuntu Server Setup

Situation: You have a development server (Running the Ubuntu Server OS), an OSX work station, and one network cable.

Task: Enable networking between the two via a network cable so that development can commence!

Action:

1. Plug cable in, notice that network is non existent.
2. Install needed software on server.
sudo apt-get install avahi-daemon avahi-autoipd

3. Modify /etc/network/interface file. Change or add a section like this:
auto eth0
iface eth0 inet ipv4ll
(The ipv4ll is the magic bit.)

4. Restart networking.
sudo /etc/init.d/networking restart

5. Ping yourserver.local from your workstation.


Response: Voila, networking is now working.

Popular Posts