浏览代码

Adding logic for displaying an image for each symbol object, and choosing a randomized sequence of symbols. Also adding basic UI for displaying a player's target sequence.

(Known issue that I just found: It looks like having three players doesn't allow one to join. Will investigate.)
/main/staging/ngo_minigame_structure
nathaniel.buck@unity3d.com 3 年前
当前提交
9614d153
共有 12 个文件被更改,包括 1068 次插入607 次删除
  1. 953
      Assets/Art/Font/CheckboxFLF SDF.asset
  2. 383
      Assets/Prefabs/InGame/InGameLogic.prefab
  3. 14
      Assets/Prefabs/InGame/SymbolContainer.prefab
  4. 59
      Assets/Prefabs/InGame/SymbolObject.prefab
  5. 68
      Assets/Scripts/Game/InGame/InGameRunner.cs
  6. 8
      Assets/Scripts/Game/InGame/PlayerCursor.cs
  7. 37
      Assets/Scripts/Game/InGame/SymbolObject.cs
  8. 4
      Assets/Scripts/Infrastructure/Locator.cs
  9. 19
      Assets/Scripts/Game/InGame/IInGameInputHandler.cs
  10. 11
      Assets/Scripts/Game/InGame/IInGameInputHandler.cs.meta
  11. 108
      Assets/Scripts/Game/InGame/SequenceSelector.cs
  12. 11
      Assets/Scripts/Game/InGame/SequenceSelector.cs.meta

953
Assets/Art/Font/CheckboxFLF SDF.asset
文件差异内容过多而无法显示
查看文件

383
Assets/Prefabs/InGame/InGameLogic.prefab


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

- component: {fileID: 5289034077109495657}
- component: {fileID: 8928417086886337167}
- component: {fileID: 6673480979101889538}
- component: {fileID: 6829526275642584874}
m_Layer: 0
m_Name: InGameRunner
m_TagString: Untagged

m_playerCursorPrefab: {fileID: -1321688216342888635, guid: 905594b4ee5bb864a84af916cc445d1b, type: 3}
m_symbolContainerPrefab: {fileID: 3984715711634906321, guid: f42ed38d10b57ec48870f76a7a63389e, type: 3}
m_symbolObjectPrefab: {fileID: 1734492152380024498, guid: e371ca3112f9e244ab574b472387b64b, type: 3}
m_sequenceSelector: {fileID: 6829526275642584874}
--- !u!114 &6829526275642584874
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: 77910b5766924f044995dfe427e9eea3, type: 3}
m_Name:
m_EditorClassIdentifier:
m_symbolData: {fileID: 11400000, guid: 84a81a2f14d442a49b46eccefb73933f, type: 2}
m_targetSequenceOutput:
- {fileID: 5510123497471373079}
- {fileID: 7719423909162636986}
- {fileID: 1757541467328778976}
--- !u!1 &4526823870396177388
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6730446115375840747}
- component: {fileID: 3785456348135010168}
m_Layer: 5
m_Name: TargetSequence
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6730446115375840747
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4526823870396177388}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 1250806127447164487}
- {fileID: 5631146859424201791}
- {fileID: 1818570891453540108}
m_Father: {fileID: 8063659512749448051}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.4, y: 0}
m_AnchorMax: {x: 0.6, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 1}
--- !u!114 &3785456348135010168
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4526823870396177388}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 5
m_ChildAlignment: 7
m_Spacing: 5
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 0
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!1 &5136082879691915103
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5631146859424201791}
- component: {fileID: 3850712973059559546}
- component: {fileID: 7719423909162636986}
m_Layer: 5
m_Name: Target1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5631146859424201791
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5136082879691915103}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 6730446115375840747}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 120, y: 120}
m_Pivot: {x: 0.5, y: 0}
--- !u!222 &3850712973059559546
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5136082879691915103}
m_CullTransparentMesh: 1
--- !u!114 &7719423909162636986
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5136082879691915103}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, 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_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &6206372352141055644
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1250806127447164487}
- component: {fileID: 6923185428760574559}
- component: {fileID: 5510123497471373079}
m_Layer: 5
m_Name: Target0
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1250806127447164487
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6206372352141055644}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 6730446115375840747}
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: 0, y: 0}
m_SizeDelta: {x: 120, y: 120}
m_Pivot: {x: 0.5, y: 0}
--- !u!222 &6923185428760574559
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6206372352141055644}
m_CullTransparentMesh: 1
--- !u!114 &5510123497471373079
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6206372352141055644}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, 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_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &6833483067312116932
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1818570891453540108}
- component: {fileID: 6526272343525014204}
- component: {fileID: 1757541467328778976}
m_Layer: 5
m_Name: Target2
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1818570891453540108
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6833483067312116932}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 6730446115375840747}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 120, y: 120}
m_Pivot: {x: 0.5, y: 0}
--- !u!222 &6526272343525014204
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6833483067312116932}
m_CullTransparentMesh: 1
--- !u!114 &1757541467328778976
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6833483067312116932}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, 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_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &7460461347722702836
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8063659512749448051}
- component: {fileID: 3393947122200987252}
- component: {fileID: 3239721210338799976}
m_Layer: 5
m_Name: InGameCanvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8063659512749448051
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7460461347722702836}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 6730446115375840747}
m_Father: {fileID: 485451675458297819}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!223 &3393947122200987252
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7460461347722702836}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 8873025738981341763}
m_PlaneDistance: 5
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!114 &3239721210338799976
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7460461347722702836}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 1
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 1920, y: 1080}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!1 &9019482255774987314
GameObject:
m_ObjectHideFlags: 0

