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.

Thursday 24 April 2014

Manual vs Automation

I spent several hours trying to use what I was expecting to be better methods to control spawning character when the level changes in a network game.  I'm using the Photon Unity Networks (PUN) methods.

According to the following forum post the automated methods should handle pausing messages when the scene changes.
http://forum.exitgames.com/viewtopic.php?f=17&t=2575

PhotonNetwork.automaticallySyncScene = true
PhotonNetwork.LoadLevel(...)


As far as I can tell, they work but the trouble with automation is that it is only useful if the person designing the automation was thinking the same way as you are.  In this case, Exit Games were not.


I have a scene selection popup and a lobby scene before the level loads.


The server and the client can sit at that lobby and join the game when they are ready.

By manually controlling the network message flow I can allow the server and the client to join in any order.   If I use the automated methods the server must be in the game scene before any of the clients!

PhotonNetwork.SetSendingEnabled(0, false);
PhotonNetwork.isMessageQueueRunning = false;

Obviously I prefer my method.  I simple disable the messages before loading a level and start them again only when the scene has started and before the spawn message is sent.

The only reason I tried to change to the automated methods was because I was trying to get rid of some annoying PhotonNetworks warning and error log messages whenever I exit a scene.

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.


The automated methods did not fix those either!

Back to finding a fix for the errors!

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.