Saturday, 21 September 2013

Muzzle Flash

I've just been asked a question by way of a comment to this blog.  It's easier to reply by way of a new post.  I also haven't had anything to update on the blog for a while so this does two jobs in one.

State of Play

I haven't had anything to say for a while because at the moment my code is broken.  By that I mean I'm in the middle of changing something fundamental and it won't compile, let alone run.

I don't like it when the code is in this state.  I prefer to make small changes and at the end of every day the code compiles and I can test it.  For the last few weeks it has been a mess.

I am in the process of separating the client processes from the server processes and adding all the messages to communicate between the two.

I am concentrating on just the messages to get to a state where I can compile and run the game again but just to spawn a character in to the world, the state of every part of the world needs to be sent from the server to the client and the message to say what and where to spawn.

There's been a fair bit of design and redesign but now it's mainly a lot of typing.  I'm nearly there but not quite.

The Question

As mentioned, this post is to specifically answer a question.

"I've been working on a project in which you've helped me out a lot and now I'm at point at which I'm experimenting with particles. I've seen the muzzle flash screenshots on your blog and wanted to ask how did you go about positioning the exact spot to your weapon for the muzzle flash effect."
It was asked by 49ers94 from the Xbox Live developer forums.



It refers not only to the muzzle flash shown in a previous post, but to the start of the projectile particle effects shown in several of the screen shots.

The answer is simple.  I store the relative position of the muzzle in relation to the origin of the weapon.  That position is transformed by the direction the weapon is aimed in to calculate the position of the muzzle at any point in time.



 /// 
 /// Get the muzzle location in world space. 
 /// Grenades will return the hand position because the offset is zero.
 /// 
 public Vector3 MuzzleLocation()
 {
     // Vector3 MuzzleOffset - relative to the weapon origin
     // Matrix WorldPosition - position and rotation of the weapon model
     return Vector3.Transform(MuzzleOffset, WorldPosition);
 }


The above code is within the weapon model wrapper class that I use.  The weapon is already moved by whichever bone the weapon is attached to.

If you calculate that relative muzzle offset position manually for each weapon, that is all you should need to do.

One note on the particle effect.  The muzzle moves quickly as the character does but to make a nice effect the flash is not instantaneous.  I found that if the particle effect stayed still for its duration it looked odd when the weapon moved away from the still visible flash. 

The solution I used was to have very short duration particles and create new particles at the new position of the muzzle each frame.  This is not how a real flash would happen but this is what I found looked best.

Coding my own Tools

I've taken it a bit further to make it easier to create and edit more weapons.  I included a visual method to adjust the muzzle position in to the model viewer that I use for my game.



To make my life even easier I create all my weapons facing the same way and at the same scale.  I have not created all my own models but I take the model in to Blender, change its position and scale and output the model to FBX for use in my model viewer and in my game.



I save the relative muzzle position in to a text file for each weapon which is loaded in the content pipeline.

I think that covers everything on that topic.  Back to coding my client server architecture...  After an all day breakfast that is.

Sunday, 28 July 2013

Multi-Threaded Code Is Hard

I know that title is probably stating the obvious to anyone who has tried it but it bears repeating.  I have other articles that refer to the same in different ways.

Nearly a month ago I thought I was nearly finished with the change to the physics to get it ready for networking, just a few peculiarities to sort out.  Little did I know how long some of those 'peculiarities' would take to find. 



Today I have finally confirmed that one of the hardest problems to find was, yet again, caused by multi-threading.  When the player shot towards another character sometimes but not always the shot would fire, the muzzle flash would display but no projectile trail was shown and the enemy was not damaged.

Lots of debug code and head scratching later I narrowed it down to the line of sight intersect code with the enemy bounding spheres was returning a distance of zero from the muzzle of the shooting weapon.



In my head and all the maths I did and all the debug code said this was an impossible situation but there it was on my output window, length zero to target and the shot in reverse!  Aaaaah!

I got so frustrated I started looking at Unity3D and the possibility of re-writing from scratch and targeting the Xbox One instead of the Xbox 360.  After installing Unity3D and taking a quick look at that I decided all I would be doing was moving from one frustrating problem to a different set of frustrating problems. 

The break from XNA gave me some time to think and I eventually noticed that the spheres I use to intersect with are updated in a different thread to where I calculate collision.  I am not sure how I prove this next statement but all the elements of the position of the spheres update atomically (in one operation) and therefore cannot be out of sync on an individual level but my guess is that as each vector contains three floating point numbers any one could be out of sync with the other two.

Anyway, I've changed the code to lock the changes to the spheres and I can no longer reproduce the problem.  At last :-)



Just when you thought this article was over, sorry... moments before writing this up I have come across another error.  This time with particles.  At least this time I can see immediately it is also caused by multi-threading.

My last word on this today...  That particle code has been unchanged for a very long time but threading problems can hit you at any time and may not be easily repeatable!


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.