Nowadays online chats are an important part of everyday life. From a multiplayer game all the way to Whatsapp, Twitch and Discord, a chat is an integral feature of so many online services.

For this reason I wanted to try and write a basic chat in Unity3D. I will use this as a starting point and keep building on top of it. I want to experiment with different server infrastructures in the future.

Turns out that the Unity HLAPI makes this process surprisingly simple! Let’s have a look.

Setting up your scene

Since this was to be a simple project, I decided to go with just one scene which I have called Main. In the main scene there are two important game objects.

  • Network Manager
  • UIChat

The Network Manager will be responsible for the creation and destruction of game objects. While the UIChat will be used as a helper to display the chat as the players type in their messages.

Network Manager Setup

We need to set-up a couple of things in the Network Manager, since it will be responsible for spawning game objects over the network.

First of all, we need to register a player object to be spawned when a player connects. For this, I have created a prefab called PlayerChat and reference it in the Network Manager inspector under Spawn Info –> Player Prefab field. I will explain what PlayerChat does in a moment.

I also created a prefab, called UIChatEntry which I’m going to use to represent a single chat message. The prefab needs to be registered to the Network Manager as well.

Note that all these objects have a Network Identity component attached, which is used by the Network Manager for Creation and destruction of such objects.

PlayerChat.cs

PlayerChat is the game object which represents the user within the chat and there is going to be one of these game objects per user.

public class PlayerChat : NetworkBehaviour
{
    [SyncVar]
    public Color playerColor = new Color();
}

You will notice that PlayerChat is a NetworkBehaviour. This is because we are going to have network functionality within the class.
The second thing you’ll notice is the SyncVar attribute on top of the playerColor variable. The Network Manager is only responsible for the creation and destruction of networked game objects: this means that it leaves the state synchronisation up to us. SyncVar does exactly that, it syncs playerColor across all PlayerChat instances on the network. This way I can have a different colour per user, and that user will have the same colour on all other clients. That is, if I’m a user with a Red colour, all the other clients connected to the chat will see me as Red.

    public override void OnStartClient()
    {
        base.OnStartClient();

        chat = FindObjectOfType<UIChat&gt;();
        chat.sendButton.onClick.AddListener( OnSendButton );
        playerColor = new Color( Random.Range( 0, 256 ) / 256f, Random.Range( 0, 256 ) / 256f, Random.Range( 0, 256 ) / 256f );

        Debug.Log( "Client" );
    }
    
    public override void OnStartLocalPlayer()
    {
        base.OnStartLocalPlayer();

        CmdSendChatMessage( "New Client joined chat... say hello! \n" );

        Debug.Log( "Local Player" );
    }

    [Command]
    private void CmdSendChatMessage( string inMessage )
    {
        if ( !string.IsNullOrEmpty( inMessage ) )
        {
            chat.CreateChatEntry( inMessage, playerColor );

            Debug.Log( "Received Message " + inMessage );
        }
    }

OnStartClient is called in all instances when a Player Object is created. I’m using this method to hook up a reference to the UIChat object and to determine the colour of the player.

OnStartLocalPlayer is then called only in the instance that represents the local user. This is useful since we want to send the Welcome Message only once, when the user logs into the chat. Since there are going to be multiple PlayerChat objects, it is very useful to have a method that identifies the local user only.

To send a Chat message we call the CmdSendChatMessage method with a string representing the message. This method has the Command attribute above it. The attribute is used to invoke a method on the server from a client. When the server invokes this method, it calls UIChat to instantiate a UIChatEntry for the chat message. The UIChatEntry will then be created on all clients.

UIChat.cs

    public void CreateChatEntry( string message, Color inPlayerColor )
    {
        UIChatEntry chat = GameObject.Instantiate( chatPrefab ).GetComponent<UIChatEntry&gt;();

        chat.ShowMessage( message, inPlayerColor );
        PositionEntryInGrid( chat.gameObject );

        NetworkServer.Spawn( chat.gameObject );
    }

This class is simply used to display the chat messages by instantiating a UIChatEntry and then calling NetworkServer.Spawn. The Spawn method is used to create game objects over the network. The Server will serialise the UIChatEntry instance and send its data to the clients, which will, in turn, deserialise it and create their own copy of that instance.

UIChatEntry itself is a very simple class which just contains a player’s colour and the message to show.

public class UIChatEntry : NetworkBehaviour
{
    public Text chatText = null;
    public Image backing = null;

    [SyncVar]
    string text = null;
    [SyncVar]
    Color color = new Color();
}

Test the project

In order to test the project you will need 2 players chatting to each other. It is very easy to set up 2 clients talking to each other. You will build your project for one client and run the chat within unity for the other.

To build your project go to File -> Build Settings… and select PC, Mac and Linux Standalone. Then click Build and Run.

Press Play in your Unity Editor. The Network Manager will show the default HUD. Click Lan Host.Once the project is built and is running, click LAN Client.

You can now chat between the two clients!

Conclusion

