using UnityEngine; using Mirror; // Networked player. This is the authority object used to run Commands. public class Player : NetworkBehaviour { [SyncVar(hook = nameof(OnNameChanged))] public string playerName; [Header("Name Tag")] [Tooltip("Optional prefab for the name tag. If empty a simple 3D TextMesh will be created.")] [SerializeField] private GameObject nameTagPrefab; [SerializeField] private float nameTagHeight = 0.2f; // runtime instance of the name tag private GameObject nameTagInstance; private TextMesh nameTagTextMesh; public override void OnStartServer() { playerName = (string)connectionToClient.authenticationData; } public override void OnStartClient() { base.OnStartClient(); CreateNameTag(); // initialize text from current syncvar value UpdateNameTagText(playerName); } public override void OnStartLocalPlayer() { // initialize local UI var ui = FindFirstObjectByType(); if (ui != null) { ui.Initialize(this); ChatUI.localPlayerName = playerName; } // request history from server if (isLocalPlayer) CmdRequestHistory(); } public override void OnStopClient() { base.OnStopClient(); if (nameTagInstance != null) { Destroy(nameTagInstance); nameTagInstance = null; nameTagTextMesh = null; } } void OnNameChanged(string oldName, string newName) { if (isLocalPlayer) ChatUI.localPlayerName = newName; UpdateNameTagText(newName); } // Called by the local ChatUI to send a message public void SendChatToServer(string message) { if (!isLocalPlayer) return; if (string.IsNullOrWhiteSpace(message)) return; CmdSendChat(message); } // Runs on server: store message and broadcast to all clients [Command] private void CmdSendChat(string message) { var manager = NetworkManager.singleton as ChatNetworkManager; manager?.AddServerMessage($"{playerName}: {message}"); RpcReceiveChat(playerName, message); } // Runs on all clients: update local ChatUI [ClientRpc] private void RpcReceiveChat(string senderName, string message) { var ui = FindFirstObjectByType(); if (ui == null) return; var prettyMessage = senderName == ChatUI.localPlayerName ? $"{senderName}: {message}" : $"{senderName}: {message}"; ui.AddMessage(prettyMessage); } // Request history from server; server will reply with TargetReceiveHistory [Command] private void CmdRequestHistory() { var manager = NetworkManager.singleton as ChatNetworkManager; if (manager == null) return; var history = manager.GetHistory(); TargetReceiveHistory(connectionToClient, history); } // Sent only to the requesting client [TargetRpc] private void TargetReceiveHistory(NetworkConnection target, string[] history) { var ui = FindFirstObjectByType(); if (ui == null) return; ui.SetHistory(history); } // Create a simple world-space name tag. If a prefab is assigned it will be instantiated instead. private void CreateNameTag() { if (nameTagInstance != null) return; if (nameTagPrefab != null) { nameTagInstance = Instantiate(nameTagPrefab, transform); nameTagInstance.transform.localPosition = new Vector3(0f, nameTagHeight, 0f); nameTagInstance.transform.localRotation = Quaternion.identity; // try to find either a TextMesh or legacy TextMesh in the prefab nameTagTextMesh = nameTagInstance.GetComponentInChildren(); } else { // create a simple 3D text (TextMesh) so no extra UI package is required nameTagInstance = new GameObject("NameTag"); nameTagInstance.transform.SetParent(transform, false); nameTagInstance.transform.localPosition = new Vector3(0f, nameTagHeight, 0f); nameTagTextMesh = nameTagInstance.AddComponent(); nameTagTextMesh.alignment = TextAlignment.Center; nameTagTextMesh.anchor = TextAnchor.MiddleCenter; nameTagTextMesh.fontSize = 48; nameTagTextMesh.characterSize = 0.025f; nameTagTextMesh.color = Color.white; } } // Updates the name text if the name tag exists private void UpdateNameTagText(string name) { if (nameTagTextMesh != null) { nameTagTextMesh.text = string.IsNullOrEmpty(name) ? "Player" : name; } } // Keep the name tag facing the camera so it's readable private void LateUpdate() { if (nameTagInstance == null) return; Camera cam = Camera.main; if (cam == null) return; // billboard: rotate the name tag to face the camera Vector3 dir = nameTagInstance.transform.position - cam.transform.position; dir.y = 0f; // optional: keep upright if (dir.sqrMagnitude > 0.0001f) nameTagInstance.transform.rotation = Quaternion.LookRotation(dir); } }