Showing posts with label XNA. Show all posts
Showing posts with label XNA. Show all posts

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.

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, 20 December 2012

Winform Map Editor

Last night I finished my side project of migrating the map editor from being a game screen to being a normal windows application (Winform.)  I've been doing this when I needed a break from 3D modelling. 


At the moment all I have attempted to do is move all the features I had before in to a separate project.  I did find a few minor bug fixes but it is basically the same.

The only major change was to the controls for moving about the map.  It now works more like a 3D modelling programme, in fact the rotate, zoom and move are very similar to Blender.  I have however retained first person keyboard controls for fine adjustments.

I am doing most of the modelling in Blender so the new controls are much more familiar when I bring the models in to the editor.

I have left space to the side and bottom of the main view window to put properties.  I have not coded anything to go there yet but in time I will add something useful.

The editor is still linked to the main game.  It shares some of the rendering code and all of the map loading code.  I compile the editor project in the same solution as the main game.  This means that the map files will always remain compatible.

I find it works well.  The only peculiarity I had was that the game view control from the Microsoft sample is external.  Exceptions thrown in the game view crash the control but the rest of the Winform app continues without error.  The view window goes white with a red cross in it!

I fixed that with a simple try and catch wrapped round my update and draw code:


/// 
/// Redraws the control in response 
/// to a WinForms paint message.
/// 
protected override void OnPaint(PaintEventArgs e)
{
    try
    {
        string beginDrawError = BeginDraw();

        if (string.IsNullOrEmpty(beginDrawError))
        {
            // Slow the game down to no more 
            // than 60 fps (16.667ms per frame)
            if (gameTime.ElapsedUpdateTime > 
                TimeSpan.FromMilliseconds(16))
            {
                // My own version of GameTime
                // based on a stopwatch.
                gameTime.Update();
                // Simulate the update loop in 
                // an XNA game.
                Update(gameTime);
                // Draw the control using the 
                // GraphicsDevice.
                Draw(gameTime);
                EndDraw();
            }
        }
        else
        {
            // If BeginDraw failed, show an error 
            // message using System.Drawing.
            PaintUsingSystemDrawing(e.Graphics, 
                                    beginDrawError);
        }
    }
    catch (Exception ex)
    {
        // Exceptions within external applications do 
        // not automatically break the parent form.
        // These lines trap the exception, display the 
        // error and break the parent code for debugging.
        System.Diagnostics.Debug.WriteLine(
            "Exception in Update or Draw: " + 
            ex.Message + " in " + ex.Source);
        System.Diagnostics.Debug.WriteLine(
            "Stack trace: " + ex.StackTrace);
        throw new Exception(ex.Message);
    }
}

The whole app now crashes and the exception is displayed to help debugging.

In addition to being able to position, models, triggers, waypoints and generate the navigation mesh used by Bots, I have loads of helper overlays within the editor.  Too many to list:


I nearly forgot.  There was a point to moving the editor out of the main game. 

Every time I tried to change the game I had to keep it compatible with the editor.  There was often lots of extra code just so the game remained fast and the editor had features.

When I started looking at adding in the networking code it just got too complicated to do what I wanted and retain compatibility.  That's what kicked off this little side project.

It's how I should have done it in the first place.

Sunday, 9 December 2012

One Line Of Code

I am sure that everyone has these days when coding.  This is just a quick note to remind people to keep on going no matter how bleak things appear!

I had been working away on the new weapons and effects and got round to testing everything on the Xbox.  I do this from time to time because the Xbox does not perform in the same way as the PC does.  Much to my surprise the performance was a disaster on the Xbox!

That was late yesterday and I was going out in the evening so I left it wondering how long it would take to find whatever it was I had done to cause a frame rate of just 9 frames per second!  It was perfect on the PC but the Xbox was unplayable!

I had thoughts of the level being too complicated and having to come up with some clever code to cull more efficiently, or that the texture files were too large, so I would have to reduce the quality.  I had quickly ruled out the particle effects because although they were pretty the code, quantity and textures sizes were unchanged to effects I had been using for years and the performance graphs did not change when they were on the screen.