14
Assets/Prefabs/InGame/SymbolContainer.prefab


- component: {fileID: 2974066117406568032}
- component: {fileID: 2302923454152093614}
- component: {fileID: 210836793418873202}
- component: {fileID: -8143583295587582139}
m_Layer: 0
m_Name: SymbolContainer
m_TagString: Untagged

m_Script: {fileID: 11500000, guid: 73a6c5df5c3139e448d76f8918fece73, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!65 &-8143583295587582139
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5240148789413552765}
m_Material: {fileID: 0}
m_IsTrigger: 1
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6633045103840132799
GameObject:
m_ObjectHideFlags: 0

59
Assets/Prefabs/InGame/SymbolObject.prefab


serializedVersion: 6
m_Component:
- component: {fileID: 2207893811600523791}
- component: {fileID: 6202594079177850961}
- component: {fileID: 8777844193165894662}
- component: {fileID: 1572105415912633256}
- component: {fileID: 1069079606563513835}
m_Layer: 0
m_Name: Visual
m_TagString: Untagged

m_GameObject: {fileID: 3226722501078289267}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_LocalScale: {x: 0.05, y: 0.05, z: 0.05}
--- !u!33 &6202594079177850961
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3226722501078289267}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &8777844193165894662
MeshRenderer:
--- !u!212 &1069079606563513835
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}

m_CastShadows: 1
m_ReceiveShadows: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_RayTracingMode: 2
m_RayTracingMode: 0
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0

m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89

m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!135 &1572105415912633256
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3226722501078289267}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
m_Sprite: {fileID: 21300000, guid: f18a89c12a346a8488ed507a0bd79d2e, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 30.000002, y: 30.000002}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &8828823320646980938
GameObject:
m_ObjectHideFlags: 0

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

