Showing posts with label Code Sample. Show all posts
Showing posts with label Code Sample. Show all posts

Sunday, 27 April 2014

Avoid Photon OnDestroy Warnings

I like my code to compile and run without errors.  The Photon Unity Networking (PUN) code did not.

When exiting scenes I would get the following errors for each instantiated object in the level:

OnDestroy for PhotonView View (0)1001 on CharacterPlaceholder(Clone)  but GO is still in instantiatedObjects. instantiationId: 1001. Use PhotonNetwork.Destroy().

Failed to 'network-remove' GameObject because it's null.


I searched the Internet and Exit Games forums but found little help that actually solved the problem. I found plenty of other people reporting the same issue.



I tried reading the PUN documentation but that was unhelpful as were any of the samples that I tried so I experimented.



I now have a work round which avoids the warnings and errors.



/// Tidies up before exit from the scene.
/// Attempts to destroy network objects correctly.
/// Pauses the messages while the scene changes.
private static void TidyUpBeforeExit()
{
  // Destroy any photonView game objects.
  PhotonView[] views = 
          GameObject.FindObjectsOfType<photonview>();

  // Whichever client exits first cannot 
  // destroy the objects owned by
  // another client.
  foreach (PhotonView view in views)
  {
    GameObject obj = view.gameObject;

    if (view.isMine)
    {
      // Clients can destroy their 
      // own instantiated objects
      PhotonNetwork.Destroy (obj);
    }
    else
    {
      // Clients cannot destroy objects 
      // instantiated by other clients.
      // This avoids the error by 
      // removing the instantiated 
      // object before the network view 
      // is destroyed
      if (PhotonNetwork.
            networkingPeer.
            instantiatedObjects.
            ContainsKey(view.instantiationId))
      {
        PhotonNetwork.
            networkingPeer.
            instantiatedObjects.
            Remove (view.instantiationId);
      }
    }
  }

  // By pausing and starting the 
  // messages manually I can start
  // any client or server in any 
  // order and all spawn messages
  // are retained.
  PhotonNetwork.SetSendingEnabled(0, false);
  PhotonNetwork.isMessageQueueRunning = false;
}



I simply include that method before any scene change.

Friday, 18 April 2014

Unity Gizmos

I've been using Unity 3D for months now and I somehow missed Gizmos as a concept.  I had seen the camera and speaker icons in the editor view but I think I had just assumed they were built in symbols used for built in objects and I had a blind spot towards them.

I now know better.  You can easily create your own icons which can be included in the scene.  I started using them when I wanted to make spawn points visible in the scene.



It is very easy to attach your icon to any object in a scene and that icon displays over the object.




That works well if you want to mark otherwise invisible points in your scene.  An example of this might be waypoints for AI controlled characters.

I prefer to create the Gizmos in code.  This adds them to the Gizmos list in the game view.



Creating the Icon

This can be done in any image editor and just needs whatever shape you want with a transparent background.

The image can be any size if you intend to use the default automatic scaling method.  For good quality I use anything between 512 and 1024 pixels high or wide images.  If you don't use the automatic scaling the image in the 3D scene, at those sizes, is quite small.

I used Inkscape to create the following image.



The important bit to know before you can use the image in your code is that it must be in the special Gizmos folder in Unity.

There can only be one Gizmos folder and it must be in the root of the Assets folder.



If you try to put your icon anywhere else, it will not work as a Gizmo in code.

Using the Icon for Your Object

Well if you can write any Unity code this is the simplest possible.  Just add the following in the code for your object or create a small script with this in.



/// http://docs.unity3d.com/
///        Documentation/ScriptReference/Gizmos.html
void OnDrawGizmos()
{
    // The icon image must be in the 
    // special folder called 'Gizmos'
    // There can only be one Gizmos 
    // folder in the root of Assets.
    Gizmos.DrawIcon (transform.position, 
                     "PersonIcon.png", 
                     true);
}



That's it, you now have a Gizmo shown wherever you position that object.


Saturday, 29 March 2014

Unity Inspector Tool Tips and more

When I started working with Unity 3D I looked up adding tool tips to properties in the inspector.  All the results of the searches came with a lot of code and no downloadable simple examples.

I did not spend much time on the subject because I was looking for a quick answer.  I was also probably using the wrong search criterion.



Further along I had a more important requirement that I thought was worth coding because it would save me making mistakes when setting up scenes.  As a consequence, I have leaned enough, very quickly, to create a simple but effective tool tip option for the properties, visible in the inspector.



I don't know if it is because I am using the latest version 4.3 of Unity but my solution does not need much code.  Many of the samples I looked at had huge switch or if statement blocks to allow for different property types.  I was able to use a built in method that replicated the default behaviour and allowed me to add the tool tip.