I've been working in I.T. long enough to know that whatever you last changed is the most likely cause for whatever problem you are now investigating.  This morning I therefore thought through all the changes since the last time I had successfully tested on the Xbox...

I thought it was a fairly easy non-performance affecting change but I have completely re-written the Audio Manager to change from using XACT to using the SoundEffect classes.

Some quick commenting out and I very rapidly confirmed that somewhere in my conversion I had made a mistake.  To cut an hours searching down I found it.

Just one very short line of code:
nextSong = "";

I had an endless loop that would start playing the next waiting piece of background music every frame because I had not cleared the variable once it started playing!

I am a lot happier now and I can go back to creating assets and effects for the game.

Saturday, 13 October 2012

MipMaps and Textures

I've been tidying up and improving my code.  Mainly to get my level map files in to a single file and to use the content pipeline to load the maps in to the editor rather than having two sets of loading code.

In the process I have worked out how to create a texture in the content pipeline.

As I am now loading everything in the Content Pipeline I will no longer be using the code that creates MipMaps at run time.  Rather than forget how I did this, I thought I'd add the code here to remind me.

I won't go in to detail but a MipMap is simply a half size version of the main image stored in the same file. Typically several levels of ever decreasing images are stored in the same file.  They are used so that textures displayed at a distance look less pixelated.



If the above image did not use MipMaps then the distance would look grainy.

MipMaps At Run Time

This bit of code generates MipMaps at run time:


/// Create mipmaps for a texture
/// See: http://xboxforums.create.msdn.com/forums/p/60738/377862.aspx
/// Make sure this is only called from the main thread or 
/// when drawing is not already happening.
public static void GenerateMipMaps(
  GraphicsDevice graphicsDevice, 
  SpriteBatch spriteBatch, 
  ref Texture2D inOutImage)
{
  RenderTarget2D target = 
    new RenderTarget2D(
      graphicsDevice, 
      inOutImage.Width, 
      inOutImage.Height, 
      true, 
      SurfaceFormat.Color, 
      DepthFormat.None);
  graphicsDevice.SetRenderTarget(target);
  graphicsDevice.Clear(Color.Black);
  spriteBatch.Begin();
  spriteBatch.Draw(inOutImage, Vector2.Zero, Color.White);
  spriteBatch.End();
  graphicsDevice.SetRenderTarget(null);
  inOutImage.Dispose();
  inOutImage = (Texture2D)target;
}


Textures In The Pipeline

The second bit of code creates images from a byte array in a content processor using the XNA Content Pipeline:


/// Create the texture image from the 
/// Color byte array, Red, Green, Blue, Alpha.
private Texture2DContent BuildTexture(
  byte[] bytes, 
  int sizeWide, 
  string uniqueAssetName)
{
    PixelBitmapContent<Color> bitmap = 
      new PixelBitmapContent<Color>(sizeWide, sizeWide);
    bitmap.SetPixelData(bytes);

    Texture2DContent texture = new Texture2DContent();
    texture.Name = uniqueAssetName;
    texture.Identity = new ContentIdentity();
    texture.Mipmaps.Add(bitmap);
    texture.GenerateMipmaps(true);
    return texture;
}

When I was looking I found similar samples hard to find.  I hope the bits of code may be useful to others.

MipMaps In The Pipeline

The following is how I now create the mipmaps in the content pipeline while loading an image:



///
/// Load and convert an image to the desired format.
/// Returns the image suitable to use as the texture on the Landscape.
///
private Texture2DContent BuildLayerImage(
    ContentProcessorContext context, 
    string filepath)
{
    ExternalReference layerRef = 
        new ExternalReference(filepath);
    
    // Mipmaps required to avoid speckling effects and to avoid moiray patterns.
    // Premultipled alpha required so that the texture layers fade together. 
    OpaqueDataDictionary processorParams = new OpaqueDataDictionary();
    processorParams["ColorKeyColor"] = new Color(255, 0, 255, 255);
    processorParams["ColorKeyEnabled"] = false;
    processorParams["TextureFormat"] = TextureProcessorOutputFormat.DxtCompressed;
    processorParams["GenerateMipmaps"] = true;
    processorParams["ResizeToPowerOfTwo"] = false;
    processorParams["PremultiplyAlpha"] = true;

    // Texture2DContent does not have a default processor.  
    // Use TextureContent and cast to Texture2DContent
    // A processor is required to use the parameters specified.
    return (Texture2DContent)context.BuildAndLoadAsset(
            layerRef, 
            typeof(TextureProcessor).Name, 
            processorParams, 
            null);
}