m_Script: {fileID: 11500000, guid: 63adacb71a52dcb41be1952af26573d8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_symbolData: {fileID: 11400000, guid: 84a81a2f14d442a49b46eccefb73933f, type: 2}
m_renderer: {fileID: 1069079606563513835}
symbolIndex:
m_InternalValue: 0

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


/// Once the NetworkManager has been spawned, we need something to manage the game state and setup other in-game objects
/// that is itself a networked object, to track things like network connect events.
/// </summary>
public class InGameRunner : NetworkBehaviour
public class InGameRunner : NetworkBehaviour, IInGameInputHandler
private const int k_symbolCount = 100;
private float m_symbolSpawnTimer = 0.5f; // Initial time buffer to ensure connectivity before loading objects.
[SerializeField] private SequenceSelector m_sequenceSelector;
private ulong m_localClientId; // This is not necessarily the same as the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
private ulong m_localId; // This is not necessarily the same as the OwnerClientId, since all clients will see all spawned objects regardless of ownership.
public void Initialize(Action onConnectionVerified, int expectedPlayerCount)
{

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 OnNetworkSpawn()

m_localClientId = NetworkManager.Singleton.LocalClientId;
VerifyConnection_ServerRpc(m_localClientId);
m_localId = NetworkManager.Singleton.LocalClientId;
VerifyConnection_ServerRpc(m_localId);
}
public override void OnNetworkDespawn()

private void ResetPendingSymbolPositions()
{
m_pendingSymbolPositions.Clear();
for (int n = 0; n < k_symbolCount; n++)
for (int n = 0; n < SequenceSelector.k_symbolCount; n++)
{
// TEMP we need to do a BSP or some such to mix up the positions.
m_pendingSymbolPositions.Enqueue(new Vector2(-9 + (n % 10) * 2, n / 10 * 3));

[ClientRpc]
private void VerifyConnection_ClientRpc(ulong clientId)
{
if (clientId == m_localClientId)
VerifyConnectionConfirm_ServerRpc(m_localClientId);
if (clientId == m_localId)
VerifyConnectionConfirm_ServerRpc(m_localId);
}
/// <summary>
/// Once the connection is confirmed, check if all players have connected.

[ClientRpc]
private void VerifyConnectionConfirm_ClientRpc(ulong clientId, bool shouldStartImmediately)
{
if (clientId == m_localClientId)
if (clientId == m_localId)
// TODO: BSP for choosing symbol spawn positions?
// TODO: Remove the timer to test for packet loss.
private float m_timer = 0.04f; // We'll want to space out the object spawning a little to reduce the risk of packet loss. It will happen in the background, so we have time.
if (!m_canSpawnInGameObjects || m_symbolContainerInstance?.childCount >= k_symbolCount || !IsHost)
return;
if (m_pendingSymbolPositions.Count > 0)
CheckIfCanSpawnNewSymbol();
// TODO: BSP for choosing symbol spawn positions?
// TODO: Remove the timer to test for packet loss.
void CheckIfCanSpawnNewSymbol()
m_timer -= Time.deltaTime;
if (m_timer < 0)
if (!m_canSpawnInGameObjects || m_symbolContainerInstance?.childCount >= SequenceSelector.k_symbolCount || !IsHost)
return;
if (m_pendingSymbolPositions.Count > 0)
m_timer = 0.04f;
Vector3 pendingPos = m_pendingSymbolPositions.Dequeue();
NetworkObject symbolObj = NetworkObject.Instantiate(m_symbolObjectPrefab);
symbolObj.Spawn();
symbolObj.name = "Symbol" + (k_symbolCount - m_pendingSymbolPositions.Count);
symbolObj.TrySetParent(m_symbolContainerInstance, false);
symbolObj.transform.localPosition = pendingPos;
m_symbolSpawnTimer -= Time.deltaTime;
if (m_symbolSpawnTimer < 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.
SpawnNewSymbol();
}
void SpawnNewSymbol()
{
int index = SequenceSelector.k_symbolCount - m_pendingSymbolPositions.Count;
Vector3 pendingPos = m_pendingSymbolPositions.Dequeue();
NetworkObject symbolObj = NetworkObject.Instantiate(m_symbolObjectPrefab);
symbolObj.Spawn();
symbolObj.name = "Symbol" + index;
symbolObj.TrySetParent(m_symbolContainerInstance, false);
symbolObj.transform.localPosition = pendingPos;
symbolObj.GetComponent<SymbolObject>().symbolIndex.Value = m_sequenceSelector.GetNextSymbol(index);
}
public void OnPlayerInput(SymbolObject selectedSymbol)
{
if (m_sequenceSelector.ConfirmSymbolCorrect(selectedSymbol.symbolIndex.Value))
selectedSymbol.OnSelectConfirmed();
}
public void OnReProvided(IInGameInputHandler previousProvider) { /*No-op*/ }
}
}

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


{
private Camera m_mainCamera;
private NetworkVariable<Vector3> m_position = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone, Vector3.zero);
private ulong m_localId;
// The host is responsible for determining if a player has successfully selected a symbol object, since collisions should be handled serverside.
private List<SymbolObject> m_currentlyCollidingSymbols;

m_mainCamera = GameObject.Find("InGameCamera").GetComponent<Camera>();
if (IsHost)
m_currentlyCollidingSymbols = new List<SymbolObject>();
m_localId = NetworkManager.Singleton.LocalClientId;
}
// Don't love having the input here, but it doesn't need to be anywhere else.

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();
SendInput_ServerRpc(m_localId);
}
[ServerRpc] // Leave RequireOwnership = true for these so that only the player whose cursor this is can make updates.

}
[ServerRpc]
private void SendInput_ServerRpc()
private void SendInput_ServerRpc(ulong id)
symbol.OnSelect();
symbol.OnSelect_ClientRpc(id);
}
}

