This commit is contained in:
2025-06-16 15:14:23 +02:00
commit 074e590073
3174 changed files with 428263 additions and 0 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ad2253a5702ed854995bf034be09a1ae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,149 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4903779300334215825
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8667093524748208693}
- component: {fileID: 792638734159983555}
- component: {fileID: 2985632515421915780}
- component: {fileID: 9218727033388977555}
- component: {fileID: 2439948753870522382}
m_Layer: 5
m_Name: CellGUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8667093524748208693
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4903779300334215825}
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: 0}
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: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &792638734159983555
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4903779300334215825}
m_CullTransparentMesh: 0
--- !u!114 &2985632515421915780
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4903779300334215825}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &9218727033388977555
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4903779300334215825}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Highlighted
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 2985632515421915780}
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 2439948753870522382}
m_MethodName: MakePlay
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
--- !u!114 &2439948753870522382
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4903779300334215825}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9cda2a394a443474689a3c6d6044f7b0, type: 3}
m_Name:
m_EditorClassIdentifier:
matchController: {fileID: 0}
cellValue: 0
image: {fileID: 2985632515421915780}
button: {fileID: 9218727033388977555}
playerIdentity: {fileID: 0}

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 5ab8c221183f0094eb04b7f6eb569e7b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Prefabs/CellGUI.prefab
uploadId: 736421

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: e101d385946c91b4c81f318efc723a88
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab
uploadId: 736421

View File

@ -0,0 +1,311 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &732536732566260629
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6013388882030083517}
- component: {fileID: 2895310434674857523}
- component: {fileID: 7191823870515597218}
m_Layer: 5
m_Name: Players
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6013388882030083517
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 732536732566260629}
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: 3533216216399874122}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: -10, y: 0}
m_SizeDelta: {x: 60, y: 30}
m_Pivot: {x: 1, y: 0.5}
--- !u!222 &2895310434674857523
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 732536732566260629}
m_CullTransparentMesh: 0
--- !u!114 &7191823870515597218
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 732536732566260629}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.78431374}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 20
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 5
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: X / X
--- !u!1 &3533216216399874123
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3533216216399874122}
- component: {fileID: 3533216216399874119}
- component: {fileID: 8159776287394259959}
- component: {fileID: 8871144159682159596}
- component: {fileID: 114572912279416540}
m_Layer: 5
m_Name: MatchGUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3533216216399874122
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3533216216399874123}
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: 8536989563668766087}
- {fileID: 6013388882030083517}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3533216216399874119
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3533216216399874123}
m_CullTransparentMesh: 0
--- !u!114 &8159776287394259959
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3533216216399874123}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.19607843}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &8871144159682159596
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3533216216399874123}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Highlighted
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 8159776287394259959}
toggleTransition: 1
graphic: {fileID: 0}
m_Group: {fileID: 0}
onValueChanged:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 114572912279416540}
m_MethodName: OnToggleClicked
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_IsOn: 0
--- !u!114 &114572912279416540
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3533216216399874123}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e875a237ce12c3145b20f17222f10b68, type: 3}
m_Name:
m_EditorClassIdentifier:
image: {fileID: 8159776287394259959}
toggleButton: {fileID: 8871144159682159596}
matchName: {fileID: 4525608779212400833}
playerCount: {fileID: 7191823870515597218}
canvasController: {fileID: 0}
--- !u!1 &5341423849544309394
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8536989563668766087}
- component: {fileID: 3979716480785526962}
- component: {fileID: 4525608779212400833}
m_Layer: 5
m_Name: Name
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8536989563668766087
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5341423849544309394}
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: 3533216216399874122}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: 10, y: 0}
m_SizeDelta: {x: 200, y: 30}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &3979716480785526962
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5341423849544309394}
m_CullTransparentMesh: 0
--- !u!114 &4525608779212400833
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5341423849544309394}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.78431374}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 20
m_FontStyle: 1
m_BestFit: 0
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Match X

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: df3727c2222378b4ca786485a8b09981
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchGUI.prefab
uploadId: 736421

View File

@ -0,0 +1,67 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &3559083313143303799
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1204658229548937417}
- component: {fileID: 7660209086417859164}
- component: {fileID: -3992624706284245286}
m_Layer: 0
m_Name: MatchPlayer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1204658229548937417
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3559083313143303799}
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: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &7660209086417859164
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3559083313143303799}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3}
m_Name:
m_EditorClassIdentifier:
sceneId: 0
_assetId: 3049278420
serverOnly: 0
visibility: 0
hasSpawned: 0
--- !u!114 &-3992624706284245286
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3559083313143303799}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5d17e718851449a6879986e45c458fb7, type: 3}
m_Name:
m_EditorClassIdentifier:
syncDirection: 0
syncMode: 0
syncInterval: 0.1
MatchID:

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 11d2414f1e120a14c98cba6866e5348f
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab
uploadId: 736421

View File

