Sunday, 22 December 2013

Learning Windows 8 Game Development Written by Michael Quandt

Packt

I have reviewed one or two books that have been published by Packt publishing, and I have to be honest, the content, while normally accurate, has been a little thin on the ground. I am pleased to say, not with this book!
I am pleased to say this as I have known the author for a number of years. As is the case with on line communities, I have never met him physically, but I have known Michael for many years.

Chr0n1x

I first encountered him when I started to get into XNA on a well know community board called the Hazy Mind, he went by the name Chr0n1x then, board was ran by another XNA/DX guru Micael Schuld. Chr0n1x was one of the main go to guys on that board after the admin, and he helped me a great deal, not just in the early days but even today with various GPU related issues I have. At that time I don’t think Michael was even in college/Uni then but his understanding and knowledge of the GPU and it’s pipeline was hard to beat.

The Book

So, enough of all that, what about this book, we know it’s rich in content, but is it any good? I think it is, it takes you right from the basics of setting up your project and creating a simple game loop along with a graphics device, drawing sprites and using input devices and accelerometers to live tiles, networking publishing to the store and monetization. Now, I know, this all sounds a bit 2D, but in the appendix Michael quickly covers some of the basics of 3D too, but to be honest, you really need the first lot of tools and can create a great 2D game, but you still need the skills from the previous chapters to get your game together whether its 2D or 3D.

Should I buy this book?

If you are not new to C++, but new to DirectX and/or game development and want a great book to get you into it, and take you to an intermediate level then, yes, this is the book for you. If you know C++ and have done some game development, then yes, there is some great stuff in here for building games for Windows 8, personally I am going to find it very useful for padding out a lot of holes in my C++ and DX knowledge.

Anything Missing?

The only thing I would have liked to have seen in this book was audio, it gets a mention, but there is no implementation shown, which is a shame, but you know what, the amount of stuff that’s in here, you can find this out somewhere else anyway.

Where Is It?

You can get a copy of the book here.

Friday, 13 December 2013

Reinventing some of Unity’s Wheels: Terrain

Why?

First of all, why? Why create this when there is a perfectly good terrain package that comes with Unity? Well, I do like to have a mess about and find out what I can do, no other reason than that really, why? Because I can :) I dare say that the Unity Terrain asset is a much better way of creating your terrain, having not really played with it I don’t know, but I would like to implement my version of Geo Clip mapping see here:
And to do that I am going to need to implement height maps and generate the geometry as I need to. Also, later on I may want to procedurally generate my terrain, and I am guessing Ill need something like this.

How?

I started off trying to find out if I could generate my own geometry in Unity, and it turns out you can, this was great news on my quest as it meant I can pass my script a height map and using that to generate the mesh needed to render the terrain.
I started off creating a script called TerrainGenerator in my Unity project, in here I added a number of public parameters so they can be set in the editor and I can pass in the height map and the min and max height of my terrain, the txMod is just a variable I can use to modify the texcoords in the vertex, it’s a throw back to the version I did in XNA, and as you can change an assets tiling in the editor, probably not needed now.
public class TerrainGenerator : MonoBehaviour
{
    public Texture2D HeightMap;

    public float min = 0;
    public float max = 30;

    public float txMod = 4.25f;
I then add an Awake method, this gets called when the script is instanced, in here is where I read the height map and turn it into a mesh, this mesh, if a mesh collider is provided can then be used for that mesh collider, naturally you can’t set this in the editor as the mesh has not been created yet :)
Naturally you will need a mesh filter to store the mesh in, if you don’t Unity will throw an error when you go to set the mesh in it. I then check to see if a height map has been given and if not generate a plane and render that instead.
Here is the script in it’s entirety:-
public class TerrainGenerator : MonoBehaviour
{
    public Texture2D HeightMap;

    public float min = 0;
    public float max = 30;

    public float txMod = 4.25f;

    int myWidth;
    int myHeight;
    float[,] height;



