172 lines
5.3 KiB
C#
172 lines
5.3 KiB
C#
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<ChatUI>();
|
|
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<ChatUI>();
|
|
if (ui == null) return;
|
|
|
|
var prettyMessage = senderName == ChatUI.localPlayerName ?
|
|
$"<color=red>{senderName}:</color> {message}" :
|
|
$"<color=blue>{senderName}:</color> {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<ChatUI>();
|
|
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<TextMesh>();
|
|
}
|
|
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<TextMesh>();
|
|
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);
|
|
}
|
|
}
|