Sunday, 30 June 2013

Quaternion Axes

It's been a while since I last posted so before I get in to the topic of this post I'll mention what I've been up to as it has some relevance.

I have been working on adding network game play.  It did not take me long to realise that the way I originally did the physics in the game, that works for single player and split screen, does not work for a network client server based game!

My mistakes were that I used variable time step with a lot of relatively simple maths to ensure all movement was smooth and I used a single state for working out what the character was doing. I have now learnt that I need to keep a history of the movement and states of the entities and use the state at a point in time to best approximate where all the characters are in relation to each other and what they were doing at that same point in the past.

I have nearly finished making those changes so at least the single player game works with a history of states.  In the process I changed the way the movement physics was calculated.  Probably not essential but I came across better ways of doing things while I was looking in to networked physics.

I ended up using more quaternions than I had previously.  I still convert to matrices in several places to get some results but where possible I have tried to leave the quaternions as quaternions and use quaternion maths to get the results.  Just in case it was not obvious, I am not very familiar with quaternion maths.  I think I now get it but basically I look up formulae on the Internet and convert them to C# code to get the results I need.

The XNA Matrix structure has methods to get the Forward, Right and Up axes vectors from the matrix.  The Quaternion structure does not!  With a bit of research I found some C++ code that nearly got the axes directly from a quaternion and with a minor change and some testing I ended up with:



/// 
/// Returns the forward vector from a quaternion orientation.
/// 
public static Vector3 Forward(Quaternion q)
{
    return new Vector3(
      -2 * (q.X * q.Z + q.W * q.Y),
      -2 * (q.Y * q.Z - q.W * q.X),
      -1 + 2 * (q.X * q.X + q.Y * q.Y));
}
/// 
/// Returns the up vector from a quaternion orientation.
/// 
public static Vector3 Up(Quaternion q)
{
    return new Vector3(
      2 * (q.X * q.Y - q.W * q.Z),
      1 - 2 * (q.X * q.X + q.Z * q.Z),
      2 * (q.Y * q.Z + q.W * q.X));
}
/// 
/// Returns the right vector from a quaternion orientation.
/// 
public static Vector3 Right(Quaternion q)
{
    return new Vector3(
      1 - 2 * (q.Y * q.Y + q.Z * q.Z),
      2 * (q.X * q.Y + q.W * q.Z),
      2 * (q.X * q.Z - q.W * q.Y));
}


I am sure any professional developers will understand the importance of testing.  I may not be a professional but from experience I know I cannot trust my own typing or other people's sample code or maths.  To ensure the results are as intended I have validated the code above using versions of the following method, one for each axis.  I am not sure if this is 'Unit Testing' but it is my interpretation.



/// 
/// Check that the result of the quaternion elements is facing where expected.
/// The class containing the methods is called MoreMaths
/// 
public static void ValidateQuarternionForward()
{
    const float acceptable = 0.0009f;
    System.Diagnostics.Debug.WriteLine(
      "\n== Start Test ===============================================");
    System.Diagnostics.Debug.WriteLine(
      "== MoreMaths.ValidateQuaternionForward() =====================");

    // List of values to test
    Vector3[] positions = new Vector3[] 
    { 
        new Vector3(10, 20, 15),
        new Vector3(15, -5, 12),
        new Vector3(10, 20, -12),
        new Vector3(-10, 20, 15),
        new Vector3(2, 1, 0),
    };

    Vector3[] facing = new Vector3[] 
    { 
        new Vector3(10, 9, 20),
        new Vector3(25, -5, 12),
        new Vector3(10, 17, -19),
        new Vector3(-12, 20, 15),
        new Vector3(2, 1, 10),
    };

    int countTotal = 0;
    int countFails = 0;

    foreach (Vector3 location in positions)
    {
        foreach (Vector3 target in facing)
        {
            countTotal++;

            Matrix orient = 
              Matrix.CreateLookAt(location, target, Vector3.Up);
            Quaternion face = 
              Quaternion.CreateFromRotationMatrix(orient);
            face.Normalize();
            orient = Matrix.CreateFromQuaternion(face);
            Vector3 item = MoreMaths.Forward(face);

            Vector3 net = Vector3.Subtract(orient.Forward, item);

            string result = "Pass | ";
            float length = net.Length();
            if (length > acceptable ||
                float.IsNaN(length))
            {
                result = "FAIL | ";
                countFails++;
            }
            System.Diagnostics.Debug.WriteLine(
                result + "Forward" +
                " Result M: " + orient.Forward.ToString() +
                " Result Q: " + item.ToString() +
                " Difference M-Q: " + net.ToString()
                );
        }
    }


    System.Diagnostics.Debug.WriteLine(
      "Total Count: " + countTotal + 
      " | Failed Count: " + 
      countFails.ToString());

    if (countFails < 1)
    {
        System.Diagnostics.Debug.WriteLine("PASS");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("FAIL");
    }
    System.Diagnostics.Debug.WriteLine(
      "== End ======================================================\n");
}