public class ToolTip : PropertyAttribute
{
    public string TipText = "";

    /// Display text when the mouse is over the label.
    public ToolTip(string message)
    {
 TipText = message;
    }
}





using UnityEditor;
using UnityEngine;
using System;

/// Display a tool tip when the mouse is over the label.
[CustomPropertyDrawer(typeof(ToolTip))]
public class ToolTipDrawer : PropertyDrawer
{
    private ToolTip source { get { return ((ToolTip)attribute); } }

 /// The height returned here must be appropriate for the property
 /// being dawn.
 public override float GetPropertyHeight (SerializedProperty property,
                                          GUIContent label) 
 {
  return GetOriginalHeight (property, label);
 }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
  // Must reset the tip otherwise all labels get the same tooltip displayed!
  string previousTip = label.tooltip;
  if (!string.IsNullOrEmpty (source.TipText))
  {
   label.tooltip = source.TipText;
  }

  // Display the default fields for this property
  EditorGUI.PropertyField (position, property, label, true);

  // Must reset the tip otherwise all labels get the same tooltip displayed!
  label.tooltip = previousTip;
 }

 /// Returns the height of the default control.
 /// 
 /// This can be reused by other property drawers.
 public static float GetOriginalHeight (SerializedProperty prop,
                                        GUIContent label) 
 {
  const float spacing = 3f;
  
  float baseSize = EditorStyles.label.lineHeight;
  
  // Most types only use one line so they work with the base but a few are 
  // multiline and need more work.
  float extraLines = 0f;
  if (prop.propertyType == SerializedPropertyType.Bounds)
  {
   extraLines = EditorStyles.label.lineHeight * 2.2f;
   baseSize = baseSize + extraLines + spacing;
  }
  else if (prop.propertyType == SerializedPropertyType.Rect)
  {
   extraLines = EditorStyles.label.lineHeight * 1f;
   baseSize = baseSize + extraLines + spacing;
  }
  
  return baseSize + spacing;
 }
}



That's it, not much code.

If you don't want to copy and paste, I've included the tool tip code and the code for other property attributes in to a GitHub project called Unity3D Utility Kit.

Download the Utility Kit from:
https://github.com/ThatJCB/Unity3DUtilityKit

The kit includes a test scene to show the usage of the attributes and to prove that they work.

At the moment the property attributes included are:
- A box to display a helpful message, which was my first idea before I got tool tips working.



- The tool tip which displays when the mouse hovers over the label.
- A Regular Expression mask for fields, created by  , many thanks.
- An integer slider that also works with float values to keep whole numbers.
- A divider with an optional heading.
- Scene selection list so you can only enter a scene name that has been included in the build list.
There's some work in progress in there as well and I'll probably add more to the list over time.



That last item, for scene selection, was the reason I started looking at the Property Attributes and Drawers in the first place.

I hope the examples will be useful to others.

Saturday, 22 March 2014

Networking with Photon for Unity

I have not had as much time to spare for this project over the last month as I would have liked.  My real work that pays the bills has been so busy I have only been able to tinker here and there but I am still making good progress. Tinkering in Unity produces results much quicker than it did in XNA.

My decision to develop the menus first to learn how to use Unity has been a good choice. I have learnt a lot about the component model used by Unity and some of the best ways to do things.



I have a menu system that works with the keyboard, mouse and gamepads and I have got as far as adding split screen including a test scene to show it in action.  I have added the usual console style features, like popping up a message when  a gampad disconnects and buttons to show which keys to press.

I am itching to get on and build the levels and add characters to the game BUT from my experiences with XNA I want to sort out how network play will work before I start on the game scenes.  I will eventually get to the main subject of this article which is about using the Photon Unity Networking service.  First a bit about why I chose to use that.

With XNA I made the mistake of developing movement that was difficult to translate in to something that works over a network.

To avoid that I have been reading up on Unity networking.  To cut a long story short the Unity network model is limiting.  Not bad, it just has limits, which is exactly what I needed to know before going too far in the wrong direction.

I won't go in to detail about how Unity networking is used because that was not the point of writing this article.

All that I need to say is that having read lots of forums and tutorials most people have concluded that using a third party network provider has advantages over the built in Unity networking.  Most commonly in threads people quote that Unity have not put much effort in to improving their networking for several years.  The other area mentioned is that the Network Address Translation (NAT) punch through that end users can often have to struggle with on their home routers can be avoided with a cloud provider.

I have therefore decided to use a third party network solution by Exit Games called Photon.

There are several providers to select from but I liked Photon because it has a similar object model to the built in solution which helps when reading samples and tutorials and is also relatively easy to convert from one to the other.  They have also put a lot of effort in to producing Unity samples.

