AuthorMichael Zehr

Spawn network objects with the correct rotation

Network objects (with a NetworkIdentity script attached) are spawned at the correct location, but not with the correct rotation and scale. This is simply because the information is not transmitted during spawn. There may be a fix for this in the future.

The rotation of the yellow objects was not synchronized across the network.
The rotation of the yellow objects was not synchronized across the network.

In my opinion that makes sense, because there may be objects that do not even need position, rotation or scale. For this reason, there is already a script called NetworkTransform, which synchronizes the properties of Transforms or Rigidbodies. And it works. However, the rotation is only applied after a short delay, which is noticeable by the player. So, if Unity is going to fix this, I guess the NetworkTransform might be the right place to do it.

In the meantime the problem can be worked around with this little script:

using UnityEngine.Networking;

public class NetworkObject : NetworkBehaviour
{
    public override bool OnSerialize(NetworkWriter writer, bool initialState)
    {
        if (initialState)
        {
            // Transmit the rotation (and scale) only once (initalState)
            writer.Write(transform.localRotation);
            //writer.Write(transform.localScale); // Uncomment this line, if you need scale too
            return true;
        }
        // Don't send further state updates
        return false;
    }

    public override void OnDeserialize(NetworkReader reader, bool initialState)
    {
        if (initialState)
        {
            // Apply the retrieved rotation (and scale)
            transform.localRotation = reader.ReadQuaternion();
            //transform.localScale = reader.ReadVector3(); // Uncomment this line, if you need scale too
        }
    }
}

Just attach the new NetworkObject script in addition to the NetworkIdentity script and it should work:

The rotation of the yellow objects was synchronized across the network.
The rotation of the yellow objects was synchronized across the network.

Things worth knowing about coroutines

What are coroutines?

Coroutines are functions which are executed over multiple frames. They are very convenient and can be used for scripted sequences, for example to move an object from point A to B smoothly over multiple frames. Another coroutine could use the first coroutine to move an object from A to B, then pause 5 seconds and finally move from B to C. This can be achieved by letting a couroutine wait for the completion of another coroutine. Here is an example:

class MovingObject : MonoBehaviour
{
    void Start()
    {
        // Start the first coroutine
        StartCoroutine(Move());
    }

    // Move to 0,0,10 then wait 5 seconds and then move to 10,0,10
    IEnumerator Move()
    {
        // Stop execution until the MoveTo-coroutine has completed
        yield return StartCoroutine(MoveTo(new Vector3(0,0,10)));
        // Stop execution until 5 seconds have passed
        yield return new WaitForSeconds(5);
        // Stop execution until the MoveTo-coroutine has completed
        yield return StartCoroutine(MoveTo(new Vector3(10,0,10)));
    }

    // Move to the specified target over multiple frames (smoothed out)
    IEnumerator MoveTo(Vector3 target)
    {
        Transform t = transform;
        while(Vector3.Distance(t.position, target) > 0.2f)
        {
            t.position = Vector3.Lerp(t.position, target, Time.deltaTime);
            // Stop execution until the next frame
            yield return null;
        }
    }
}

The big advantage of coroutines is that the state is maintained while the coroutine is running (in this case the argument "target" and the local variable "t"). Thus, even the most complex sequences can be scripted in a relatively simple way.

How do coroutines work from a technical perspective?

Coroutines run in parallel, but no threads are used. Basically, a coroutine is a C# feature (other languages probably also support this feature).

For a foreach loop in C# to work at all, an array, a list, or any other class to be enumerated, must implement the interface IEnumerable. This interface declares a single method:

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

The method returns an object that implements the IEnumerator interface. The IEnumerator interface looks something like:

interface IEnumerator
{
    // Gets the current element in the collection
    object Current { get; }

    // Advances the enumerator to the next element of the collection
    bool MoveNext();

    // Sets the enumerator to its initial position, which is before the first element in the collection
    void Reset();
}

A regular foreach loop is then converted to the following code:

IEnumerator enumerator = myList.GetEnumerator();
while (enumerator.MoveNext())
{
    ListElementType myElement = (ListElementType)enumerator.Current;
    // .. code that does something with myElement
    ..
}

To write your own enumerator, you would therefore need to create a class that implements the interface IEnumerator. At this point the above mentioned C# feature comes into play. Instead of having to write a complete class, an enumerator function can be created. This is a simplified syntax for creating custom enumerators. It's the exact same syntax that the coroutines in Unity use:

IEnumerator EverySecondChar(string name)
{
    for (int i = 0; i < name.Length; i += 2)
    {
        yield return name[i];
    }
}

The above enumerator that returns every second character of a string is converted to something like this:

class EverySecondCharEnumerator : IEnumerator
{
    private string _name;
    private int _index;

    public EverySecondCharEnumerator(string name)
    {
        _name = name;
        _index = -1;
    }

    public object Current
    {
        get { return _name[_index]; }
    }

    public bool MoveNext()
    {
        if (_index + 2 >= _name.Length)
            return false;
        _index += 2;
        return true;
    }

    public void Reset()
    {
        _index = -1;
    }
}

And can be used like this:

EverySecondCharEnumerator e = new EverySecondCharEnumerator(name);
while (e.MoveNext())
{
    char c = (char)e.Current;
    // ...
}

The same thing with the enumerator function:

IEnumerator e = EverySecondChar(name);
while (e.MoveNext())
{
    char c = (char)e.Current;
    // ...
}