@ -0,0 +1,185 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2742256683483721700
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4805460816392144898}
- component: {fileID: 4240234839557606510}
- component: {fileID: 733636089880751452}
- component: {fileID: 8081368318735942264}
m_Layer: 5
m_Name: PlayerGUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4805460816392144898
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2742256683483721700}
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: 4975002542353798605}
m_Father: {fileID: 0}
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: 0, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4240234839557606510
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2742256683483721700}
m_CullTransparentMesh: 0
--- !u!114 &733636089880751452
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2742256683483721700}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.2509804, g: 0.2509804, b: 0.2509804, a: 1}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &8081368318735942264
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2742256683483721700}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f61e9bc3715eb904b91222a5526f63d8, type: 3}
m_Name:
m_EditorClassIdentifier:
playerName: {fileID: 893070459539714110}
--- !u!1 &4667088617155248724
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4975002542353798605}
- component: {fileID: 8874787277914191185}
- component: {fileID: 893070459539714110}
- component: {fileID: 1559385578385937998}
m_Layer: 5
m_Name: Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4975002542353798605
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4667088617155248724}
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: 4805460816392144898}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 300, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8874787277914191185
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4667088617155248724}
m_CullTransparentMesh: 0
--- !u!114 &893070459539714110
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4667088617155248724}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 25
m_FontStyle: 1
m_BestFit: 0
m_MinSize: 0
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Player X
--- !u!114 &1559385578385937998
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4667088617155248724}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3}
m_Name:
m_EditorClassIdentifier:
m_EffectColor: {r: 0, g: 0, b: 0, a: 0.5}
m_EffectDistance: {x: 1, y: -1}
m_UseGraphicAlpha: 1

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 504f86497324bc040be44f6f88b6cc73
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Prefabs/PlayerGUI.prefab
uploadId: 736421

View File