That code can be called from within the processor using something similar to:


string filepath = Path.Combine(directory, filename);
Texture2DContent LayerImage = BuildLayerImage(context, filepath);


The above was worked out from the following forum and blog posts:
http://xboxforums.create.msdn.com/forums/p/44185/263136.aspx
http://blogs.msdn.com/b/shawnhar/archive/2009/09/14/texture-filtering-mipmaps.aspx

It also helps to know what the parameters are.  You can look them up from the xml file generated by the content pipeline:
Content/obj/x86/Debug/ContentPipeline.xml

The following being extracted from that:


<Importer>TextureImporter</Importer>
<Processor>TextureProcessor</Processor>
<Parameters>
    <Data Key="ColorKeyColor" Type="Framework:Color">FFFF00FF</Data>
    <Data Key="ColorKeyEnabled" Type="bool">true</Data>
    <Data Key="TextureFormat" Type="Processors:TextureProcessorOutputFormat">DxtCompressed</Data>
    <Data Key="GenerateMipmaps" Type="bool">true</Data>
    <Data Key="ResizeToPowerOfTwo" Type="bool">false</Data>
    <Data Key="PremultiplyAlpha" Type="bool">true</Data>
</Parameters>

Already a Texture in the Pipeline

If you want to chain from one processor to another or do more than one thing to a texture you can use the ContentProcessorContext.Convert method to run another processor on the same object.  In the following example the input is an existing texture loaded elsewhere in the pipeline:


TextureContent output = 
    context.Convert<TextureContent, TextureContent>(
        input, 
        typeof(TextureProcessor).Name, 
        processorParams);


If you want to just add the mipmaps to an existing TextureContent image, as shown in the bitmap example earlier in this post, you can call the following method:

input.GenerateMipmaps( true );

The pipeline code is a bit harder to understand but is probably more useful.

===

Here's a discussion thread I found recently about mipmap coding in general:
http://xboxforums.create.msdn.com/forums/t/111550.aspx
And that came from here:
http://xboxforums.create.msdn.com/forums/p/111606/667162.aspx#667162

JCB 23 Sept 2013.

Sunday, 19 August 2012

Follow My Own Advice

I've just spent several hours trying to find out what was wrong with my navigation code in my game.  It had worked up until the last changes I made to the level.  This time when I tried to calculate the navigation mesh I got an invisible wall blocking my way!

I could not see anything in my code and none of my debug output showed what could be causing the problem.  Eventually I found it.  Not my code!  It was that one of the models I had exported from Blender had a rotation  left on it!


The rotation in the top right of the above screen shot from Blender is what had been causing me problems.  I will repeat for my own benefit...

Before exporting for use in XNA make sure that none of the rotaions or scale have been set and make sure the object is located at zero.

That's what it should be.

I don't know why but the model appeared to look correct in XNA.  When I displayed where I calculated all the triangles were they were at 90 degrees to where the model was rendered!

Rotating the model in Blender and exporting again fixed the problems.

I'm happy it was not my code but annoyed I didn't spot it while creating the model!

All models need to be created just like my animated export instructions:
http://blog.diabolicalgame.co.uk/2011/07/exporting-animated-models-from-blender.html

One more thing...



remember to add the Edge Split modifier to ensure square edges appear square when rendered.

Wednesday, 25 July 2012

Diabolical Editor

I noticed the other day that I had not mentioned much about the level editor that I use for Diabolical: The Shooter.  I've spent a lot of time adding features to it so I thought it worth describing.


The editor is used for shaping and colouring the terrain and for positioning the static models on to the map.  With hindsight I should have created a completely separate application but my original design was to be able to quickly play test any level I created so the Editor uses much of the same code as the game and is run from the game's main menu.