Another important consideration, for me, is that during development it is low cost.  In fact free for 10 concurrent users, which is plenty for testing.

Photon have good documentation and is easy to sign up to at no cost.

https://www.exitgames.com/

I should point out that I have no connection with Exit Games or the Photon service.

Adding Photon networking in to the game is as easy as importing the package.  Making the game work as a network game still requires the same processes of adding network views to moving objects or using Remote Procedure Call (RPC) methods but Exit games have several examples and tutorials.

I was up and running in no time and the add-in includes a Unity Editor menu for putting in the necessary details in to the demo project:


You just need your own AppID from the Photon web site:



Watch out for their web site though.  It has a drop down to swap between the various services they provide.  Look out for Photon Unity Networking (PUN).



You get different menus, different documentation and different account details if you have selected a different service.

Testing

Unity will only run one instance of the same project so I made two copies of the Photon Demo project, set one to run in the background...




...left the other unchanged.  Opened both in Unity and ran them both side by side:





Photon has another advantage in that single player and multiplayer can share the same code.  Photon can be set to offline mode and all the code just works without any changes.  I have tried that out to confirm it and it does.




if (PhotonNetwork.offlineMode)
{
    if (GUILayout.Button("Use Online Mode"))
    {
        PhotonNetwork.offlineMode = false;
    }
}
else
{
    if (GUILayout.Button("Use Offline Mode"))
    {
        PhotonNetwork.offlineMode = true;
    }
}



I added the above code in to the networked Photon Viking demo to try offline single player.

The one missing bit that I would like, is local LAN play for testing multiplayer code while not connected to the Internet.  This is not documented by Exit Games.  They do offline single player or online multiplayer.

There is a solution for offline multiplayer if you have a Windows PC.  It requires installing the server locally.  Photon provide the server executables so you can run your own server.  I don't think it was intended for testing offline but it works.

Photon provide good and quick documentation to get the server installed on a Windows PC so I won't repeat that.

I will just summarize it:

  • Download the server package
  • Copy it all to a folder
  • Run 'Photon Control' from the appropriate folder for your machine.



When I tried it out I was connected to a network.  I ran the 'Load Balancing (My Cloud)' option and set the IP address to Local.  That was just two clicks from the sys tray menus.


I had to initially set the Windows Firewall options to enable network access but that was from an automated Windows popup.  It all worked flawlessly.

I could swap between local and cloud server by changing the settings from the Photon menu:



I used the same demo, side by side but this time it used the local server.




if (!PhotonNetwork.connected)
{
    if (GUILayout.Button("Use Local Cloud"))
    {
        PhotonNetwork.PhotonServerSettings.UseMyServer(
                ServerSettings.DefaultServerAddress, 
                ServerSettings.DefaultMasterPort, 
                PhotonNetwork.PhotonServerSettings.AppID);

        PhotonNetwork.ConnectUsingSettings("v1.0"); 
    }
    else if (GUILayout.Button("Use Internet Cloud"))
    {
        PhotonNetwork.PhotonServerSettings.UseCloud (
                PhotonNetwork.PhotonServerSettings.AppID, 
                 (int)CloudServerRegion.EU);

        PhotonNetwork.ConnectUsingSettings("v1.0");
    }
}



I added the above code in to the networked Photon Viking demo to swap between local and cloud servers.

The above is all that most people will need.

The following is only useful to a very small minority that want to test networking while totally disconnected, such as on a train or in an airplane.

I hit one snag with the local server.  When I had no network connection at all the server does not run. This happens to me every day when I am working on the train.

As far as I can tell this is an oversight in the design. If all the network adapters on a Windows PC are disconnected then there is no IP address for the Photon server to bind to.

There may be some Photon server settings to enable it to work but I got round this quickly by installing the Microsoft Loopback adapter.  That is basically a dummy network interface that is always present. It is intended for testing networks but it does no harm and is included as standard with Windows installs, although not enabled.

For Windows 8 it is installed from Device Manager.
  • Open Device Manager
  • Select the Network Adapters line
  • Action -> Add legacy hardware -> Network Adapters





  • ...Manually Select from a list (It takes a minute to display the list) then select...
  • Microsoft -> Microsoft KM-TEST Loopback Adapter



  • Next, Next, Finish


That will add the extra adapter in to networking but I did not have to do anything with it.

By default this is set to use DHCP but luckily it falls back to Microsoft's
default IP address which is fine.  Just the adapter's presence is enough.



The Photon Server will now always have an IP to use locally.  Remember that IP is only any good for testing and only really necessary when no other network connections are available.

That last little bit may only be useful to a few but there is little if any documentation that I could find on using the server totally disconnected.

I can now run the demos completely offline:



I'm all setup to work on the connection and lobby scenes.

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, 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.

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.

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.

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

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.