@ -0,0 +1,9 @@
# MultipleMatches Example
This example demonstrates how to run a large number of non-physics games concurrently in a single game server instance.
This would be most appropriate for Card, Board, Puzzle, and Arcade games where there is no physics involved, just presentation and messaging.
While this example is turn-based, real-time games work just as well.
When a match is started, a MatchController and Player objects are spawned, all with a Network Match Checker component with the same matchId set. Only clients with the same matchID get messages about each others actions and about their own match. They don't receive any data about other matches that may be running concurrently.

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 9749e23e4e90e0646afc81061710a927
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/README.md
uploadId: 736421

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a92fd9255da5fe9449e612566195a3aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: aa1f875396fa7d348a782cebc2f75d7c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scenes/MirrorMultipleMatches.unity
uploadId: 736421

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d418be768b6d32e4d9c3b8828929c3eb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,644 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.MultipleMatch
{
public class CanvasController : MonoBehaviour
{
/// <summary>
/// Match Controllers listen for this to terminate their match and clean up
/// </summary>
public event Action<NetworkConnectionToClient> OnPlayerDisconnect;
/// <summary>
/// Cross-reference of client that created the corresponding match in openMatches below
/// </summary>
internal static readonly Dictionary<NetworkConnectionToClient, Guid> playerMatches = new Dictionary<NetworkConnectionToClient, Guid>();
/// <summary>
/// Open matches that are available for joining
/// </summary>
internal static readonly Dictionary<Guid, MatchInfo> openMatches = new Dictionary<Guid, MatchInfo>();
/// <summary>
/// Network Connections of all players in a match
/// </summary>
internal static readonly Dictionary<Guid, HashSet<NetworkConnectionToClient>> matchConnections = new Dictionary<Guid, HashSet<NetworkConnectionToClient>>();
/// <summary>
/// Player informations by Network Connection
/// </summary>
internal static readonly Dictionary<NetworkConnectionToClient, PlayerInfo> playerInfos = new Dictionary<NetworkConnectionToClient, PlayerInfo>();
/// <summary>
/// Network Connections that have neither started nor joined a match yet
/// </summary>
internal static readonly List<NetworkConnectionToClient> waitingConnections = new List<NetworkConnectionToClient>();
/// <summary>
/// GUID of a match the local player has created
/// </summary>
internal Guid localPlayerMatch = Guid.Empty;
/// <summary>
/// GUID of a match the local player has joined
/// </summary>
internal Guid localJoinedMatch = Guid.Empty;
/// <summary>
/// GUID of a match the local player has selected in the Toggle Group match list
/// </summary>
internal Guid selectedMatch = Guid.Empty;
// Used in UI for "Player #"
int playerIndex = 1;
[Header("GUI References")]
public GameObject matchList;
public GameObject matchPrefab;
public GameObject matchControllerPrefab;
public Button createButton;
public Button joinButton;
public GameObject lobbyView;
public GameObject roomView;
public RoomGUI roomGUI;
public ToggleGroup toggleGroup;
// RuntimeInitializeOnLoadMethod -> fast playmode without domain reload
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void ResetStatics()
{
playerMatches.Clear();
openMatches.Clear();
matchConnections.Clear();
playerInfos.Clear();
waitingConnections.Clear();
}
#region UI Functions
// Called from several places to ensure a clean reset
// - MatchNetworkManager.Awake
// - OnStartServer
// - OnStartClient
// - OnClientDisconnect
// - ResetCanvas
internal void InitializeData()
{
playerMatches.Clear();
openMatches.Clear();
matchConnections.Clear();
waitingConnections.Clear();
localPlayerMatch = Guid.Empty;
localJoinedMatch = Guid.Empty;
}
// Called from OnStopServer and OnStopClient when shutting down
void ResetCanvas()
{
InitializeData();
lobbyView.SetActive(false);
roomView.SetActive(false);
gameObject.SetActive(false);
}
#endregion
#region Button Calls
/// <summary>
/// Called from <see cref="MatchGUI.OnToggleClicked"/>
/// </summary>
/// <param name="matchId"></param>
[ClientCallback]
public void SelectMatch(Guid matchId)
{
if (matchId == Guid.Empty)
{
selectedMatch = Guid.Empty;
joinButton.interactable = false;
}
else
{
if (!openMatches.Keys.Contains(matchId))
{
joinButton.interactable = false;
return;
}
selectedMatch = matchId;
MatchInfo infos = openMatches[matchId];
joinButton.interactable = infos.players < infos.maxPlayers;
}
}
/// <summary>
/// Assigned in inspector to Create button
/// </summary>
[ClientCallback]
public void RequestCreateMatch()
{
NetworkClient.Send(new ServerMatchMessage { serverMatchOperation = ServerMatchOperation.Create });
}
/// <summary>
/// Assigned in inspector to Cancel button
/// </summary>
[ClientCallback]
public void RequestCancelMatch()
{
if (localPlayerMatch == Guid.Empty) return;
NetworkClient.Send(new ServerMatchMessage { serverMatchOperation = ServerMatchOperation.Cancel });
}
/// <summary>
/// Assigned in inspector to Join button
/// </summary>
[ClientCallback]
public void RequestJoinMatch()
{
if (selectedMatch == Guid.Empty) return;
NetworkClient.Send(new ServerMatchMessage { serverMatchOperation = ServerMatchOperation.Join, matchId = selectedMatch });
}
/// <summary>
/// Assigned in inspector to Leave button
/// </summary>
[ClientCallback]
public void RequestLeaveMatch()
{
if (localJoinedMatch == Guid.Empty) return;
NetworkClient.Send(new ServerMatchMessage { serverMatchOperation = ServerMatchOperation.Leave, matchId = localJoinedMatch });
}
/// <summary>
/// Assigned in inspector to Ready button
/// </summary>
[ClientCallback]
public void RequestReadyChange()
{
if (localPlayerMatch == Guid.Empty && localJoinedMatch == Guid.Empty) return;
Guid matchId = localPlayerMatch == Guid.Empty ? localJoinedMatch : localPlayerMatch;
NetworkClient.Send(new ServerMatchMessage { serverMatchOperation = ServerMatchOperation.Ready, matchId = matchId });
}
/// <summary>
/// Assigned in inspector to Start button
/// </summary>
[ClientCallback]
public void RequestStartMatch()
{
if (localPlayerMatch == Guid.Empty) return;
NetworkClient.Send(new ServerMatchMessage { serverMatchOperation = ServerMatchOperation.Start });
}
/// <summary>
/// Called from <see cref="MatchController.RpcExitGame"/>
/// </summary>
[ClientCallback]
public void OnMatchEnded()
{
localPlayerMatch = Guid.Empty;
localJoinedMatch = Guid.Empty;
ShowLobbyView();
}
#endregion
#region Server & Client Callbacks
// Methods in this section are called from MatchNetworkManager's corresponding methods
[ServerCallback]
internal void OnStartServer()
{
InitializeData();
NetworkServer.RegisterHandler<ServerMatchMessage>(OnServerMatchMessage);
}
[ServerCallback]
internal void OnServerReady(NetworkConnectionToClient conn)
{
waitingConnections.Add(conn);
playerInfos.Add(conn, new PlayerInfo { playerIndex = this.playerIndex, ready = false });
playerIndex++;
SendMatchList();
}
[ServerCallback]
internal IEnumerator OnServerDisconnect(NetworkConnectionToClient conn)
{
// Invoke OnPlayerDisconnect on all instances of MatchController
OnPlayerDisconnect?.Invoke(conn);
if (playerMatches.TryGetValue(conn, out Guid matchId))
{
playerMatches.Remove(conn);
openMatches.Remove(matchId);
foreach (NetworkConnectionToClient playerConn in matchConnections[matchId])
{
PlayerInfo _playerInfo = playerInfos[playerConn];
_playerInfo.ready = false;
_playerInfo.matchId = Guid.Empty;
playerInfos[playerConn] = _playerInfo;
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Departed });
}
}
foreach (KeyValuePair<Guid, HashSet<NetworkConnectionToClient>> kvp in matchConnections)
kvp.Value.Remove(conn);
PlayerInfo playerInfo = playerInfos[conn];
if (playerInfo.matchId != Guid.Empty)
{
if (openMatches.TryGetValue(playerInfo.matchId, out MatchInfo matchInfo))
{
matchInfo.players--;
openMatches[playerInfo.matchId] = matchInfo;
}
HashSet<NetworkConnectionToClient> connections;
if (matchConnections.TryGetValue(playerInfo.matchId, out connections))
{
PlayerInfo[] infos = connections.Select(playerConn => playerInfos[playerConn]).ToArray();
foreach (NetworkConnectionToClient playerConn in matchConnections[playerInfo.matchId])
if (playerConn != conn)
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos });
}
}
SendMatchList();
yield return null;
}
[ServerCallback]
internal void OnStopServer()
{
ResetCanvas();
}
[ClientCallback]
internal void OnStartClient()
{
InitializeData();
ShowLobbyView();
createButton.gameObject.SetActive(true);
joinButton.gameObject.SetActive(true);
NetworkClient.RegisterHandler<ClientMatchMessage>(OnClientMatchMessage);
}
[ClientCallback]
internal void OnClientDisconnect()
{
InitializeData();
}
[ClientCallback]
internal void OnStopClient()
{
ResetCanvas();
}
#endregion
#region Server Match Message Handlers
[ServerCallback]
void OnServerMatchMessage(NetworkConnectionToClient conn, ServerMatchMessage msg)
{
switch (msg.serverMatchOperation)
{
case ServerMatchOperation.None:
{
Debug.LogWarning("Missing ServerMatchOperation");
break;
}
case ServerMatchOperation.Create:
{
OnServerCreateMatch(conn);
break;
}
case ServerMatchOperation.Cancel:
{
OnServerCancelMatch(conn);
break;
}
case ServerMatchOperation.Join:
{
OnServerJoinMatch(conn, msg.matchId);
break;
}
case ServerMatchOperation.Leave:
{
OnServerLeaveMatch(conn, msg.matchId);
break;
}
case ServerMatchOperation.Ready:
{
OnServerPlayerReady(conn, msg.matchId);
break;
}
case ServerMatchOperation.Start:
{
OnServerStartMatch(conn);
break;
}
}
}
[ServerCallback]
void OnServerCreateMatch(NetworkConnectionToClient conn)
{
if (playerMatches.ContainsKey(conn)) return;
Guid newMatchId = Guid.NewGuid();
matchConnections.Add(newMatchId, new HashSet<NetworkConnectionToClient>());
matchConnections[newMatchId].Add(conn);
playerMatches.Add(conn, newMatchId);
openMatches.Add(newMatchId, new MatchInfo { matchId = newMatchId, maxPlayers = 2, players = 1 });
PlayerInfo playerInfo = playerInfos[conn];
playerInfo.ready = false;
playerInfo.matchId = newMatchId;
playerInfos[conn] = playerInfo;
PlayerInfo[] infos = matchConnections[newMatchId].Select(playerConn => playerInfos[playerConn]).ToArray();
conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Created, matchId = newMatchId, playerInfos = infos });
SendMatchList();
}
[ServerCallback]
void OnServerCancelMatch(NetworkConnectionToClient conn)
{
if (!playerMatches.ContainsKey(conn)) return;
conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Cancelled });
Guid matchId;
if (playerMatches.TryGetValue(conn, out matchId))
{
playerMatches.Remove(conn);
openMatches.Remove(matchId);
foreach (NetworkConnectionToClient playerConn in matchConnections[matchId])
{
PlayerInfo playerInfo = playerInfos[playerConn];
playerInfo.ready = false;
playerInfo.matchId = Guid.Empty;
playerInfos[playerConn] = playerInfo;
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Departed });
}
SendMatchList();
}
}
[ServerCallback]
void OnServerJoinMatch(NetworkConnectionToClient conn, Guid matchId)
{
if (!matchConnections.ContainsKey(matchId) || !openMatches.ContainsKey(matchId)) return;
MatchInfo matchInfo = openMatches[matchId];
matchInfo.players++;
openMatches[matchId] = matchInfo;
matchConnections[matchId].Add(conn);
PlayerInfo playerInfo = playerInfos[conn];
playerInfo.ready = false;
playerInfo.matchId = matchId;
playerInfos[conn] = playerInfo;
PlayerInfo[] infos = matchConnections[matchId].Select(playerConn => playerInfos[playerConn]).ToArray();
SendMatchList();
conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Joined, matchId = matchId, playerInfos = infos });
foreach (NetworkConnectionToClient playerConn in matchConnections[matchId])
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos });
}
[ServerCallback]
void OnServerLeaveMatch(NetworkConnectionToClient conn, Guid matchId)
{
MatchInfo matchInfo = openMatches[matchId];
matchInfo.players--;
openMatches[matchId] = matchInfo;
PlayerInfo playerInfo = playerInfos[conn];
playerInfo.ready = false;
playerInfo.matchId = Guid.Empty;
playerInfos[conn] = playerInfo;
foreach (KeyValuePair<Guid, HashSet<NetworkConnectionToClient>> kvp in matchConnections)
kvp.Value.Remove(conn);
HashSet<NetworkConnectionToClient> connections = matchConnections[matchId];
PlayerInfo[] infos = connections.Select(playerConn => playerInfos[playerConn]).ToArray();
foreach (NetworkConnectionToClient playerConn in matchConnections[matchId])
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos });
SendMatchList();
conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Departed });
}
[ServerCallback]
void OnServerPlayerReady(NetworkConnectionToClient conn, Guid matchId)
{
PlayerInfo playerInfo = playerInfos[conn];
playerInfo.ready = !playerInfo.ready;
playerInfos[conn] = playerInfo;
HashSet<NetworkConnectionToClient> connections = matchConnections[matchId];
PlayerInfo[] infos = connections.Select(playerConn => playerInfos[playerConn]).ToArray();
foreach (NetworkConnectionToClient playerConn in matchConnections[matchId])
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos });
}
[ServerCallback]
void OnServerStartMatch(NetworkConnectionToClient conn)
{
if (!playerMatches.ContainsKey(conn)) return;
Guid matchId;
if (playerMatches.TryGetValue(conn, out matchId))
{
GameObject matchControllerObject = Instantiate(matchControllerPrefab);
matchControllerObject.GetComponent<NetworkMatch>().matchId = matchId;
NetworkServer.Spawn(matchControllerObject);
MatchController matchController = matchControllerObject.GetComponent<MatchController>();
foreach (NetworkConnectionToClient playerConn in matchConnections[matchId])
{
playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Started });
GameObject player = Instantiate(NetworkManager.singleton.playerPrefab);
player.GetComponent<NetworkMatch>().matchId = matchId;
NetworkServer.AddPlayerForConnection(playerConn, player);
if (matchController.player1 == null)
matchController.player1 = playerConn.identity;
else
matchController.player2 = playerConn.identity;
/* Reset ready state for after the match. */
PlayerInfo playerInfo = playerInfos[playerConn];
playerInfo.ready = false;
playerInfos[playerConn] = playerInfo;
}
matchController.startingPlayer = matchController.player1;
matchController.currentPlayer = matchController.player1;
playerMatches.Remove(conn);
openMatches.Remove(matchId);
matchConnections.Remove(matchId);
SendMatchList();
OnPlayerDisconnect += matchController.OnPlayerDisconnect;
}
}
/// <summary>
/// Sends updated match list to all waiting connections or just one if specified
/// </summary>
/// <param name="conn"></param>
[ServerCallback]
internal void SendMatchList(NetworkConnectionToClient conn = null)
{
if (conn != null)
conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.List, matchInfos = openMatches.Values.ToArray() });
else
foreach (NetworkConnectionToClient waiter in waitingConnections)
waiter.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.List, matchInfos = openMatches.Values.ToArray() });
}
#endregion
#region Client Match Message Handler
[ClientCallback]
void OnClientMatchMessage(ClientMatchMessage msg)
{
switch (msg.clientMatchOperation)
{
case ClientMatchOperation.None:
{
Debug.LogWarning("Missing ClientMatchOperation");
break;
}
case ClientMatchOperation.List:
{
openMatches.Clear();
foreach (MatchInfo matchInfo in msg.matchInfos)
openMatches.Add(matchInfo.matchId, matchInfo);
RefreshMatchList();
break;
}
case ClientMatchOperation.Created:
{
localPlayerMatch = msg.matchId;
ShowRoomView();
roomGUI.RefreshRoomPlayers(msg.playerInfos);
roomGUI.SetOwner(true);
break;
}
case ClientMatchOperation.Cancelled:
{
localPlayerMatch = Guid.Empty;
ShowLobbyView();
break;
}
case ClientMatchOperation.Joined:
{
localJoinedMatch = msg.matchId;
ShowRoomView();
roomGUI.RefreshRoomPlayers(msg.playerInfos);
roomGUI.SetOwner(false);
break;
}
case ClientMatchOperation.Departed:
{
localJoinedMatch = Guid.Empty;
ShowLobbyView();
break;
}
case ClientMatchOperation.UpdateRoom:
{
roomGUI.RefreshRoomPlayers(msg.playerInfos);
break;
}
case ClientMatchOperation.Started:
{
lobbyView.SetActive(false);
roomView.SetActive(false);
break;
}
}
}
[ClientCallback]
void ShowLobbyView()
{
lobbyView.SetActive(true);
roomView.SetActive(false);
foreach (Transform child in matchList.transform)
if (child.gameObject.GetComponent<MatchGUI>().GetMatchId() == selectedMatch)
{
Toggle toggle = child.gameObject.GetComponent<Toggle>();
toggle.isOn = true;
}
}
[ClientCallback]
void ShowRoomView()
{
lobbyView.SetActive(false);
roomView.SetActive(true);
}
[ClientCallback]
void RefreshMatchList()
{
foreach (Transform child in matchList.transform)
Destroy(child.gameObject);
joinButton.interactable = false;
foreach (MatchInfo matchInfo in openMatches.Values)
{
GameObject newMatch = Instantiate(matchPrefab, Vector3.zero, Quaternion.identity);
newMatch.transform.SetParent(matchList.transform, false);
newMatch.GetComponent<MatchGUI>().SetMatchInfo(matchInfo);
Toggle toggle = newMatch.GetComponent<Toggle>();
toggle.group = toggleGroup;
if (matchInfo.matchId == selectedMatch)
toggle.isOn = true;
}
}
#endregion
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c12d7cb6cdb799041927819f22a2c931
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs
uploadId: 736421