That works but there has been a lot of struggling to keep the game code tidy whilst also adding in the Editor methods.  This has resulted in a lot compiler directives of the type '#if EDITOR'.

To be able to use Windows menus I have had to do some messing about.  The WinForm menus are not fully supported in the XNA game loop.  I have an open source test project that shows how I have managed to get the menus to work with some limitations:
http://code.google.com/p/xna-game-menu/



The top level menus and the drop downs have to be coded by hand but they can launch forms created with the Visual Studio GUI.  The menus work fine for the Editor used only by me but I would never use the WinForm menus in an XNA game.

Over time I have added stacks of features, too many to list but just a few are: texture terrain with up to four layers, adjust the height of the terrain with various helpers such as flatten, noise, slope etc.  use a round cursor, a rectangular cursor, add structures, waypoints, triggers, static particle and sound effects and so on...


The whole terrain is based on a height map using a regular grid.  This makes finding heights relatively quick.  The grid only has one value, the height at each corner.  The limitation of this is that it cannot do vertical faces only slopes.  For the vertical cliff faces seen in some of these screen shots I have created 3D models.


To position the cursor I project a line from the view and calculate where it intersects the terrain.  At first the cursor always followed the view but I found this difficult to see what I was doing so now the cursor stays still unless I hold down the Ctrl key.

I am not sure if my method for finding the point on the terrain to position the cursor is efficient but it is fine for this editor.  I step along the line projected from the view, testing the height at each point.  The step being just under one grid width to ensure every grid is tested.  As soon as the end point of the line goes underground I know that somewhere between the last step and this step it intersects with the ground.  I then back step in smaller and smaller segments to get a nearly accurate point of impact.  I only need to know which grid I'm in.



I use the top left corner of the grid in which the line intersects the terrain as the centre position of the cursor.  For some modes I offset this by half a grid in others I stay with the corner point.

I can size the terrain cursor using a simple popup form.  Whatever action I carry out is done for every point under the cursor.  The positions are all easily calculated using simple maths.



The terrain cursor and the many helper shapes I draw are just simple lines drawn in 3D space.  The cursor samples the terrain height at the ends of each line and sets the heights a fraction above the terrain.  This means the cursor follows the contours of the terrain.


There has been a lot of work over the years getting to this stage with many features I have not mentioned yet.  If I knew then what I know now I would probably use an existing editor and spend the time writing an importer rather than adding all these features to my editor.  Having said that, I've learnt a lot and I do enjoy knowing that I did all this.

To complete the picture, this is a list of all the features of the game engine:
  • Walk round a 3D world
  • - first person controls
  • - over the shoulder view of yourself (more fiddly than it sounds)
  • - resolution 1024x576 (for better performance on the Xbox 360)
  • - jump
  • - spectate
  • - two player split screen
  • Animated
  • - blend animations
  • - merge in arm movement to follow which way the player is looking
  • - hold attachments that move with whatever they are attached to
  • - shared animation files (and a way to get them in to the pipeline.)
  • Physics
  • - collide with characters and structures
  • - projectile impacts
  • - projectile trajectories (thrown grenades)
  • Terrain and game editor (development only)
  • - change heights
  • - change textures
  • Add and remove (only in the development editor):
  • - models
  • - triggers
  • - particle effects
  • - goals
  • - trigger goal success
  • - trigger add a new goal
  • - trigger spawn player
  • - trigger spawn non-players
  • - trigger particle effects
  • - waypoints for AI pathfinding
  • - spawnpoints
  • - weapon or equipment pickups
  • Lighting
  • - single shadow casting light
  • - three effect lights
  • - shadows (not a trivial task, many many months spent on this)
  • Full menu system
  • - select which map to play
  • - customise the player character with hats etc.
  • - load and save character choices
  • - change music and effect volumes
  • - in game pause menu, resume or exit
  • - display goals outstanding and completed
  • Combat system
  • - select weapons
  • - shoot weapons
  • - throw grenades
  • - melee (elbow bash while holding a gun)
  • - projectile trails
  • - impact damage decals (instanced)
  • - impact effects, debris and smoke
  • - explosions
  • - muzzle flash
  • - sound effects
  • - drop weapons
  • - pickup weapons
  • - pickup ammunition
  • Head Up Display (HUD)
  • - weapon sights
  • - sniper sights
  • - zoom in
  • - display ammo as used
  • - compass
  • - radar showing friend and enemy positions if close
  • Non-Player Artificial Intelligence (AI)
  • - pathfinding (A* using navigation rooms)
  • - select a target if in range
  • - shoot at a target
  • - move to better cover to shoot from
  • and I'm sure there's more...