    void Awake()
    {
        MeshFilter meshFilter = GetComponent<MeshFilter>();

        meshFilter.mesh = new Mesh();

        if (HeightMap != null)
        {
            myWidth = HeightMap.width;
            myHeight = HeightMap.height;

            transform.position = new Vector3(transform.position.x - (myWidth / 2), transform.position.y, transform.position.z - (myHeight / 2));

            Color[] heightMapCol = new Color[myWidth * myHeight];

            Vector3[] verts = new Vector3[myWidth * myHeight];
            Vector3[] normals = new Vector3[myWidth * myHeight];
            Vector2[] uv = new Vector2[myWidth * myHeight];
            Vector4[] tan = new Vector4[myWidth * myHeight];
            Color[] col = new Color[myWidth * myHeight];

            height = new float[myWidth, myHeight];

            heightMapCol = HeightMap.GetPixels();

        
            for (int x = 0; x < myWidth; x++)
                for (int y = 0; y < myHeight; y++)
                    height[x, y] = Mathf.Lerp(min, max, heightMapCol[x + y * myWidth].r);


            // Verts
            for (int x = 0; x < myWidth; x++)
                for (int y = 0; y < myHeight; y++)
                {
                    verts[x + y * myWidth] = new Vector3(y, height[x, y], x);
                    normals[x + y * myWidth] = Vector3.up;
                    uv[x + y * myWidth] = new Vector2((float)x / (myWidth / txMod), (float)y / (myHeight / txMod));

                    // blend
                    col[x + y * myWidth].r = Mathf.Clamp(1.0f - Mathf.Abs(height[x, y] - 0) / 8, 0, 1);
                    col[x + y * myWidth].g = Mathf.Clamp(1.0f - Mathf.Abs(height[x, y] - 12) / 6, 0, 1);
                    col[x + y * myWidth].b = Mathf.Clamp(1.0f - Mathf.Abs(height[x, y] - 20) / 6, 0, 1);
                    col[x + y * myWidth].a = Mathf.Clamp(1.0f - Mathf.Abs(height[x, y] - 30) / 6, 0, 1);

                    float totalWeight = col[x + y * myWidth].r;
                    totalWeight += col[x + y * myWidth].g;
                    totalWeight += col[x + y * myWidth].b;
                    totalWeight += col[x + y * myWidth].a;

                    col[x + y * myWidth].r /= totalWeight;
                    col[x + y * myWidth].g /= totalWeight;
                    col[x + y * myWidth].b /= totalWeight;
                    col[x + y * myWidth].a /= totalWeight;
                }

        
            // Calc normals
            for (int x = 0; x < myWidth; x++)
                for (int y = 0; y < myHeight; y++)
                {
                    // Tangent Data.
                    if (x != 0 && x < myWidth - 1)
                        tan[x + y * myWidth] = verts[x + 1 + y * myWidth] - verts[x - 1 + y * myWidth];
                    else
                        if (x == 0)
                            tan[x + y * myWidth] = verts[x + 1 + y * myWidth] - verts[x + y * myWidth];
                        else
                            tan[x + y * myWidth] = verts[x + y * myWidth] - verts[x - 1 + y * myWidth];

                    tan[x + y * myWidth].Normalize();

                    // Normals
                    Vector3 normX = Vector3.one;
                    Vector3 normZ = Vector3.one;

                    if (x != 0 && x < myWidth - 1)
                        normX = new Vector3((verts[x - 1 + y * myWidth].y - verts[x + 1 + y * myWidth].y) / 2, 1, 0);
                    else
                        if (x == 0)
                            normX = new Vector3((verts[x + y * myWidth].y - verts[x + 1 + y * myWidth].y) / 2, 1, 0);
                        else
                            normX = new Vector3((verts[x - 1 + y * myWidth].y - verts[x + y * myWidth].y) / 2, 1, 0);

                    if (y != 0 && y < myHeight - 1)
                        normZ = new Vector3(0, 1, (verts[x + (y - 1) * myWidth].y - verts[x + (y + 1) * myWidth].y) / 2);
                    else
                        if (y == 0)
                            normZ = new Vector3(0, 1, (verts[x + y * myWidth].y - verts[x + (y + 1) * myWidth].y) / 2);
                        else
                            normZ = new Vector3(0, 1, (verts[x + (y - 1) * myWidth].y - verts[x + (y) * myWidth].y) / 2);

                    normals[x + y * myWidth] = normX + normZ;
                    normals[x + y * myWidth].Normalize();
                }

        
            meshFilter.mesh.vertices = verts;
            meshFilter.mesh.tangents = tan;
            meshFilter.mesh.normals = normals;
            meshFilter.mesh.uv = uv;
            meshFilter.mesh.colors = col;

            // Index
            int[] terrainIndices = new int[(myWidth - 1) * (myHeight - 1) * 6];
            for (int x = 0; x < myWidth - 1; x++)
            {
                for (int y = 0; y < myHeight - 1; y++)
                {
                    terrainIndices[(x + y * (myWidth - 1)) * 6 + 5] = ((x + 1) + (y + 1) * myWidth);
                    terrainIndices[(x + y * (myWidth - 1)) * 6 + 4] = ((x + 1) + y * myWidth);
                    terrainIndices[(x + y * (myWidth - 1)) * 6 + 3] = (x + y * myWidth);

                    terrainIndices[(x + y * (myWidth - 1)) * 6 + 2] = ((x + 1) + (y + 1) * myWidth);
                    terrainIndices[(x + y * (myWidth - 1)) * 6 + 1] = (x + y * myWidth);
                    terrainIndices[(x + y * (myWidth - 1)) * 6] = (x + (y + 1) * myWidth);
                }
            }

            meshFilter.mesh.triangles = terrainIndices;
        }
        else
        {

            // Verts
            Vector3[] verts = new Vector3[4];

            verts[0] = new Vector3(-1, 0, -1);
            verts[1] = new Vector3(1, 0, -1);
            verts[2] = new Vector3(-1, 0, 1);
            verts[3] = new Vector3(1, 0, 1);

            meshFilter.mesh.vertices = verts;

            // Index
            int[] index = new int[6];

            index[0] = 0;
            index[1] = 2;
            index[2] = 1;

            index[3] = 2;
            index[4] = 3;
            index[5] = 1;

            meshFilter.mesh.triangles = index;

            // Normals
            Vector3[] normals = new Vector3[4];

            normals[0] = Vector3.up;
            normals[1] = Vector3.up;
            normals[2] = Vector3.up;
            normals[3] = Vector3.up;

            meshFilter.mesh.normals = normals;

            // UV
            Vector2[] uv = new Vector2[4];

            uv[0] = new Vector2(0, 0);
            uv[1] = new Vector2(1, 0);
            uv[2] = new Vector2(0, 1);
            uv[3] = new Vector2(1, 1);

            uv[0] = Vector3.zero;
            uv[1] = Vector3.zero;
            uv[2] = Vector3.zero;
            uv[3] = Vector3.zero;

            meshFilter.mesh.uv = uv;
        }

        MeshCollider mc = GetComponent<MeshCollider>();

        if (mc != null)
            mc.sharedMesh = meshFilter.mesh;
    }

}
Now, a word of warning, in order for this to work you MUST set your heightmap textures in Unity to be readable, by default the texture importer locks it, simply select your height map, choose the “Advanced” texture type and set the “Read\Write” enabled to true. If you don’t do this you will get an error on the line calling GetPixels()
I had a bit of a shock when I first ported this code from XNA, nothing rendered, but I had no errors, then I found out that Unity is left handed, so I just had to reverse the winding order of the index and it worked :) something to be mindful of if you are porting code from XNA.
So, that will generate your mesh AND if you give it a mesh collider, will add it to the Unity Physics system too, it was quite cool to then just add some sphere’s and cubes, give them a rigid body and watch them roll and tumble down the hills :D
So, you can now render the terrain and with a diffuse shader will look something like this:
885017_10151806984342218_1902863551_o[1]
If you look at the code above, you can see I am setting the vertex colour, I am actually not using this as a colour, but a blend weight used to blend the textures as the height of the terrain alters, my custom shader will now use that value to determine what textures should be used where. Also note that Unity (well there may be a way around this, but I am new to Unity so forgive me) will not render large height maps with this technique, if I go for a big height map texture I get this error:
Mesh.vertices is too large. A mesh may not have more than 65000 vertices.
UnityEngine.Mesh:set_vertices(Vector3[])  

