浏览代码

Adding logic for correctly disconnecting all players when the game ends, with some esoteric fixes (setting IsLocalSpace for the SymbolObject transforms so the client can interpolate them correctly, ensuring clients are disconnected before the host).

/main/staging/ngo_minigame_structure
nathaniel.buck@unity3d.com 3 年前
当前提交
2e0ba992
共有 8 个文件被更改,包括 181 次插入25 次删除
  1. 61
      Assets/Prefabs/InGame/InGameLogic.prefab
  2. 4
      Assets/Prefabs/InGame/SymbolObject.prefab
  3. 71
      Assets/Scripts/Game/InGame/InGameRunner.cs
  4. 1
      Assets/Scripts/Game/InGame/PlayerCursor.cs
  5. 19
      Assets/Scripts/Game/InGame/SetupInGame.cs
  6. 5
      Assets/Scripts/Game/InGame/SymbolContainer.cs
  7. 34
      Assets/Scripts/Game/InGame/SymbolKillVolume.cs
  8. 11
      Assets/Scripts/Game/InGame/SymbolKillVolume.cs.meta

61
Assets/Prefabs/InGame/InGameLogic.prefab


- {fileID: 6532331214593598572}
- {fileID: 5289034077109495657}
- {fileID: 8063659512749448051}
- {fileID: 155099452610090021}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
far clip plane: 30
field of view: 60
orthographic: 0
orthographic size: 5

m_symbolObjectPrefab: {fileID: 1734492152380024498, guid: e371ca3112f9e244ab574b472387b64b, type: 3}
m_sequenceSelector: {fileID: 6829526275642584874}
m_scorer: {fileID: 2250928641321586401}
m_killVolume: {fileID: 3287911880781162359}
--- !u!114 &6829526275642584874
MonoBehaviour:
m_ObjectHideFlags: 0

m_Name:
m_EditorClassIdentifier:
m_scoreOutputText: {fileID: 4345563381811920084}
--- !u!1 &2272744577637093653
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 155099452610090021}
- component: {fileID: 3287911880781162359}
- component: {fileID: 4653186051165768580}
m_Layer: 0
m_Name: SymbolKillPlane
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &155099452610090021
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2272744577637093653}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: -9, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 485451675458297819}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &3287911880781162359
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2272744577637093653}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cdfa9a141b4e8ee4a8d88982568d94c8, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!65 &4653186051165768580
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2272744577637093653}
m_Material: {fileID: 0}
m_IsTrigger: 1
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 100, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &4526823870396177388
GameObject:
m_ObjectHideFlags: 0

4
Assets/Prefabs/InGame/SymbolObject.prefab


m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8828823320646980938}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 100, z: 0}
m_LocalPosition: {x: 0, y: 10, z: 40}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 2207893811600523791}

PositionThreshold: 0.001
RotAngleThreshold: 0.01
ScaleThreshold: 0.01
InLocalSpace: 0
InLocalSpace: 1
Interpolate: 1
CanCommitToTransform: 0
--- !u!65 &1363360377255918887

71
Assets/Scripts/Game/InGame/InGameRunner.cs


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

/// </summary>
public class InGameRunner : NetworkBehaviour, IInGameInputHandler
{
private Action m_onConnectionVerified;
private Action m_onConnectionVerified, m_onGameEnd;
private bool m_canSpawnInGameObjects;
private bool? m_canSpawnInGameObjects;
private int m_remainingSymbolCount = 0; // Only used by the host.
[SerializeField] private NetworkObject m_playerCursorPrefab = default;
[SerializeField] private NetworkObject m_symbolContainerPrefab = default;
private Transform m_symbolContainerInstance;
[SerializeField] private NetworkObject m_symbolObjectPrefab = default;
[SerializeField] private NetworkObject m_playerCursorPrefab = default;
[SerializeField] private NetworkObject m_symbolContainerPrefab = default;
[SerializeField] private NetworkObject m_symbolObjectPrefab = default;
[SerializeField] private Scorer m_scorer = default;
[SerializeField] private Scorer m_scorer = default;
[SerializeField] private SymbolKillVolume m_killVolume = default;
private Transform m_symbolContainerInstance;
public void Initialize(Action onConnectionVerified, int expectedPlayerCount)
public void Initialize(Action onConnectionVerified, int expectedPlayerCount, Action onGameEnd)
m_canSpawnInGameObjects = false;
m_onGameEnd = onGameEnd;
m_canSpawnInGameObjects = null;
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).
}

public override void OnNetworkDespawn()
{
// This will be where to do full clean up?
UnityEngine.Debug.LogError("InGameRunner despawn");
m_onGameEnd(); // As a backup to ensure in-game objects get cleaned up, if this is disconnected unexpectedly.
}
private void FinishInitialize()

m_killVolume.Initialize(OnSymbolDeactivated);
}
private void ResetPendingSymbolPositions()

{
VerifyConnection_ClientRpc(clientId);
// If not spawning things in the background, start that.
// If not spawning things in the background, start doing so.
m_canSpawnInGameObjects = true;
}
[ClientRpc]