Friday, 13 July 2012

Performance Solved

I have just had the oddest result by fixing one problem I have solved another.  As my wife just pointed out normally it works the other way round.  I normally solve one problem and create two other problems!  Not this time. :-)



To monitor performance while I play the game I use some graphs to avoid creating garbage.  These I display at the sides of the screen to show me memory allocations and CPU usage.  The memory bar goes up and eventually I can see garbage being collected with the momentary slow down on the Xbox.



On the other side I have a performance monitor that measures the time that hardware thread one Update and Draw methods take to complete and a third bar that measures the time taken for the Update that runs on hardware thread four.  These are my primary methods.  The graph is in milliseconds (ms) up to 17 and the bar changes colour to red when any result exceeds 16ms (one frame). 


For a long time I have known that looking in some directions causes a noticeable slowdown and the third bar shows red and off the top of the chart!  Visually the screen would stutter because the position of the view was not being updated quickly enough!  It was probably as annoying as the glitch caused by garbage collection.

As I had run out of things to try to get rid of my remaining garbage I decided it was time that I needed to deal with the performance related hiccup to make it feel nicer to play.

After a bit of fiddling I decided I could not guess where the problem was I needed some way to measure it.  I created myself a simple timer.








    ///  

    /// A static timer which displays the duration of a method in the trace output. 

    /// This is NOT thread safe and cannot be nested. 

    ///  

    public class MethodTimer 
        public static System.Diagnostics.Stopwatch Clock = new Stopwatch(); 
        public static TimeSpan StartTime = TimeSpan.Zero; 
 
        ///  

        /// Mark the starting time to measure from. 

        /// This also starts the timer if it is not already running. 

        ///  

        [Conditional("DEBUG")] 
        public static void Start() 
            if (!Clock.IsRunning) 
                Clock.Start(); 
            StartTime = Clock.Elapsed; 
 
        ///  

        /// Output to the trace window if the duration measured is greater than that specified. 

        ///  

        [Conditional("DEBUG")] 
        public static void Stop(string logName, TimeSpan logDurationOver) 
            if (Clock.IsRunning) 
                TimeSpan duration = Clock.Elapsed; 
                duration -= StartTime; 
                if (duration > logDurationOver) 
                    System.Diagnostics.Debug.WriteLine("Method: " + logName + " took " + duration.TotalMilliseconds.ToString() + " milliseconds!"); 
                return
            System.Diagnostics.Debug.WriteLine("The clock was not running when the Stop method was called!"); 
 




I put it either side of a method and get it to log a result if the time that method takes is longer than I expect.

I was surprised how quickly I got it down to one method causing my problem.  I won't go in to detail about the specific method.  It just simply attempted to do far to much every frame to get more accuracy than I needed.  I produced a much quicker but less accurate version.

Now for the great news.  When I tested that on the Xbox not only had my performance problem completely gone but so had ALL of my memory allocations, no garbage collection!  Two in one.

It's not entirely true that all my memory allocations have gone, but as long as you don't make any new sounds (XACT) or respawn there are none and the tiny allocations that do happen from those methods take a very long time before they would trigger the garbage collection :-)

==

Downloads:
'Performance Tools' including the graphs mentioned above:
http://code.google.com/p/xna-game-menu/downloads/list

Update: 27 July 2012
I have just come across an article written a few years ago  suggesting the same method as above:
http://blogs.msdn.com/b/shawnhar/archive/2009/07/07/profiling-with-stopwatch.aspx

Sunday, 8 July 2012

Memory Allocations

A couple of weeks ago I mentioned that I had managed to work out how to run the CLR Profiler with XNA 4.  I now understand enough to work out what the results mean.  At least enough to track back to the parts of my code that are allocating memory during the game and therefore creating garbage to be cleaned up!