37
Assets/Scripts/Game/InGame/SymbolObject.cs


namespace LobbyRelaySample.inGame
{
/// <summary>
/// This holds the logic and data for an individual symbol, which can be "clicked" if the server detects the collision with a player who sends a click input.
/// </summary>
public void OnSelect()
[SerializeField] private SymbolData m_symbolData;
[SerializeField] private SpriteRenderer m_renderer;
[HideInInspector] public NetworkVariable<int> symbolIndex; // The index into SymbolData, not the index of this object.
private ulong m_localId;
public override void OnNetworkSpawn()
{
symbolIndex.OnValueChanged += OnSymbolIndexSet;
m_localId = NetworkManager.Singleton.LocalClientId;
}
/// <summary>
/// Because of the need to distinguish host vs. client calls, we use the symbolIndex NetworkVariable to learn what symbol to display.
/// </summary>
private void OnSymbolIndexSet(int prevValue, int newValue)
{
m_renderer.sprite = m_symbolData.GetSymbolForIndex(symbolIndex.Value);
symbolIndex.OnValueChanged -= OnSymbolIndexSet;
}
/// <summary>
/// The host has detected a player clicking on this symbol, but it also needs to check if this symbol is next in that player's target sequence.
/// </summary>
[ClientRpc]
public void OnSelect_ClientRpc(ulong id)
{
if (m_localId == id)
Locator.Get.InGameInputHandler.OnPlayerInput(this);
}
/// <summary>
/// The host has confirmed this symbol as a valid selection, so handle any visual feedback.
/// </summary>
public void OnSelectConfirmed()
{
Destroy_ServerRpc();
}

4
Assets/Scripts/Infrastructure/Locator.cs


Provide(new Messenger());
Provide(new UpdateSlowNoop());
Provide(new IdentityNoop());
Provide(new inGame.InGameInputHandlerNoop());
FinishConstruction();
}

public IIdentity Identity => Locate<IIdentity>();
public void Provide(IIdentity identity) { ProvideAny(identity); }
public inGame.IInGameInputHandler InGameInputHandler => Locate<inGame.IInGameInputHandler>();
public void Provide(inGame.IInGameInputHandler inputHandler) { ProvideAny(inputHandler); }
// As you add more Provided types, be sure their default implementations are included in the constructor.
}

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


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LobbyRelaySample.inGame
{
public interface IInGameInputHandler : IProvidable<IInGameInputHandler>
{
void OnPlayerInput(SymbolObject selectedSymbol);
}
public class InGameInputHandlerNoop : IInGameInputHandler
{
public void OnPlayerInput(SymbolObject selectedSymbol) { }
public void OnReProvided(IInGameInputHandler previousProvider) { }
}
}

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


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

108
Assets/Scripts/Game/InGame/SequenceSelector.cs


