Adding new Races, Main Menu -> Lobby flow, and what's needed to set up multiplayer testing.
This commit is contained in:
parent
60fa58b07f
commit
fdada6f132
29 changed files with 2581 additions and 176 deletions
8
Assets/_Project/Art/Sprites.meta
Normal file
8
Assets/_Project/Art/Sprites.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2d10906489190644baf28d93e5197c42
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/_Project/Art/Sprites/BloodAngels.jpg
Normal file
3
Assets/_Project/Art/Sprites/BloodAngels.jpg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4fa63ce77f2808fb9a275dcfea9747c91e2c6e521589b1af1bf85d29ef7752ae
|
||||
size 7854
|
||||
117
Assets/_Project/Art/Sprites/BloodAngels.jpg.meta
Normal file
117
Assets/_Project/Art/Sprites/BloodAngels.jpg.meta
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dbc19961a66b0b6478efea006e0a0fee
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/_Project/Art/Sprites/Ultramarines.jpg
Normal file
3
Assets/_Project/Art/Sprites/Ultramarines.jpg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9033f5e4e490ee5a82e550aae4f690eefc49463a804ecc91e2874a28e8f988d1
|
||||
size 5726
|
||||
117
Assets/_Project/Art/Sprites/Ultramarines.jpg.meta
Normal file
117
Assets/_Project/Art/Sprites/Ultramarines.jpg.meta
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f19f71b7dd6671841abd34ce8b359351
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/_Project/Data/Races.meta
Normal file
8
Assets/_Project/Data/Races.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0d631899488b6db4a8d9af9a97a9b2d5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/_Project/Data/Races/BloodAngels_Placeholder.asset
Normal file
23
Assets/_Project/Data/Races/BloodAngels_Placeholder.asset
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 98b7d5d116870f94da912007f6aa5cbb, type: 3}
|
||||
m_Name: BloodAngels_Placeholder
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.RaceDefinition
|
||||
Id: 2
|
||||
DisplayName: Blood Angels
|
||||
Icon: {fileID: 21300000, guid: dbc19961a66b0b6478efea006e0a0fee, type: 3}
|
||||
BuilderName: Mother Teresa
|
||||
BuilderDescription: Evil beyond measure, a truly fucked up woman
|
||||
LoreText: Crucified and brought back by God because her job wasn't finished. She's
|
||||
here to bring death to to Xenos scum.
|
||||
BuilderPrefab: {fileID: 116861493430507844, guid: 3398cc5831880954487717577f61b6d7, type: 3}
|
||||
Towers: []
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4d91bbd27e96fb845af6bb1bf0a22fe4
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/_Project/Data/Races/Ultramarines_Placeholder.asset
Normal file
23
Assets/_Project/Data/Races/Ultramarines_Placeholder.asset
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 98b7d5d116870f94da912007f6aa5cbb, type: 3}
|
||||
m_Name: Ultramarines_Placeholder
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.RaceDefinition
|
||||
Id: 1
|
||||
DisplayName: Ultramarines
|
||||
Icon: {fileID: 21300000, guid: f19f71b7dd6671841abd34ce8b359351, type: 3}
|
||||
BuilderName: Master Chief
|
||||
BuilderDescription: My man makes some bangin towers
|
||||
LoreText: King of the protoss, he's here to kick bubblegum and chew ass, and he's
|
||||
all out of ass.
|
||||
BuilderPrefab: {fileID: 116861493430507844, guid: 3398cc5831880954487717577f61b6d7, type: 3}
|
||||
Towers: []
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c8b11f545e3990049a5953a34459f58a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -146,7 +146,7 @@ MonoBehaviour:
|
|||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||
GlobalObjectIdHash: 4180539217
|
||||
GlobalObjectIdHash: 3501512193
|
||||
InScenePlacedSourceGlobalObjectIdHash: 3702618695
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
|
|
@ -977,7 +977,6 @@ GameObject:
|
|||
- component: {fileID: 4960685830559074027}
|
||||
- component: {fileID: 4757296063414367819}
|
||||
- component: {fileID: 336275605508886593}
|
||||
- component: {fileID: 4966682596699708025}
|
||||
m_Layer: 0
|
||||
m_Name: SelectionRing
|
||||
m_TagString: Untagged
|
||||
|
|
@ -1057,18 +1056,6 @@ MeshRenderer:
|
|||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!114 &4966682596699708025
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2652716240617921727}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 67895f626233fdc499dffbbfcc225530, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.SelectionRingVisual
|
||||
--- !u!1 &2781170330021425455
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
|||
|
|
@ -1502,12 +1502,12 @@ MonoBehaviour:
|
|||
edgePanEnabled: 1
|
||||
minDollyDistance: 5
|
||||
maxDollyDistance: 50
|
||||
startDollyDistance: 35
|
||||
startDollyDistance: 25
|
||||
zoomSpeed: 3
|
||||
cursorAnchoredZoom: 1
|
||||
minPitchDegrees: 30
|
||||
maxPitchDegrees: 75
|
||||
startPitchDegrees: 60
|
||||
startPitchDegrees: 50
|
||||
pitchSpeed: 4
|
||||
--- !u!4 &1239994224
|
||||
Transform:
|
||||
|
|
@ -1950,112 +1950,6 @@ MonoBehaviour:
|
|||
serializedVersion: 2
|
||||
m_Bits: 64
|
||||
raycastMaxDistance: 500
|
||||
--- !u!1 &1682341399
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1682341402}
|
||||
- component: {fileID: 1682341401}
|
||||
- component: {fileID: 1682341400}
|
||||
m_Layer: 0
|
||||
m_Name: NetworkManager
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1682341400
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1682341399}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.Transports.UTP.UnityTransport
|
||||
m_ProtocolType: 0
|
||||
m_UseWebSockets: 0
|
||||
m_UseEncryption: 0
|
||||
m_MaxPacketQueueSize: 128
|
||||
m_MaxPayloadSize: 6144
|
||||
m_HeartbeatTimeoutMS: 500
|
||||
m_ConnectTimeoutMS: 1000
|
||||
m_MaxConnectAttempts: 60
|
||||
m_DisconnectTimeoutMS: 30000
|
||||
ConnectionData:
|
||||
Address: 127.0.0.1
|
||||
Port: 7777
|
||||
WebSocketPath: /
|
||||
ServerListenAddress: 127.0.0.1
|
||||
ClientBindPort: 0
|
||||
DebugSimulator:
|
||||
PacketDelayMS: 0
|
||||
PacketJitterMS: 0
|
||||
PacketDropRate: 0
|
||||
--- !u!114 &1682341401
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1682341399}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkManager
|
||||
NetworkManagerExpanded: 0
|
||||
NetworkConfig:
|
||||
ProtocolVersion: 0
|
||||
NetworkTransport: {fileID: 1682341400}
|
||||
PlayerPrefab: {fileID: 3493329038866903420, guid: 9a9c23b8584ab444aa5066a48579a9ec, type: 3}
|
||||
Prefabs:
|
||||
NetworkPrefabsLists:
|
||||
- {fileID: 11400000, guid: 481ab1d7456efd044bc3e349aacd92ae, type: 2}
|
||||
TickRate: 30
|
||||
ClientConnectionBufferTimeout: 10
|
||||
ConnectionApproval: 0
|
||||
ConnectionData:
|
||||
EnableTimeResync: 0
|
||||
TimeResyncInterval: 30
|
||||
EnsureNetworkVariableLengthSafety: 0
|
||||
EnableSceneManagement: 1
|
||||
ForceSamePrefabs: 1
|
||||
RecycleNetworkIds: 1
|
||||
NetworkIdRecycleDelay: 120
|
||||
RpcHashSize: 0
|
||||
LoadSceneTimeOut: 120
|
||||
SpawnTimeout: 10
|
||||
EnableNetworkLogs: 1
|
||||
NetworkTopology: 0
|
||||
UseCMBService: 0
|
||||
AutoSpawnPlayerPrefabClientSide: 1
|
||||
NetworkProfilingMetrics: 1
|
||||
OldPrefabList: []
|
||||
RunInBackground: 1
|
||||
LogLevel: 1
|
||||
--- !u!4 &1682341402
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1682341399}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1731269685
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -2598,7 +2492,6 @@ SceneRoots:
|
|||
m_Roots:
|
||||
- {fileID: 410087041}
|
||||
- {fileID: 832575519}
|
||||
- {fileID: 1682341402}
|
||||
- {fileID: 441239881}
|
||||
- {fileID: 167151709}
|
||||
- {fileID: 1507514109}
|
||||
|
|
|
|||
546
Assets/_Project/Scenes/UI/Lobby.unity
Normal file
546
Assets/_Project/Scenes/UI/Lobby.unity
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &178257552
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 178257555}
|
||||
- component: {fileID: 178257554}
|
||||
- component: {fileID: 178257553}
|
||||
- component: {fileID: 178257556}
|
||||
m_Layer: 5
|
||||
m_Name: LobbyUI
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &178257553
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 178257552}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8c40427d598ba944c82f3790429c5532, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.UI.LobbyController
|
||||
raceOverlay: {fileID: 178257556}
|
||||
--- !u!114 &178257554
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 178257552}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
|
||||
m_PanelSettings: {fileID: 11400000, guid: 6aa0af71585acea4db4995c3931dc946, type: 2}
|
||||
m_ParentUI: {fileID: 0}
|
||||
sourceAsset: {fileID: 0}
|
||||
m_SortingOrder: 0
|
||||
m_Position: 0
|
||||
m_WorldSpaceSizeMode: 1
|
||||
m_WorldSpaceWidth: 1920
|
||||
m_WorldSpaceHeight: 1080
|
||||
m_PivotReferenceSize: 0
|
||||
m_Pivot: 0
|
||||
m_WorldSpaceCollider: {fileID: 0}
|
||||
--- !u!4 &178257555
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 178257552}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &178257556
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 178257552}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9ab3ad11b37d4c54d9f310f5356d31ac, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.UI.RaceSelectionOverlay
|
||||
--- !u!1 &203844586
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 203844589}
|
||||
- component: {fileID: 203844588}
|
||||
- component: {fileID: 203844587}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &203844587
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 203844586}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_CustomShadowLayers: 0
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
m_RenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_ShadowRenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_Version: 4
|
||||
m_LightLayerMask: 1
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!108 &203844588
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 203844586}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 13
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize2D: {x: 10, y: 10}
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShapeRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &203844589
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 203844586}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1 &961739749
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 961739753}
|
||||
- component: {fileID: 961739752}
|
||||
- component: {fileID: 961739751}
|
||||
- component: {fileID: 961739750}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &961739750
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
m_Version: 2
|
||||
--- !u!81 &961739751
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &961739752
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &961739753
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &2030215725
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2030215727}
|
||||
- component: {fileID: 2030215726}
|
||||
- component: {fileID: 2030215728}
|
||||
m_Layer: 0
|
||||
m_Name: LobbyService
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &2030215726
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2030215725}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||
GlobalObjectIdHash: 1514133910
|
||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
AlwaysReplicateAsRoot: 0
|
||||
SynchronizeTransform: 1
|
||||
ActiveSceneSynchronization: 0
|
||||
SceneMigrationSynchronization: 1
|
||||
SpawnWithObservers: 1
|
||||
DontDestroyWithOwner: 0
|
||||
AutoObjectParentSync: 1
|
||||
SyncOwnerTransformWhenParented: 1
|
||||
AllowOwnerToParent: 0
|
||||
--- !u!4 &2030215727
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2030215725}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &2030215728
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2030215725}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 46ab03868ea4bd541bd6be3446c2bd3d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.LobbyService
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 961739753}
|
||||
- {fileID: 203844589}
|
||||
- {fileID: 2030215727}
|
||||
- {fileID: 178257555}
|
||||
7
Assets/_Project/Scenes/UI/Lobby.unity.meta
Normal file
7
Assets/_Project/Scenes/UI/Lobby.unity.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a3639dca674a8049a570cb848bb69d2
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
662
Assets/_Project/Scenes/UI/MainMenu.unity
Normal file
662
Assets/_Project/Scenes/UI/MainMenu.unity
Normal file
|
|
@ -0,0 +1,662 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &203844586
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 203844589}
|
||||
- component: {fileID: 203844588}
|
||||
- component: {fileID: 203844587}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &203844587
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 203844586}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_CustomShadowLayers: 0
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
m_RenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_ShadowRenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_Version: 4
|
||||
m_LightLayerMask: 1
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!108 &203844588
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 203844586}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 13
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize2D: {x: 10, y: 10}
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShapeRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &203844589
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 203844586}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1 &514623720
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 514623722}
|
||||
- component: {fileID: 514623721}
|
||||
m_Layer: 0
|
||||
m_Name: RaceRegistry
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &514623721
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 514623720}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 50ccfdaa301b6a7439b4edfc750550aa, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.RaceRegistry
|
||||
definitions:
|
||||
- {fileID: 11400000, guid: c8b11f545e3990049a5953a34459f58a, type: 2}
|
||||
- {fileID: 11400000, guid: 4d91bbd27e96fb845af6bb1bf0a22fe4, type: 2}
|
||||
--- !u!4 &514623722
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 514623720}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &626141751
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 626141754}
|
||||
- component: {fileID: 626141753}
|
||||
- component: {fileID: 626141752}
|
||||
m_Layer: 5
|
||||
m_Name: MainMenuUI
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &626141752
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 626141751}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 01199add5a12f4a4bb9a94d1e44fbb4d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.UI.MainMenuController
|
||||
defaultPort: 7777
|
||||
defaultJoinAddress: 127.0.0.1
|
||||
--- !u!114 &626141753
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 626141751}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
|
||||
m_PanelSettings: {fileID: 11400000, guid: 6aa0af71585acea4db4995c3931dc946, type: 2}
|
||||
m_ParentUI: {fileID: 0}
|
||||
sourceAsset: {fileID: 0}
|
||||
m_SortingOrder: 0
|
||||
m_Position: 0
|
||||
m_WorldSpaceSizeMode: 1
|
||||
m_WorldSpaceWidth: 1920
|
||||
m_WorldSpaceHeight: 1080
|
||||
m_PivotReferenceSize: 0
|
||||
m_Pivot: 0
|
||||
m_WorldSpaceCollider: {fileID: 0}
|
||||
--- !u!4 &626141754
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 626141751}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &769692006
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 769692009}
|
||||
- component: {fileID: 769692008}
|
||||
- component: {fileID: 769692007}
|
||||
m_Layer: 0
|
||||
m_Name: NetworkManager
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &769692007
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 769692006}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.Transports.UTP.UnityTransport
|
||||
m_ProtocolType: 0
|
||||
m_UseWebSockets: 0
|
||||
m_UseEncryption: 0
|
||||
m_MaxPacketQueueSize: 128
|
||||
m_MaxPayloadSize: 6144
|
||||
m_HeartbeatTimeoutMS: 500
|
||||
m_ConnectTimeoutMS: 1000
|
||||
m_MaxConnectAttempts: 60
|
||||
m_DisconnectTimeoutMS: 30000
|
||||
ConnectionData:
|
||||
Address: 127.0.0.1
|
||||
Port: 7777
|
||||
WebSocketPath: /
|
||||
ServerListenAddress: 127.0.0.1
|
||||
ClientBindPort: 0
|
||||
DebugSimulator:
|
||||
PacketDelayMS: 0
|
||||
PacketJitterMS: 0
|
||||
PacketDropRate: 0
|
||||
--- !u!114 &769692008
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 769692006}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkManager
|
||||
NetworkManagerExpanded: 0
|
||||
NetworkConfig:
|
||||
ProtocolVersion: 0
|
||||
NetworkTransport: {fileID: 769692007}
|
||||
PlayerPrefab: {fileID: 3493329038866903420, guid: 9a9c23b8584ab444aa5066a48579a9ec, type: 3}
|
||||
Prefabs:
|
||||
NetworkPrefabsLists:
|
||||
- {fileID: 11400000, guid: 481ab1d7456efd044bc3e349aacd92ae, type: 2}
|
||||
TickRate: 30
|
||||
ClientConnectionBufferTimeout: 10
|
||||
ConnectionApproval: 0
|
||||
ConnectionData:
|
||||
EnableTimeResync: 0
|
||||
TimeResyncInterval: 30
|
||||
EnsureNetworkVariableLengthSafety: 0
|
||||
EnableSceneManagement: 1
|
||||
ForceSamePrefabs: 1
|
||||
RecycleNetworkIds: 1
|
||||
NetworkIdRecycleDelay: 120
|
||||
RpcHashSize: 0
|
||||
LoadSceneTimeOut: 120
|
||||
SpawnTimeout: 10
|
||||
EnableNetworkLogs: 1
|
||||
NetworkTopology: 0
|
||||
UseCMBService: 0
|
||||
AutoSpawnPlayerPrefabClientSide: 1
|
||||
NetworkProfilingMetrics: 1
|
||||
OldPrefabList: []
|
||||
RunInBackground: 1
|
||||
LogLevel: 1
|
||||
--- !u!4 &769692009
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 769692006}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &961739749
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 961739753}
|
||||
- component: {fileID: 961739752}
|
||||
- component: {fileID: 961739751}
|
||||
- component: {fileID: 961739750}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &961739750
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
m_Version: 2
|
||||
--- !u!81 &961739751
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &961739752
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &961739753
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 961739749}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1648104262
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1648104264}
|
||||
- component: {fileID: 1648104263}
|
||||
m_Layer: 0
|
||||
m_Name: SessionFlow
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1648104263
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1648104262}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474003b8e462dcc479e313f3d4f1cf12, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Net.SessionFlow
|
||||
--- !u!4 &1648104264
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1648104262}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 961739753}
|
||||
- {fileID: 203844589}
|
||||
- {fileID: 769692009}
|
||||
- {fileID: 1648104264}
|
||||
- {fileID: 626141754}
|
||||
- {fileID: 514623722}
|
||||
7
Assets/_Project/Scenes/UI/MainMenu.unity.meta
Normal file
7
Assets/_Project/Scenes/UI/MainMenu.unity.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1f4cb4a6391f5e94285960890282b70e
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -73,14 +73,24 @@ namespace TD.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the race a player has chosen in the race-pick phase.
|
||||
/// Backed by byte. Specific race values are defined in Phase 1.8.
|
||||
/// Stable identifier per race. Values 1-16 reserve slots for the planned
|
||||
/// 16-race grid in the lobby; only races with a corresponding
|
||||
/// <c>RaceDefinition</c> asset registered with <c>RaceRegistry</c> are
|
||||
/// playable. Unregistered slots render as "Coming Soon" in the selection UI.
|
||||
///
|
||||
/// Names are intentionally generic so display names / lore can be authored
|
||||
/// on the asset without renaming the enum — renaming would change byte
|
||||
/// values and break save data once persistence lands.
|
||||
/// </summary>
|
||||
public enum RaceId : byte
|
||||
{
|
||||
/// <summary>No race selected yet (lobby / pre-pick).</summary>
|
||||
None = 0,
|
||||
// Race entries added in Phase 1.8.
|
||||
|
||||
Race1 = 1, Race2 = 2, Race3 = 3, Race4 = 4,
|
||||
Race5 = 5, Race6 = 6, Race7 = 7, Race8 = 8,
|
||||
Race9 = 9, Race10 = 10, Race11 = 11, Race12 = 12,
|
||||
Race13 = 13, Race14 = 14, Race15 = 15, Race16 = 16,
|
||||
}
|
||||
|
||||
public enum PlayerSlot : byte
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
// Assets/_Project/Scripts/Gameplay/PlayerBuilderSpawner.cs
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using TD.Core;
|
||||
using TD.Levels;
|
||||
using TD.Net;
|
||||
|
||||
namespace TD.Gameplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Lives on the Player Prefab. On the server, when the player NetworkObject spawns,
|
||||
/// instantiates and spawns a separate <see cref="Builder"/> NetworkObject owned by that
|
||||
/// player. The builder is positioned at the centroid of the player's zone before spawn.
|
||||
/// Lives on the Player Prefab. On the server, spawns and re-spawns a separate
|
||||
/// <see cref="Builder"/> NetworkObject owned by this player each time the Match
|
||||
/// scene loads. The builder is positioned at the centroid of the player's zone.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Why a separate NetworkObject?</b> Multi-builder races (Path E) become "spawn
|
||||
|
|
@ -20,31 +23,79 @@ namespace TD.Gameplay
|
|||
/// no-ops; they just receive the resulting Builder NetworkObject like any other
|
||||
/// replicated spawn.</para>
|
||||
///
|
||||
/// <para><b>Lifetime.</b> The spawned builder is destroyed when the player NetworkObject
|
||||
/// despawns (e.g., disconnect). NGO does this automatically because we set
|
||||
/// <c>destroyWithScene</c> and store no other references — the builder's despawn cleans
|
||||
/// up the static registry in <see cref="Builder.OnNetworkDespawn"/>.</para>
|
||||
/// <para><b>Lifetime & scene flow.</b> The player NetworkObject persists across
|
||||
/// scenes (MainMenu → Lobby → Match → Lobby …) because it's the
|
||||
/// <c>NetworkManager.PlayerPrefab</c>. The Builder is spawned with
|
||||
/// <c>destroyWithScene: true</c> so it's torn down on every scene unload — we
|
||||
/// only want a builder in the Match scene. <see cref="HandleSceneLoadCompleted"/>
|
||||
/// re-spawns when entering Match; <see cref="OnNetworkDespawn"/> handles
|
||||
/// disconnect cleanup.</para>
|
||||
///
|
||||
/// <para><b>Race-driven builder prefab.</b> If a <see cref="RaceRegistry"/> exists
|
||||
/// in the Match scene AND the player's selected <see cref="RaceDefinition"/> has
|
||||
/// a <c>BuilderPrefab</c> assigned, that prefab is used. Otherwise this falls back
|
||||
/// to the inspector-assigned <see cref="builderPrefab"/> (the universal default).
|
||||
/// Phase 1.8 races just need to fill in their <c>BuilderPrefab</c> field; no code
|
||||
/// change required.</para>
|
||||
/// </remarks>
|
||||
public class PlayerBuilderSpawner : NetworkBehaviour
|
||||
{
|
||||
[Tooltip("Builder prefab to instantiate. Must be registered with the NetworkManager " +
|
||||
"as a network prefab.")]
|
||||
[Tooltip("Default Builder prefab. Used when the player's race has no BuilderPrefab " +
|
||||
"assigned (or when no RaceRegistry is present in the scene). Must be " +
|
||||
"registered with the NetworkManager as a network prefab.")]
|
||||
[SerializeField] private GameObject builderPrefab;
|
||||
|
||||
// Cached reference so we can despawn the builder if needed (e.g., player disconnects).
|
||||
private NetworkObject spawnedBuilder;
|
||||
|
||||
// Track our scene-load subscription state so we can clean up correctly.
|
||||
private bool sceneSubscribed;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
if (builderPrefab == null)
|
||||
{
|
||||
Debug.LogError("[PlayerBuilderSpawner] No Builder prefab assigned. " +
|
||||
Debug.LogError("[PlayerBuilderSpawner] No default Builder prefab assigned. " +
|
||||
"Cannot spawn builder for client " + OwnerClientId + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to NGO's scene-load completion so we re-spawn the builder
|
||||
// every time the Match scene comes up (initial match start, Retry, etc.).
|
||||
if (NetworkManager != null && NetworkManager.SceneManager != null)
|
||||
{
|
||||
NetworkManager.SceneManager.OnLoadEventCompleted += HandleSceneLoadCompleted;
|
||||
sceneSubscribed = true;
|
||||
}
|
||||
|
||||
// Edge case: the player connected while a match is already in progress.
|
||||
// The Match scene is already loaded, so OnLoadEventCompleted won't fire
|
||||
// again until the next transition. Spawn now.
|
||||
if (SceneManager.GetActiveScene().name == SceneNames.Match)
|
||||
TrySpawnBuilder();
|
||||
}
|
||||
|
||||
// NGO fires this on the server once a scene load is acknowledged complete
|
||||
// by every connected client (or timed out). We only act when the Match
|
||||
// scene loads; Lobby / MainMenu loads are no-ops here.
|
||||
private void HandleSceneLoadCompleted(string sceneName,
|
||||
LoadSceneMode loadSceneMode,
|
||||
List<ulong> clientsCompleted,
|
||||
List<ulong> clientsTimedOut)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (sceneName != SceneNames.Match) return;
|
||||
TrySpawnBuilder();
|
||||
}
|
||||
|
||||
// Spawns the builder if it doesn't already exist. Defers to a SlotReady
|
||||
// event if PlayerMatchState hasn't finished assigning the slot yet.
|
||||
private void TrySpawnBuilder()
|
||||
{
|
||||
if (spawnedBuilder != null && spawnedBuilder.IsSpawned) return;
|
||||
|
||||
var pms = GetComponent<PlayerMatchState>();
|
||||
if (pms == null)
|
||||
{
|
||||
|
|
@ -53,8 +104,6 @@ namespace TD.Gameplay
|
|||
return;
|
||||
}
|
||||
|
||||
// PlayerMatchState.OnNetworkSpawn may have already fired (component order: it first)
|
||||
// or may fire after us (component order: we first). Handle both cases.
|
||||
if (pms.Slot != PlayerSlot.None)
|
||||
SpawnBuilderForOwner(pms.Slot);
|
||||
else
|
||||
|
|
@ -65,11 +114,21 @@ namespace TD.Gameplay
|
|||
{
|
||||
var pms = GetComponent<PlayerMatchState>();
|
||||
if (pms != null) pms.SlotReady -= OnOwnerSlotReady;
|
||||
SpawnBuilderForOwner(slot);
|
||||
|
||||
// Only spawn if we're in the Match scene. SlotReady can fire in MainMenu
|
||||
// (during initial connection) — we don't want a builder there.
|
||||
if (SceneManager.GetActiveScene().name == SceneNames.Match)
|
||||
SpawnBuilderForOwner(slot);
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (sceneSubscribed && NetworkManager != null && NetworkManager.SceneManager != null)
|
||||
{
|
||||
NetworkManager.SceneManager.OnLoadEventCompleted -= HandleSceneLoadCompleted;
|
||||
sceneSubscribed = false;
|
||||
}
|
||||
|
||||
// When the player despawns (disconnect), also despawn their builder if it still exists.
|
||||
if (IsServer && spawnedBuilder != null && spawnedBuilder.IsSpawned)
|
||||
{
|
||||
|
|
@ -84,7 +143,19 @@ namespace TD.Gameplay
|
|||
// Falls back to origin if loader/zone data isn't available.
|
||||
Vector3 spawnPos = ComputeZoneCentroid(slot);
|
||||
|
||||
var go = Instantiate(builderPrefab, spawnPos, Quaternion.identity);
|
||||
// Pick the prefab: race-specific takes priority, default falls back.
|
||||
// Falling back is what lets all races share the default builder
|
||||
// during Phase 1.7 — each RaceDefinition just needs the same default
|
||||
// assigned, and the spawner picks it up automatically.
|
||||
GameObject prefab = ResolveBuilderPrefab();
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError("[PlayerBuilderSpawner] No builder prefab available. " +
|
||||
"Set the default in the inspector or assign one to the player's race.");
|
||||
return;
|
||||
}
|
||||
|
||||
var go = Instantiate(prefab, spawnPos, Quaternion.identity);
|
||||
var netObj = go.GetComponent<NetworkObject>();
|
||||
if (netObj == null)
|
||||
{
|
||||
|
|
@ -114,6 +185,22 @@ namespace TD.Gameplay
|
|||
|
||||
// ----- Helpers ----------------------------------------------------
|
||||
|
||||
// Picks the builder prefab to spawn for this player. Race-specific takes
|
||||
// priority when (a) RaceRegistry is in the scene, (b) the player picked
|
||||
// a race, and (c) that race's RaceDefinition has a BuilderPrefab assigned.
|
||||
// Otherwise falls back to the inspector-assigned default.
|
||||
private GameObject ResolveBuilderPrefab()
|
||||
{
|
||||
var pms = GetComponent<PlayerMatchState>();
|
||||
if (pms != null && pms.RaceSelection != RaceId.None && RaceRegistry.Instance != null)
|
||||
{
|
||||
var raceDef = RaceRegistry.Instance.Get(pms.RaceSelection);
|
||||
if (raceDef != null && raceDef.BuilderPrefab != null)
|
||||
return raceDef.BuilderPrefab;
|
||||
}
|
||||
return builderPrefab;
|
||||
}
|
||||
|
||||
private static Vector3 ComputeZoneCentroid(PlayerSlot slot)
|
||||
{
|
||||
var loader = LevelLoader.Instance;
|
||||
|
|
|
|||
75
Assets/_Project/Scripts/Gameplay/RaceDefinition.cs
Normal file
75
Assets/_Project/Scripts/Gameplay/RaceDefinition.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Assets/_Project/Scripts/Gameplay/RaceDefinition.cs
|
||||
using UnityEngine;
|
||||
using TD.Core;
|
||||
using TD.Towers;
|
||||
|
||||
namespace TD.Gameplay
|
||||
{
|
||||
/// <summary>
|
||||
/// One asset per playable race. Holds the race's identity (the
|
||||
/// <see cref="RaceId"/> binding for networked selection), the visual + lore
|
||||
/// content shown in the lobby's race-selection UI, and stub fields for the
|
||||
/// Phase 1.8 gameplay payload (race-specific builder + tower roster).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Creating a new race.</b> Right-click in the project window →
|
||||
/// <c>Create → TD → Race Definition</c>. Fill in the inspector:
|
||||
/// <list type="bullet">
|
||||
/// <item><b>Id</b> — pick an unused <see cref="RaceId"/> value (Race1..Race16).</item>
|
||||
/// <item><b>Display Name</b> — what shows in the grid + detail header.</item>
|
||||
/// <item><b>Icon</b> — square sprite, ~256x256, drawn in the grid + detail.</item>
|
||||
/// <item><b>Builder Name / Description</b> — text in the detail panel.</item>
|
||||
/// <item><b>Lore Text</b> — longer description in the detail panel.</item>
|
||||
/// <item><b>Builder Prefab / Towers</b> — <i>stubs</i>, wired in Phase 1.8.</item>
|
||||
/// </list>
|
||||
/// Then drag the asset into the <c>RaceRegistry</c>'s <c>Definitions</c>
|
||||
/// array on the scene's <c>RaceRegistry</c> GameObject.</para>
|
||||
///
|
||||
/// <para><b>Why <see cref="Id"/> is serialized rather than inferred from the
|
||||
/// asset name.</b> Race selection is networked via a
|
||||
/// <see cref="UnityEngine.SerializeField"/>-backed enum on
|
||||
/// <c>PlayerMatchState</c>. The enum byte value is the wire identity; the
|
||||
/// asset is just the local lookup. Keeping the binding explicit on the
|
||||
/// asset prevents accidental drift if assets get renamed.</para>
|
||||
/// </remarks>
|
||||
[CreateAssetMenu(fileName = "RaceDefinition", menuName = "TD/Race Definition", order = 5)]
|
||||
public class RaceDefinition : ScriptableObject
|
||||
{
|
||||
[Header("Identity")]
|
||||
[Tooltip("Enum value used by PlayerMatchState.RaceSelection on the network. " +
|
||||
"Pick an unused RaceId (Race1..Race16). Each asset must use a unique value.")]
|
||||
public RaceId Id = RaceId.None;
|
||||
|
||||
[Tooltip("Race name shown in the lobby grid and detail panel header.")]
|
||||
public string DisplayName;
|
||||
|
||||
[Tooltip("Square icon shown in the grid cell and as the larger image in the detail panel. " +
|
||||
"~256x256 PNG works well; the UI scales to fit.")]
|
||||
public Sprite Icon;
|
||||
|
||||
[Header("Builder")]
|
||||
[Tooltip("Builder name shown in the detail panel (the builder is the in-match avatar " +
|
||||
"that gates tower placement by proximity).")]
|
||||
public string BuilderName;
|
||||
|
||||
[Tooltip("Short description of the builder shown beneath the name in the detail panel.")]
|
||||
[TextArea(2, 4)]
|
||||
public string BuilderDescription;
|
||||
|
||||
[Header("Lore")]
|
||||
[Tooltip("Race lore / background shown in the detail panel. Lorem ipsum is fine " +
|
||||
"for placeholder races; replace when actual writing is ready.")]
|
||||
[TextArea(5, 15)]
|
||||
public string LoreText;
|
||||
|
||||
[Header("Gameplay payload (Phase 1.8 — not wired yet)")]
|
||||
[Tooltip("STUB (Phase 1.8): race-specific builder prefab. Currently every race spawns " +
|
||||
"the default builder. When Phase 1.8 lands, PlayerBuilderSpawner will pick " +
|
||||
"the prefab based on the player's RaceSelection.")]
|
||||
public GameObject BuilderPrefab;
|
||||
|
||||
[Tooltip("STUB (Phase 1.8): tower roster available to this race. TowerRegistry will " +
|
||||
"filter to this list when the active player belongs to this race.")]
|
||||
public TowerDefinition[] Towers;
|
||||
}
|
||||
}
|
||||
2
Assets/_Project/Scripts/Gameplay/RaceDefinition.cs.meta
Normal file
2
Assets/_Project/Scripts/Gameplay/RaceDefinition.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 98b7d5d116870f94da912007f6aa5cbb
|
||||
152
Assets/_Project/Scripts/Gameplay/RaceRegistry.cs
Normal file
152
Assets/_Project/Scripts/Gameplay/RaceRegistry.cs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
// Assets/_Project/Scripts/Gameplay/RaceRegistry.cs
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using TD.Core;
|
||||
|
||||
namespace TD.Gameplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Persistent (DontDestroyOnLoad) singleton that holds every
|
||||
/// <see cref="RaceDefinition"/> available in the current build and lets
|
||||
/// any code look one up by <see cref="RaceId"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Inspector setup.</b> Place ONE <c>RaceRegistry</c> GameObject in
|
||||
/// the <b>MainMenu scene</b> only. Drag every <c>RaceDefinition</c> asset
|
||||
/// into the <c>Definitions</c> array. The registry marks itself
|
||||
/// <c>DontDestroyOnLoad</c> on Awake, so it survives the scene transitions
|
||||
/// MainMenu → Lobby → Match → back to Lobby and is available to all of
|
||||
/// them through <see cref="Instance"/>.</para>
|
||||
///
|
||||
/// <para><b>Why not also in Lobby/Match scenes?</b> Maintaining the
|
||||
/// <c>Definitions</c> array in multiple places is a designer trap — update
|
||||
/// one, forget the other, runtime mismatch. Single source of truth in
|
||||
/// MainMenu eliminates that class of bug. Duplicate instances in other
|
||||
/// scenes are detected in <see cref="Awake"/> and self-destruct, so an
|
||||
/// accidental copy doesn't break anything but does log a warning.</para>
|
||||
///
|
||||
/// <para><b>Editor-only standalone testing.</b> If you open the Lobby or
|
||||
/// Match scene directly from the editor (without going through MainMenu
|
||||
/// first), no RaceRegistry will exist and race-dependent code falls back
|
||||
/// gracefully (<see cref="Get"/> returns null; UI shows "Coming Soon" or
|
||||
/// the default builder is used). For standalone-scene testing, you can
|
||||
/// temporarily add a registry to whatever scene you're testing — but don't
|
||||
/// commit it as part of normal play flow.</para>
|
||||
///
|
||||
/// <para><b>Slot model.</b> The lobby grid shows 16 slots (one per
|
||||
/// <see cref="RaceId"/> value 1-16) regardless of how many are filled.
|
||||
/// <see cref="Get"/> returns null for unregistered slots, which the UI
|
||||
/// renders as a "Coming Soon" placeholder.</para>
|
||||
///
|
||||
/// <para><b>Plain MonoBehaviour.</b> Not a NetworkBehaviour — the registry
|
||||
/// is identical on every peer (same ScriptableObject assets), so nothing
|
||||
/// to sync. Network state tracks only the chosen <see cref="RaceId"/> on
|
||||
/// <c>PlayerMatchState</c>; the rest is local lookup.</para>
|
||||
/// </remarks>
|
||||
public class RaceRegistry : MonoBehaviour
|
||||
{
|
||||
// ----- Singleton -------------------------------------------------
|
||||
|
||||
public static RaceRegistry Instance { get; private set; }
|
||||
|
||||
// ----- Inspector --------------------------------------------------
|
||||
|
||||
[Tooltip("All RaceDefinition assets available in this build. Drag each asset " +
|
||||
"into the array. Duplicate Ids are rejected with a warning; null entries " +
|
||||
"are skipped.")]
|
||||
[SerializeField] private RaceDefinition[] definitions;
|
||||
|
||||
// ----- Internal lookup -------------------------------------------
|
||||
|
||||
private readonly Dictionary<RaceId, RaceDefinition> byId
|
||||
= new Dictionary<RaceId, RaceDefinition>();
|
||||
|
||||
// ----- Lifecycle --------------------------------------------------
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Persistent-singleton pattern: the FIRST instance to wake up wins
|
||||
// and survives scene loads. Subsequent instances (e.g. a stale
|
||||
// copy left over in the Lobby or Match scene) are self-destroyed,
|
||||
// not just ignored — we want the scene to "self-heal" if someone
|
||||
// accidentally drops a second copy in.
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"[RaceRegistry] Persistent instance already exists. " +
|
||||
$"Destroying duplicate in scene '{gameObject.scene.name}'. " +
|
||||
$"Keep RaceRegistry in the MainMenu scene only.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
BuildLookup();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
// ----- Public API -------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="RaceDefinition"/> for the given id, or null
|
||||
/// if no asset is registered for that id (e.g. "Coming Soon" slots).
|
||||
/// </summary>
|
||||
public RaceDefinition Get(RaceId id)
|
||||
{
|
||||
byId.TryGetValue(id, out var def);
|
||||
return def;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates the canonical 16 lobby slots (Race1..Race16). For each
|
||||
/// slot returns either the registered <see cref="RaceDefinition"/> or
|
||||
/// null. UI consumers use this to render a stable 16-cell grid where
|
||||
/// unfilled slots show a placeholder.
|
||||
/// </summary>
|
||||
public IEnumerable<(RaceId id, RaceDefinition def)> AllSlots()
|
||||
{
|
||||
for (int i = (int)RaceId.Race1; i <= (int)RaceId.Race16; i++)
|
||||
{
|
||||
var id = (RaceId)i;
|
||||
yield return (id, Get(id));
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Private ----------------------------------------------------
|
||||
|
||||
private void BuildLookup()
|
||||
{
|
||||
byId.Clear();
|
||||
if (definitions == null || definitions.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("[RaceRegistry] No RaceDefinition assets assigned. " +
|
||||
"Drag assets into the Definitions array.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var def in definitions)
|
||||
{
|
||||
if (def == null) continue;
|
||||
if (def.Id == RaceId.None)
|
||||
{
|
||||
Debug.LogWarning($"[RaceRegistry] '{def.name}' has Id=None — set it to " +
|
||||
"an unused Race1..Race16 value.");
|
||||
continue;
|
||||
}
|
||||
if (byId.ContainsKey(def.Id))
|
||||
{
|
||||
Debug.LogWarning($"[RaceRegistry] Duplicate Id '{def.Id}' detected on " +
|
||||
$"'{def.name}'. Earlier registration kept; rename one.");
|
||||
continue;
|
||||
}
|
||||
byId[def.Id] = def;
|
||||
}
|
||||
|
||||
Debug.Log($"[RaceRegistry] Registered {byId.Count} race(s).");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/_Project/Scripts/Gameplay/RaceRegistry.cs.meta
Normal file
2
Assets/_Project/Scripts/Gameplay/RaceRegistry.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50ccfdaa301b6a7439b4edfc750550aa
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// Assets/_Project/Scripts/UI/LobbyController.cs
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
|
@ -45,6 +46,22 @@ namespace TD.UI
|
|||
private Button leaveButton;
|
||||
private Label statusLabel;
|
||||
|
||||
// ----- Race selection overlay ------------------------------------
|
||||
|
||||
[Tooltip("Sibling RaceSelectionOverlay component that owns the race-pick UI. " +
|
||||
"Auto-located on the same GameObject if not assigned.")]
|
||||
[SerializeField] private RaceSelectionOverlay raceOverlay;
|
||||
|
||||
// Snapshot of player-list state from the last rebuild. RefreshPlayerList
|
||||
// skips rebuilding when this matches the current frame's signature —
|
||||
// critical because rebuilding every frame destroys the per-row buttons
|
||||
// mid-click, and UI Toolkit's Clickable manipulator needs the same
|
||||
// element instance to receive PointerDown AND PointerUp for the action
|
||||
// to fire. Pre-fix, clicks on Select Race / Ready / Unready were silently
|
||||
// lost because the button was destroyed between press and release.
|
||||
private string lastPlayerListSignature = string.Empty;
|
||||
private readonly StringBuilder signatureBuffer = new StringBuilder();
|
||||
|
||||
// ----- Lifecycle --------------------------------------------------
|
||||
|
||||
private void Start()
|
||||
|
|
@ -59,6 +76,13 @@ namespace TD.UI
|
|||
}
|
||||
|
||||
BuildUI(root);
|
||||
|
||||
// Initialize the race overlay with our root so it can install its
|
||||
// UI elements on top of the lobby. Hidden by default.
|
||||
if (raceOverlay == null) raceOverlay = GetComponent<RaceSelectionOverlay>();
|
||||
if (raceOverlay != null) raceOverlay.Initialize(root);
|
||||
else Debug.LogWarning("[LobbyController] No RaceSelectionOverlay component found — " +
|
||||
"race-picker button will be disabled. Add one to this GameObject.");
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
|
@ -131,16 +155,23 @@ namespace TD.UI
|
|||
|
||||
private void RefreshPlayerList()
|
||||
{
|
||||
// Sort by slot for stable ordering. AllPlayers is keyed by clientId
|
||||
// which may not be slot-ordered.
|
||||
var players = PlayerMatchState.AllPlayers.OrderBy(p => (int)p.Slot).ToList();
|
||||
|
||||
// Skip rebuild when the player-relevant state hasn't changed.
|
||||
// Without this guard the per-row buttons are destroyed every frame,
|
||||
// which loses clicks (see lastPlayerListSignature comment above).
|
||||
string signature = ComputePlayerListSignature(players);
|
||||
if (signature == lastPlayerListSignature) return;
|
||||
lastPlayerListSignature = signature;
|
||||
|
||||
playerListContainer.Clear();
|
||||
|
||||
ulong localId = NetworkManager.Singleton != null
|
||||
? NetworkManager.Singleton.LocalClientId
|
||||
: ulong.MaxValue;
|
||||
|
||||
// Sort by slot for stable ordering. AllPlayers is keyed by clientId
|
||||
// which may not be slot-ordered.
|
||||
var players = PlayerMatchState.AllPlayers.OrderBy(p => (int)p.Slot).ToList();
|
||||
|
||||
foreach (var pms in players)
|
||||
{
|
||||
bool isLocal = pms.OwnerClientId == localId;
|
||||
|
|
@ -151,6 +182,26 @@ namespace TD.UI
|
|||
playerListContainer.Add(new Label("(no players connected)") { style = { color = Color.gray } });
|
||||
}
|
||||
|
||||
// Compact signature of every field the player rows depend on. When this
|
||||
// changes we rebuild; when it's identical we leave the existing rows
|
||||
// (and their button event handlers) intact for click handling.
|
||||
private string ComputePlayerListSignature(System.Collections.Generic.List<PlayerMatchState> players)
|
||||
{
|
||||
signatureBuffer.Clear();
|
||||
foreach (var pms in players)
|
||||
{
|
||||
signatureBuffer.Append(pms.OwnerClientId);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append((int)pms.Slot);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append((int)pms.RaceSelection);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append(pms.IsReady ? '1' : '0');
|
||||
signatureBuffer.Append(';');
|
||||
}
|
||||
return signatureBuffer.ToString();
|
||||
}
|
||||
|
||||
private VisualElement BuildPlayerRow(PlayerMatchState pms, bool isLocal)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
|
|
@ -200,23 +251,17 @@ namespace TD.UI
|
|||
// Local-only controls.
|
||||
if (isLocal)
|
||||
{
|
||||
// PLACEHOLDER race picker — the RaceId enum only has None right
|
||||
// now (Phase 1.8 will fill it). For now the single button submits
|
||||
// RaceId.None, which keeps the ready-up gate effectively a no-op
|
||||
// (the server requires RaceSelection != None to allow ready). To
|
||||
// exercise the flow end-to-end before races exist, comment out
|
||||
// the gate in PlayerMatchState.SubmitReadyRpc.
|
||||
//
|
||||
// TODO (Phase 1.8): replace with a dropdown of RaceDefinition
|
||||
// assets discovered at runtime, each option calling
|
||||
// pms.SubmitRaceRpc(definition.Id).
|
||||
var pickRaceBtn = new Button(() => pms.SubmitRaceRpc(RaceId.None))
|
||||
// Race selection — opens the overlay that lets the player browse
|
||||
// the 4x4 race grid and pick one. The overlay handles submission
|
||||
// (PlayerMatchState.SubmitRaceRpc) directly, so we just open it.
|
||||
var pickRaceBtn = new Button(OpenRaceOverlay)
|
||||
{
|
||||
text = "Pick Race (stub)"
|
||||
text = "Select Race"
|
||||
};
|
||||
pickRaceBtn.style.minWidth = 130;
|
||||
pickRaceBtn.style.height = 28;
|
||||
pickRaceBtn.style.marginLeft = 12;
|
||||
pickRaceBtn.SetEnabled(raceOverlay != null);
|
||||
row.Add(pickRaceBtn);
|
||||
|
||||
var readyBtn = new Button(() => pms.SubmitReadyRpc(!pms.IsReady))
|
||||
|
|
@ -247,6 +292,16 @@ namespace TD.UI
|
|||
|
||||
// ----- Button handlers --------------------------------------------
|
||||
|
||||
private void OpenRaceOverlay()
|
||||
{
|
||||
if (raceOverlay == null)
|
||||
{
|
||||
Debug.LogWarning("[LobbyController] Race overlay not assigned.");
|
||||
return;
|
||||
}
|
||||
raceOverlay.Show();
|
||||
}
|
||||
|
||||
private void OnStartMatchClicked()
|
||||
{
|
||||
var svc = LobbyService.Instance;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
// Assets/_Project/Scripts/UI/MainMenuController.cs
|
||||
using System.Collections;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using TD.Core;
|
||||
using TD.Gameplay;
|
||||
using TD.Net;
|
||||
|
||||
namespace TD.UI
|
||||
|
|
@ -44,6 +47,7 @@ namespace TD.UI
|
|||
private Button hostButton;
|
||||
private Button joinButton;
|
||||
private Button quitButton;
|
||||
private Button quickStartButton;
|
||||
private VisualElement joinPanel;
|
||||
private TextField joinAddressField;
|
||||
private TextField joinPortField;
|
||||
|
|
@ -96,12 +100,14 @@ namespace TD.UI
|
|||
buttonColumn.style.alignItems = Align.Center;
|
||||
root.Add(buttonColumn);
|
||||
|
||||
hostButton = MakeMenuButton("Host Game", OnHostClicked);
|
||||
joinButton = MakeMenuButton("Join Game", OnJoinClicked);
|
||||
quitButton = MakeMenuButton("Quit", OnQuitClicked);
|
||||
hostButton = MakeMenuButton("Host Game", OnHostClicked);
|
||||
joinButton = MakeMenuButton("Join Game", OnJoinClicked);
|
||||
quitButton = MakeMenuButton("Quit", OnQuitClicked);
|
||||
quickStartButton = MakeMenuButton("Quick Start", OnQuickStartClicked);
|
||||
buttonColumn.Add(hostButton);
|
||||
buttonColumn.Add(joinButton);
|
||||
buttonColumn.Add(quitButton);
|
||||
buttonColumn.Add(quickStartButton);
|
||||
|
||||
// Join sub-panel — hidden until Join is clicked. Holds the IP+port
|
||||
// fields and the Connect / Cancel buttons.
|
||||
|
|
@ -120,14 +126,14 @@ namespace TD.UI
|
|||
joinAddressField = new TextField("Host address");
|
||||
joinAddressField.value = defaultJoinAddress;
|
||||
joinAddressField.style.width = 280;
|
||||
joinAddressField.style.color = Color.white;
|
||||
StyleJoinFieldText(joinAddressField);
|
||||
joinPanel.Add(joinAddressField);
|
||||
|
||||
joinPortField = new TextField("Port");
|
||||
joinPortField.value = defaultPort.ToString();
|
||||
joinPortField.style.width = 280;
|
||||
joinPortField.style.marginTop = 8;
|
||||
joinPortField.style.color = Color.white;
|
||||
StyleJoinFieldText(joinPortField);
|
||||
joinPanel.Add(joinPortField);
|
||||
|
||||
var joinButtons = new VisualElement();
|
||||
|
|
@ -154,6 +160,19 @@ namespace TD.UI
|
|||
root.Add(statusLabel);
|
||||
}
|
||||
|
||||
// TextField's visible text color lives on the inner "unity-text-input"
|
||||
// element, not on the TextField root. Setting it on the root alone
|
||||
// leaves the inner element's inherited white color in place — which is
|
||||
// invisible against the default white input background. Same gotcha as
|
||||
// the chat input's dark-styling path in HUDController.
|
||||
private static void StyleJoinFieldText(TextField field)
|
||||
{
|
||||
field.style.color = Color.black;
|
||||
var inner = field.Q("unity-text-input");
|
||||
if (inner != null)
|
||||
inner.style.color = Color.black;
|
||||
}
|
||||
|
||||
private static Button MakeMenuButton(string text, System.Action onClick)
|
||||
{
|
||||
var btn = new Button(() => onClick?.Invoke()) { text = text };
|
||||
|
|
@ -229,5 +248,53 @@ namespace TD.UI
|
|||
Application.Quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Dev / testing shortcut: skips the lobby entirely. Hosts a single-player
|
||||
// session, auto-selects Race1 for the local player, and loads the Match
|
||||
// scene directly. Useful for iterating on gameplay without clicking
|
||||
// through Host → Lobby → Pick Race → Ready → Start every time.
|
||||
//
|
||||
// To remove for a shipping build: delete the button-creation lines in
|
||||
// BuildUI plus this method and its coroutine. No other consumers.
|
||||
private void OnQuickStartClicked()
|
||||
{
|
||||
statusLabel.text = "Quick starting…";
|
||||
StartCoroutine(QuickStartCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator QuickStartCoroutine()
|
||||
{
|
||||
if (!NetworkBootstrap.StartHost(defaultPort))
|
||||
{
|
||||
statusLabel.text = "Failed to start host. Check the console.";
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Wait for the local player's PlayerMatchState to spawn. Empirically
|
||||
// this happens synchronously inside StartHost, but waiting one frame
|
||||
// is cheap insurance against future NGO changes to spawn timing.
|
||||
// Cap the wait at 60 frames so a real failure doesn't silently hang.
|
||||
int safetyFrames = 0;
|
||||
while (PlayerMatchState.Local == null && safetyFrames++ < 60)
|
||||
yield return null;
|
||||
|
||||
var pms = PlayerMatchState.Local;
|
||||
if (pms == null)
|
||||
{
|
||||
Debug.LogError("[MainMenuController] Quick Start: PlayerMatchState.Local " +
|
||||
"didn't appear within 60 frames after StartHost. Aborting.");
|
||||
statusLabel.text = "Quick Start failed: player did not spawn.";
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Server-only setters — host is server + client, so these are valid
|
||||
// directly. Skipping the RPC round-trip avoids any frame-of-latency
|
||||
// before LoadSceneAsHost reads RaceSelection on the way into Match.
|
||||
pms.SetRaceSelection(RaceId.Race1);
|
||||
pms.SetReady(true);
|
||||
|
||||
// Skip Lobby — drop straight into the Match scene.
|
||||
NetworkBootstrap.LoadSceneAsHost(SceneNames.Match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
510
Assets/_Project/Scripts/UI/RaceSelectionOverlay.cs
Normal file
510
Assets/_Project/Scripts/UI/RaceSelectionOverlay.cs
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
// Assets/_Project/Scripts/UI/RaceSelectionOverlay.cs
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UIElements;
|
||||
using TD.Core;
|
||||
using TD.Gameplay;
|
||||
|
||||
namespace TD.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Lobby-scene overlay that lets the local player pick their race.
|
||||
/// Shows a 4x4 grid of race icons + a detail panel that pops up to the
|
||||
/// right when an icon is clicked. Reads other players' picks from
|
||||
/// <see cref="PlayerMatchState.AllPlayers"/> each frame to grey out
|
||||
/// races that have already been taken.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Wiring.</b> Attach this component to the same GameObject as
|
||||
/// <c>LobbyController</c>. The controller calls <see cref="Initialize"/>
|
||||
/// once with its root <see cref="VisualElement"/>, and <see cref="Show"/>
|
||||
/// when the "Select Race" button is clicked.</para>
|
||||
///
|
||||
/// <para><b>Why same UIDocument.</b> The overlay's elements are inserted as
|
||||
/// absolute-positioned children of the lobby root — that way one UIDocument
|
||||
/// + one PanelSettings serves both the lobby and the overlay, and z-order is
|
||||
/// natural (overlay last → on top).</para>
|
||||
///
|
||||
/// <para><b>Visual states per grid cell.</b>
|
||||
/// <list type="bullet">
|
||||
/// <item><b>Free</b> — race not picked by anyone. Icon in color, clickable.</item>
|
||||
/// <item><b>Taken by another player</b> — greyed out, clickable for viewing
|
||||
/// the detail panel but confirm button is disabled. Player slot
|
||||
/// number overlaid in color so others know who claimed it.</item>
|
||||
/// <item><b>Taken by local player</b> — bordered highlight, clickable.
|
||||
/// Confirm button enabled (no-op if already selected, but lets the
|
||||
/// player re-confirm). Local slot number overlaid.</item>
|
||||
/// <item><b>Unregistered slot</b> — placeholder "Coming Soon", not
|
||||
/// clickable. No RaceDefinition asset exists for this RaceId yet.</item>
|
||||
/// </list></para>
|
||||
/// </remarks>
|
||||
public class RaceSelectionOverlay : MonoBehaviour
|
||||
{
|
||||
// ----- UI state ---------------------------------------------------
|
||||
|
||||
private VisualElement overlayRoot;
|
||||
private VisualElement gridContainer;
|
||||
private VisualElement detailPanel;
|
||||
private Label detailHeader;
|
||||
private VisualElement detailIcon;
|
||||
private Label detailBuilderName;
|
||||
private Label detailBuilderDesc;
|
||||
private Label detailLore;
|
||||
private Button detailConfirmButton;
|
||||
private Label detailUnavailableNote;
|
||||
|
||||
// Currently-viewed race in the detail panel. RaceId.None means no
|
||||
// detail is open and the panel shows a hint instead.
|
||||
private RaceId viewedRace = RaceId.None;
|
||||
|
||||
// Re-built each Update from PlayerMatchState data. Maps each taken
|
||||
// RaceId to the slot of the player that picked it.
|
||||
private readonly Dictionary<RaceId, PlayerSlot> picksByRace = new Dictionary<RaceId, PlayerSlot>();
|
||||
|
||||
// Snapshot of pick-state from the last grid rebuild. RefreshGrid skips
|
||||
// rebuilding when this matches the current frame — same click-eating
|
||||
// problem as the lobby's player list. Cleared on Show() so the grid
|
||||
// always rebuilds when the overlay reopens.
|
||||
private string lastGridSignature = string.Empty;
|
||||
private readonly StringBuilder signatureBuffer = new StringBuilder();
|
||||
|
||||
// ----- Public API -------------------------------------------------
|
||||
|
||||
public bool IsVisible =>
|
||||
overlayRoot != null && overlayRoot.style.display.value == DisplayStyle.Flex;
|
||||
|
||||
/// <summary>
|
||||
/// Called by LobbyController once to attach the overlay's UI to the
|
||||
/// lobby's UIDocument root. Builds the elements (hidden) and is then
|
||||
/// reused across Show / Hide cycles.
|
||||
/// </summary>
|
||||
public void Initialize(VisualElement lobbyRoot)
|
||||
{
|
||||
if (lobbyRoot == null)
|
||||
{
|
||||
Debug.LogError("[RaceSelectionOverlay] Initialize received null lobby root.");
|
||||
return;
|
||||
}
|
||||
BuildUI(lobbyRoot);
|
||||
Hide();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reveals the overlay, refreshes the grid against current picks, and
|
||||
/// auto-opens the detail panel for the local player's currently-picked
|
||||
/// race (if any) so they can change it directly.
|
||||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
if (overlayRoot == null) return;
|
||||
overlayRoot.style.display = DisplayStyle.Flex;
|
||||
|
||||
// If the local player already has a race, seed the detail panel with
|
||||
// it. Otherwise leave the panel showing the placeholder hint.
|
||||
var local = PlayerMatchState.Local;
|
||||
viewedRace = (local != null) ? local.RaceSelection : RaceId.None;
|
||||
|
||||
// Force a fresh grid build on each open so visuals reflect any
|
||||
// picks that happened while the overlay was closed.
|
||||
lastGridSignature = string.Empty;
|
||||
RefreshGrid();
|
||||
RefreshDetail();
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
if (overlayRoot == null) return;
|
||||
overlayRoot.style.display = DisplayStyle.None;
|
||||
viewedRace = RaceId.None;
|
||||
}
|
||||
|
||||
// ----- Lifecycle --------------------------------------------------
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsVisible) return;
|
||||
|
||||
// Esc closes the overlay. Read directly via Input System — we want
|
||||
// this even when nothing is focused.
|
||||
var kb = Keyboard.current;
|
||||
if (kb != null && kb.escapeKey.wasPressedThisFrame)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh against current picks every frame — cheap (max 16 cells,
|
||||
// max 9 players) and means other-player changes show up immediately.
|
||||
RefreshGrid();
|
||||
RefreshDetail();
|
||||
}
|
||||
|
||||
// ----- UI construction --------------------------------------------
|
||||
|
||||
private void BuildUI(VisualElement lobbyRoot)
|
||||
{
|
||||
// Root overlay — fills the screen with a dark backdrop.
|
||||
overlayRoot = new VisualElement();
|
||||
overlayRoot.pickingMode = PickingMode.Position;
|
||||
overlayRoot.style.position = Position.Absolute;
|
||||
overlayRoot.style.left = 0;
|
||||
overlayRoot.style.right = 0;
|
||||
overlayRoot.style.top = 0;
|
||||
overlayRoot.style.bottom = 0;
|
||||
overlayRoot.style.backgroundColor = new Color(0f, 0f, 0f, 0.85f);
|
||||
overlayRoot.style.alignItems = Align.Center;
|
||||
overlayRoot.style.justifyContent = Justify.Center;
|
||||
lobbyRoot.Add(overlayRoot);
|
||||
|
||||
// Title + close button in a top bar.
|
||||
var topBar = new VisualElement();
|
||||
topBar.style.position = Position.Absolute;
|
||||
topBar.style.top = 20;
|
||||
topBar.style.left = 40;
|
||||
topBar.style.right = 40;
|
||||
topBar.style.flexDirection = FlexDirection.Row;
|
||||
topBar.style.justifyContent = Justify.SpaceBetween;
|
||||
topBar.style.alignItems = Align.Center;
|
||||
overlayRoot.Add(topBar);
|
||||
|
||||
var title = new Label("Select Race");
|
||||
title.style.fontSize = 32;
|
||||
title.style.color = Color.white;
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
topBar.Add(title);
|
||||
|
||||
var closeBtn = new Button(Hide) { text = "X" };
|
||||
closeBtn.style.width = 44;
|
||||
closeBtn.style.height = 44;
|
||||
closeBtn.style.fontSize = 20;
|
||||
topBar.Add(closeBtn);
|
||||
|
||||
// Main content row: grid on the left, detail panel on the right.
|
||||
var content = new VisualElement();
|
||||
content.style.flexDirection = FlexDirection.Row;
|
||||
content.style.alignItems = Align.Stretch;
|
||||
content.style.marginTop = 40;
|
||||
overlayRoot.Add(content);
|
||||
|
||||
// Grid container — 4x4 of cells. Uses wrap to flow rows.
|
||||
gridContainer = new VisualElement();
|
||||
gridContainer.style.flexDirection = FlexDirection.Row;
|
||||
gridContainer.style.flexWrap = Wrap.Wrap;
|
||||
gridContainer.style.width = 560; // 4 cells * (120 + 8 margin) = 512 + slack
|
||||
gridContainer.style.marginRight = 32;
|
||||
content.Add(gridContainer);
|
||||
|
||||
// Detail panel — built once, populated in RefreshDetail.
|
||||
detailPanel = new VisualElement();
|
||||
detailPanel.style.width = 380;
|
||||
detailPanel.style.minHeight = 520;
|
||||
detailPanel.style.paddingTop = 20;
|
||||
detailPanel.style.paddingBottom = 20;
|
||||
detailPanel.style.paddingLeft = 20;
|
||||
detailPanel.style.paddingRight = 20;
|
||||
detailPanel.style.backgroundColor = new Color(0.10f, 0.10f, 0.14f, 0.95f);
|
||||
detailPanel.style.borderTopWidth = detailPanel.style.borderBottomWidth =
|
||||
detailPanel.style.borderLeftWidth = detailPanel.style.borderRightWidth = 2;
|
||||
var detailBorder = new Color(0.4f, 0.4f, 0.5f);
|
||||
detailPanel.style.borderTopColor = detailPanel.style.borderBottomColor =
|
||||
detailPanel.style.borderLeftColor = detailPanel.style.borderRightColor = detailBorder;
|
||||
detailPanel.style.flexDirection = FlexDirection.Column;
|
||||
content.Add(detailPanel);
|
||||
|
||||
detailHeader = new Label("Pick a race");
|
||||
detailHeader.style.fontSize = 22;
|
||||
detailHeader.style.color = Color.white;
|
||||
detailHeader.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
detailHeader.style.marginBottom = 12;
|
||||
detailPanel.Add(detailHeader);
|
||||
|
||||
detailIcon = new VisualElement();
|
||||
detailIcon.style.width = 200;
|
||||
detailIcon.style.height = 200;
|
||||
detailIcon.style.alignSelf = Align.Center;
|
||||
detailIcon.style.backgroundColor = new Color(0.18f, 0.18f, 0.22f);
|
||||
detailIcon.style.marginBottom = 12;
|
||||
detailPanel.Add(detailIcon);
|
||||
|
||||
detailBuilderName = new Label();
|
||||
detailBuilderName.style.fontSize = 16;
|
||||
detailBuilderName.style.color = new Color(0.95f, 0.85f, 0.5f);
|
||||
detailBuilderName.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
detailPanel.Add(detailBuilderName);
|
||||
|
||||
detailBuilderDesc = new Label();
|
||||
detailBuilderDesc.style.fontSize = 13;
|
||||
detailBuilderDesc.style.color = new Color(0.85f, 0.85f, 0.85f);
|
||||
detailBuilderDesc.style.marginBottom = 10;
|
||||
detailBuilderDesc.style.whiteSpace = WhiteSpace.Normal;
|
||||
detailPanel.Add(detailBuilderDesc);
|
||||
|
||||
detailLore = new Label();
|
||||
detailLore.style.fontSize = 12;
|
||||
detailLore.style.color = new Color(0.75f, 0.75f, 0.75f);
|
||||
detailLore.style.marginBottom = 16;
|
||||
detailLore.style.whiteSpace = WhiteSpace.Normal;
|
||||
detailLore.style.flexGrow = 1;
|
||||
detailPanel.Add(detailLore);
|
||||
|
||||
// Notice shown when the viewed race is taken by another player.
|
||||
detailUnavailableNote = new Label();
|
||||
detailUnavailableNote.style.color = new Color(1f, 0.5f, 0.3f);
|
||||
detailUnavailableNote.style.fontSize = 12;
|
||||
detailUnavailableNote.style.marginBottom = 8;
|
||||
detailUnavailableNote.style.whiteSpace = WhiteSpace.Normal;
|
||||
detailPanel.Add(detailUnavailableNote);
|
||||
|
||||
detailConfirmButton = new Button(OnConfirmClicked) { text = "Confirm Selection" };
|
||||
detailConfirmButton.style.height = 40;
|
||||
detailConfirmButton.style.fontSize = 16;
|
||||
detailPanel.Add(detailConfirmButton);
|
||||
}
|
||||
|
||||
// ----- Per-frame refresh ------------------------------------------
|
||||
|
||||
// Walks every PlayerMatchState and records which RaceId each non-None
|
||||
// pick belongs to. Used by both the grid (grey out taken) and the
|
||||
// detail panel (gate the confirm button).
|
||||
private void RebuildPickIndex()
|
||||
{
|
||||
picksByRace.Clear();
|
||||
foreach (var pms in PlayerMatchState.AllPlayers)
|
||||
{
|
||||
if (pms.RaceSelection == RaceId.None) continue;
|
||||
picksByRace[pms.RaceSelection] = pms.Slot;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshGrid()
|
||||
{
|
||||
if (gridContainer == null) return;
|
||||
if (RaceRegistry.Instance == null) return;
|
||||
|
||||
RebuildPickIndex();
|
||||
|
||||
// Skip rebuild when nothing changed — keeps the cell event handlers
|
||||
// alive across frames so clicks aren't eaten mid-press.
|
||||
PlayerSlot localSlot = PlayerMatchState.Local != null
|
||||
? PlayerMatchState.Local.Slot
|
||||
: PlayerSlot.None;
|
||||
string signature = ComputeGridSignature(localSlot);
|
||||
if (signature == lastGridSignature) return;
|
||||
lastGridSignature = signature;
|
||||
|
||||
gridContainer.Clear();
|
||||
foreach (var (id, def) in RaceRegistry.Instance.AllSlots())
|
||||
{
|
||||
gridContainer.Add(BuildCell(id, def, localSlot));
|
||||
}
|
||||
}
|
||||
|
||||
// Compact signature of every input the grid cells depend on: the
|
||||
// current pick map (RaceId → PlayerSlot) plus the local player's slot
|
||||
// (which affects "is this mine" highlighting on each cell).
|
||||
private string ComputeGridSignature(PlayerSlot localSlot)
|
||||
{
|
||||
signatureBuffer.Clear();
|
||||
signatureBuffer.Append((int)localSlot);
|
||||
signatureBuffer.Append('|');
|
||||
foreach (var kvp in picksByRace.OrderBy(p => (int)p.Key))
|
||||
{
|
||||
signatureBuffer.Append((int)kvp.Key);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append((int)kvp.Value);
|
||||
signatureBuffer.Append(';');
|
||||
}
|
||||
return signatureBuffer.ToString();
|
||||
}
|
||||
|
||||
private VisualElement BuildCell(RaceId id, RaceDefinition def, PlayerSlot localSlot)
|
||||
{
|
||||
// Outer cell with fixed size for consistent grid layout.
|
||||
var cell = new VisualElement();
|
||||
cell.style.width = 120;
|
||||
cell.style.height = 120;
|
||||
cell.style.marginTop = cell.style.marginBottom =
|
||||
cell.style.marginLeft = cell.style.marginRight = 4;
|
||||
cell.style.borderTopWidth = cell.style.borderBottomWidth =
|
||||
cell.style.borderLeftWidth = cell.style.borderRightWidth = 2;
|
||||
|
||||
bool isLocked = (def == null);
|
||||
bool isPicked = picksByRace.TryGetValue(id, out var picker);
|
||||
bool isMine = isPicked && picker == localSlot;
|
||||
bool isTakenByOther = isPicked && !isMine;
|
||||
|
||||
// Border color signals state at a glance.
|
||||
Color borderColor = isMine
|
||||
? new Color(0.3f, 0.85f, 0.3f) // green for "mine"
|
||||
: isTakenByOther
|
||||
? new Color(0.6f, 0.2f, 0.2f) // red-ish for taken
|
||||
: isLocked
|
||||
? new Color(0.3f, 0.3f, 0.3f) // dim for placeholder
|
||||
: new Color(0.5f, 0.5f, 0.6f); // neutral for free
|
||||
cell.style.borderTopColor = cell.style.borderBottomColor =
|
||||
cell.style.borderLeftColor = cell.style.borderRightColor = borderColor;
|
||||
cell.style.backgroundColor = new Color(0.10f, 0.10f, 0.14f);
|
||||
|
||||
// Icon area (or placeholder text for locked slots).
|
||||
var iconHolder = new VisualElement();
|
||||
iconHolder.pickingMode = PickingMode.Ignore;
|
||||
iconHolder.style.flexGrow = 1;
|
||||
iconHolder.style.alignItems = Align.Center;
|
||||
iconHolder.style.justifyContent = Justify.Center;
|
||||
iconHolder.style.unityBackgroundImageTintColor = isTakenByOther
|
||||
? new Color(0.4f, 0.4f, 0.4f) // greyed out
|
||||
: Color.white;
|
||||
if (def != null && def.Icon != null)
|
||||
iconHolder.style.backgroundImage = new StyleBackground(def.Icon);
|
||||
else if (isLocked)
|
||||
{
|
||||
var placeholder = new Label("?");
|
||||
placeholder.style.fontSize = 36;
|
||||
placeholder.style.color = new Color(0.4f, 0.4f, 0.4f);
|
||||
iconHolder.Add(placeholder);
|
||||
}
|
||||
cell.Add(iconHolder);
|
||||
|
||||
// Race name strip at the bottom — visible even when icon is missing.
|
||||
var nameLabel = new Label(def != null ? def.DisplayName : "Coming Soon");
|
||||
nameLabel.pickingMode = PickingMode.Ignore;
|
||||
nameLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
nameLabel.style.fontSize = 11;
|
||||
nameLabel.style.color = isLocked ? new Color(0.45f, 0.45f, 0.45f) : Color.white;
|
||||
nameLabel.style.paddingTop = 2;
|
||||
nameLabel.style.paddingBottom = 2;
|
||||
nameLabel.style.backgroundColor = new Color(0f, 0f, 0f, 0.55f);
|
||||
cell.Add(nameLabel);
|
||||
|
||||
// Picker badge — top-right corner, only when taken. Shows the slot
|
||||
// number of whoever picked it.
|
||||
if (isPicked)
|
||||
{
|
||||
var badge = new Label($"P{(int)picker}");
|
||||
badge.pickingMode = PickingMode.Ignore;
|
||||
badge.style.position = Position.Absolute;
|
||||
badge.style.top = 4;
|
||||
badge.style.right = 4;
|
||||
badge.style.fontSize = 16;
|
||||
badge.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
badge.style.color = isMine
|
||||
? new Color(0.3f, 0.85f, 0.3f)
|
||||
: new Color(1f, 0.7f, 0.2f);
|
||||
badge.style.paddingLeft = 4;
|
||||
badge.style.paddingRight = 4;
|
||||
badge.style.backgroundColor = new Color(0f, 0f, 0f, 0.7f);
|
||||
cell.Add(badge);
|
||||
}
|
||||
|
||||
// Click handler — only enabled for non-locked cells. Locked cells
|
||||
// still get the visual treatment but no click response.
|
||||
if (!isLocked)
|
||||
{
|
||||
cell.RegisterCallback<ClickEvent>(_ => OnCellClicked(id));
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
// ----- Detail panel -----------------------------------------------
|
||||
|
||||
private void OnCellClicked(RaceId id)
|
||||
{
|
||||
viewedRace = id;
|
||||
RefreshDetail();
|
||||
}
|
||||
|
||||
private void RefreshDetail()
|
||||
{
|
||||
if (detailPanel == null) return;
|
||||
var registry = RaceRegistry.Instance;
|
||||
if (registry == null) return;
|
||||
|
||||
var def = registry.Get(viewedRace);
|
||||
|
||||
if (def == null)
|
||||
{
|
||||
// Empty / locked / no selection — show placeholder hint.
|
||||
detailHeader.text = "Pick a race";
|
||||
detailBuilderName.text = string.Empty;
|
||||
detailBuilderDesc.text = string.Empty;
|
||||
detailLore.text = "Click a race icon to see details.";
|
||||
detailIcon.style.backgroundImage = null;
|
||||
detailUnavailableNote.text = string.Empty;
|
||||
detailConfirmButton.SetEnabled(false);
|
||||
detailConfirmButton.text = "Confirm Selection";
|
||||
return;
|
||||
}
|
||||
|
||||
detailHeader.text = def.DisplayName;
|
||||
detailBuilderName.text = string.IsNullOrEmpty(def.BuilderName)
|
||||
? "Builder: (unnamed)"
|
||||
: $"Builder: {def.BuilderName}";
|
||||
detailBuilderDesc.text = def.BuilderDescription ?? string.Empty;
|
||||
detailLore.text = def.LoreText ?? string.Empty;
|
||||
detailIcon.style.backgroundImage = def.Icon != null
|
||||
? new StyleBackground(def.Icon)
|
||||
: null;
|
||||
|
||||
// Is this race available to the local player?
|
||||
bool isPicked = picksByRace.TryGetValue(viewedRace, out var picker);
|
||||
PlayerSlot localSlot = PlayerMatchState.Local != null
|
||||
? PlayerMatchState.Local.Slot
|
||||
: PlayerSlot.None;
|
||||
bool takenByOther = isPicked && picker != localSlot;
|
||||
bool takenByMe = isPicked && picker == localSlot;
|
||||
|
||||
if (takenByOther)
|
||||
{
|
||||
detailUnavailableNote.text = $"Already selected by P{(int)picker}.";
|
||||
detailConfirmButton.SetEnabled(false);
|
||||
detailConfirmButton.text = "Unavailable";
|
||||
}
|
||||
else if (takenByMe)
|
||||
{
|
||||
detailUnavailableNote.text = "Your current selection.";
|
||||
detailConfirmButton.SetEnabled(true);
|
||||
detailConfirmButton.text = "Confirm Selection";
|
||||
}
|
||||
else
|
||||
{
|
||||
detailUnavailableNote.text = string.Empty;
|
||||
detailConfirmButton.SetEnabled(true);
|
||||
detailConfirmButton.text = "Confirm Selection";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConfirmClicked()
|
||||
{
|
||||
var local = PlayerMatchState.Local;
|
||||
if (local == null)
|
||||
{
|
||||
Debug.LogWarning("[RaceSelectionOverlay] No local PlayerMatchState — cannot submit race.");
|
||||
return;
|
||||
}
|
||||
if (viewedRace == RaceId.None) return;
|
||||
|
||||
// Re-check exclusivity right before submitting — between detail open
|
||||
// and confirm click, another player may have grabbed it.
|
||||
if (picksByRace.TryGetValue(viewedRace, out var picker) && picker != local.Slot)
|
||||
{
|
||||
Debug.Log($"[RaceSelectionOverlay] Race already taken by P{(int)picker}. " +
|
||||
"Refreshing detail.");
|
||||
RefreshDetail();
|
||||
return;
|
||||
}
|
||||
|
||||
local.SubmitRaceRpc(viewedRace);
|
||||
|
||||
// Per the spec: confirming clears the detail panel but keeps the
|
||||
// overlay open. The grid will update to show the new picked state
|
||||
// on the next Update tick.
|
||||
viewedRace = RaceId.None;
|
||||
RefreshDetail();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/_Project/Scripts/UI/RaceSelectionOverlay.cs.meta
Normal file
2
Assets/_Project/Scripts/UI/RaceSelectionOverlay.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ab3ad11b37d4c54d9f310f5356d31ac
|
||||
|
|
@ -5,6 +5,12 @@ EditorBuildSettings:
|
|||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Scenes:
|
||||
- enabled: 1
|
||||
path: Assets/_Project/Scenes/UI/MainMenu.unity
|
||||
guid: 1f4cb4a6391f5e94285960890282b70e
|
||||
- enabled: 1
|
||||
path: Assets/_Project/Scenes/UI/Lobby.unity
|
||||
guid: 3a3639dca674a8049a570cb848bb69d2
|
||||
- enabled: 1
|
||||
path: Assets/_Project/Scenes/Levels/Main.unity
|
||||
guid: 99c9720ab356a0642a771bea13969a05
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &1
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 53
|
||||
m_ObjectHideFlags: 61
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
|
|
|
|||
|
|
@ -75,20 +75,24 @@ The most architecturally significant pending system. This blocks the Phase 2 tow
|
|||
- 4-connected, no diagonals (matches existing maze validation)
|
||||
- Re-pathing on tower placement/removal events. Fire on: `TowerInstance` spawn/despawn, construction-start, construction-finish, shelved-tower despawn.
|
||||
|
||||
### 1.7 Lobby & Connection (CURRENT)
|
||||
### 1.7 Lobby & Connection (COMPLETE — 2026-05-17)
|
||||
|
||||
Replaces the current bare "Start Host" button with a proper main menu → lobby → match flow.
|
||||
Replaced the bare "Start Host" button with a proper main menu → lobby → match flow.
|
||||
Lobby is its own scene; players gather, pick races, ready up, and the host starts the match.
|
||||
|
||||
**v1 scope (Direct IP, Option A host-leaves-closes-lobby):**
|
||||
**v1 shipped (Direct IP, Option A host-leaves-closes-lobby):**
|
||||
|
||||
- `MainMenu` scene — Host / Join (with IP+port field) / Quit
|
||||
- `Lobby` scene — player list, per-player race picker, ready toggle, host-only Start button, Leave button
|
||||
- `MainMenu` scene — Host / Join (with IP+port field) / Quit / **Quick Start** (dev shortcut)
|
||||
- `Lobby` scene — player list, per-player **Select Race** button opening the race-selection overlay, ready toggle, host-only Start button, Leave button
|
||||
- `LobbyService` `NetworkBehaviour` scene singleton — Start Match RPC, Return to Lobby RPC
|
||||
- `NetworkBootstrap` static — wraps `UnityTransport` host/join/disconnect. Designed for the Steam swap-out below: same call sites, different transport.
|
||||
- `PlayerMatchState` extended with `IsReady` NetworkVariable and `SetReadyRpc` / `SetRaceRpc` for client → server submissions
|
||||
- `SessionFlow` DontDestroyOnLoad singleton — routes disconnected peers back to MainMenu on host loss
|
||||
- `PlayerMatchState` extended with `IsReady` NetworkVariable and `SubmitRaceRpc` / `SubmitReadyRpc` for client → server submissions
|
||||
- `PlayerBuilderSpawner` refactored to spawn on `NetworkSceneManager.OnLoadEventCompleted` for the Match scene, not on initial player spawn. Pulls race-specific `BuilderPrefab` from `RaceRegistry` when available, falls back to the inspector default. Re-spawns cleanly across Match → Lobby → Match cycles.
|
||||
- Match-end overlay extended: **Retry** (back to lobby) AND **Return to Main Menu** (this player only)
|
||||
- Host leaves → server raises a "lobby closed" RPC; all clients shutdown + return to main menu. Same applies if the host clicks "Return to Main Menu" after a match — session ends for everyone, each player returns independently to their own main menu.
|
||||
- Host leaves → all clients return to main menu via `SessionFlow.OnClientDisconnectCallback` (Option A). Same applies if the host clicks "Return to Main Menu" after a match.
|
||||
- **Race data + selection UI** delivered as part of 1.7 (originally scoped for 1.8): `RaceId` enum expanded to 16 slots, `RaceDefinition` ScriptableObject (Id, DisplayName, Icon, BuilderName, BuilderDescription, LoreText, BuilderPrefab, Towers stub), `RaceRegistry` (DontDestroyOnLoad singleton in MainMenu — single source of truth across all scenes), `RaceSelectionOverlay` (4×4 grid + detail panel, taken-race greying with picker badge, Esc/X close, server-side exclusivity enforced via `SubmitRaceRpc`).
|
||||
- **Quick Start dev button** in MainMenu — bypasses lobby: hosts → waits for `PlayerMatchState.Local` → sets Race1 + ready → loads Match scene directly. Coroutine in `MainMenuController.QuickStartCoroutine`.
|
||||
|
||||
### 1.7-Future Steam Lobby Migration (Option C — DEFERRED)
|
||||
|
||||
|
|
@ -119,16 +123,29 @@ Decision deferred per existing context document; Builder code is already terrain
|
|||
|
||||
Options previously discussed: Unity Terrain, mesh-based (Blender), ProBuilder, per-tile heights via volumes.
|
||||
|
||||
### 1.8 Race system (Path E)
|
||||
### 1.8 Race system (Path E) — PARTIALLY COMPLETE
|
||||
|
||||
The largest content-shaped piece of Phase 1. Required before Phase 2 because the prototype tower (Space Marine) is conceptually a unit of the Adeptus Astartes race, and the data model needs to reflect that.
|
||||
Data model and lobby selection UI shipped as part of 1.7 (2026-05-17). Remaining work is gameplay-side integration of the race payload during a match.
|
||||
|
||||
- `RaceDefinition` `ScriptableObject` — race content bundle (tower roster, race-specific units, builder variants, starting bonuses)
|
||||
- Race-pick UI (in-match overlay) — grid of race cards, timer-based auto-lock. First concrete `MatchState` consumer.
|
||||
- Multi-builder race support — revisit `PlayerBuilderSpawner` to spawn N builders
|
||||
- `TowerRegistry` filtering by active match's race rosters
|
||||
- Auto-discovery of `RaceDefinition` assets (mirror the `Resources.LoadAll` pattern from `TowerRegistry`)
|
||||
**Already done (delivered with 1.7):**
|
||||
|
||||
- `RaceDefinition` ScriptableObject with Identity / Builder / Lore / Gameplay-payload sections
|
||||
- `RaceRegistry` DontDestroyOnLoad singleton (placed in MainMenu, persists into Lobby + Match)
|
||||
- `RaceId` enum (None + Race1..Race16) — 16-slot grid reserved
|
||||
- Lobby race-selection overlay (4×4 grid + detail panel + exclusivity enforcement)
|
||||
- `PlayerMatchState.RaceSelection` NetworkVariable + `SubmitRaceRpc`
|
||||
- `PlayerBuilderSpawner` reads race-specific `BuilderPrefab` when available
|
||||
- Two placeholder RaceDefinition assets configured with the default builder (proves the data path works without race-specific content)
|
||||
|
||||
**Still pending:**
|
||||
|
||||
- Replace lobby race-pick with in-match race-pick overlay (timer-based auto-lock, `MatchState.RacePickTimer`) — OR retain lobby pick and remove the in-match flow concept. **Open decision** (see 1.10 reconciliation question).
|
||||
- Multi-builder race support — revisit `PlayerBuilderSpawner` to spawn N builders for races that need them
|
||||
- `TowerRegistry` filtering by active match's race rosters (currently shows all registered towers regardless of race)
|
||||
- Auto-discovery of `RaceDefinition` assets (today's flow: drag each asset into `RaceRegistry.Definitions` manually)
|
||||
- Replace `TowerPlacementManager.towerDefinitions[]` inspector array with race-driven discovery
|
||||
- Race-specific builder prefabs (currently every race uses the same default builder)
|
||||
- Real race content (display names, lore, icons, distinct builder visuals) — placeholder races exist but are interchangeable
|
||||
|
||||
### 1.9 Camera polish
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue