浏览代码

Adding in nameplates to player cursors, so that player names are shown except for that of the local client. Adding a networked data store to live on the server, since we'll need to retrieve the names for the end-of-game UI as well.

/main/staging/ngo_minigame_cleanup
nathaniel.buck@unity3d.com 2 年前
当前提交
3c745e1e
共有 10 个文件被更改,包括 401 次插入29 次删除
  1. 14
      Assets/Prefabs/InGame/InGameLogic.prefab
  2. 220
      Assets/Prefabs/InGame/PlayerCursor.prefab
  3. 44
      Assets/Scripts/Netcode/InGameRunner.cs
  4. 29
      Assets/Scripts/Netcode/PlayerCursor.cs
  5. 2
      Assets/Scripts/Netcode/Scorer.cs
  6. 8
      Assets/Scripts/Netcode/SetupInGame.cs
  7. 20
      Assets/Scripts/Netcode/LobbyUserData.cs
  8. 11
      Assets/Scripts/Netcode/LobbyUserData.cs.meta
  9. 71
      Assets/Scripts/Netcode/NetworkedDataStore.cs
  10. 11
      Assets/Scripts/Netcode/NetworkedDataStore.cs.meta

14
Assets/Prefabs/InGame/InGameLogic.prefab


- component: {fileID: 6673480979101889538}
- component: {fileID: 6829526275642584874}
- component: {fileID: 2250928641321586401}
- component: {fileID: 2257191875943382526}
m_Layer: 0
m_Name: InGameRunner
m_TagString: Untagged

m_scorer: {fileID: 2250928641321586401}
m_killVolume: {fileID: 3287911880781162359}
m_introOutroRunner: {fileID: 6828649701235740815}
m_dataStore: {fileID: 2257191875943382526}
--- !u!114 &6829526275642584874
MonoBehaviour:
m_ObjectHideFlags: 0

m_Name:
m_EditorClassIdentifier:
m_scoreOutputText: {fileID: 4345563381811920084}
--- !u!114 &2257191875943382526
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1306704497370578788}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5a054eb742dd9f748afc05982dc263b0, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1912141680601921478
GameObject:
m_ObjectHideFlags: 0

220
Assets/Prefabs/InGame/PlayerCursor.prefab


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &604286645995213136
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 708127520262250088}
- component: {fileID: 5352723520210415014}
- component: {fileID: 1472899085118295698}
m_Layer: 5
m_Name: NameText
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &708127520262250088
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 604286645995213136}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.01, y: 0.01, z: 0.01}
m_Children: []
m_Father: {fileID: 8458688300638924921}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5352723520210415014
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 604286645995213136}
m_CullTransparentMesh: 1
--- !u!114 &1472899085118295698
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 604286645995213136}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 008daa1b26adf2b4ba0fbd11b7f79b6e, type: 2}
m_sharedMaterial: {fileID: 7878303141229832600, guid: 008daa1b26adf2b4ba0fbd11b7f79b6e, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 36
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 0
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &3227847727972158006
GameObject:
m_ObjectHideFlags: 0

m_EditorClassIdentifier:
m_renderer: {fileID: 3936431673663208488}
m_onClickParticles: {fileID: 8199063143616850966}
m_nameOutput: {fileID: 1472899085118295698}
--- !u!65 &-1476701697690489341
BoxCollider:
m_ObjectHideFlags: 0

serializedVersion: 2
m_Size: {x: 0.2, y: 0.2, z: 0.2}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &4674889942549093605
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8458688300638924921}
- component: {fileID: 5020605847119323726}
- component: {fileID: 407076462024115991}
m_Layer: 5
m_Name: NameCanvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8458688300638924921
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4674889942549093605}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 33.333336, y: 33.333336, z: 33.333336}
m_Children:
- {fileID: 708127520262250088}
m_Father: {fileID: 6055385295167824334}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 16, y: -40}
m_SizeDelta: {x: 1, y: 1}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!223 &5020605847119323726
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4674889942549093605}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 2
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 25
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!114 &407076462024115991
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4674889942549093605}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 1
--- !u!1 &6446591093953186703
GameObject:
m_ObjectHideFlags: 0

m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -0.5}
m_LocalScale: {x: 0.03, y: 0.03, z: 0.03}
m_Children: []
m_Children:
- {fileID: 8458688300638924921}
m_Father: {fileID: 3227847727972158004}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

44
Assets/Scripts/Netcode/InGameRunner.cs


private Queue<Vector2> m_pendingSymbolPositions = new Queue<Vector2>();
private float m_symbolSpawnTimer = 0.5f; // Initial time buffer to ensure connectivity before loading objects.
private int m_remainingSymbolCount = 0; // Only used by the host.
private float m_timeout = 10;
[SerializeField] private NetworkObject m_playerCursorPrefab = default;
[SerializeField] private NetworkObject m_symbolContainerPrefab = default;
[SerializeField] private NetworkObject m_symbolObjectPrefab = default;
[SerializeField] private SequenceSelector m_sequenceSelector = default;
[SerializeField] private Scorer m_scorer = default;
[SerializeField] private SymbolKillVolume m_killVolume = default;
[SerializeField] private IntroOutroRunner m_introOutroRunner = default;
[SerializeField] private NetworkObject m_playerCursorPrefab = default;
[SerializeField] private NetworkObject m_symbolContainerPrefab = default;
[SerializeField] private NetworkObject m_symbolObjectPrefab = default;
[SerializeField] private SequenceSelector m_sequenceSelector = default;
[SerializeField] private Scorer m_scorer = default;
[SerializeField] private SymbolKillVolume m_killVolume = default;
[SerializeField] private IntroOutroRunner m_introOutroRunner = default;
[SerializeField] private NetworkedDataStore m_dataStore = default;
private ulong m_localId; // This is not necessarily the same as the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
private LobbyUserData m_localUserData; // This has an ID that's not necessarily the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
private float m_timeout = 10;
public void Initialize(Action onConnectionVerified, int expectedPlayerCount, Action onGameEnd)
public void Initialize(Action onConnectionVerified, int expectedPlayerCount, Action onGameEnd, LobbyUser localUser)
m_localUserData = new LobbyUserData(localUser.DisplayName, 0);
Locator.Get.Provide(this); // Simplifies access since some networked objects can't easily communicate locally (e.g. the host might call a ClientRpc without that client knowing where the call originated).
}

FinishInitialize();
m_localId = NetworkManager.Singleton.LocalClientId;
VerifyConnection_ServerRpc(m_localId);
m_localUserData = new LobbyUserData(m_localUserData.name, NetworkManager.Singleton.LocalClientId);
VerifyConnection_ServerRpc(m_localUserData.id);
}
public override void OnNetworkDespawn()

[ClientRpc]
private void VerifyConnection_ClientRpc(ulong clientId)
{
if (clientId == m_localId)
VerifyConnectionConfirm_ServerRpc(m_localId);
if (clientId == m_localUserData.id)
VerifyConnectionConfirm_ServerRpc(m_localUserData);
private void VerifyConnectionConfirm_ServerRpc(ulong clientId)
private void VerifyConnectionConfirm_ServerRpc(LobbyUserData clientData)
playerCursor.SpawnWithOwnership(clientId);
playerCursor.name += clientId;
playerCursor.SpawnWithOwnership(clientData.id);
playerCursor.name += clientData.name;
m_dataStore.AddPlayer(clientData.id, clientData.name);
VerifyConnectionConfirm_ClientRpc(clientId, areAllPlayersConnected);
VerifyConnectionConfirm_ClientRpc(clientData.id, areAllPlayersConnected);
if (clientId == m_localId)
if (clientId == m_localUserData.id)
m_onConnectionVerified?.Invoke();
if (shouldStartImmediately)
{

private void BeginGame()
{
m_canSpawnInGameObjects = true;
Locator.Get.Messenger.OnReceiveMessage(MessageType.GameBeginning, null); // TODO: Might need to delay this a frame to ensure the client finished initializing (since I sometimes hit the "failed to join" message even when joining).
Locator.Get.Messenger.OnReceiveMessage(MessageType.GameBeginning, null);
m_introOutroRunner.DoIntro();
}

29
Assets/Scripts/Netcode/PlayerCursor.cs


using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;

/// Each player's cursor needs to be controlled by them and visible to the other players.
/// </summary>
[RequireComponent(typeof(Collider))]
public class PlayerCursor : NetworkBehaviour
public class PlayerCursor : NetworkBehaviour, IReceiveMessages
[SerializeField] private TMPro.TMP_Text m_nameOutput = default;
private Action<ulong, Action<string>> m_retrieveName;
public void Awake()
{
Locator.Get.Messenger.Subscribe(this);
}
m_retrieveName = NetworkedDataStore.Instance.GetPlayerName;
m_mainCamera = GameObject.Find("InGameCamera").GetComponent<Camera>();
if (IsHost)
m_currentlyCollidingSymbols = new List<SymbolObject>();

var trails = m_onClickParticles.trails;
trails.colorOverLifetime = new ParticleSystem.MinMaxGradient(Color.grey);
}
}
[ClientRpc]
private void SetName_ClientRpc(string name)
{
if (!IsOwner)
m_nameOutput.text = name;
}
// Don't love having the input here, but it doesn't need to be anywhere else.

return;
if (m_currentlyCollidingSymbols.Contains(symbol))
m_currentlyCollidingSymbols.Remove(symbol);
}
public void OnReceiveMessage(MessageType type, object msg)
{
if (type == MessageType.GameBeginning)
{
m_retrieveName.Invoke(OwnerClientId, SetName_ClientRpc);
Locator.Get.Messenger.Unsubscribe(this);
}
}
}
}

