LogoLogo
⚠️ Outdated documentationGo to LatestHomeAPI
SDK 1.0
SDK 1.0
  • Welcome
  • Overview
    • What is coherence?
    • How does coherence work?
    • Rooms and Worlds
    • Features and Roadmap
    • Release Notes
    • Known Issues and Troubleshooting
  • Learning coherence
    • Beginner's Guide to Networking Games
    • First Steps tutorial
      • 1. Basic syncing
        • 1.2. Animation parameters
        • 1.3. Sending commands
      • 2. Physics / Authority transfer
      • 3. Areas of interest
      • 4. Parenting entities
      • 5. Complex hierarchies
      • 6. Persistence
    • Campfire project
      • Game mechanics
      • Leveraging object pooling
      • Remote interactions: Chairs
      • Remote interactions: Trees
      • A unique object with complex state
      • Custom instantiation and destruction
      • Running a server-side NPC
      • Playing audio and particles
      • A simple text chat
    • How to network...
      • Racing
      • Turn-based
      • First-Person Shooter
      • MMO
      • Fighting
  • Get started
    • Installation
    • Scene Setup
      • Samples
    • Prefab Setup: CoherenceSync
    • Local Development
      • Tips and Recommendations
    • coherence Cloud
      • Create a Free Account
      • Deploy a Replication Server
      • Share Builds
  • coherence SDK for Unity
    • Components
      • CoherenceSync
      • CoherenceBridge
      • CoherenceLiveQuery
      • CoherenceTagQuery
      • Order of execution
    • Asset Management
      • Using CoherenceSyncConfig to instantiate GameObjects locally
      • CoherenceSyncConfigRegistry Save Modes
    • Networking State Changes
      • Messaging with Commands
      • Hierarchies & Child Objects
        • Child GameObjects
        • Child CoherenceSyncs
        • Deep Child CoherenceSyncs
      • Animations
      • CoherenceSync References
      • [Sync] and [Command] Attributes
      • [OnValueSynced] Attribute
      • Supported Types
      • Creating your own syncable member
    • Baking (Code Generation)
    • Scene Management
    • Authority
      • Authority transfer
      • Server-authoritative setup
    • Lifetime
      • Persistence
      • Example – a global counter
    • Optimization
      • Simulation Frequency
      • Areas of Interest
      • Level of Detail (LOD)
    • Profiling
    • Interpolation
    • Rigid Bodies
    • Settings
    • Simulation Frame
    • Replication Server
    • Simulators
      • Scripting: Client vs Simulator
      • Local Development
      • World Simulators
      • Room Simulators
      • Simulator Slugs
      • Multi-Room Simulators
      • Build and Publish
      • Command-line arguments
      • Load Balancing
    • Client-Hosting
    • Client Connections
    • Rollback Networking Support
    • World Origin Shifting
    • CLI
    • Upgrading Unity SDK
      • Upgrading to coherence Unity SDK 1.0.0
      • Upgrading to coherence Unity SDK 0.9.0
  • coherence Cloud
    • Developer Portal
    • Dashboard
    • Worlds
    • Rooms
    • Lobbies
    • Game Services
      • Account
      • Key-Value Store
    • Using coherence Cloud in Unity
      • Worlds
      • Rooms
      • Lobbies
      • Game Services
        • Authentication Service (Player Accounts)
        • Key-value store
  • Schema explained
    • Overview
    • Specification
    • Field settings
    • Archetypes
  • coherence Scripting API
  • Additional resources
    • Community
    • Quick Samples
    • Continuous Integration
    • Unreal Engine Support
    • WebGL Support
    • Peer-to-Peer Support (P2P)
    • Pricing
    • SLA
    • Glossary
Powered by GitBook
On this page

Was this helpful?

Export as PDF
  1. Additional resources

Quick Samples

Some simple scripts covering different networking needs that you may find useful:

A simple spawn script that Instantiates the Networked Player on a keypress.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnCharacter: MonoBehaviour
{
    public KeyCode KeyPress;
    public GameObject NetworkedCharacter;
    
    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyPress))
        {
            Instantiate(NetworkedCharacter);    
        }
    }
}
Simple Input to get your Game Objects moving.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleMovement : MonoBehaviour
{
    public float speed = 1f;

    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        transform.position += transform.forward * (v * speed * Time.deltaTime);
        transform.position += transform.right * (h * speed * Time.deltaTime);
    }
}
An Event System you put on the Prefab with coherenceSync
that can handle Network Commands and transfer Authority.
using Coherence;
using UnityEngine;
using UnityEngine.Events;
using Coherence.Toolkit;

public class CoherenceCommandHandler : MonoBehaviour
{
    private CoherenceSync sync;

    public UnityEvent onCommandReceived;

    private GameObject fxInstance;

    private void Awake()
    {
        sync = GetComponent<CoherenceSync>();
    }