// TODO: Remove the timer to test for packet loss.
void CheckIfCanSpawnNewSymbol()
{
if (!m_canSpawnInGameObjects || m_symbolContainerInstance?.childCount >= SequenceSelector.k_symbolCount || !IsHost)
if (!m_canSpawnInGameObjects.GetValueOrDefault() || m_remainingSymbolCount >= SequenceSelector.k_symbolCount || !IsHost)
return;
if (m_pendingSymbolPositions.Count > 0)
{

m_symbolSpawnTimer = 0.04f; // Space out the object spawning a little to reduce the load. It will happen in the background, so we have time.
m_symbolSpawnTimer = 0.02f; // Space out the object spawning a little to prevent a lag spike.
if (m_remainingSymbolCount >= SequenceSelector.k_symbolCount)
m_canSpawnInGameObjects = false;
}
}
}

symbolObj.TrySetParent(m_symbolContainerInstance, false);
symbolObj.transform.localPosition = pendingPos;
symbolObj.GetComponent<SymbolObject>().symbolIndex.Value = m_sequenceSelector.GetNextSymbol(index);
m_remainingSymbolCount++;
}
}

public void OnPlayerInput(ulong id, SymbolObject selectedSymbol)
{
if (m_sequenceSelector.ConfirmSymbolCorrect(id, selectedSymbol.symbolIndex.Value))
{ selectedSymbol.OnSelectConfirmed_ClientRpc();
{
selectedSymbol.OnSelectConfirmed_ClientRpc();
OnSymbolDeactivated();
}
public void OnSymbolDeactivated()
{
if (--m_remainingSymbolCount <= 0)
EndGame_ServerRpc();
}
/// <summary>
/// The server determines when the game should end. Once it does, it needs to inform the clients to clean up their networked objects first,
/// since disconnecting before that happens will prevent them from doing so (since they can't receive despawn events from the disconnected server).
/// </summary>
[ServerRpc]
private void EndGame_ServerRpc()
{
// TODO: Display results
this.StartCoroutine(EndGame());
}
private IEnumerator EndGame()
{
EndGame_ClientRpc();
yield return null;
m_onGameEnd();
}
[ClientRpc]
private void EndGame_ClientRpc()
{
if (IsHost)
return;
m_onGameEnd();
}
public void OnReProvided(IInGameInputHandler previousProvider) { /*No-op*/ }

1
Assets/Scripts/Game/InGame/PlayerCursor.cs


/// <summary>
/// Each player's cursor needs to be controlled by them and visible to the other players.
/// </summary>
[RequireComponent(typeof(Collider))]
public class PlayerCursor : NetworkBehaviour
{
private Camera m_mainCamera;

19
Assets/Scripts/Game/InGame/SetupInGame.cs


m_inGameManagerObj = GameObject.Instantiate(m_prefabNetworkManager);
m_networkManager = m_inGameManagerObj.GetComponentInChildren<NetworkManager>();
m_inGameRunner = m_inGameManagerObj.GetComponentInChildren<InGameRunner>();
m_inGameRunner.Initialize(OnConnectionVerified, m_playerCount);
m_inGameRunner.Initialize(OnConnectionVerified, m_playerCount, OnGameEnd);
// TODO: I'll need this when we switch to the Relay Unity Transport option.
//UnityTransport transport = m_inGameManagerObj.GetComponentInChildren<UnityTransport>();

else if (type == MessageType.ChangeGameState)
{
// Once we're in-game, any state change reflects the player leaving the game, so we should clean up.
if (m_doesNeedCleanup)
{
GameObject.Destroy(m_inGameManagerObj); // Since this destroys the NetworkManager, that will kick off cleaning up networked objects.
SetMenuVisibility(true);
m_doesNeedCleanup = false;
}
OnGameEnd();
}
}
private void OnGameEnd()
{
if (m_doesNeedCleanup)
{
GameObject.Destroy(m_inGameManagerObj); // Since this destroys the NetworkManager, that will kick off cleaning up networked objects.
SetMenuVisibility(true);
m_doesNeedCleanup = false;
}
}
}

5
Assets/Scripts/Game/InGame/SymbolContainer.cs


{
if (IsHost)
{
// Note: The SymbolObjects, which will be children of this object, need their NetworkTransforms to have IsLocalSpace set to true. Otherwise, they might get desynced.
// (This would manifest as packet loss errors.)
// Also note: The initial position of the SymbolObject prefab is set to be outside the camera view in the Z-direction, so that it doesn't interpolate past the actual
// position when it spawns on a client (as opposed to in the Y-direction, since this SymbolContainer is also moving downward).
// TODO: Does that matter if we delay for the instructions?
Rigidbody m_rb = this.GetComponent<Rigidbody>();
m_rb.MovePosition(Vector3.up * 10);
m_rb.velocity = Vector3.down;

34
Assets/Scripts/Game/InGame/SymbolKillVolume.cs


using System;
using UnityEngine;
namespace LobbyRelaySample.inGame
{
/// <summary>
/// Used by the host to deactivate symbol objects once they're off-screen.
/// </summary>
[RequireComponent(typeof(Collider))]
public class SymbolKillVolume : MonoBehaviour
{
private bool m_isInitialized = false;
private Action m_onSymbolCollided;
public void Initialize(Action onSymbolCollided)
{
m_onSymbolCollided = onSymbolCollided;
m_isInitialized = true;
}
public void OnTriggerEnter(Collider other)
{
if (!m_isInitialized)
return;
SymbolObject symbolObj = other.GetComponent<SymbolObject>();
if (symbolObj != null)
{
symbolObj.Destroy_ServerRpc();
m_onSymbolCollided?.Invoke();
}
}
}
}

11
Assets/Scripts/Game/InGame/SymbolKillVolume.cs.meta


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