View File

@ -0,0 +1,47 @@
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.MultipleMatch
{
public class CellGUI : MonoBehaviour
{
public MatchController matchController;
public CellValue cellValue;
[Header("GUI References")]
public Image image;
public Button button;
[Header("Diagnostics")]
[ReadOnly, SerializeField] internal NetworkIdentity playerIdentity;
public void Awake()
{
matchController.MatchCells.Add(cellValue, this);
}
[ClientCallback]
public void MakePlay()
{
if (matchController.currentPlayer.isLocalPlayer)
matchController.CmdMakePlay(cellValue);
}
[ClientCallback]
public void SetPlayer(NetworkIdentity playerIdentity)
{
if (playerIdentity != null)
{
this.playerIdentity = playerIdentity;
image.color = this.playerIdentity.isLocalPlayer ? Color.blue : Color.red;
button.interactable = false;
}
else
{
this.playerIdentity = null;
image.color = Color.white;
button.interactable = true;
}
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9cda2a394a443474689a3c6d6044f7b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/CellGUI.cs
uploadId: 736421

View File

@ -0,0 +1,312 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.MultipleMatch
{
[RequireComponent(typeof(NetworkMatch))]
public class MatchController : NetworkBehaviour
{
internal readonly SyncDictionary<NetworkIdentity, MatchPlayerData> matchPlayerData = new SyncDictionary<NetworkIdentity, MatchPlayerData>();
internal readonly Dictionary<CellValue, CellGUI> MatchCells = new Dictionary<CellValue, CellGUI>();
CellValue boardScore = CellValue.None;
bool playAgain = false;
[Header("GUI References")]
public CanvasGroup canvasGroup;
public Text gameText;
public Button exitButton;
public Button playAgainButton;
public Text winCountLocal;
public Text winCountOpponent;
[Header("Diagnostics")]
[ReadOnly, SerializeField] internal CanvasController canvasController;
[ReadOnly, SerializeField] internal NetworkIdentity player1;
[ReadOnly, SerializeField] internal NetworkIdentity player2;
[ReadOnly, SerializeField] internal NetworkIdentity startingPlayer;
[SyncVar(hook = nameof(UpdateGameUI))]
[ReadOnly, SerializeField] internal NetworkIdentity currentPlayer;
void Awake()
{
#if UNITY_2022_2_OR_NEWER
canvasController = GameObject.FindAnyObjectByType<CanvasController>();
#else
// Deprecated in Unity 2023.1
canvasController = GameObject.FindObjectOfType<CanvasController>();
#endif
}
public override void OnStartServer()
{
StartCoroutine(AddPlayersToMatchController());
}
// For the SyncDictionary to properly fire the update callback, we must
// wait a frame before adding the players to the already spawned MatchController
IEnumerator AddPlayersToMatchController()
{
yield return null;
matchPlayerData.Add(player1, new MatchPlayerData { playerIndex = CanvasController.playerInfos[player1.connectionToClient].playerIndex });
matchPlayerData.Add(player2, new MatchPlayerData { playerIndex = CanvasController.playerInfos[player2.connectionToClient].playerIndex });
}
public override void OnStartClient()
{
canvasGroup.alpha = 1f;
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
exitButton.gameObject.SetActive(false);
playAgainButton.gameObject.SetActive(false);
// Assign handler for SyncDictionary changes
matchPlayerData.OnChange = UpdateWins;
}
[ClientCallback]
public void UpdateGameUI(NetworkIdentity _, NetworkIdentity newPlayerTurn)
{
if (!newPlayerTurn) return;
if (newPlayerTurn.gameObject.GetComponent<NetworkIdentity>().isLocalPlayer)
{
gameText.text = "Your Turn";
gameText.color = Color.blue;
}
else
{
gameText.text = "Their Turn";
gameText.color = Color.red;
}
}
[ClientCallback]
public void UpdateWins(SyncDictionary<NetworkIdentity, MatchPlayerData>.Operation op, NetworkIdentity key, MatchPlayerData matchPlayerData)
{
if (key.gameObject.GetComponent<NetworkIdentity>().isLocalPlayer)
winCountLocal.text = $"Player {matchPlayerData.playerIndex}\n{matchPlayerData.wins}";
else
winCountOpponent.text = $"Player {matchPlayerData.playerIndex}\n{matchPlayerData.wins}";
}
[Command(requiresAuthority = false)]
public void CmdMakePlay(CellValue cellValue, NetworkConnectionToClient sender = null)
{
// If wrong player or cell already taken, ignore
if (sender.identity != currentPlayer || MatchCells[cellValue].playerIdentity != null)
return;
MatchCells[cellValue].playerIdentity = currentPlayer;
RpcUpdateCell(cellValue, currentPlayer);
MatchPlayerData mpd = matchPlayerData[currentPlayer];
mpd.currentScore = mpd.currentScore | cellValue;
matchPlayerData[currentPlayer] = mpd;
boardScore |= cellValue;
if (CheckWinner(mpd.currentScore))
{
mpd.wins += 1;
matchPlayerData[currentPlayer] = mpd;
RpcShowWinner(currentPlayer);
currentPlayer = null;
}
else if (boardScore == CellValue.Full)
{
RpcShowWinner(null);
currentPlayer = null;
}
else
{
// Set currentPlayer SyncVar so clients know whose turn it is
currentPlayer = currentPlayer == player1 ? player2 : player1;
}
}
[ServerCallback]
bool CheckWinner(CellValue currentScore)
{
if ((currentScore & CellValue.TopRow) == CellValue.TopRow)
return true;
if ((currentScore & CellValue.MidRow) == CellValue.MidRow)
return true;
if ((currentScore & CellValue.BotRow) == CellValue.BotRow)
return true;
if ((currentScore & CellValue.LeftCol) == CellValue.LeftCol)
return true;
if ((currentScore & CellValue.MidCol) == CellValue.MidCol)
return true;
if ((currentScore & CellValue.RightCol) == CellValue.RightCol)
return true;
if ((currentScore & CellValue.Diag1) == CellValue.Diag1)
return true;
if ((currentScore & CellValue.Diag2) == CellValue.Diag2)
return true;
return false;
}
[ClientRpc]
public void RpcUpdateCell(CellValue cellValue, NetworkIdentity player)
{
MatchCells[cellValue].SetPlayer(player);
}
[ClientRpc]
public void RpcShowWinner(NetworkIdentity winner)
{
foreach (CellGUI cellGUI in MatchCells.Values)
cellGUI.GetComponent<Button>().interactable = false;
if (winner == null)
{
gameText.text = "Draw!";
gameText.color = Color.yellow;
}
else if (winner.gameObject.GetComponent<NetworkIdentity>().isLocalPlayer)
{
gameText.text = "Winner!";
gameText.color = Color.blue;
}
else
{
gameText.text = "Loser!";
gameText.color = Color.red;
}
exitButton.gameObject.SetActive(true);
playAgainButton.gameObject.SetActive(true);
}
// Assigned in inspector to ReplayButton::OnClick
[ClientCallback]
public void RequestPlayAgain()
{
playAgainButton.gameObject.SetActive(false);
CmdPlayAgain();
}
[Command(requiresAuthority = false)]
public void CmdPlayAgain(NetworkConnectionToClient sender = null)
{
if (!playAgain)
playAgain = true;
else
{
playAgain = false;
RestartGame();
}
}
[ServerCallback]
public void RestartGame()
{
boardScore = CellValue.None;
NetworkIdentity[] keys = new NetworkIdentity[matchPlayerData.Keys.Count];
matchPlayerData.Keys.CopyTo(keys, 0);
foreach (NetworkIdentity identity in keys)
{
MatchPlayerData mpd = matchPlayerData[identity];
mpd.currentScore = CellValue.None;
matchPlayerData[identity] = mpd;
}
RpcRestartGame();
startingPlayer = startingPlayer == player1 ? player2 : player1;
currentPlayer = startingPlayer;
}
[ClientRpc]
public void RpcRestartGame()
{
foreach (CellGUI cellGUI in MatchCells.Values)
cellGUI.SetPlayer(null);
exitButton.gameObject.SetActive(false);
playAgainButton.gameObject.SetActive(false);
}
// Assigned in inspector to BackButton::OnClick
[Client]
public void RequestExitGame()
{
exitButton.gameObject.SetActive(false);
playAgainButton.gameObject.SetActive(false);
CmdRequestExitGame();
}
[Command(requiresAuthority = false)]
public void CmdRequestExitGame(NetworkConnectionToClient sender = null)
{
StartCoroutine(ServerEndMatch(sender, false));
}
[ServerCallback]
public void OnPlayerDisconnect(NetworkConnectionToClient conn)
{
// Check that the disconnecting client is a player in this match
if (player1 == conn.identity || player2 == conn.identity)
StartCoroutine(ServerEndMatch(conn, true));
}
[ServerCallback]
public IEnumerator ServerEndMatch(NetworkConnectionToClient conn, bool disconnected)
{
RpcExitGame();
canvasController.OnPlayerDisconnect -= OnPlayerDisconnect;
// Wait for the ClientRpc to get out ahead of object destruction
yield return new WaitForSeconds(0.1f);
// Mirror will clean up the disconnecting client so we only need to clean up the other remaining client.
// If both players are just returning to the Lobby, we need to remove both connection Players
if (!disconnected)
{
NetworkServer.RemovePlayerForConnection(player1.connectionToClient, RemovePlayerOptions.Destroy);
CanvasController.waitingConnections.Add(player1.connectionToClient);
NetworkServer.RemovePlayerForConnection(player2.connectionToClient, RemovePlayerOptions.Destroy);
CanvasController.waitingConnections.Add(player2.connectionToClient);
}
else if (conn == player1.connectionToClient)
{
// player1 has disconnected - send player2 back to Lobby
NetworkServer.RemovePlayerForConnection(player2.connectionToClient, RemovePlayerOptions.Destroy);
CanvasController.waitingConnections.Add(player2.connectionToClient);
}
else if (conn == player2.connectionToClient)
{
// player2 has disconnected - send player1 back to Lobby
NetworkServer.RemovePlayerForConnection(player1.connectionToClient, RemovePlayerOptions.Destroy);
CanvasController.waitingConnections.Add(player1.connectionToClient);
}
// Skip a frame to allow the Removal(s) to complete
yield return null;
// Send latest match list
canvasController.SendMatchList();
NetworkServer.Destroy(gameObject);
}
[ClientRpc]
public void RpcExitGame()
{
canvasController.OnMatchEnded();
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9ccb1dd1fc7cc624e9bff1d0d7a5c741
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs
uploadId: 736421

View File

@ -0,0 +1,48 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.MultipleMatch
{
public class MatchGUI : MonoBehaviour
{
Guid matchId;
[Header("GUI Elements")]
public Image image;
public Toggle toggleButton;
public Text matchName;
public Text playerCount;
[Header("Diagnostics")]
[ReadOnly, SerializeField] internal CanvasController canvasController;
public void Awake()
{
#if UNITY_2022_2_OR_NEWER
canvasController = GameObject.FindAnyObjectByType<CanvasController>();
#else
// Deprecated in Unity 2023.1
canvasController = GameObject.FindObjectOfType<CanvasController>();
#endif
}
[ClientCallback]
public void OnToggleClicked(bool isOn)
{
canvasController.SelectMatch(isOn ? matchId : Guid.Empty);
image.color = isOn ? new Color(0f, 1f, 0f, 0.5f) : new Color(1f, 1f, 1f, 0.2f);
}
[ClientCallback]
public Guid GetMatchId() => matchId;
[ClientCallback]
public void SetMatchInfo(MatchInfo infos)
{
matchId = infos.matchId;
matchName.text = $"Match {infos.matchId.ToString().Substring(0, 8)}";
playerCount.text = $"{infos.players} / {infos.maxPlayers}";
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e875a237ce12c3145b20f17222f10b68
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs
uploadId: 736421

View File

@ -0,0 +1,117 @@
using System;
namespace Mirror.Examples.MultipleMatch
{
/// <summary>
/// Match message to be sent to the server
/// </summary>
public struct ServerMatchMessage : NetworkMessage
{
public ServerMatchOperation serverMatchOperation;
public Guid matchId;
}
/// <summary>
/// Match message to be sent to the client
/// </summary>
public struct ClientMatchMessage : NetworkMessage
{
public ClientMatchOperation clientMatchOperation;
public Guid matchId;
public MatchInfo[] matchInfos;
public PlayerInfo[] playerInfos;
}
/// <summary>
/// Information about a match
/// </summary>
[Serializable]
public struct MatchInfo
{
public Guid matchId;
public byte players;
public byte maxPlayers;
}
/// <summary>
/// Information about a player
/// </summary>
[Serializable]
public struct PlayerInfo
{
public int playerIndex;
public bool ready;
public Guid matchId;
}
[Serializable]
public struct MatchPlayerData
{
public int playerIndex;
public int wins;
public CellValue currentScore;
}
/// <summary>
/// Match operation to execute on the server
/// </summary>
public enum ServerMatchOperation : byte
{
None,
Create,
Cancel,
Start,
Join,
Leave,
Ready
}
/// <summary>
/// Match operation to execute on the client
/// </summary>
public enum ClientMatchOperation : byte
{
None,
List,
Created,
Cancelled,
Joined,
Departed,
UpdateRoom,
Started
}
// A1 | B1 | C1
// ---+----+---
// A2 | B2 | C2
// ---+----+---
// A3 | B3 | C3
[Flags]
public enum CellValue : ushort
{
None,
A1 = 1 << 0,
B1 = 1 << 1,
C1 = 1 << 2,
A2 = 1 << 3,
B2 = 1 << 4,
C2 = 1 << 5,
A3 = 1 << 6,
B3 = 1 << 7,
C3 = 1 << 8,
// winning combinations
TopRow = A1 + B1 + C1,
MidRow = A2 + B2 + C2,
BotRow = A3 + B3 + C3,
LeftCol = A1 + A2 + A3,
MidCol = B1 + B2 + B3,
RightCol = C1 + C2 + C3,
Diag1 = A1 + B2 + C3,
Diag2 = A3 + B2 + C1,
// board is full (winner / draw)
Full = TopRow + MidRow + BotRow
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a98b377d8481e52449b996e45a015b8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/MatchMessages.cs
uploadId: 736421

View File

@ -0,0 +1,110 @@
using System.Collections;
using UnityEngine;
namespace Mirror.Examples.MultipleMatch
{
[AddComponentMenu("")]
public class MatchNetworkManager : NetworkManager
{
[Header("Match GUI")]
public GameObject canvas;
public CanvasController canvasController;
/// <summary>
/// Runs on both Server and Client
/// Networking is NOT initialized when this fires
/// </summary>
public override void Awake()
{
base.Awake();
canvasController.InitializeData();
}
#region Server System Callbacks
/// <summary>
/// Called on the server when a client is ready.
/// <para>The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process.</para>
/// </summary>
/// <param name="conn">Connection from client.</param>
public override void OnServerReady(NetworkConnectionToClient conn)
{
base.OnServerReady(conn);
canvasController.OnServerReady(conn);
}
/// <summary>
/// Called on the server when a client disconnects.
/// <para>This is called on the Server when a Client disconnects from the Server. Use an override to decide what should happen when a disconnection is detected.</para>
/// </summary>
/// <param name="conn">Connection from client.</param>
public override void OnServerDisconnect(NetworkConnectionToClient conn)
{
StartCoroutine(DoServerDisconnect(conn));
}
IEnumerator DoServerDisconnect(NetworkConnectionToClient conn)
{
yield return canvasController.OnServerDisconnect(conn);
base.OnServerDisconnect(conn);
}
#endregion
#region Client System Callbacks
/// <summary>
/// Called on clients when disconnected from a server.
/// <para>This is called on the client when it disconnects from the server. Override this function to decide what happens when the client disconnects.</para>
/// </summary>
public override void OnClientDisconnect()
{
canvasController.OnClientDisconnect();
base.OnClientDisconnect();
}
#endregion
#region Start & Stop Callbacks
/// <summary>
/// This is invoked when a server is started - including when a host is started.
/// <para>StartServer has multiple signatures, but they all cause this hook to be called.</para>
/// </summary>
public override void OnStartServer()
{
if (mode == NetworkManagerMode.ServerOnly)
canvas.SetActive(true);
canvasController.OnStartServer();
}
/// <summary>
/// This is invoked when the client is started.
/// </summary>
public override void OnStartClient()
{
canvas.SetActive(true);
canvasController.OnStartClient();
}
/// <summary>
/// This is called when a server is stopped - including when a host is stopped.
/// </summary>
public override void OnStopServer()
{
canvasController.OnStopServer();
canvas.SetActive(false);
}
/// <summary>
/// This is called when a client is stopped.
/// </summary>
public override void OnStopClient()
{
canvasController.OnStopClient();
}
#endregion
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0b15c78bb8ab8da42a94aa0bc3081814
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs
uploadId: 736421

View File

@ -0,0 +1,17 @@
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.MultipleMatch
{
public class PlayerGUI : MonoBehaviour
{
public Text playerName;
[ClientCallback]
public void SetPlayerInfo(PlayerInfo info)
{
playerName.text = $"Player {info.playerIndex}";
playerName.color = info.ready ? Color.green : Color.red;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f61e9bc3715eb904b91222a5526f63d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs
uploadId: 736421

View File

@ -0,0 +1,45 @@
using UnityEngine;
using UnityEngine.UI;
namespace Mirror.Examples.MultipleMatch
{
public class RoomGUI : MonoBehaviour
{
public GameObject playerList;
public GameObject playerPrefab;
public GameObject cancelButton;
public GameObject leaveButton;
public Button startButton;
public bool owner;
[ClientCallback]
public void RefreshRoomPlayers(PlayerInfo[] playerInfos)
{
foreach (Transform child in playerList.transform)
Destroy(child.gameObject);
startButton.interactable = false;
bool everyoneReady = true;
foreach (PlayerInfo playerInfo in playerInfos)
{
GameObject newPlayer = Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
newPlayer.transform.SetParent(playerList.transform, false);
newPlayer.GetComponent<PlayerGUI>().SetPlayerInfo(playerInfo);
if (!playerInfo.ready)
everyoneReady = false;
}
startButton.interactable = everyoneReady && owner && (playerInfos.Length > 1);
}
[ClientCallback]
public void SetOwner(bool owner)
{
this.owner = owner;
cancelButton.SetActive(owner);
leaveButton.SetActive(!owner);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: fc0f113afba9a074b9a3e4fb56f16abb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Examples/MultipleMatches/Scripts/RoomGUI.cs
uploadId: 736421