using System;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
namespace LobbyRelaySample.inGame
{
/// <summary>
/// Handles selecting the randomized sequence of symbols to spawn. This also selects a subset of the selected symbols to be the target
/// sequence that each player needs to select in order.
/// </summary>
public class SequenceSelector : NetworkBehaviour
{
[SerializeField] private SymbolData m_symbolData = default;
[SerializeField] private RawImage[] m_targetSequenceOutput = default;
public const int k_symbolCount = 100;
private List<int> m_fullSequence = new List<int>(); // This is owned by the host, and each index is assigned as a NetworkVariable to each SymbolObject.
private NetworkList<int> m_targetSequence; // This is owned by the host but needs to be available to all clients, so it's a NetworkedList here.
private int m_targetSequenceCurrentIndex = -1;
public void Awake()
{
m_targetSequence = new NetworkList<int>();
}
public override void OnNetworkSpawn()
{
if (IsHost)
{
// Choose some subset of the list of symbols to be present in this game, along with a target sequence.
List<int> symbolsForThisGame = SelectSymbols(m_symbolData.m_availableSymbols.Count, 8);
m_targetSequence.Add(symbolsForThisGame[0]);
m_targetSequence.Add(symbolsForThisGame[1]);
m_targetSequence.Add(symbolsForThisGame[2]);
// Then, ensure that the target sequence is present in order throughout most of the full set of symbols to spawn.
int numTargetSequences = k_symbolCount / 6; // About 1/2 of the 3 symbols will be definitely part of the target sequence.
for (; numTargetSequences >= 0; numTargetSequences--)
{ m_fullSequence.Add(m_targetSequence[2]); // We want a List instead of a Queue or Stack for faster insertion, but we will remove indices backwards so as to not reshift other entries.
m_fullSequence.Add(m_targetSequence[1]);
m_fullSequence.Add(m_targetSequence[0]);
}
// Then, fill in with a good mix of the remaining symbols.
AddHalfRemaining(3, 2);
AddHalfRemaining(4, 2);
AddHalfRemaining(5, 2);
AddHalfRemaining(6, 2);
AddHalfRemaining(7, 1);
void AddHalfRemaining(int symbolIndex, int divider)
{
int remaining = k_symbolCount - m_fullSequence.Count;
for (int n = 0; n < remaining / divider; n++)
{
int randomIndex = UnityEngine.Random.Range(0, m_fullSequence.Count);
m_fullSequence.Insert(randomIndex, symbolsForThisGame[symbolIndex]);
}
}
}
}
// Very simple random selection. Duplicates are allowed.
private static List<int> SelectSymbols(int numOptions, int targetCount)
{
List<int> list = new List<int>();
for (int n = 0; n < targetCount; n++)
list.Add(UnityEngine.Random.Range(0, numOptions));
return list;
}
public void Update()
{
// We can't guarantee timing with the host's selection of the target sequence, so retrieve it once it's available.
if (m_targetSequenceCurrentIndex < 0 && m_targetSequence.Count > 0)
{
for (int n = 0; n < m_targetSequence.Count; n++)
m_targetSequenceOutput[n].texture = m_symbolData.GetSymbolForIndex(m_targetSequence[n]).texture;
m_targetSequenceCurrentIndex = 0;
ScaleTargetUi();
}
}
/// <summary>
/// If the index is correct, this will advance the current sequence index.
/// </summary>
public bool ConfirmSymbolCorrect(int symbolIndex)
{
if (symbolIndex != m_targetSequence[m_targetSequenceCurrentIndex])
return false;
if (++m_targetSequenceCurrentIndex >= m_targetSequence.Count)
m_targetSequenceCurrentIndex = 0;
ScaleTargetUi();
return true;
}
private void ScaleTargetUi()
{
for (int i = 0; i < m_targetSequenceOutput.Length; i++)
m_targetSequenceOutput[i].transform.localScale = Vector3.one * (m_targetSequenceCurrentIndex == i ? 1 : 0.7f);
}
public int GetNextSymbol(int symbolObjectIndex)
{
return m_fullSequence[symbolObjectIndex];
}
}
}

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


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