Now imagine what the "yield return" does:

  • On the first call to MoveNext() the function executes until the first yield return or a yield break statement.
  • The yield return provides an object that will then be available in the "Current" property of the enumerator.
  • On the next call to MoveNext() the execution of the function is resumed right where it stopped (after the first yield return statement).
  • From there, the enumerator function is executed until the next yield return and this process is repeated until the function is either completed, or a yield break is encountered. The yield break and the end of the function make sure that MoveNext() will return false and the loop is broken.

Unity simply uses the main loop of the game instead of the while loop from the example above. The function MoveNext() is called once per frame for each coroutine. If MoveNext() returns false, then the coroutine has completed. If the yield return statement returns a null, then Unity will call MoveNext() in the next frame. If it returns a WaitForSeconds object, then Unity reads the number of seconds from this object and ensures that the next call to MoveNext() occurs only after this amount of time has passed. If it returns another coroutine, then calling MoveNext() is delayed until the other coroutine has also completed in the exact same way.

Is there anything else worth knowing about coroutines?

The coroutines are executed by the class MonoBehaviour, which is the base class of most unity scripts. Thus, when a game object is destroyed or if the script component is removed from the game object, any coroutines running on this script are interrupted immediately because there is no longer a script that will call the MoveNext() function.

For this reason you should always be careful when working with nested coroutines. Here is a small example:

class MyCube : MonoBehaviour
{
    public IEnumerator MoveAsync(Vector3 target)
    {
        // movement code with yield returns
    }
}

class ScriptedMovement : MonoBehaviour
{
    public MyCube cube;

    IEnumerator Movement1(Vector3 target)
    {
        // MoveAsync will be executed on this object
        yield return StartCoroutine(cube.MoveAsync(target));
    }

    IEnumerator Movement2(Vector3 target)
    {
        // MoveAsync will be executed on the cube object
        yield return cube.StartCoroutine(cube.MoveAsync(target));
    }
}

When you add a StartCoroutine(Movement1()) call, then the cube will begin to move. When the ScriptedMovement object is destroyed while the cube is still moving, the cube movement will be interrupted because the coroutine was running inside the ScriptedMovement object and not inside the MyCube object.

If you use the Movement2 function instead, the cube will continue its movement until it arrives at the destination, because this time the coroutine will run on the cube itself.

So, be careful. Otherwise you might encounter some unexpected coroutine interruptions in more complex projects...

To make sure that coroutines will always run in the class in which they are defined, you can use this little trick:

class MyCube : MonoBehaviour
{
    // Only this function can be called from the outside,
    // which returns a Coroutine-Object
    public Coroutine Move(Vector3 target)
    {
        // MoveAsync will run on this object
        return StartCoroutine(MoveAsync(target));
    }

    // The coroutine can't be called from the
    // outside because it's private
    private IEnumerator MoveAsync(Vector3 target)
    {
        // movement code with yield returns
    }
}

class ScriptedMovement : MonoBehaviour
{
    public MyCube cube;

    IEnumerator Movement(Vector3 target)
    {
        // The coroutine MoveAsync will run on the cube object because
        // Move(..) contains the StartCoroutine(MoveAsync(..)) call
        yield return cube.Move(target);
    }
}

There is a public method which has no other purpose than starting the private coroutine and returning a Coroutine object. This object is returned by the StartCoroutine() method.

A yield return StartCoroutine(..); could also be written as:

Coroutine c = StartCoroutine(...);
yield return c;

And hence:

Coroutine c = cube.Move(target);
yield return c;

In both cases, a Coroutine object is returned in the end.

Show or hide variables in the editor

As most people probably know, public fields in Unity scripts (MonoBehaviour) are automatically displayed in the editor and serialized/saved to the scene file. Protected and private fields are not displayed or serialized by default.

Interestingly, there are some attributes that can influence the default behaviour:

public class EditorVisibility : MonoBehaviour
{
	// Fields decorated with the HideInInspector-attribute do
	// not show up in the editor but will be serialized
	[HideInInspector] public int invisible = 5;

	// Fields decorated with the SerializeField-attribute are
	// visible in the editor and will be serialized
	[SerializeField] private int visible = 5;

	// Properties can't be serialized
	// (You can serialize the backing field of a property, however)
	[SerializeField] public int MyProperty { get; set; } // does not work

	// Fields decorated with the NonSerialized-attribute do
	// not show up in the editor and won't be serialized
	[System.NonSerialized] public int invisibleNotSerialized = 5;
}

But which attribute ist useful in which case?

HideInInspector is especially useful if there's an editor script that processes the field from the outside. This way, the field can be hidden from the user while the editor script has access to it. It is still being serialized to the scene.

SerializeField should be used quite often as a replacement for public fields, because most of the time it does not make much sense that other scripts can access your fields directly. By using SerializeField the user is able to edit private and protected fields in the editor while other scripts are unable to manipulate them at runtime (, which could lead to unexpected behaviour). Fields with the SerializeField-attribute are serialized to the scene file, obviously.

System.NonSerialized is similar to HideInInspector, but values are not being serialized to the scene. This can be useful for fields that should be publicly accessible from other scripts, but are irrelevant to the user. Public properties are a much better choice for this scenario, because they are not being serialized by default.

© 2019 michael-zehr.ch

Theme by Anders NorénUp ↑