I have not finished getting rid of all the allocations yet but I was able to run through some testing on the Xbox 360 without the Garbage Collector triggering too frequently.


I found the CLR Profiler daunting at first but once you know which bits to ignore it's not so bad.

For my purposes all I need is the Allocation Graph button.


There's a tiny bit to know before you'll get anything at all and that is to make sure the 'Allocations' Profile: is ticked before unticking 'Profiling active'.  The 'Calls' profile collects a lot more data and as far as I can tell does not help with finding memory allocations.

At the point in the game where you want to start logging from simply Alt-Tab out of your game and in to the CLR Profiler window to tick the 'Profiling active' box.  Then Alt-Tab back to the game.  If you do it the other way round and try and tick the Profile: Allocations after starting the application then nothing is logged!

You could log everything from the start of the game but I found the graphs just far too confusing to read if I tried to do that.

When you have played enough of the game to get some data, skip back to the CLR Profiler window and 'Kill Application'.

For my purposes I can now ignore everything except the 'Allocation Graph'.  Open that up and skip to the RIGHT hand end.  This is the biggest tip I can give.  You work backwards from the right hand end towards the left hand end.

Start at the largest allocations at the top right.


Sometimes you can easily follow the lines but when they get lost in the others, either increase the scale or just click on any one of the boxes in the line and the lines change to a cross hatching either side to make them easier to follow.  You can also double click a method to see the connecting methods either side.
Just work backwards from method to method until you recognise a method in your code.


So far I have found the changes necessary to avoid the garbage have been quite minor.  Most of my own allocations have been caused by List<T>.AddRange(...) where <T> is one of my own classes.  This is easy to fix by using a loop instead of the built in AddRange().

I have had one where it was caused by an internal XNA framework method but luckily I did not need to be doing that during the game, so I simply paused its update during game and restarted whenever the menus open.

I'm now left with a few tiny allocations that I have to go through one at a time to eliminate them.  Then I can enjoy testing again on the Xbox 360 to have a garbage collection free game, I hope.


==

EDIT:
Just came across a tutorial that explains how to use the timeline to only look at allocations over a given period.  This saves having to activate the profiling mid game:
http://spacedjase.com/post/2010/07/02/How-to-eliminate-frame-by-frame-Garbage-Generation-using-CLR-Profiler.aspx

Tuesday, 10 April 2012

GPU Particles Tool

I have done some additions to Diabolical:The Shooter but there is not much that I can easily show yet.  The additions are a few more animations but they need a bit of adjustment before they are ready to show.  These are the actions for changing weapon, reloading, melee and throwing grenades that needed to be merged in to whatever other animation was playing at the time.

The throwing grenades needed some additional work to line up the point at which the grenade leaves the hand with the moment when the particle effect starts to show the grenade in motion in a nice arc.

I was pleased with that and it leads me on to what else I have been working on...  A tool to help create particle effects.  If you are not a game developer you may not be familiar with what Particle Effects are.  This is really a technique for producing things like smoke and explosions, usually using lots of copies of the same image shown many times in different positions.

Combining that in different ways can produce some very interesting and realistic visual effects.




For performance I have elected to use particle effects that are mainly calculated by the GPU, the graphics card, rather than the CPU of the computer or in this case the Xbox 360.

The disadvantage of using the GPU is that the effects cannot be changed once they have started.  They rely entirely on mathematical formulae setup in advance.  The advantage is that they have less of an impact on performance of the rest of the game.


There are several CPU particle engines available with editors to help create the effects but I struggled to find even one tool to help design GPU particle effects... So I created one and put it on CodePlex:
https://gpuparticles.codeplex.com/

It's nothing grand but will do the job for me and I hope a few others may find it useful.

Thursday, 23 February 2012

Update On Another Thread

For some time I have been considering putting the Update processing on a separate thread to the Draw processing.  I had assumed it was difficult to synchronise all the values from one thread to another so I had put it off. 

I have several sections of distinct code, including the Artificial Intelligence (AI) and the Storage which already run on their own threads.  On the Xbox this allows me to specify which processor core they run on and most importantly allows tasks to run in parallel without degrading the performance of the other threads.