    public void Adopt()
    {
        if (!sync || sync.HasStateAuthority)
        {
            return;
        }

        sync.RequestAuthority(AuthorityType.Full);
    }

    public void Orphan()
    {
        if (!sync || !sync.HasStateAuthority)
        {
            return;
        }

        sync.AbandonAuthority();
    }

    public void ToggleAuthority()
    {
        if (!sync)
        {
            return;
        }

        if (sync.HasStateAuthority)
        {
            Orphan();
        }
        else
        {
            Adopt();
        }
    }

    public void Command(CoherenceSync sender)
    {
        sync.SendCommand<CoherenceCommandHandler>(nameof(CoherenceCommandHandler.ReceiveCommand), MessageTarget.All);
    }

    public void ReceiveCommand()
    {
        onCommandReceived.Invoke();
    }

    public void InstantiateFX(GameObject fx)
    {
        if (fxInstance)
        {
            Destroy(fxInstance);
        }

        var t = fx.transform;
        fxInstance = Instantiate(fx, transform.position + t.position, transform.rotation);
    }
}
An Event System you put on an empty Game Object in your 
Scene that handles Global Connection/Disconnection.
using UnityEngine;
using UnityEngine.Events;
using Coherence.Connection;
using Coherence.Toolkit;

public class CoherenceConnectionEvents : MonoBehaviour
{
    public UnityEvent onConnect;
    public UnityEvent onDisconnect;
    private CoherenceBridge Bridge;

    private void OnEnable()
    {
        Bridge = FindObjectOfType<CoherenceBridge>();
        Bridge.onConnected.AddListener(HandleConnected);
        Bridge.onDisconnected.AddListener(HandleDisconnected);
        Bridge.OnLiveQuerySynced.AddListener(HandleLiveQuerySynced);
    }

    private void OnDisable()
    {
        Bridge.onConnected.RemoveListener(HandleConnected);
        Bridge.onDisconnected.RemoveListener(HandleDisconnected);
        Bridge.onLiveQuerySynced.RemoveListener(HandleLiveQuerySynced);
    }

    private void HandleConnected(CoherenceBridge _)
    {
        onConnect?.Invoke();
    }

    private void HandleLiveQuerySynced(CoherenceBridge _)
    {
        onConnect?.Invoke();
    }

    private void HandleDisconnected(CoherenceBridge _, ConnectionCloseReason __)
    {
        onDisconnect?.Invoke();
    }

Keeps a score that is synced across the Network.

using Coherence.Toolkit;
using Coherence;
using UnityEngine;

// Keeps a score that is synced across the network. 
// Put this script on a singleton object in the Editor. 
// That Game Object needs a CoherenceSync that doesn't allow duplicates. 
// Remember to sync the score variable, and the PointCommand method.  

public class ScoreKeeper : MonoBehaviour
{
    static ScoreKeeper inst;
    CoherenceSync sync;

    public int score; // Remember to sync this variable! 

    void Awake( ) {
        if( inst != null ) {
            Destroy( gameObject );
            return;
        }
        inst = this;
        sync = GetComponent<CoherenceSync>( );
    }

    // Remember to sync this method! 
    public void PointCommand( int amount ) { score += amount; }

    public static void GivePoint( int amount ) {
        inst.sync.SendCommand<ScoreKeeper>(
            nameof( ScoreKeeper.PointCommand ),
            MessageTarget.AuthorityOnly, amount );
    }
}
Display a name tag above all player characters with their name. 
Put this script on your player character and populate the nameTagPrefab variable (a basic TextMeshPro Object will do fine). 
You also need to sync the playerName variable through the coherence Configuration tab. 
using Coherence.UI;
using UnityEngine;
using TMPro;
using Coherence.Toolkit;

// Creates a TextMeshPro Object from a Prefab.
// Finds a canvas in the Scene and attaches the TextMeshPro Object to it.
// Looks for the player name in World or Room ConnectDialog.
// Updates the position of the name tag in update.

// nameTagPrefab can be just a default TextMeshPro Object.
// playerNameField is an InputField in either the World, Room, or Lobby connection dialog. The code below looking for it might fail if your hierarchy is organised in a very different way.

public class NameTag : MonoBehaviour
{
    public GameObject nameTagPrefab;
    public Vector2 offset = new Vector2( 0, 2 );
    [Sync] public string playerName; 

    CoherenceSync sync;
    TextMeshProUGUI text;
    InputField playerNameField;
    Camera cam;