Custom Terrain Shader

Again, this was a pretty simple port from my XNA sample to Unity, main issue I had was working out how to extend the vertex structure, but finding you can’t and having to use the Color element of the vertx to store my blend values in. I guess I could calculate them in the shader, but that might be another post ;) Again, the online Unity docs were a massive help with this.
My shader properties are pretty simple, 4 textures along with their bump maps:
Properties
{
  _MainTex ("Dirt", 2D) = "red" {}
  _BumpMap ("Dirt Bumpmap", 2D) = "bump" {}

  _MainTex2 ("Grass", 2D) = "green" {}
  _BumpMap2 ("Grass Bumpmap", 2D) = "bump" {}

  _MainTex3 ("Stone", 2D) = "grey" {}
  _BumpMap3 ("Stone Bumpmap", 2D) = "bump" {}

  _MainTex4 ("Snow", 2D) = "white" {}
  _BumpMap4 ("Snow Bumpmap", 2D) = "bump" {}
}
I did run out of arithmetic instruction so had to set the SM to 3.0 as well as add a vertex fragment to the shader to push on my blend data like this
#pragma surface surf Lambert vertex:vert
#pragma target 3.0
Then we just calculate the texture and bump map values based on the blend settings in the surface shader :)
void vert (inout appdata_full v, out Input o)
{
    UNITY_INITIALIZE_OUTPUT(Input,o);
    o.customColor = abs(v.color);
}