#if XBOX
  Thread.CurrentThread.SetProcessorAffinity(
                                     new int[] { 4 });
#endif



The part of the game I am working on is adding non-line of sight weapons.  The trouble is this dramatically increased the number of collision calculations I needed.  Even with only a handful of projectiles on the screen at once the Xbox started to drop a couple of frames.

I considered multi-threading the projectile collision calculations but the design got complicated.  At the same time I have been peer reviewing End Of Days: Infected vs Mercs.  A fun first person shooter which has many elements in common with my game.  Kevin, the developer of that game, was happy to chat about the design.  He said the best performance boost was to have the Update on a separate thread to the Draw.  Thanks Kevin and for the triangle code.

That was enough to make me look further and I found several postings commenting that it was not too difficult and it definitely gave the desired increase in speed.

I had plenty of experience with my other multi-threaded code to know what I needed to look out for.  I had standard code I use to launch and control the thread so I got under way.



private void StartUpdateThread()
{
  // Avoid duplicate threads running.
  if (!isUpdateThreadActive)
  {
    isUpdateThreadActive = true;
    killThreads = false;
    Thread threadUpdate = 
                    new Thread(ProcessUpdateThread);
    // Set to background so that when the foreground 
    // thread dies the background one dies as well
    threadUpdate.IsBackground = true;
    threadUpdate.Start();
  }
}

// Runs on another hardware thread on the Xbox. 
private void ProcessUpdateThread()
{
#if XBOX
  Thread.CurrentThread.SetProcessorAffinity(
                                     new int[] { 4 });
#endif
  updateTime = new GameSelfTimer();
  updateRandom = new Random();
  // Wait a moment for everything to catch up
  Thread.Sleep(5);
  while (!killThreads)
  {
    if (readyToUpdate && !readyToDraw)
    {
      readyToUpdate = false;
      updateTime.Update();
      ProcessUpdate(updateTime);
      readyToDraw = true;
    }
  }
  isUpdateThreadActive = false;
}


The above is the code I use throughout my game for launching and running the threads.

GameTime

I have mentioned elsewhere about the trouble with getting the standard XNA GameTime class on another thread so the first job was to completely replace GameTime throughout the entire code.

I just use my own timer based on a StopWatch.  My timer class has the same properties as GameTime to minimise the code changes.  It took an hour to carefully replace every occurrence and then test while it was still a single thread.

Synchronisation and Buffers

I swapped over and tested without any value being buffered from one thread to the other just simple synchronisation so the update thread only runs when something worth updating has changed.

It worked and showed a dramatic speed improvement but as expected the models on the screen stuttered because they were using incomplete values which were being calculated out of sync by the Update thread.

What did surprise me was how few values are changed and then shared between the Update and Draw threads:


  • View matrix calculated from the player camera position
  • Projection matrix calculated occasionally when the weapon sight zooms in
  • World position of every model
  • Skin transform matrix array for animations

I simply buffer those and use a boolean variable to indicate when changes are ready to update the buffers.  There may be a couple of others I find in time but the above have smoothed the display.



// From the main thread
public virtual void UpdateThreadBuffers()
{
  lock (lockPosition)
  {
    WorldPositionDraw = worldPositionBuffer;
  }
}


I use a base class for all models which mean that I only had to add the Update buffers method in a very few places.

The tricky bit was making sure I called it in the correct place for all instances.  That did not take as long as I expected.  Especially as static models don't need any changes after they are positioned.



// From the main thread.
protected void UpdateThreadBuffers()
{
  if (readyToDraw)
  {
    gameManager.Shading.UpdateThreadBuffers();
    for (int i = 0; i < Controllers.Count; i++)
    {
      Controllers[i].UpdateThreadBuffers();
    }
    PortableItems.UpdateDroppedThreadBuffers();
    // Always the last thing so update will run again
    readyToDraw = false;
  }
}


I also use 3D moving and animated models on some menus and option screens so I had to make changes outside of the main game to keep those screens compatible.

Job done.  Loads more processing capacity for the extra collision calculations I need, good frame rate and it should make future changes much easier.