There is significantly more code to check the results so the above just shows validating the forward vector but it should be obvious where to change that to do the Up and Right axes vectors.


    Matrix orient = 
      Matrix.CreateLookAt(location, Vector3.Forward, target);
    Quaternion face = 
      Quaternion.CreateFromRotationMatrix(orient);
    face.Normalize();
    orient = Matrix.CreateFromQuaternion(face);
    Vector3 item = MoreMaths.Up(face);


I have many bits of maths and other methods that I need to ensure produce the results I expect, so I have similar debug code that I can run as stand alone methods to test the result of sections of code.

I hope the above is useful to some of you.

I have a little bit more to do to get the AI to use the newly added history of states and then I can get on with creating the server side code.

Monday, 15 April 2013

Wooshy Transitions

'Wooshy' is how my wife described the latest menu transitions that I've added to the game.

For those not familiar with the use of the term in games, a transition is the way one screen changes to another.  A sudden jump from one page to another looks wrong or unfinished.  Fading between screens or some other effect makes the change look better and more polished.  Transitions are used in film and video production, DVD menus and computer games to name just a few.



It still surprises me how much of the game is connected together in unexpected ways. I started working on the networking code but have spent the last few weeks doing screen transitions. How does that happen?

It happened because I needed a background for a screen to display the list of available servers to select from.

I created a sci-fi looking frame with a blank background which I liked so much I decided I could also replace some of the placeholder artwork I had been using elsewhere...

While I was creating graphics I might as well do something about some of the other placeholder art on the menus...

With the new art assets the menu transitions looked a little inconsistent...

So on and so forth until I had changed nearly all the backgrounds to the menus and designed a set of matching slide on and off (wooshy) transitions that worked for all the menus.



It's never quite as easy as it sounds. Several of my menu screens have moving 3D models on them and getting them to nicely 'woosh' off and on was going to be somewhat troublesome so I opted for a different but matching transition for those screens.

On those difficult screens a panel slides over the entire screen and the menu changes at the point the panel obscures the whole screen and then it continues to slide off revealing the new screen.

Hopefully when you get to see the thing in action you'll like the effect.

Thursday, 7 March 2013

View Occlusion

Since the play testing a few weeks ago I have been busy but I have not got a great deal to show for it yet.

My main task is trying to improve the draw performance so I can add a bit more detail to the levels and counter some of the additions I made based on the suggestions from the play testing.

I already do the typical view frustum culling and restrict the far plane range so I am only left with the more complex things to try.  Pretty much that is some form of pre-calculation of what is in view.

From the reading I have done Binary Space Partitioning (BSP) is not appropriate to the type of exterior vistas I would like to have in the game.  Therefore I have been concentrating on Occlusion Volumes (View or Visibility Cells).  My understanding being that these are areas of the map that if you are inside that area the models and meshes in view have been pre-calculated and therefore exclude meshes that are hidden by other meshes.  Both Unity and the Unreal Engine have variations of this that can be enabled depending on if it is appropriate for the map.


My map already uses a grid for storing, triangles and other information so adding additional heights to set the boundaries of the Occlusion Volume is trivial.  The tricky task has been calculating the mesh occlusion information.

I tried to calculate the view using the GPU.  I wrote a shader that stored an index for each model and returned that in the render target.  By drawing the view in sufficient directions from various points in each Occlusion Volume I would get a fairly accurate chance of knowing what was not in view and could therefore be ignored and not drawn in game.

I wrote all the code to do the calculations using the GPU but now for the problem.  Based on my initial trials it would take 72 hours to calculate the information for the entire usable area of the map!  I decided that was too long to run every time I made a tiny change to any game level!

I was also not confident that the result from the render target was completely accurate because my tests showed that if I put a float value in, the float I got out was close but not identical when rendered!