We have seen how we can write an online chat using Unity HLAPI. We represent one message in the chat with a Networked Game Object.

Further implementations could involve writing the chat without the help of the HLAPI; write a host server which accepts connections and acts as the middleman in exchanging messages between clients.

You can find the code for this chat on my personal GitHub: https://github.com/lormori/UnityChat

References

https://docs.unity3d.com/Manual/UNetUsingHLAPI.html

Flocking is usually referred to as the behaviour that animals follow when travelling in groups. In the natural world, flocking can be easily seen in birds that fly together, but also in fishes, insects and so on. I did a small AI project some time ago, and I thought I would share it with everyone.

Flocking behaviour can be modelled using three rules:

  • Alignment: As the name suggests, alignment is the rule by which an agent of the flock tries to align with the rest, essentially steering towards the average forward direction of travel of its neighboring agents.
  • Cohesion: Cohesion is the rule by which an agent tries to move towards the center of mass of a flock.
  • Separation: Separation works very similarly to cohesion, but it is the short distance repulsion between agents.

Using a few principles and ideas, it is possible to reproduce this behaviour in computers which can be used in games as well. This article will focus on the implementation of the Flocking Behaviour in Unity3D engine. I have seen many demonstrations and tutorials of the flocking behaviour in 2D on the web, but I couldn’t find a tutorial for how to implement this in Unity. Also, I wanted the demo to be in 3D, so even if the difference between 2D and 3D are minimal, this gives me the excuse to write an article about it.

I decided to go for a fish tank theme, where all my agents are fishes instead of birds. Because why not.

Entity.cs

First off, we will need an Entity class to hold data about the state of an agent inside the flock. The agent will have a velocity Vector3 that will be updated each frame so as to align and move according to the flocking behaviour.

public class Entity : MonoBehaviour
{
    private Vector3 mVelocity = new Vector3();
}

The Entity class really only needs to hold its velocity because position and orientation are contained in the transform component of the MonoBehaviour class.

On its update function, the flocking behaviour adds to the current agent’s velocity so as to get a smooth movement each frame.

void Update()
{
    mVelocity += FlockingBehaviour();
    mVelocity = Vector3.ClampMagnitude( mVelocity, mMaxVelocity );
    transform.position += mVelocity * Time.deltaTime;
    transform.forward = mVelocity.normalized;
    Reposition();
}

It also sets its forward vector with the one of the velocity vector.

Applying Flocking behaviour rules

When calculating Alignment, every agent of the flock will check the direction of all the other agents. This means that each agent will check all the other agents and calculate the average direction of the flock. To get this direction you can simply access the forward vector from the transform of the game object.

private Vector3 Alignment()
{
    List<Entity> theFlock = App.instance.theFlock;

    Vector3 alignmentVector = new Vector3();

    int count = 0;

    for( int index = 0; index < theFlock.Count; index++ )
    {
        if( mID != theFlock[ index ].ID )
        {
            float distance = ( transform.position - theFlock[ index ].transform.position ).sqrMagnitude; 

            if( distance > 0 && distance < mRadiusSquaredDistance )
            {
                alignmentVector += theFlock[ index ].transform.forward;
                count++;
            }
        }
    }

    if( count == 0 )
    {
         return Vector3.zero;
    }

    // forward step
    alignmentVector /= count;

    return alignmentVector;
}

Cohesion is none other than the vector pointing towards the center of mass of the flock. In order to calculate cohesion, each agent needs to calculate the average position of all the other agents. Then, it needs to calculate the direction towards the center of the flock from its current location. Since the step is very similar to the previous one, I will highlight the differences only.

for( int index = 0; index < theFlock.Count; index++ )
{
    if( mID != theFlock[ index ].ID )
    {
        float distance = ( transform.position - theFlock[ index ].transform.position ).sqrMagnitude; 

        if( distance > 0 && distance < mRadiusSquaredDistance )
        {
            cohesionVector += theFlock[ index ].transform.position;

            count++;
        }
    }
}

if( count == 0 )
{
    return Vector3.zero;
}

// cohesion step
cohesionVector /= count;
cohesionVector = ( cohesionVector - transform.position );

Separation is the last vector that needs to be calculated. Separation is expressed like the average distance from the current agent to all the other agents. Since the average distance vector obtained this way will point towards the other agents, we also need to invert it to get the separation vector.

for( int index = 0; index < theFlock.Count; index++ )
{
    // [excluding code...]
    separateVector += theFlock[ index ].transform.position - transform.position;
    // [excluding code...]
}

// [excluding code...]

// revert vector
// separation step
separateVector /= count;
separateVector *= -1;

Putting it all together

Instead of running the three loops separately for each rule, I decided to combine all the calculations into the same loop. Once all the three vectors are calculated, the easiest way to combine them is to simply sum them up. This will result in the velocity vector that is returned by the FlockingBehaviour method, as discussed previously with the Update method.

I also decided to add weights to each single vector. This makes it possible to change the flocking behaviour at runtime and experiment with how these three rules work together.