    private void Start( )
    {
        sync = GetComponent<CoherenceSync>( );

        Canvas canvas = FindObjectOfType<Canvas>( );
        if( canvas == null ) Debug.Log( "couldn't find a canvas to parent NameTag to" );
        if( nameTagPrefab == null ) Debug.Log( "NameTag prefab is null!" );

        text = Instantiate( nameTagPrefab ).GetComponent<TextMeshProUGUI>( );
        text.transform.SetParent( canvas.transform );

        // Only get player name if we have Authority, which gets synced with other Clients. 
        if( sync.HasStateAuthority )
        {
            playerNameField = GameObject.Find("Connect Dialog").transform.Find("PlayerName").GetComponentInChildren<InputField>();
            if( playerNameField == null ) Debug.Log("Couldn't find the player name input field. Your hierarchy might be organised in a different way than expected.");
        
            // Use the input field value to display the player name
            playerName = playerNameField.text; 
            text.text = playerName;
        
            // Listen to potential changes, in case the user types in a new one
            playerNameField.onValueChanged.AddListener(TextFieldChanged);
        }

        cam = FindObjectOfType<Camera>( );
    }

    private void Update( )
    {
        // Here we assume the game is 2D. 
        // Use the Camera Facing UI script if your game is 3D!
        Vector2 pos = transform.position;
        text.transform.position = cam.WorldToScreenPoint( pos + offset );
        
        if( text.text != playerName ) text.text = playerName;
    }
    
    private void TextFieldChanged(string newName)
    {
        playerName = newName;
    }

    private void OnDestroy( )
    {
        playerNameField.onValueChanged.RemoveListener(TextFieldChanged);
        if( text != null ) Destroy( text.gameObject );
    }
}
Following the previous example, put this line of code in the Update() function to have the UI always face the camera. 
text.transform.LookAt(text.transform.position + cam.rotation * Vector3.forward, cam.rotation * Vector3.up);
This method returns a random position off screen (in 2D!). Very useful for spawning enemies off screen in your prototypes.
 Vector2 RandomBorderPosition( ) {
        Camera cam = FindObjectOfType<Camera>( ); // you should probably move this line to Awake() or Start() 

        float unitsOffScreen = 1;

        Vector2 campos = cam.transform.position;
        float XY_Ratio = (float)cam.scaledPixelWidth / (float)cam.scaledPixelHeight;
        Vector2 camdim = new Vector2( cam.orthographicSize * XY_Ratio, cam.orthographicSize );
        Vector2 randomDirectionFromCenter = camdim + Vector2.one * unitsOffScreen;
        if( Random.Range( 0, 100 ) < 50 ) randomDirectionFromCenter.x *= -1;
        if( Random.Range( 0, 100 ) < 50 ) randomDirectionFromCenter.y *= -1;

        if( Random.Range( 0, 100 ) < 50 ) {
            randomDirectionFromCenter.x = Random.Range( -1f, 1f ) * camdim.x;
        } else {
            randomDirectionFromCenter.y = Random.Range( -1f, 1f ) * camdim.y;
        }

        return campos + randomDirectionFromCenter;
    }
When set up, this script will display a sprite (an arrow for example) on the edge of the screen, indicating that something is off screen.

To set it up, create a GameObject and attach this script and a SpriteRenderer to it. Put what you want to track into subject, and you're good to go!
using UnityEngine;

public class OffscreenIndicator : MonoBehaviour
{
    public Transform subject;
    public float radius = .3f; // units outside screen before indicator starts showing. 
    public float border = -1; // units from screen border to display indicator. positive values will display outside screen.  
    public bool pointTowardsSubject = true; // if the indicator is an arrow or something, you probably want it to point towards your subject. 

    Camera cam;

    void Awake( ) {
        transform.localScale = Vector2.zero; 
        cam = FindObjectOfType<Camera>( );
    }

    void Update( ) {
        if( subject == null ) return; 
        bool onscreen = true;

        Vector2 pos = subject.position;
        Vector2 campos = cam.transform.position;
        float heightRatio = (float)cam.pixelWidth / (float)cam.pixelHeight; // viewport returns screen as (1,1), so we gotta factor in the screens height to width ratio
        Vector2 dirFromCamera = pos - campos;
        if( Mathf.Abs( dirFromCamera.x ) > ( cam.orthographicSize * heightRatio + border ) ) {
            dirFromCamera.x = Mathf.Sign( dirFromCamera.x ) * ( cam.orthographicSize * heightRatio + border );
            onscreen = false;
        }
        if( Mathf.Abs( dirFromCamera.y ) > cam.orthographicSize + border ) {
            dirFromCamera.y = Mathf.Sign( dirFromCamera.y ) * ( cam.orthographicSize + border );
            onscreen = false;
        }

        if( onscreen ) {
            transform.localScale = new Vector2( 0, 0 );
        } else {
            transform.position = campos + dirFromCamera;
            transform.localScale = new Vector2( 1, 1 );
            if( pointTowardsSubject ) {
                Vector2 direction = pos - ( campos + dirFromCamera );
                float atan2 = Mathf.Atan2( direction.y, direction.x );
                transform.rotation = Quaternion.Euler( 0, 0, ( atan2 * Mathf.Rad2Deg + 270 ) % 360.0f );
            }
        }
    }
}
c#

Last updated 1 year ago

Was this helpful?