For my next trick I am attempting to use the grid and simply ray cast between cells.  I have not finished coding this but in the process I have used the Bresenham Line Algorithm.


The algorithm was originally intended to plot a line on early printers in the '60's.  I have come across it before but as it does not include every possible cell that a line could pass through it was no good for my impact calculations.  However for view occlusion I need something that is fast and where a few false positives for grid squares in view will not be harmful to the overall result.

It's only a tiny bit of code but I decided to put my C# XNA versions on CodePlex.  The important bit is that one of the variations I've included gives the result in order from the start to the finish point.  If it is of interest you can view and download it from there.

Monday, 11 February 2013

Play Testing Is Very Useful

I put the game in to Xbox Live play testing with little expectations especially as it is such an early version.

What I got back was incredible.  I knew the way others look at the game would be different to how I see it, with the 'rose tinted glasses' of the developer on but I was not expecting such useful feedback.

http://xboxforums.create.msdn.com/forums/t/110018.aspx

After the initial essential bug fix, many of the comments are about balancing the combat. I got feedback on AI behaviour, the relative strengths of the weapons and even some suggestions for the bullet effect via Twitter.  I also got some useful comments, with screen shots, on projectile behaviour which I knew about but did not expect others to notice.  I know now that if I know about it chances are others will also find it distracting.


I have already fixed or changed many of the things based on the suggestions and just have the more time consuming ones to go.  Some changes have hit performance so I am in the process of moving things about to get a few more milliseconds out of the CPU...  I hope!

Thanks again to all.

Sunday, 3 February 2013

Alpha Playtest Version

UPDATE: This playtest has now expired.  The forum remains for anyone interested and there will be future versions when I have made sufficient changes for more testing to be worthwhile.

I'd like to thank everyone who tested the game.  I received some excellent feedback and and am working through it all to make changes.

==

I have just uploaded the first version that other developers can play.

For those who may be interested please be aware that this is only available to Xbox Live Independent Game (XBLIG) developers who have a premium subscription with Microsoft.  That's the way Microsoft set up the publishing mechanism for the Xbox.  That's a long way of saying only other Xbox developers can get this download:

http://xboxforums.create.msdn.com/forums/t/110018.aspx

 
 


I am a bit nervous and excited because this will be the first time that anyone other than me has played it. I've demo'd older versions to a few friends before but these only had partial test levels. No one else has played a version where a level can be played through from start to finish.

The game is still a long way from completion.  Lots of the graphics need improving both in game and on the menus.  I need more player characters, more types of aliens, better animations and most importantly I need to write the network code so it can be played on line.

That's what's not there but what is there...

- Character selection.
- Single player
- Two player split screen
- Playable level with an end objective (a bit simplistic at the moment I admit)
- Selection of weapons including different types of grenades
- AI controlled Bots. Despite their limited skill set they still make difficult opponents
- Ammo counters, weapon sights, grenades, the usual stuff...
- Weapon and ammo pickups
- Lots of enemies to shoot at
- End of game scores
and more...

If you are an XBLIG developer I hope you try it out and enjoy it.

http://xboxforums.create.msdn.com/forums/t/110018.aspx

==

For anyone else at this stage it took about 3 hours between uploading the file and it being available to download.

Thursday, 24 January 2013

Wednesday, 16 January 2013

Assumptions...

You know all the sayings about assumptions.  They all boil down to don't take things for granted but how do you know when you are making an assumption?

I have been chasing a problem with my grenade throwing code. They would disappear unexpectedly or explode at the wrong time or place.


I have lots of visualisation to confirm that the calculations are being performed and that the positions were correct and in view!




It turns out that the confusion was because I had unconsciously assumed that my update thread was always in advance of my draw thread.
Nearly everywhere else in the code the update does not matter if it runs out of sync with the draw thread but for grenades that have to bounce, the difference was noticeable.  The collision is calculated in one thread but the grenade is drawn in another thread in real time.

Overall the update thread does less work and so has plenty of spare performance therefore it seamed logical in the back of my mind that it would process the collision faster than needed.  What I had not allowed for is that it might have started after the draw thread. 

I had varying results that I could not explain.  Sometimes working and sometimes not which I now know is because it is multi-threaded.  The launching of the grenade could sometimes be picked up instantly by the draw thread because that is where it was in its cycle but the update thread could be a few milliseconds or even a frame behind that draw.

Problem solved and another lesson learnt :-)