void surf (Input IN, inout SurfaceOutput o)
{

  float3 col = 0;
  col = tex2D(_MainTex,IN.uv_MainTex) * IN.customColor.x;
  col += tex2D(_MainTex2,IN.uv_MainTex) * IN.customColor.y;
  col += tex2D(_MainTex3,IN.uv_MainTex) * IN.customColor.z;
  col += tex2D(_MainTex4,IN.uv_MainTex) * IN.customColor.w;

  o.Albedo = pow(col * .5,1.25);

  float3 n = 0;
  n = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap) * IN.customColor.x);
  n += UnpackNormal (tex2D (_BumpMap2, IN.uv_BumpMap) * IN.customColor.y);
  n += UnpackNormal (tex2D (_BumpMap3, IN.uv_BumpMap) * IN.customColor.z);
  n += UnpackNormal (tex2D (_BumpMap4, IN.uv_BumpMap) * IN.customColor.w);
  o.Normal = n;
}
And shazam! The blighter only worked :D
1425267_10151810425112218_1951188246_o[1]
To implement it, I created an empty game object, gave it the terrain script, a mesh filter, a mesh renderer and a mesh collider, I then changed the shader to be my custom\terrainshader set the params on script and shader as I wanted.
There are a few other bits and bobs in there to iron out and polish, but think I am now ready to move on to that geo clip map implementation :) I hope it’s going to be as easy as I think it will be….
Here is my shader in full
Shader "Custom/TerrainShader"
{
    Properties
    {
      _MainTex ("Dirt", 2D) = "red" {}
      _BumpMap ("Dirt Bumpmap", 2D) = "bump" {}

      _MainTex2 ("Grass", 2D) = "green" {}
      _BumpMap2 ("Grass Bumpmap", 2D) = "bump" {}

      _MainTex3 ("Stone", 2D) = "grey" {}
      _BumpMap3 ("Stone Bumpmap", 2D) = "bump" {}

      _MainTex4 ("Snow", 2D) = "white" {}
      _BumpMap4 ("Snow Bumpmap", 2D) = "bump" {}
    }
    SubShader
    {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
  
      #pragma surface surf Lambert vertex:vert
      #pragma target 3.0

      struct Input
      {
        float2 uv_MainTex;
        float2 uv_BumpMap;

        float4 customColor;
      };
  
      sampler2D _MainTex;
      sampler2D _BumpMap;

      sampler2D _MainTex2;
      sampler2D _BumpMap2;

      sampler2D _MainTex3;
      sampler2D _BumpMap3;

      sampler2D _MainTex4;
      sampler2D _BumpMap4;

      void vert (inout appdata_full v, out Input o)
      {
          UNITY_INITIALIZE_OUTPUT(Input,o);
          o.customColor = abs(v.color);
      }
  
      void surf (Input IN, inout SurfaceOutput o)
      {

        float3 col = 0;
        col = tex2D(_MainTex,IN.uv_MainTex) * IN.customColor.x;
        col += tex2D(_MainTex2,IN.uv_MainTex) * IN.customColor.y;
        col += tex2D(_MainTex3,IN.uv_MainTex) * IN.customColor.z;
        col += tex2D(_MainTex4,IN.uv_MainTex) * IN.customColor.w;

        o.Albedo = pow(col * .5,1.25);

        float3 n = 0;
        n = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap) * IN.customColor.x);
        n += UnpackNormal (tex2D (_BumpMap2, IN.uv_BumpMap) * IN.customColor.y);
        n += UnpackNormal (tex2D (_BumpMap3, IN.uv_BumpMap) * IN.customColor.z);
        n += UnpackNormal (tex2D (_BumpMap4, IN.uv_BumpMap) * IN.customColor.w);
        o.Normal = n;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }
As ever, comments are more than welcome.

[Edit]
The discovery of the class attribute [ExecuteInEditMode] means that my height map terrain can now be viewed in the editor too :D Awesome !!



[/Edit]


Monday, 2 December 2013

Unity3D 4.3 and WCF Web Service

This post is about how I got Unity3D to talk to my WCF web service. I don’t know if this will be of any use in the future for development on the XB1 as I have no idea what MS are going to allow on their network, I am guessing it will be quite locked down, just as it was for XNA on the 360, but felt it was worth looking at anyway, as I am using Unity3D I may want to deploy to other platforms anyway, so this could still be useful.

So, why can’t I just add a service reference and use that?

Well, you can, and in VS it will build, but, Unity3D won’t have a clue what you are trying to reference..

What helped me find the solution I came to?

As you do, I had a mooch on google, and came across a number of post and cross posts that pointed to this method of accessing a web service in Unity. For some reason, when I used this method, Unity3D would complain about the System.ServiceModel namespace, so, I thought, after finding this link saying I can just drop an assembly in and it gets picked up by Unity3D, I dragged and drop System.ServiceModel and System.Runtime.Serialization into my assets script folder. All this seemed to do was throw up another issue, with the HTTP binding. The assemblies I was adding were .NET 3.0, as I now know, Unity3D is really only compliant with 2.0, though I am sure I read somewhere that it’s a bit of a mix between 2.0 and 3.5, what ever the case, it didn’t like me assembles. In the original post I found it mentioned moving the assemblies to the mono folder, naturally I ignored this, I was not using mono, but, it gave me the idea to start looking around the Unity3D mono installation, and I found a set of C# assemblies in there, two of which are the ones I wanted, so, in an act of desperation, I copied those two assemblies over into my assets script folder….and it only bloody worked!

How am I doing it now?