2
Assets/Scripts/Netcode/Scorer.cs


public void ScoreSuccess(ulong id)
{
m_scoresByClientId[id] += 5;
m_scoresByClientId[id] += 2;
UpdateScoreOutput_ClientRpc(id, m_scoresByClientId[id]);
}

8
Assets/Scripts/Netcode/SetupInGame.cs


private NetworkManager m_networkManager;
private InGameRunner m_inGameRunner;
private bool m_isHost;
private LobbyUser m_localUser;
public void Start()

m_inGameManagerObj = GameObject.Instantiate(m_prefabNetworkManager);
m_networkManager = m_inGameManagerObj.GetComponentInChildren<NetworkManager>();
m_inGameRunner = m_inGameManagerObj.GetComponentInChildren<InGameRunner>();
m_inGameRunner.Initialize(OnConnectionVerified, m_lobby.PlayerCount, OnGameEnd);
m_inGameRunner.Initialize(OnConnectionVerified, m_lobby.PlayerCount, OnGameEnd, m_localUser);
if (m_isHost)
if (m_localUser.IsHost)
m_inGameManagerObj.AddComponent<RelayUtpNGOSetupHost>().Initialize(this, m_lobby, () => { m_initializeTransport(transport); m_networkManager.StartHost(); });
else
m_inGameManagerObj.AddComponent<RelayUtpNGOSetupClient>().Initialize(this, m_lobby, () => { m_initializeTransport(transport); m_networkManager.StartClient(); });

{ m_lobby = lobby; // Most of the time this is redundant, but we need to get multiple members of the lobby to the Relay setup components, so might as well just hold onto the whole thing.
}
public void OnLocalUserChange(LobbyUser user)
{ m_isHost = user.IsHost;
{ m_localUser = user; // Same, regarding redundancy.
}
public void SetRelayServerData(string address, int port, byte[] allocationBytes, byte[] key, byte[] connectionData, byte[] hostConnectionData, bool isSecure)

20
Assets/Scripts/Netcode/LobbyUserData.cs


using Unity.Netcode;
namespace LobbyRelaySample.ngo
{
/// <summary>
/// An example of a custom type serialized for use in RPC calls.
/// </summary>
public struct LobbyUserData : INetworkSerializable
{
public string name;
public ulong id;
public LobbyUserData(string name, ulong id) { this.name = name; this.id = id; }
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref name);
serializer.SerializeValue(ref id);
}
}
}

11
Assets/Scripts/Netcode/LobbyUserData.cs.meta


fileFormatVersion: 2
guid: 3e81b917b9edfc64f95cebddd2deb7b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

71
Assets/Scripts/Netcode/NetworkedDataStore.cs


using System;
using System.Collections.Generic;
using Unity.Netcode;
namespace LobbyRelaySample.ngo
{
/// <summary>
/// A place to store data needed by networked behaviors. Each client has an instance, but the server's instance stores the actual data.
/// </summary>
public class NetworkedDataStore : NetworkBehaviour
{
// Using a singleton here since we need spawned PlayerCursors to be able to find it, but we don't need the flexibility offered by the Locator.
public static NetworkedDataStore Instance;
private Dictionary<ulong, string> m_playerNames = new Dictionary<ulong, string>();
private ulong m_localId;
public void Awake()
{
Instance = this;
}
public override void OnDestroy()
{
base.OnDestroy();
if (Instance == this)
Instance = null;
}
public override void OnNetworkSpawn()
{
m_localId = NetworkManager.Singleton.LocalClientId;
}
public void AddPlayer(ulong id, string name)
{
if (!IsServer)
return;
if (!m_playerNames.ContainsKey(id))
m_playerNames.Add(id, name);
else
m_playerNames[id] = name;
}
// NetworkList and NetworkDictionary are not considered suitable for production, so instead we use RPC calls to retrieve names from the host.
private Action<string> m_onGetCurrent;
public void GetPlayerName(ulong ownerId, Action<string> onGet)
{
m_onGetCurrent = onGet;
GetPlayerName_ServerRpc(ownerId, m_localId);
}
[ServerRpc(RequireOwnership = false)]
private void GetPlayerName_ServerRpc(ulong id, ulong callerId)
{
string name = string.Empty;
if (m_playerNames.ContainsKey(id))
name = m_playerNames[id];
GetPlayerName_ClientRpc(callerId, name);
}
[ClientRpc]
public void GetPlayerName_ClientRpc(ulong callerId, string name)
{
if (callerId == m_localId)
{ m_onGetCurrent?.Invoke(name);
m_onGetCurrent = null;
}
}
}
}

11
Assets/Scripts/Netcode/NetworkedDataStore.cs.meta


fileFormatVersion: 2
guid: 5a054eb742dd9f748afc05982dc263b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存