private Vector3 FlockingBehaviour()
{
    List&amp;lt;Entity&amp;gt; theFlock = App.instance.theFlock;

    Vector3 cohesionVector = new Vector3();
    Vector3 separateVector = new Vector3();
    Vector3 forwardVector = new Vector3();

    int count = 0;

    for( int index = 0; index &amp;lt; theFlock.Count; index++ )
    {
        if( mID != theFlock[ index ].ID )
        {
            float distance = ( transform.position - theFlock[ index ].transform.position ).sqrMagnitude;

            if( distance &amp;gt; 0 &amp;amp;&amp;amp; distance &amp;lt; mRadiusSquaredDistance )
            {
                cohesionVector += theFlock[ index ].transform.position;
                separateVector += theFlock[ index ].transform.position - transform.position;
                forwardVector += theFlock[ index ].transform.forward;

                count++;
            }
        }
    }

    if( count == 0 )
    {
        return Vector3.zero;
    }

    // revert vector
    // separation step
    separateVector /= count;
    separateVector *= -1;

    // forward step
    forwardVector /= count;

    // cohesione step
    cohesionVector /= count;
    cohesionVector = ( cohesionVector - transform.position );

    Vector3 flockingVector = ( ( separateVector.normalized * App.instance.separationWeight ) +
                             ( cohesionVector.normalized * App.instance.cohesionWeight ) +
                             ( forwardVector.normalized * App.instance.alignmentWeight ) );

    return flockingVector;
}

If you want to check out the whole project, you can find it on my Github.

Further implementations

Since every agent in the flock has to check against every other one each frame, this can be quite expensive when dealing with large number of agents of a flock. It would be a cool exercise to try optimizing the algorithm so that this is avoided. One way to go about it could be to subdivide the space into smaller spaces, for example using a grid approach. Each agent will register to the corresponding cell and then check their behavior only against the agents currently in its own cell, and the cells adjacent to it.

If you had a close look at the Update() method, you have certainly seen that there is a Reposition() method call in it. The reposition method force the fish to stay inside the boundaries of the tank. One thing that I would like to improve is the steering pattern of the method. It would be nice to have a smooth curve instead of the sudden change of direction that is currently implemented, so maybe adding an avoiding behavior of some sort would be great to improve the AI.

What about adding new rules to the flocking behaviour itself? Maybe adding Fear to the Entity state? This could be used if the flock has to run away from a Shark. Or maybe the flock is following some object of sort, like when lions follow the leader of their pack, for instance. That could be fun!

Flocking behaviour has a simple enough implementation but carries fascinating results that imitate natural behaviour. Use it in games when you are dealing with large groups of agents can be the way to go! Or maybe you have a fish tank where your Solid Snake-like character is thrown in by the baddie during a boss fight.

When working with Unity UI system, sometimes it is useful to know what the size of a given RectTransform is. Maybe you need to know how big a widget needs to be, or to see if a mouse pointer is within that given rect.

There are two ways to do so.

Using sizeDelta

RectTransform.sizeDelta

The catch with this is that it returns a size which is relative to the parent object, so it is possible to get zero/negative values if the RectTransform is smaller than the parent object. BUT, if the pivots of the transform that you are trying to measure coincide, then sizeDelta will return the actual size of the transform.

This is especially important to note when working with transforms that are set to stretch, because their pivots do not match up.

Using rect

RectTransform.rect

The other way to get the size of a RectTransform is to use its rect property. This one always returns the correct size of the transform, so I would suggest to use this whenever you need the size of a transform and don’t want to worry about how its pivots are set up.

The only problem with this approach occurs when you are instantiating new objects, like a prefab for a vertical list, and accessing their rect property on the same frame that they are created. Unity, in fact, takes one frame to re calculate the values, so you might end up with incorrect results if you do not wait.

I found two solutions to solve this problem.

Solution 1: Rebuild Canvases

Canvas.ForceRebuildCanvases()

If you need to call this only once then this is probably the easiest solution and the quickest. However, re-building canvases allocates quite a bit of memory, especially if it is called repeatedly when instantiating many objects for a list in your UI.

Solution 2: Wait one frame

You can do this in a couple of ways.

StartCoroutine(ReadRectSize);

IEnumerator ReadRectSize()
{
 // Read rect transform size....
}

Calling a Coroutine is a simple way to wait one frame to then grab the size of a RectTransform. The problem with this approach is that the implementation is less straight forward than rebuilding canvasses, since you will have to write a new method. However, it is still quite readable.

bool readRectSize = false;

void Update()
{
    if(readRectSize)
    {
        // read rect size...
        readRectSize = false;
    }
}
void InstantiateObjects()
{
     // create objects
     readRectSize = true;
}

If you don’t want to use Coroutines then the next solution is to use a Boolean flag. When instantiating objects you can set the flag and then check it on your Update function. This is the safest solution in terms of simplicity and memory allocation, but it comes at the cost of having to add an extra flag in your implementation.