So, create your WCF service as you would normally, I am using .NET4.0 for mine. Before you do anything else, create a WebClient folder in your script folder, I do this just to keep it separate, you could just drop it anywhere in your assets folder I guess.
As in the original post describing how to do this I use svcutil to build the proxy class for my Unity3d scripts to use, I created a very simple cmd file that I can run for this, before you run it, make sure your service is running, or it wont be able to build it.

cd "f:\development\unity\killercore\webservice"
cls
svcutil -out:F:\Development\Unity\KillerCore\Assets\Scripts\WebClient\KillerCoreWebService.cs http://localhost:9997/KillerCoreWebService.svc?wsdl

copy "C:\Program Files (x86)\Unity\Editor\Data\Mono\lib\mono\2.0\System.Runtime.Serialization.dll" "F:\Development\Unity\KillerCore\Assets\Scripts\WebClient\System.Runtime.Serialization.dll"
copy "C:\Program Files (x86)\Unity\Editor\Data\Mono\lib\mono\2.0\System.ServiceModel.dll" "F:\Development\Unity\KillerCore\Assets\Scripts\WebClient\System.ServiceModel.dll"

IMPORTANT: Only run this cmd in a Visual Studio Command prompt.
As you can see it copies my resulting code into my script folder, it then also copies the mono assemblies I need into that folder too. Switch back to Unity3D and it will load the new cs script and dll’s
KCUWSRef
And that is pretty much that, I can now access my WCF web service from with in my Unity3D game.

KillerCoreWebService Call

My very simple service has one method at the moment called GetHighScoreTable, and for the sake of testing looks like this:
public HighScoreResponse GetHighScoreTable()
{
    HighScoreResponse hst = new HighScoreResponse();

    for (int x = 0; x < 10; x++)
        hst.HighScoreTable.Add(new HighScoreEntity(string.Format("High Score {0}", x + 1), (x + 1 * 501).ToString()));

    return hst;
}
So, my response class holds a list or array of HighScoreEntity, this “entity” just holds a name and a value for a high score, naturally I would wire this up to a database on my server, but this will do for my test, just passing back 10 values.

Client side use of the Web Service

I have a globals script that I use to hold stuff I want to use all over the place, so, the first thing I need is a client object to connect to the service, like this
static KillerCoreWebServiceClient client = new KillerCoreWebServiceClient(new BasicHttpBinding(BasicHttpSecurityMode.None), new EndpointAddress("http://localhost:9997/KillerCoreWebService.svc"));
This client is connecting to the service running on my system, so you would alter the url to point at the service on your server. I then create a static method I can use to call a method on the service.
public static List<HighScoreEntity> GetHighScores()
{
    HighScoreResponse response = client.GetHighScoreTable();

    return response.HighScoreTable.ToList();
}
So, this simply goes off to the web service and gets the high score table.. Now in my test UI I wire it up under a button click.
if (GUI.Button(new Rect(0, 0, 100, 50), "Get Highscores"))
{
    scores = Globals.GetHighScores();
}

if (scores.Count > 0)
{
    GUI.Label(new Rect(10, 110, 100, 20), string.Format("{0} Records", scores.Count));
    int cnt = 0;
    foreach (HighScoreEntity score in scores)
    {
        cnt++;
        GUI.Label(new Rect(10, 110 + (cnt * 20), 100, 20), string.Format("{0} - {1}", score.Name, score.Score));
    }
}
scores is just a list of HighScoreEntity, once populated I display how many records have been returned and the records returned.
1398370_10151784623107218_659661404_o[1]
So, after all my moaning and whinging, it turned out not to be that difficult to set up Unity3D so it can access a WCF web service. I hope this helps make your life a bit easier integrating WCF services into your Unity3D projects. If you find a better way, or have issues with this method, then let me know.

[UPDATE]
Tested this for the Web player and it does not work, I guess just the full executable version, if I do need WS ill end up having to write a post/get class to pull stuff back from a server, unless Unity3D gets an update to the .NET framework I guess.
[/UPDATE]