您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
140 行
5.5 KiB
140 行
5.5 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace LobbyRelaySample.ngo
|
|
{
|
|
/// <summary>
|
|
/// This cursor object will follow the owning player's mouse cursor and be visible to the other players.
|
|
/// The host will use this object's movement for detecting collision with symbol objects.
|
|
/// </summary>
|
|
[RequireComponent(typeof(Collider))]
|
|
public class PlayerCursor : NetworkBehaviour
|
|
{
|
|
[SerializeField]
|
|
SpriteRenderer m_renderer = default;
|
|
[SerializeField]
|
|
ParticleSystem m_onClickParticles = default;
|
|
[SerializeField]
|
|
TMPro.TMP_Text m_nameOutput = default;
|
|
Camera m_mainCamera;
|
|
NetworkVariable<Vector3> m_position = new NetworkVariable<Vector3>(Vector3.zero);
|
|
ulong m_localId;
|
|
|
|
// If the local player cursor spawns before this cursor's owner, the owner's data won't be available yet. This is used to retrieve the data later.
|
|
Action<ulong, Action<PlayerData>> m_retrieveName;
|
|
|
|
// The host is responsible for determining if a player has successfully selected a symbol object, since collisions should be handled serverside.
|
|
List<SymbolObject> m_currentlyCollidingSymbols;
|
|
|
|
/// <summary>
|
|
/// This cursor is spawned in dynamically but needs references to some scene objects. Pushing full object references over RPC calls
|
|
/// is an option if we create custom data for each and ensure they're all spawned on the host correctly, but it's simpler to do
|
|
/// some one-time retrieval here instead.
|
|
/// This also sets up the visuals to make remote player cursors appear distinct from the local player's cursor.
|
|
/// </summary>
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
m_retrieveName = NetworkedDataStore.Instance.GetPlayerData;
|
|
m_mainCamera = GameObject.Find("InGameCamera").GetComponent<Camera>();
|
|
InGameRunner.Instance.onGameBeginning += OnGameBegan;
|
|
if (IsHost)
|
|
m_currentlyCollidingSymbols = new List<SymbolObject>();
|
|
m_localId = NetworkManager.Singleton.LocalClientId;
|
|
|
|
// Other players' cursors should be less prominent than the local player's cursor.
|
|
if (OwnerClientId != m_localId)
|
|
{
|
|
m_renderer.transform.localScale *= 0.75f;
|
|
m_renderer.color = new Color(1, 1, 1, 0.5f);
|
|
var trails = m_onClickParticles.trails;
|
|
trails.colorOverLifetime = new ParticleSystem.MinMaxGradient(Color.grey);
|
|
}
|
|
else
|
|
{
|
|
m_renderer.enabled =
|
|
false; // The local player should see their cursor instead of the simulated cursor object, since the object will appear laggy.
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void SetName_ClientRpc(PlayerData data)
|
|
{
|
|
if (!IsOwner)
|
|
m_nameOutput.text = data.name;
|
|
}
|
|
|
|
// It'd be better to have a separate input handler, but we don't need the mouse input anywhere else, so keep it simple.
|
|
private bool IsSelectInputHit()
|
|
{
|
|
return Input.GetMouseButtonDown(0);
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
transform.position = m_position.Value;
|
|
if (m_mainCamera == null || !IsOwner)
|
|
return;
|
|
|
|
Vector3 targetPos = (Vector2)m_mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
|
|
Input.mousePosition.y, -m_mainCamera.transform.position.z));
|
|
SetPosition_ServerRpc(targetPos); // Client can't set a network variable value.
|
|
if (IsSelectInputHit())
|
|
SendInput_ServerRpc(m_localId);
|
|
}
|
|
|
|
[ServerRpc] // Leave (RequireOwnership = true) for these so that only the player whose cursor this is can make updates.
|
|
private void SetPosition_ServerRpc(Vector3 position)
|
|
{
|
|
m_position.Value = position;
|
|
}
|
|
|
|
[ServerRpc]
|
|
private void SendInput_ServerRpc(ulong id)
|
|
{
|
|
if (m_currentlyCollidingSymbols.Count > 0)
|
|
{
|
|
SymbolObject symbol = m_currentlyCollidingSymbols[0];
|
|
InGameRunner.Instance.OnPlayerInput(id, symbol);
|
|
}
|
|
|
|
OnInputVisuals_ClientRpc();
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void OnInputVisuals_ClientRpc()
|
|
{
|
|
m_onClickParticles.Stop(false, ParticleSystemStopBehavior.StopEmitting);
|
|
m_onClickParticles.Play();
|
|
}
|
|
|
|
public void OnTriggerEnter(Collider other)
|
|
{
|
|
if (!IsHost)
|
|
return;
|
|
SymbolObject symbol = other.GetComponent<SymbolObject>();
|
|
if (symbol == null)
|
|
return;
|
|
if (!m_currentlyCollidingSymbols.Contains(symbol))
|
|
m_currentlyCollidingSymbols.Add(symbol);
|
|
}
|
|
|
|
public void OnTriggerExit(Collider other)
|
|
{
|
|
if (!IsHost)
|
|
return;
|
|
SymbolObject symbol = other.GetComponent<SymbolObject>();
|
|
if (symbol == null)
|
|
return;
|
|
if (m_currentlyCollidingSymbols.Contains(symbol))
|
|
m_currentlyCollidingSymbols.Remove(symbol);
|
|
}
|
|
|
|
public void OnGameBegan()
|
|
{
|
|
m_retrieveName.Invoke(OwnerClientId, SetName_ClientRpc);
|
|
InGameRunner.Instance.onGameBeginning -= OnGameBegan;
|
|
}
|
|
}
|
|
}
|