diff --git a/Assets/Audio/Settings/GameAudioSettings.asset b/Assets/Audio/Settings/GameAudioSettings.asset index 5d2dbbb..8f47512 100644 --- a/Assets/Audio/Settings/GameAudioSettings.asset +++ b/Assets/Audio/Settings/GameAudioSettings.asset @@ -84,14 +84,6 @@ MonoBehaviour: volume: 0.55 pitchRange: {x: 0.96, y: 1.04} spatial: 1 - - eventId: 31 - channel: 2 - outputGroupOverride: {fileID: 0} - clips: - - {fileID: 8300000, guid: e3dc6a0fbf543b64388f7b60615f4145, type: 3} - volume: 0.7 - pitchRange: {x: 0.96, y: 1.04} - spatial: 1 - eventId: 32 channel: 2 outputGroupOverride: {fileID: 0} diff --git a/Assets/Plugins/x86_64.meta b/Assets/Plugins/x86_64.meta new file mode 100644 index 0000000..dfd6df4 --- /dev/null +++ b/Assets/Plugins/x86_64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8480e16cd88119a4083023fb213dffce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/x86_64/ComNative.dll b/Assets/Plugins/x86_64/ComNative.dll new file mode 100644 index 0000000..e0f6d6b Binary files /dev/null and b/Assets/Plugins/x86_64/ComNative.dll differ diff --git a/Assets/Plugins/x86_64/ComNative.dll.meta b/Assets/Plugins/x86_64/ComNative.dll.meta new file mode 100644 index 0000000..202fc90 --- /dev/null +++ b/Assets/Plugins/x86_64/ComNative.dll.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 265471db36c70b54180317c720985aad \ No newline at end of file diff --git a/Assets/Scenes/Game.unity b/Assets/Scenes/Game.unity index 85fa2b9..2f075ef 100644 --- a/Assets/Scenes/Game.unity +++ b/Assets/Scenes/Game.unity @@ -260,7 +260,7 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::DanroJump.UI.Gameplay.GameplayHudController visualTreeAsset: {fileID: 9197481963319205126, guid: e5019473f4474704f8a8e8c93608eafc, type: 3} panelSettings: {fileID: 11400000, guid: d0960c9338bdb86469da533ffbbbc1fb, type: 2} - sortingOrder: 10 + sortingOrder: 5000 scoreLabelName: ScoreValue heightLabelName: HeightValue bestHeightLabelName: BestHeightValue diff --git a/Assets/Screenshots/gameplay-hud-aligned-no-overlap.png b/Assets/Screenshots/gameplay-hud-aligned-no-overlap.png new file mode 100644 index 0000000..07fd929 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-aligned-no-overlap.png differ diff --git a/Assets/Screenshots/gameplay-hud-aligned-no-overlap.png.meta b/Assets/Screenshots/gameplay-hud-aligned-no-overlap.png.meta new file mode 100644 index 0000000..eda49d6 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-aligned-no-overlap.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: e5281feb3e8fadf4195b417283d66194 +TextureImporter: + internalIDToNameTable: + - first: + 213: -6613811265633659565 + second: gameplay-hud-aligned-no-overlap_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-aligned-no-overlap_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 355dd6402b50734a0800000000000000 + internalID: -6613811265633659565 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-aligned-no-overlap_0: -6613811265633659565 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-final-aligned.png b/Assets/Screenshots/gameplay-hud-final-aligned.png new file mode 100644 index 0000000..5b71f3a Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-final-aligned.png differ diff --git a/Assets/Screenshots/gameplay-hud-final-aligned.png.meta b/Assets/Screenshots/gameplay-hud-final-aligned.png.meta new file mode 100644 index 0000000..9c0d678 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-final-aligned.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 33859e507258e7f4bb6888557906181e +TextureImporter: + internalIDToNameTable: + - first: + 213: -88504916756509590 + second: gameplay-hud-final-aligned_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-final-aligned_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: a6c4ad34f3195cef0800000000000000 + internalID: -88504916756509590 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-final-aligned_0: -88504916756509590 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-layout-check-1.png b/Assets/Screenshots/gameplay-hud-layout-check-1.png new file mode 100644 index 0000000..bb664f2 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-layout-check-1.png differ diff --git a/Assets/Screenshots/gameplay-hud-layout-check-1.png.meta b/Assets/Screenshots/gameplay-hud-layout-check-1.png.meta new file mode 100644 index 0000000..1a9f988 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-layout-check-1.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 18810d73ea59a4b449afdc1c8e6f5863 +TextureImporter: + internalIDToNameTable: + - first: + 213: 1204845118186171250 + second: gameplay-hud-layout-check-1_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-layout-check-1_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1080 + height: 1920 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 27ba9de504878b010800000000000000 + internalID: 1204845118186171250 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-layout-check-1_0: 1204845118186171250 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-layout-check.png b/Assets/Screenshots/gameplay-hud-layout-check.png new file mode 100644 index 0000000..bb664f2 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-layout-check.png differ diff --git a/Assets/Screenshots/gameplay-hud-layout-check.png.meta b/Assets/Screenshots/gameplay-hud-layout-check.png.meta new file mode 100644 index 0000000..ab2f371 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-layout-check.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 387580a6fdae7ff42900412e409ef078 +TextureImporter: + internalIDToNameTable: + - first: + 213: -7993827095130345330 + second: gameplay-hud-layout-check_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-layout-check_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1080 + height: 1920 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: e80fcde2c98301190800000000000000 + internalID: -7993827095130345330 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-layout-check_0: -7993827095130345330 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-left-padding-center-true-center.png b/Assets/Screenshots/gameplay-hud-left-padding-center-true-center.png new file mode 100644 index 0000000..dc9f484 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-left-padding-center-true-center.png differ diff --git a/Assets/Screenshots/gameplay-hud-left-padding-center-true-center.png.meta b/Assets/Screenshots/gameplay-hud-left-padding-center-true-center.png.meta new file mode 100644 index 0000000..7b9df1d --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-left-padding-center-true-center.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 7ab9dc1160c053340b1ba0dd9805f456 +TextureImporter: + internalIDToNameTable: + - first: + 213: -2984284117762596666 + second: gameplay-hud-left-padding-center-true-center_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-left-padding-center-true-center_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 6c0abe30861b596d0800000000000000 + internalID: -2984284117762596666 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-left-padding-center-true-center_0: -2984284117762596666 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger-final.png b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger-final.png new file mode 100644 index 0000000..81c09c1 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger-final.png differ diff --git a/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger-final.png.meta b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger-final.png.meta new file mode 100644 index 0000000..fe9ce4e --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger-final.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 2c74775832b90654dac4c794c1d0e647 +TextureImporter: + internalIDToNameTable: + - first: + 213: 192430222544730116 + second: gameplay-hud-left-smaller-center-bigger-final_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-left-smaller-center-bigger-final_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 408f3d29446aba200800000000000000 + internalID: 192430222544730116 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-left-smaller-center-bigger-final_0: 192430222544730116 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger.png b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger.png new file mode 100644 index 0000000..7365121 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger.png differ diff --git a/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger.png.meta b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger.png.meta new file mode 100644 index 0000000..65361d5 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-left-smaller-center-bigger.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 61863c92cb6f70f4f824aaa5b38a05e0 +TextureImporter: + internalIDToNameTable: + - first: + 213: -5553616215781586425 + second: gameplay-hud-left-smaller-center-bigger_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-left-smaller-center-bigger_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 70a4d7fd9779de2b0800000000000000 + internalID: -5553616215781586425 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-left-smaller-center-bigger_0: -5553616215781586425 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-runtime-check.png b/Assets/Screenshots/gameplay-hud-runtime-check.png new file mode 100644 index 0000000..312daf4 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-runtime-check.png differ diff --git a/Assets/Screenshots/gameplay-hud-runtime-check.png.meta b/Assets/Screenshots/gameplay-hud-runtime-check.png.meta new file mode 100644 index 0000000..5494807 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-runtime-check.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 1000adfacd6f4bf4b972e3c18b1abd29 +TextureImporter: + internalIDToNameTable: + - first: + 213: -7200965495179829446 + second: gameplay-hud-runtime-check_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-runtime-check_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: a3f16f3d808011c90800000000000000 + internalID: -7200965495179829446 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-runtime-check_0: -7200965495179829446 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-final-fixed.png b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final-fixed.png new file mode 100644 index 0000000..a91fcff Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final-fixed.png differ diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-final-fixed.png.meta b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final-fixed.png.meta new file mode 100644 index 0000000..ac1a66c --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final-fixed.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: e2b4729c8644bc5439846cb05511ba42 +TextureImporter: + internalIDToNameTable: + - first: + 213: -1398601438865487644 + second: gameplay-hud-runtime-ui-check-final-fixed_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-runtime-ui-check-final-fixed_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 4e88216c66b279ce0800000000000000 + internalID: -1398601438865487644 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-runtime-ui-check-final-fixed_0: -1398601438865487644 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-final.png b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final.png new file mode 100644 index 0000000..cb09c8d Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final.png differ diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-final.png.meta b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final.png.meta new file mode 100644 index 0000000..2153a57 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-runtime-ui-check-final.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 2885c47a0ecb53e47b356f30dd10bd5b +TextureImporter: + internalIDToNameTable: + - first: + 213: 8421283597357811696 + second: gameplay-hud-runtime-ui-check-final_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-runtime-ui-check-final_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 0f7065d50d86ed470800000000000000 + internalID: 8421283597357811696 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-runtime-ui-check-final_0: 8421283597357811696 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-fixed.png b/Assets/Screenshots/gameplay-hud-runtime-ui-check-fixed.png new file mode 100644 index 0000000..a8a54c5 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-runtime-ui-check-fixed.png differ diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-fixed.png.meta b/Assets/Screenshots/gameplay-hud-runtime-ui-check-fixed.png.meta new file mode 100644 index 0000000..2f3a08d --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-runtime-ui-check-fixed.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 0ce49b63caafebb4d877b2bfd5421507 +TextureImporter: + internalIDToNameTable: + - first: + 213: 8905311186297662870 + second: gameplay-hud-runtime-ui-check-fixed_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-runtime-ui-check-fixed_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 69da5917755069b70800000000000000 + internalID: 8905311186297662870 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-runtime-ui-check-fixed_0: 8905311186297662870 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-sorting.png b/Assets/Screenshots/gameplay-hud-runtime-ui-check-sorting.png new file mode 100644 index 0000000..221802b Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-runtime-ui-check-sorting.png differ diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check-sorting.png.meta b/Assets/Screenshots/gameplay-hud-runtime-ui-check-sorting.png.meta new file mode 100644 index 0000000..3330116 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-runtime-ui-check-sorting.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: de42086cba591cd4fa8265deb11c9809 +TextureImporter: + internalIDToNameTable: + - first: + 213: 4112799035560915661 + second: gameplay-hud-runtime-ui-check-sorting_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-runtime-ui-check-sorting_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: dc27d2baee8931930800000000000000 + internalID: 4112799035560915661 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-runtime-ui-check-sorting_0: 4112799035560915661 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check.png b/Assets/Screenshots/gameplay-hud-runtime-ui-check.png new file mode 100644 index 0000000..8f382ef Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-runtime-ui-check.png differ diff --git a/Assets/Screenshots/gameplay-hud-runtime-ui-check.png.meta b/Assets/Screenshots/gameplay-hud-runtime-ui-check.png.meta new file mode 100644 index 0000000..a913fb3 --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-runtime-ui-check.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: b707582a4e3e16c4182a4f982fd90ea8 +TextureImporter: + internalIDToNameTable: + - first: + 213: 7529376751934918786 + second: gameplay-hud-runtime-ui-check_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-runtime-ui-check_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 2805a277348bd7860800000000000000 + internalID: 7529376751934918786 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-runtime-ui-check_0: 7529376751934918786 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/gameplay-hud-same-height-sub-sprite.png b/Assets/Screenshots/gameplay-hud-same-height-sub-sprite.png new file mode 100644 index 0000000..12745a4 Binary files /dev/null and b/Assets/Screenshots/gameplay-hud-same-height-sub-sprite.png differ diff --git a/Assets/Screenshots/gameplay-hud-same-height-sub-sprite.png.meta b/Assets/Screenshots/gameplay-hud-same-height-sub-sprite.png.meta new file mode 100644 index 0000000..bb5af1c --- /dev/null +++ b/Assets/Screenshots/gameplay-hud-same-height-sub-sprite.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: b456d48c0a913544c97ec3620d29f2b6 +TextureImporter: + internalIDToNameTable: + - first: + 213: -3297423903188290487 + second: gameplay-hud-same-height-sub-sprite_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: gameplay-hud-same-height-sub-sprite_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 94474983e623d32d0800000000000000 + internalID: -3297423903188290487 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + gameplay-hud-same-height-sub-sprite_0: -3297423903188290487 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260706-085727.png b/Assets/Screenshots/screenshot-20260706-085727.png new file mode 100644 index 0000000..f1d6f11 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260706-085727.png differ diff --git a/Assets/Screenshots/screenshot-20260706-085727.png.meta b/Assets/Screenshots/screenshot-20260706-085727.png.meta new file mode 100644 index 0000000..c5d0afd --- /dev/null +++ b/Assets/Screenshots/screenshot-20260706-085727.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: ee6fd35711026f94abe3fa4d082a89e3 +TextureImporter: + internalIDToNameTable: + - first: + 213: 5866824435928499895 + second: screenshot-20260706-085727_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: screenshot-20260706-085727_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 7b6891f9db52b6150800000000000000 + internalID: 5866824435928499895 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + screenshot-20260706-085727_0: 5866824435928499895 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260706-085751.png b/Assets/Screenshots/screenshot-20260706-085751.png new file mode 100644 index 0000000..65ee5a7 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260706-085751.png differ diff --git a/Assets/Screenshots/screenshot-20260706-085751.png.meta b/Assets/Screenshots/screenshot-20260706-085751.png.meta new file mode 100644 index 0000000..0e9f5e0 --- /dev/null +++ b/Assets/Screenshots/screenshot-20260706-085751.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: e9b8b0aea1c298d42961d9f239378684 +TextureImporter: + internalIDToNameTable: + - first: + 213: 8860738017832214241 + second: screenshot-20260706-085751_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: screenshot-20260706-085751_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 1ee10b0f74aa7fa70800000000000000 + internalID: 8860738017832214241 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + screenshot-20260706-085751_0: 8860738017832214241 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260706-085804.png b/Assets/Screenshots/screenshot-20260706-085804.png new file mode 100644 index 0000000..4e9f5ed Binary files /dev/null and b/Assets/Screenshots/screenshot-20260706-085804.png differ diff --git a/Assets/Screenshots/screenshot-20260706-085804.png.meta b/Assets/Screenshots/screenshot-20260706-085804.png.meta new file mode 100644 index 0000000..415cb1d --- /dev/null +++ b/Assets/Screenshots/screenshot-20260706-085804.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: d9b5b32ece6d79f4c8f07068d4145c73 +TextureImporter: + internalIDToNameTable: + - first: + 213: -84736916317270727 + second: screenshot-20260706-085804_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: screenshot-20260706-085804_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 93d0c8b3934f2def0800000000000000 + internalID: -84736916317270727 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + screenshot-20260706-085804_0: -84736916317270727 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260706-091834.png b/Assets/Screenshots/screenshot-20260706-091834.png new file mode 100644 index 0000000..07ff033 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260706-091834.png differ diff --git a/Assets/Screenshots/screenshot-20260706-091834.png.meta b/Assets/Screenshots/screenshot-20260706-091834.png.meta new file mode 100644 index 0000000..5394f2d --- /dev/null +++ b/Assets/Screenshots/screenshot-20260706-091834.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: b27172e5ea1648e41bb3249612633fdf +TextureImporter: + internalIDToNameTable: + - first: + 213: -5655167601327757581 + second: screenshot-20260706-091834_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: screenshot-20260706-091834_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 3f6af89670fc481b0800000000000000 + internalID: -5655167601327757581 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + screenshot-20260706-091834_0: -5655167601327757581 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260706-092024.png b/Assets/Screenshots/screenshot-20260706-092024.png new file mode 100644 index 0000000..2a72f51 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260706-092024.png differ diff --git a/Assets/Screenshots/screenshot-20260706-092024.png.meta b/Assets/Screenshots/screenshot-20260706-092024.png.meta new file mode 100644 index 0000000..8d3c010 --- /dev/null +++ b/Assets/Screenshots/screenshot-20260706-092024.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 544707e8a80be704da33766e4e1b7b2d +TextureImporter: + internalIDToNameTable: + - first: + 213: 8766466611136954131 + second: screenshot-20260706-092024_0 + 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: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + 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 + - serializedVersion: 4 + buildTarget: WebGL + 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: WindowsStoreApps + 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: + - serializedVersion: 2 + name: screenshot-20260706-092024_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 701 + height: 1246 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 31ff324afeeb8a970800000000000000 + internalID: 8766466611136954131 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + screenshot-20260706-092024_0: 8766466611136954131 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Runtime/Bootstrap/BootstrapEntryPoint.cs b/Assets/Scripts/Runtime/Bootstrap/BootstrapEntryPoint.cs index bb93b4c..d1e743e 100644 --- a/Assets/Scripts/Runtime/Bootstrap/BootstrapEntryPoint.cs +++ b/Assets/Scripts/Runtime/Bootstrap/BootstrapEntryPoint.cs @@ -1,7 +1,9 @@ using System.IO; using System.Threading; using Cysharp.Threading.Tasks; +using DanroJump.Debugging; using UnityEngine; +using UnityEngine.InputSystem; using UnityEngine.SceneManagement; namespace DanroJump.Bootstrap @@ -13,6 +15,7 @@ namespace DanroJump.Bootstrap public sealed class BootstrapEntryPoint : MonoBehaviour { private const float AspectTolerance = 0.0025f; + private const string GameLogFileName = "game.log"; private static BootstrapEntryPoint instance; [SerializeField] private string mainMenuSceneName = "MainMenu"; @@ -26,6 +29,8 @@ namespace DanroJump.Bootstrap [SerializeField] [Min(0.1f)] private float aspectCheckInterval = 0.5f; private float nextAspectCheckTime; + private InputAction fullscreenToggleAction; + private FileLogger fileLogger; /// /// Создает singleton bootstrap-объект и применяет разрешение билда до загрузки меню. @@ -50,6 +55,38 @@ namespace DanroJump.Bootstrap ApplyBuildAspectResolution(); EnforceBuildAspectOnStartupAsync(this.GetCancellationTokenOnDestroy()).Forget(Debug.LogException); } + + // Инициализируем файловый логгер — пишем game.log в корень папки игры. + InitFileLogger(); + } + + /// + /// Регистрирует InputAction для переключения полноэкранного режима по F11. + /// + private void OnEnable() + { + fullscreenToggleAction = new InputAction("FullscreenToggle", InputActionType.Button, "/f11"); + fullscreenToggleAction.performed += OnFullscreenTogglePerformed; + fullscreenToggleAction.Enable(); + } + + /// + /// Снимает подписку и отключает InputAction при деактивации объекта. + /// + private void OnDisable() + { + if (fullscreenToggleAction != null) + { + fullscreenToggleAction.performed -= OnFullscreenTogglePerformed; + fullscreenToggleAction.Disable(); + fullscreenToggleAction.Dispose(); + fullscreenToggleAction = null; + } + } + + private void OnFullscreenTogglePerformed(InputAction.CallbackContext context) + { + ToggleFullscreen(); } /// @@ -70,6 +107,33 @@ namespace DanroJump.Bootstrap } } + /// + /// Переключает полноэкранный режим по нажатию F11. + /// В билде учитывает целевой aspect ratio и разрешение монитора. + /// + private void ToggleFullscreen() + { + bool isCurrentlyFullscreen = Screen.fullScreen; + + if (isCurrentlyFullscreen) + { + // Выход из полноэкранного режима — переходим в Windowed с aspect-корректным разрешением + Vector2Int resolution = CalculateTargetResolution(); + Screen.SetResolution(resolution.x, resolution.y, FullScreenMode.Windowed); + } + else + { + // Вход в полноэкранный режим — используем native разрешение монитора + Resolution native = Screen.currentResolution; + FullScreenMode targetMode = FullScreenMode.FullScreenWindow; +#if UNITY_STANDALONE_WIN + // На Windows ExclusiveFullScreen даёт минимальный input lag + targetMode = FullScreenMode.FullScreenWindow; +#endif + Screen.SetResolution(native.width, native.height, targetMode); + } + } + /// /// Загружает сцену главного меню после bootstrap-настройки. /// @@ -138,12 +202,66 @@ namespace DanroJump.Bootstrap /// private void OnDestroy() { + DisposeFileLogger(); + if (instance == this) { instance = null; } } + /// + /// Создаёт FileLogger и подписывает его на все сообщения Unity Debug.Log. + /// В билде лог пишется рядом с .exe, в редакторе — в корень проекта. + /// + private void InitFileLogger() + { + if (fileLogger != null) + { + return; + } + + try + { + string logDirectory = ResolveGameRootDirectory(); + string logPath = Path.Combine(logDirectory, GameLogFileName); + fileLogger = new FileLogger(logPath, append: true); + Application.logMessageReceived += fileLogger.HandleUnityLog; + Debug.Log($"[BootstrapEntryPoint] Game log writes to: {fileLogger.FilePath}", this); + } + catch (System.Exception ex) + { + Debug.LogError($"[BootstrapEntryPoint] Failed to init FileLogger: {ex.Message}"); + } + } + + private static string ResolveGameRootDirectory() + { + string dataPath = Application.dataPath; + var dataDirectory = new DirectoryInfo(dataPath); + +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + return dataDirectory.Parent?.Parent?.FullName ?? Path.GetFullPath("."); +#else + return dataDirectory.Parent?.FullName ?? Path.GetFullPath("."); +#endif + } + + /// + /// Корректно отписывается и освобождает FileLogger. + /// + private void DisposeFileLogger() + { + if (fileLogger == null) + { + return; + } + + Application.logMessageReceived -= fileLogger.HandleUnityLog; + fileLogger.Dispose(); + fileLogger = null; + } + /// /// Несколько кадров подряд повторяет установку разрешения, пока Unity завершает старт окна. /// diff --git a/Assets/Scripts/Runtime/Debugging/FileLogger.cs b/Assets/Scripts/Runtime/Debugging/FileLogger.cs new file mode 100644 index 0000000..38198e6 --- /dev/null +++ b/Assets/Scripts/Runtime/Debugging/FileLogger.cs @@ -0,0 +1,395 @@ +using System; +using System.IO; +using System.Text; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace DanroJump.Debugging +{ + /// + /// Логгер для записи сообщений в файл. Совместим с CompositeLogger. + /// + public class FileLogger : ILogger, IDisposable + { + public ILogHandler logHandler { get; set; } + public LogType filterLogType { get; set; } + + public bool logEnabled { get; set; } = true; + + private readonly string _filePath; + private readonly object _lock = new object(); + private StreamWriter _writer; + private bool _disposed; + private bool _footerWritten; + + /// + /// Создает файловый логгер с указанным путём к файлу. + /// + /// Полный путь к файлу лога. Если null, используется Application.persistentDataPath/game.log + /// Если true, добавляет к существующему файлу, иначе перезаписывает + public FileLogger(string filePath = null, bool append = true) + { + _filePath = filePath ?? Path.Combine(Application.persistentDataPath, "game.log"); + filterLogType = LogType.Exception; + + try + { + // Создаем директорию если её нет + string directory = Path.GetDirectoryName(_filePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + // Открываем файл для записи + _writer = new StreamWriter(_filePath, append, Encoding.UTF8) + { + AutoFlush = true // Автоматически сбрасывать буфер для надёжности + }; + + // Записываем заголовок + WriteHeader(); + + // Подписываемся на завершение приложения для гарантированной записи + Application.quitting += OnApplicationQuitting; + } + catch (Exception ex) + { + Debug.LogError($"[FileLogger] Не удалось открыть файл лога: {_filePath}\n{ex}"); + } + } + + public string FilePath => _filePath; + + private void WriteHeader() + { + if (_writer != null) + { + _writer.WriteLine("================================================================================"); + _writer.WriteLine($"Новая сессия логирования начата: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); + _writer.WriteLine($"Unity версия: {Application.unityVersion}"); + _writer.WriteLine($"Платформа: {Application.platform}"); + _writer.WriteLine($"Путь к логу: {_filePath}"); + _writer.WriteLine("================================================================================"); + _writer.WriteLine(); + } + } + + public bool IsLogTypeAllowed(LogType logType) + { + return logEnabled && logType <= filterLogType; + } + + private void WriteToFile(LogType logType, string message) + { + if (!logEnabled || _disposed || _writer == null) + return; + + if (!IsLogTypeAllowed(logType)) + return; + + lock (_lock) + { + try + { + string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); + string logTypeStr = GetLogTypeString(logType); + _writer.WriteLine($"[{timestamp}] [{logTypeStr}] {message}"); + } + catch (Exception ex) + { + Debug.LogError($"[FileLogger] Ошибка записи в файл: {ex.Message}"); + } + } + } + + private string GetLogTypeString(LogType logType) + { + return logType switch + { + LogType.Error => "ERROR", + LogType.Assert => "ASSERT", + LogType.Warning => "WARNING", + LogType.Log => "INFO", + LogType.Exception => "EXCEPTION", + _ => "UNKNOWN" + }; + } + + private string FormatMessage(object message, Object context = null) + { + string msg = message?.ToString() ?? "null"; + if (context != null) + { + msg += $" (Context: {context.name})"; + } + + return msg; + } + + private string FormatMessage(string tag, object message, Object context = null) + { + string msg = $"[{tag}] {message?.ToString() ?? "null"}"; + if (context != null) + { + msg += $" (Context: {context.name})"; + } + + return msg; + } + +#region ILogger Implementation + + public void Log(LogType logType, object message) + { + WriteToFile(logType, FormatMessage(message)); + } + + public void Log(LogType logType, object message, Object context) + { + WriteToFile(logType, FormatMessage(message, context)); + } + + public void Log(LogType logType, string tag, object message) + { + WriteToFile(logType, FormatMessage(tag, message)); + } + + public void Log(LogType logType, string tag, object message, Object context) + { + WriteToFile(logType, FormatMessage(tag, message, context)); + } + + public void Log(object message) + { + WriteToFile(LogType.Log, FormatMessage(message)); + } + + public void Log(string tag, object message) + { + WriteToFile(LogType.Log, FormatMessage(tag, message)); + } + + public void Log(string tag, object message, Object context) + { + WriteToFile(LogType.Log, FormatMessage(tag, message, context)); + } + + public void LogWarning(string tag, object message) + { + WriteToFile(LogType.Warning, FormatMessage(tag, message)); + } + + public void LogWarning(string tag, object message, Object context) + { + WriteToFile(LogType.Warning, FormatMessage(tag, message, context)); + } + + public void LogError(string tag, object message) + { + WriteToFile(LogType.Error, FormatMessage(tag, message)); + } + + public void LogError(string tag, object message, Object context) + { + WriteToFile(LogType.Error, FormatMessage(tag, message, context)); + } + + public void LogFormat(LogType logType, string format, params object[] args) + { + try + { + string message = string.Format(format, args); + WriteToFile(logType, message); + } + catch (FormatException ex) + { + WriteToFile(LogType.Error, $"Ошибка форматирования: {ex.Message}"); + } + } + + public void LogFormat(LogType logType, Object context, string format, params object[] args) + { + try + { + string message = string.Format(format, args); + WriteToFile(logType, FormatMessage(message, context)); + } + catch (FormatException ex) + { + WriteToFile(LogType.Error, $"Ошибка форматирования: {ex.Message}"); + } + } + + public void LogException(Exception exception) + { + if (exception == null) + return; + + WriteToFile(LogType.Exception, $"{exception.GetType().Name}: {exception.Message}"); + WriteToFile(LogType.Exception, $"StackTrace:\n{exception.StackTrace}"); + + if (exception.InnerException != null) + { + WriteToFile(LogType.Exception, + $"Inner Exception: {exception.InnerException.GetType().Name}: {exception.InnerException.Message}"); + } + } + + public void LogException(Exception exception, Object context) + { + if (exception == null) + return; + + string contextInfo = context != null ? $" (Context: {context.name})" : ""; + WriteToFile(LogType.Exception, $"{exception.GetType().Name}: {exception.Message}{contextInfo}"); + WriteToFile(LogType.Exception, $"StackTrace:\n{exception.StackTrace}"); + + if (exception.InnerException != null) + { + WriteToFile(LogType.Exception, + $"Inner Exception: {exception.InnerException.GetType().Name}: {exception.InnerException.Message}"); + } + } + +#endregion + + /// + /// Обработчик для Application.logMessageReceived — перенаправляет все Debug.Log в файл. + /// + public void HandleUnityLog(string logString, string stackTrace, LogType type) + { + if (type == LogType.Exception) + { + WriteToFile(type, $"{logString}\n{stackTrace}"); + } + else + { + WriteToFile(type, logString); + } + } + + private void OnApplicationQuitting() + { + WriteFooter(); + Dispose(); + } + + private void WriteFooter() + { + if (_footerWritten || _disposed || _writer == null) return; + + lock (_lock) + { + if (_footerWritten) return; + _footerWritten = true; + + try + { + _writer.WriteLine(); + _writer.WriteLine("================================================================================"); + _writer.WriteLine($"Сессия логирования завершена: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); + _writer.WriteLine("================================================================================"); + _writer.Flush(); + } + catch (ObjectDisposedException) { } + catch (Exception ex) + { + Debug.LogError($"[FileLogger] Ошибка при записи footer: {ex.Message}"); + } + } + } + + /// + /// Принудительно сбрасывает буфер на диск + /// + public void Flush() + { + lock (_lock) + { + try + { + _writer?.Flush(); + } + catch (Exception ex) + { + Debug.LogError($"[FileLogger] Ошибка при сбросе буфера: {ex.Message}"); + } + } + } + + /// + /// Очищает файл лога + /// + public void Clear() + { + lock (_lock) + { + try + { + _writer?.Close(); + File.WriteAllText(_filePath, string.Empty); + _writer = new StreamWriter(_filePath, true, Encoding.UTF8) { AutoFlush = true }; + WriteHeader(); + } + catch (Exception ex) + { + Debug.LogError($"[FileLogger] Ошибка при очистке файла: {ex.Message}"); + } + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) + return; + + lock (_lock) + { + if (_disposed) + return; + + try + { + // Гарантированно записываем footer + WriteFooter(); + + if (disposing) + { + Application.quitting -= OnApplicationQuitting; + } + + if (_writer != null) + { + + _writer.Dispose(); + _writer = null; + } + } + catch (ObjectDisposedException) + { + // Поток уже закрыт — игнорируем. + } + catch (Exception ex) + { + Debug.LogError($"[FileLogger] Ошибка при закрытии файла: {ex.Message}"); + } + finally + { + _disposed = true; + } + } + } + + ~FileLogger() + { + Dispose(false); + } + } +} diff --git a/Assets/Scripts/Runtime/Debugging/FileLogger.cs.meta b/Assets/Scripts/Runtime/Debugging/FileLogger.cs.meta new file mode 100644 index 0000000..818297f --- /dev/null +++ b/Assets/Scripts/Runtime/Debugging/FileLogger.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d029602fde45ab04f85586e245e48a1a \ No newline at end of file diff --git a/Assets/Scripts/Runtime/Gameplay/JumpPlayerController.cs b/Assets/Scripts/Runtime/Gameplay/JumpPlayerController.cs index 1f44aa1..82615f2 100644 --- a/Assets/Scripts/Runtime/Gameplay/JumpPlayerController.cs +++ b/Assets/Scripts/Runtime/Gameplay/JumpPlayerController.cs @@ -80,7 +80,6 @@ namespace DanroJump.Gameplay private bool moveActionWasEnabled; #endif private Transform wrapVisualCloneRoot; - private Transform visualRootClone; private readonly List sourceRenderers = new(); private readonly List wrapRendererClones = new(); private readonly List landingHandlerBuffer = new(); @@ -1591,29 +1590,13 @@ namespace DanroJump.Gameplay sourceRenderers.Clear(); wrapRendererClones.Clear(); - if (wrapVisualCloneRoot == null) + if (visualRoot != null) { - wrapVisualCloneRoot = new GameObject($"{name} Wrap Visuals").transform; - wrapVisualCloneRoot.SetParent(transform, false); - wrapVisualCloneRoot.gameObject.SetActive(false); + sourceRenderers.AddRange(visualRoot.GetComponentsInChildren(true)); } - - if (visualRootClone == null) + else { - visualRootClone = new GameObject("VisualRoot Clone").transform; - visualRootClone.SetParent(wrapVisualCloneRoot, false); - } - - var allRenderers = visualRoot != null - ? visualRoot.GetComponentsInChildren(true) - : GetComponentsInChildren(true); - - foreach (var r in allRenderers) - { - if (r != null && (wrapVisualCloneRoot == null || !r.transform.IsChildOf(wrapVisualCloneRoot))) - { - sourceRenderers.Add(r); - } + sourceRenderers.AddRange(GetComponentsInChildren(true)); } if (sourceRenderers.Count == 0) @@ -1621,6 +1604,12 @@ namespace DanroJump.Gameplay return; } + if (wrapVisualCloneRoot == null) + { + wrapVisualCloneRoot = new GameObject($"{name} Wrap Visuals").transform; + wrapVisualCloneRoot.gameObject.SetActive(false); + } + for (var index = sourceRenderers.Count - 1; index >= 0; index--) { var sourceRenderer = sourceRenderers[index]; @@ -1631,62 +1620,9 @@ namespace DanroJump.Gameplay } var clone = new GameObject($"{sourceRenderer.name} Wrap Clone").AddComponent(); - clone.transform.SetParent(visualRootClone, false); + clone.transform.SetParent(wrapVisualCloneRoot, false); wrapRendererClones.Insert(0, clone); } - - RebuildWrapCollider(); - } - - private void RebuildWrapCollider() - { - if (wrapVisualCloneRoot == null) - { - return; - } - - var oldColliders = wrapVisualCloneRoot.GetComponents(); - foreach (var old in oldColliders) - { - if (Application.isPlaying) - { - Destroy(old); - } - else - { - DestroyImmediate(old); - } - } - - var sourceCollider = GetComponent(); - if (sourceCollider != null) - { - if (sourceCollider is BoxCollider2D boxCol) - { - var cloneCol = wrapVisualCloneRoot.gameObject.AddComponent(); - cloneCol.size = boxCol.size; - cloneCol.offset = boxCol.offset; - cloneCol.isTrigger = boxCol.isTrigger; - cloneCol.sharedMaterial = boxCol.sharedMaterial; - } - else if (sourceCollider is CircleCollider2D circleCol) - { - var cloneCol = wrapVisualCloneRoot.gameObject.AddComponent(); - cloneCol.radius = circleCol.radius; - cloneCol.offset = circleCol.offset; - cloneCol.isTrigger = circleCol.isTrigger; - cloneCol.sharedMaterial = circleCol.sharedMaterial; - } - else if (sourceCollider is CapsuleCollider2D capsuleCol) - { - var cloneCol = wrapVisualCloneRoot.gameObject.AddComponent(); - cloneCol.size = capsuleCol.size; - cloneCol.offset = capsuleCol.offset; - cloneCol.direction = capsuleCol.direction; - cloneCol.isTrigger = capsuleCol.isTrigger; - cloneCol.sharedMaterial = capsuleCol.sharedMaterial; - } - } } /// @@ -1726,20 +1662,11 @@ namespace DanroJump.Gameplay CopyWrappedVisuals(new Vector3(wrapOffsetX, 0f, 0f)); } + /// + /// Копирует состояние исходных SpriteRenderer в wrap-клоны. + /// private void CopyWrappedVisuals(Vector3 offset) { - if (wrapVisualCloneRoot != null) - { - wrapVisualCloneRoot.localPosition = offset; - } - - if (visualRoot != null && visualRootClone != null) - { - visualRootClone.localPosition = visualRoot.localPosition; - visualRootClone.localRotation = visualRoot.localRotation; - visualRootClone.localScale = visualRoot.localScale; - } - for (var index = 0; index < sourceRenderers.Count; index++) { var source = sourceRenderers[index]; @@ -1769,9 +1696,8 @@ namespace DanroJump.Gameplay clone.sortingLayerID = source.sortingLayerID; clone.sortingOrder = source.sortingOrder; clone.sharedMaterial = source.sharedMaterial; - clone.transform.localPosition = source.transform.localPosition; - clone.transform.localRotation = source.transform.localRotation; - clone.transform.localScale = source.transform.localScale; + clone.transform.SetPositionAndRotation(source.transform.position + offset, source.transform.rotation); + clone.transform.localScale = source.transform.lossyScale; clone.gameObject.layer = source.gameObject.layer; } catch (MissingReferenceException) diff --git a/Assets/Scripts/Runtime/Gameplay/OneWayPlatformCollider.cs b/Assets/Scripts/Runtime/Gameplay/OneWayPlatformCollider.cs index 2bd2d72..9cea3b4 100644 --- a/Assets/Scripts/Runtime/Gameplay/OneWayPlatformCollider.cs +++ b/Assets/Scripts/Runtime/Gameplay/OneWayPlatformCollider.cs @@ -5,7 +5,7 @@ namespace DanroJump.Gameplay { /// /// Кастомная one-way логика платформы. - /// Игнорирует столкновение, если GroundCheck игрока ниже верхней границы платформы. + /// Игнорирует столкновение, если нижняя точка игрока ниже верхней границы платформы. /// [DisallowMultipleComponent] [RequireComponent(typeof(Collider2D))] @@ -14,10 +14,8 @@ namespace DanroJump.Gameplay [SerializeField] private JumpPlayerController player; [SerializeField] private float topBoundaryPadding; [SerializeField] private float landingVelocityThreshold = 0.05f; - [SerializeField] private float crossingGraceDistance = 0.8f; private Collider2D platformCollider; - private BoxCollider2D boxPlatformCollider; private Collider2D playerCollider; private float cachedTopY; private Vector3 lastPosition; @@ -33,15 +31,7 @@ namespace DanroJump.Gameplay /// public void UpdateCachedTopY() { - if (boxPlatformCollider != null) - { - var localTop = boxPlatformCollider.offset + Vector2.up * (boxPlatformCollider.size.y * 0.5f); - cachedTopY = boxPlatformCollider.transform.TransformPoint(localTop).y; - } - else - { - cachedTopY = platformCollider != null ? platformCollider.bounds.max.y : transform.position.y; - } + cachedTopY = platformCollider != null ? platformCollider.bounds.max.y : transform.position.y; } /// @@ -60,7 +50,6 @@ namespace DanroJump.Gameplay private void Awake() { platformCollider = GetComponent(); - boxPlatformCollider = platformCollider as BoxCollider2D; platformCollider.usedByEffector = false; if (TryGetComponent(out PlatformEffector2D platformEffector)) @@ -139,7 +128,7 @@ namespace DanroJump.Gameplay /// Проверяет, должен ли игрок столкнуться с платформой в текущем физическом шаге. /// /// Игрок, для которого проверяется столкновение. - /// True, если игрок падает сверху или только что пересек верх платформы сверху вниз. + /// True, если нижняя точка игрока не ниже верхней границы платформы. public bool ShouldCollideWith(JumpPlayerController targetPlayer) { if (targetPlayer == null) @@ -147,17 +136,10 @@ namespace DanroJump.Gameplay return false; } - var topY = cachedTopY + topBoundaryPadding; + var topY = cachedTopY + Mathf.Max(0f, topBoundaryPadding); + var playerLowestY = playerCollider != null ? playerCollider.bounds.min.y : targetPlayer.GroundCheckY; - if (targetPlayer.GroundCheckY >= topY) - { - return true; - } - - // Grace-зона закрывает быстрые кадры, когда GroundCheck успел проскочить немного ниже верхней границы. - var crossedFromAbove = targetPlayer.PreviousGroundCheckY >= topY && - targetPlayer.GroundCheckY >= topY - crossingGraceDistance; - return crossedFromAbove; + return playerLowestY >= topY; } /// diff --git a/Assets/Scripts/Runtime/Gameplay/Spawning/PlatformContentSpawner.cs b/Assets/Scripts/Runtime/Gameplay/Spawning/PlatformContentSpawner.cs index bfe50ee..2ed4706 100644 --- a/Assets/Scripts/Runtime/Gameplay/Spawning/PlatformContentSpawner.cs +++ b/Assets/Scripts/Runtime/Gameplay/Spawning/PlatformContentSpawner.cs @@ -53,6 +53,12 @@ namespace DanroJump.Gameplay return; } + if (!content.scene.isLoaded) + { + Object.Destroy(content); + return; + } + if (content.TryGetComponent(out PlatformSpawnedContent marker) && marker.SourcePrefab != null) { content.SetActive(false); diff --git a/Assets/Scripts/Runtime/Hardware/Com/ComHopperDispenser.cs b/Assets/Scripts/Runtime/Hardware/Com/ComHopperDispenser.cs new file mode 100644 index 0000000..d6bbeae --- /dev/null +++ b/Assets/Scripts/Runtime/Hardware/Com/ComHopperDispenser.cs @@ -0,0 +1,87 @@ +using System; +using System.Threading; +using Cysharp.Threading.Tasks; +using UnityEngine.Events; + +namespace DanroJump.Hardware.Com +{ + /// + /// Единая процедура выдачи капсулы через хоппер: запуск мотора, ожидание датчика и остановка. + /// + public static class ComHopperDispenser + { + public const string StartMotorCommand = "301"; + public const string StopMotorCommand = "300"; + public const int DefaultTimeoutSeconds = 10; + + public static UniTask DispenseAsync( + IComSystem com, + CancellationToken cancellationToken = default) + { + return DispenseAsync(com, TimeSpan.FromSeconds(DefaultTimeoutSeconds), cancellationToken); + } + + public static async UniTask DispenseAsync( + IComSystem com, + TimeSpan timeout, + CancellationToken cancellationToken = default) + { + if (com == null) + { + return ComHopperDispenseResult.Failure("COM-система недоступна."); + } + + if (!com.IsRunning) + { + return ComHopperDispenseResult.Failure("COM-система не запущена."); + } + + if (!com.SendToMain(StartMotorCommand)) + { + return ComHopperDispenseResult.Failure("Не удалось отправить команду запуска хоппера."); + } + + var sensorTriggered = new UniTaskCompletionSource(); + UnityAction onTriggered = () => sensorTriggered.TrySetResult(); + com.HopperSensorTriggered.AddListener(onTriggered); + + try + { + var winner = await UniTask.WhenAny( + sensorTriggered.Task, + UniTask.Delay(timeout, DelayType.Realtime, PlayerLoopTiming.Update, cancellationToken)); + + return winner == 0 + ? ComHopperDispenseResult.Success("Капсула выдана успешно.") + : ComHopperDispenseResult.Failure($"Датчик хоппера не сработал за {timeout.TotalSeconds:0.#} сек."); + } + finally + { + com.HopperSensorTriggered.RemoveListener(onTriggered); + com.SendToMain(StopMotorCommand); + } + } + } + + public readonly struct ComHopperDispenseResult + { + private ComHopperDispenseResult(bool isSuccess, string message) + { + IsSuccess = isSuccess; + Message = message ?? string.Empty; + } + + public bool IsSuccess { get; } + public string Message { get; } + + public static ComHopperDispenseResult Success(string message) + { + return new ComHopperDispenseResult(true, message); + } + + public static ComHopperDispenseResult Failure(string message) + { + return new ComHopperDispenseResult(false, message); + } + } +} diff --git a/Assets/Scripts/Runtime/Hardware/Com/ComHopperDispenser.cs.meta b/Assets/Scripts/Runtime/Hardware/Com/ComHopperDispenser.cs.meta new file mode 100644 index 0000000..7703408 --- /dev/null +++ b/Assets/Scripts/Runtime/Hardware/Com/ComHopperDispenser.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3402102c626636f48aa68fbc9154cde6 \ No newline at end of file diff --git a/Assets/Scripts/Runtime/Hardware/Com/ComSystem.cs b/Assets/Scripts/Runtime/Hardware/Com/ComSystem.cs index accf313..e2e38a1 100644 --- a/Assets/Scripts/Runtime/Hardware/Com/ComSystem.cs +++ b/Assets/Scripts/Runtime/Hardware/Com/ComSystem.cs @@ -301,8 +301,8 @@ namespace DanroJump.Hardware.Com return; } - portProvider = customPortProvider ?? new ReflectionComPortProvider(); - transportFactory = customTransportFactory ?? new ReflectionSerialComTransportFactory(); + portProvider = customPortProvider ?? CreateDefaultPortProvider(); + transportFactory = customTransportFactory ?? CreateDefaultTransportFactory(); } /// @@ -441,10 +441,24 @@ namespace DanroJump.Hardware.Com ResetDeviceSelections(); ClearQueues(); - string[] ports = SelectPorts(portProvider.GetPortNames()); + string[] detectedPorts = portProvider.GetPortNames(); if (ShouldLog(DebugLogChannel.ComLifecycle, logLifecycle)) { - LogDebug(DebugLogChannel.ComLifecycle, $"Starting. Selected ports: {(ports.Length > 0 ? string.Join(", ", ports) : "")}"); + LogDebug( + DebugLogChannel.ComLifecycle, + $"Detected ports: {(detectedPorts.Length > 0 ? string.Join(", ", detectedPorts) : "")}. " + + $"scanAll={useAllDetectedPorts} explicit=[{FormatList(explicitPorts)}] preferredMain={FormatDebugValue(preferredMainPort)}"); + } + + string[] ports = SelectPorts(detectedPorts); + if (ShouldLog(DebugLogChannel.ComLifecycle, logLifecycle)) + { + LogDebug( + DebugLogChannel.ComLifecycle, + $"Starting. Selected ports: {(ports.Length > 0 ? string.Join(", ", ports) : "")}. " + + $"serial={baudRate}/{dataBits}/{parity}/{stopBits} readTimeoutMs={readTimeoutMs} writeTimeoutMs={writeTimeoutMs} " + + $"encryptedHandshake={useEncryptedHandshake} handshakeTimeoutMs={handshakeTimeoutMs} " + + $"roleRequest={FormatDebugValue(roleRequestCommand)} rolePrefix={FormatDebugValue(roleResponsePrefix)}"); } if (ports.Length == 0) { @@ -474,8 +488,22 @@ namespace DanroJump.Hardware.Com private void EnsureInfrastructure() { - portProvider ??= new ReflectionComPortProvider(); - transportFactory ??= new ReflectionSerialComTransportFactory(); + portProvider ??= CreateDefaultPortProvider(); + transportFactory ??= CreateDefaultTransportFactory(); + } + + private static IComPortProvider CreateDefaultPortProvider() + { + return new FallbackComPortProvider( + new ReflectionComPortProvider(), + new NativeComPortProvider()); + } + + private static IComTransportFactory CreateDefaultTransportFactory() + { + return new FallbackComTransportFactory( + new ReflectionSerialComTransportFactory(), + new NativeComTransportFactory()); } private void EnsureLightingSystem() @@ -1257,6 +1285,7 @@ namespace DanroJump.Hardware.Com roleRequestCommand, roleResponsePrefix, roleRequestTimeoutMs, + message => LogDebug(DebugLogChannel.ComLifecycle, logLifecycle, message), message => LogWarning(DebugLogChannel.ComLifecycle, logLifecycle, message)); Subscribe(device); @@ -1611,6 +1640,25 @@ namespace DanroJump.Hardware.Com return string.IsNullOrWhiteSpace(value) ? "" : value; } + private static string FormatList(IEnumerable values) + { + if (values == null) + { + return ""; + } + + List filtered = new(); + foreach (var value in values) + { + if (!string.IsNullOrWhiteSpace(value)) + { + filtered.Add(value.Trim()); + } + } + + return filtered.Count > 0 ? string.Join(", ", filtered) : ""; + } + private static async UniTask RunLoggedTaskAsync(Func taskFactory, CancellationToken token, string operationName) { try @@ -1759,6 +1807,7 @@ namespace DanroJump.Hardware.Com private readonly string roleRequestCommand; private readonly string roleResponsePrefix; private readonly int roleRequestTimeoutMs; + private readonly Action logInfo; private readonly Action logWarning; private readonly byte[] aesKey; @@ -1798,6 +1847,7 @@ namespace DanroJump.Hardware.Com string roleRequestCommand, string roleResponsePrefix, int roleRequestTimeoutMs, + Action logInfo, Action logWarning) { this.transportFactory = transportFactory ?? throw new ArgumentNullException(nameof(transportFactory)); @@ -1817,6 +1867,7 @@ namespace DanroJump.Hardware.Com this.roleRequestCommand = roleRequestCommand?.Trim() ?? ""; this.roleResponsePrefix = string.IsNullOrWhiteSpace(roleResponsePrefix) ? "ROLE" : roleResponsePrefix.Trim(); this.roleRequestTimeoutMs = Mathf.Max(200, roleRequestTimeoutMs); + this.logInfo = logInfo; this.logWarning = logWarning; aesKey = BuildAesKey(); } @@ -1899,14 +1950,17 @@ namespace DanroJump.Hardware.Com return false; } + LogInfo($"{PortName} read loop starting."); RunLoggedTask(() => ReadLoopAsync(localCts.Token), localCts.Token, $"{PortName} read loop"); if (useEncryptedHandshake) { + LogInfo($"{PortName} encrypted handshake starting."); RunLoggedTask(() => RunHandshakeAsync(localCts.Token), localCts.Token, $"{PortName} handshake"); } else { + LogInfo($"{PortName} encrypted handshake disabled; marking operational."); MarkOperational(); } @@ -1963,6 +2017,7 @@ namespace DanroJump.Hardware.Com { try { + LogInfo($"{PortName} opening serial port. settings={baudRate}/{dataBits}/{parity}/{stopBits} readTimeoutMs={readTimeoutMs} writeTimeoutMs={writeTimeoutMs}"); lock (ioLock) { serialPort = transportFactory.Create(new SerialSettings( @@ -1980,11 +2035,12 @@ namespace DanroJump.Hardware.Com } lastCoinTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + LogInfo($"{PortName} serial port opened successfully."); return true; } catch (Exception exception) { - LogWarning($"Failed to open {PortName}: {exception.Message}"); + LogWarning($"Failed to open {PortName}: {exception.GetType().Name}: {exception.Message}"); return false; } } @@ -2092,6 +2148,7 @@ namespace DanroJump.Hardware.Com handshakeCompletion = NewCompletion(); chalCompletion = NewCompletion(); requestHex = GenerateRandomBlock16Hex(); + LogInfo($"{PortName} handshake: sending challenge request command 07."); Send("07" + requestHex, true); var (chalReceived, _) = await UniTask.WhenAny( @@ -2104,12 +2161,14 @@ namespace DanroJump.Hardware.Com return; } + LogInfo($"{PortName} handshake: CHAL received, waiting RAES."); var (handshakeFinished, handshakeSucceeded) = await UniTask.WhenAny( handshakeCompletion.Task, UniTask.Delay(handshakeTimeoutMs, DelayType.Realtime, PlayerLoopTiming.Update, token)); await UniTask.SwitchToThreadPool(); if (handshakeFinished && handshakeSucceeded) { + LogInfo($"{PortName} handshake: RAES verified."); MarkOperational(); return; } @@ -2126,12 +2185,14 @@ namespace DanroJump.Hardware.Com if (handshakeStage == HandshakeStage.WaitingChal && line.StartsWith("CHAL", StringComparison.Ordinal)) { + LogInfo($"{PortName} handshake line received: CHAL length={line.Length}."); HandleChal(line); return; } if (handshakeStage == HandshakeStage.WaitingRaes && line.StartsWith("RAES", StringComparison.Ordinal)) { + LogInfo($"{PortName} handshake line received: RAES length={line.Length}."); HandleRaes(line); } } @@ -2207,6 +2268,7 @@ namespace DanroJump.Hardware.Com isOperational = true; handshakeStage = HandshakeStage.Completed; missedInputResponses = 0; + LogInfo($"{PortName} marked operational. Sending startup commands."); Send("2171", true); Send("890003", true); @@ -2230,6 +2292,7 @@ namespace DanroJump.Hardware.Com if (!string.IsNullOrWhiteSpace(roleRequestCommand)) { roleCompletion = NewCompletion(); + LogInfo($"{PortName} identity: requesting role with command {roleRequestCommand}, expecting prefix {roleResponsePrefix}."); Send(roleRequestCommand); var (roleReceived, role) = await UniTask.WhenAny( @@ -2239,10 +2302,16 @@ namespace DanroJump.Hardware.Com if (roleReceived) { Role = role; + LogInfo($"{PortName} identity: role received '{Role}'."); + } + else + { + LogInfo($"{PortName} identity: role request timed out after {roleRequestTimeoutMs} ms."); } } macCompletion = NewCompletion(); + LogInfo($"{PortName} identity: requesting MAC with command 06."); Send("06"); var (macReceived, macAddress) = await UniTask.WhenAny( @@ -2252,6 +2321,11 @@ namespace DanroJump.Hardware.Com if (macReceived) { MacAddress = macAddress; + LogInfo($"{PortName} identity: MAC received {MacAddress}."); + } + else + { + LogInfo($"{PortName} identity: MAC request timed out after {roleRequestTimeoutMs} ms."); } } catch (OperationCanceledException) @@ -2528,6 +2602,7 @@ namespace DanroJump.Hardware.Com } isOperational = false; + LogInfo($"{PortName} notifying disconnected."); localCts?.Cancel(); ClosePort(); Disconnected?.Invoke(this); @@ -2559,9 +2634,15 @@ namespace DanroJump.Hardware.Com serialPort.Dispose(); serialPort = null; + LogInfo($"{PortName} serial port closed."); } } + private void LogInfo(string message) + { + logInfo?.Invoke(message); + } + private string EncryptHexBlock16(string plainHex) { byte[] plain = HexToBytes(plainHex); diff --git a/Assets/Scripts/Runtime/Hardware/Com/Transports/FallbackComTransport.cs b/Assets/Scripts/Runtime/Hardware/Com/Transports/FallbackComTransport.cs new file mode 100644 index 0000000..99d22d9 --- /dev/null +++ b/Assets/Scripts/Runtime/Hardware/Com/Transports/FallbackComTransport.cs @@ -0,0 +1,228 @@ +using System; +using UnityEngine; + +namespace DanroJump.Hardware.Com +{ + /// + /// Поставщик портов, который переходит на запасной источник, если основной не нашел порты или упал. + /// + public sealed class FallbackComPortProvider : IComPortProvider + { + private readonly IComPortProvider primaryProvider; + private readonly IComPortProvider fallbackProvider; + + public FallbackComPortProvider(IComPortProvider primaryProvider, IComPortProvider fallbackProvider) + { + this.primaryProvider = primaryProvider ?? throw new ArgumentNullException(nameof(primaryProvider)); + this.fallbackProvider = fallbackProvider ?? throw new ArgumentNullException(nameof(fallbackProvider)); + } + + public string[] GetPortNames() + { + try + { + string[] primaryPorts = primaryProvider.GetPortNames() ?? Array.Empty(); + if (primaryPorts.Length > 0) + { + return primaryPorts; + } + + Debug.LogWarning("[ComSystem] Primary COM port provider returned no ports. Trying native ComNative provider."); + } + catch (Exception exception) + { + Debug.LogWarning($"[ComSystem] Primary COM port provider failed: {exception.GetType().Name}: {exception.Message}. Trying native ComNative provider."); + } + + return fallbackProvider.GetPortNames() ?? Array.Empty(); + } + } + + /// + /// Фабрика транспорта, которая открывает порт через основной backend и переключается на запасной при ошибке. + /// + public sealed class FallbackComTransportFactory : IComTransportFactory + { + private readonly IComTransportFactory primaryFactory; + private readonly IComTransportFactory fallbackFactory; + + public FallbackComTransportFactory(IComTransportFactory primaryFactory, IComTransportFactory fallbackFactory) + { + this.primaryFactory = primaryFactory ?? throw new ArgumentNullException(nameof(primaryFactory)); + this.fallbackFactory = fallbackFactory ?? throw new ArgumentNullException(nameof(fallbackFactory)); + } + + public IComTransport Create(SerialSettings settings) + { + return new FallbackComTransport(settings, primaryFactory, fallbackFactory); + } + } + + internal sealed class FallbackComTransport : IComTransport + { + private const string PrimaryBackendName = "System.IO.Ports"; + private const string FallbackBackendName = "ComNative"; + + private readonly SerialSettings settings; + private readonly IComTransportFactory primaryFactory; + private readonly IComTransportFactory fallbackFactory; + private IComTransport activeTransport; + private string activeBackendName = ""; + + public FallbackComTransport( + SerialSettings settings, + IComTransportFactory primaryFactory, + IComTransportFactory fallbackFactory) + { + this.settings = settings; + this.primaryFactory = primaryFactory; + this.fallbackFactory = fallbackFactory; + } + + public bool IsOpen => activeTransport != null && activeTransport.IsOpen; + + public void Open() + { + if (IsOpen) + { + return; + } + + Exception primaryException = null; + try + { + OpenWith(primaryFactory, PrimaryBackendName); + return; + } + catch (Exception exception) + { + primaryException = exception; + DisposeActiveTransport(); + Debug.LogWarning($"[ComSystem] System.IO.Ports failed for {settings.PortName}: {exception.GetType().Name}: {exception.Message}. Trying ComNative."); + } + + try + { + OpenWith(fallbackFactory, FallbackBackendName); + } + catch (Exception fallbackException) + { + DisposeActiveTransport(); + throw new InvalidOperationException( + $"Both COM backends failed for {settings.PortName}. " + + $"System.IO.Ports: {primaryException.GetType().Name}: {primaryException.Message}. " + + $"ComNative: {fallbackException.GetType().Name}: {fallbackException.Message}", + fallbackException); + } + } + + public void Close() + { + activeTransport?.Close(); + } + + public void DiscardInBuffer() + { + try + { + RequireActiveTransport().DiscardInBuffer(); + } + catch (Exception exception) when (TrySwitchToFallback(exception, "discard input buffer")) + { + RequireActiveTransport().DiscardInBuffer(); + } + } + + public void DiscardOutBuffer() + { + try + { + RequireActiveTransport().DiscardOutBuffer(); + } + catch (Exception exception) when (TrySwitchToFallback(exception, "discard output buffer")) + { + RequireActiveTransport().DiscardOutBuffer(); + } + } + + public string ReadLine() + { + try + { + return RequireActiveTransport().ReadLine(); + } + catch (TimeoutException) + { + throw; + } + catch (Exception exception) when (TrySwitchToFallback(exception, "read")) + { + return RequireActiveTransport().ReadLine(); + } + } + + public void WriteLine(string value) + { + try + { + RequireActiveTransport().WriteLine(value); + } + catch (Exception exception) when (TrySwitchToFallback(exception, "write")) + { + RequireActiveTransport().WriteLine(value); + } + } + + public void Dispose() + { + DisposeActiveTransport(); + } + + private void OpenWith(IComTransportFactory factory, string backendName) + { + activeTransport = factory.Create(settings); + activeTransport.Open(); + activeBackendName = backendName; + Debug.Log($"[ComSystem] {settings.PortName} serial backend selected: {activeBackendName}."); + } + + private IComTransport RequireActiveTransport() + { + if (activeTransport == null) + { + throw new InvalidOperationException($"COM port {settings.PortName} has no active transport backend."); + } + + return activeTransport; + } + + private bool TrySwitchToFallback(Exception exception, string operationName) + { + if (!string.Equals(activeBackendName, PrimaryBackendName, StringComparison.Ordinal)) + { + return false; + } + + Debug.LogWarning( + $"[ComSystem] {PrimaryBackendName} {operationName} failed for {settings.PortName}: " + + $"{exception.GetType().Name}: {exception.Message}. Switching to {FallbackBackendName}."); + + DisposeActiveTransport(); + OpenWith(fallbackFactory, FallbackBackendName); + return true; + } + + private void DisposeActiveTransport() + { + try + { + activeTransport?.Dispose(); + } + finally + { + activeTransport = null; + activeBackendName = ""; + } + } + } +} diff --git a/Assets/Scripts/Runtime/Hardware/Com/Transports/FallbackComTransport.cs.meta b/Assets/Scripts/Runtime/Hardware/Com/Transports/FallbackComTransport.cs.meta new file mode 100644 index 0000000..2bf94bf --- /dev/null +++ b/Assets/Scripts/Runtime/Hardware/Com/Transports/FallbackComTransport.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 931f17b413d5e4b4ab7315bb21e810a0 \ No newline at end of file diff --git a/Assets/Scripts/Runtime/Hardware/Com/Transports/NativeComTransport.cs b/Assets/Scripts/Runtime/Hardware/Com/Transports/NativeComTransport.cs new file mode 100644 index 0000000..5fd6131 --- /dev/null +++ b/Assets/Scripts/Runtime/Hardware/Com/Transports/NativeComTransport.cs @@ -0,0 +1,266 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using UnityEngine; + +namespace DanroJump.Hardware.Com +{ + /// + /// Получает список COM-портов через native-плагин ComNative. + /// + public sealed class NativeComPortProvider : IComPortProvider + { + public string[] GetPortNames() + { + try + { + string[] ports = ComNativeMethods.ListPorts(); + Debug.Log($"[ComSystem] ComNative.ListPorts returned: {(ports.Length > 0 ? string.Join(", ", ports) : "")}"); + return ports; + } + catch (Exception exception) + { + Debug.LogWarning($"[ComSystem] ComNative.ListPorts failed: {exception.GetType().Name}: {exception.Message}"); + return Array.Empty(); + } + } + } + + /// + /// Создает COM-транспорт через native-плагин ComNative. + /// + public sealed class NativeComTransportFactory : IComTransportFactory + { + public IComTransport Create(SerialSettings settings) + { + return new NativeComTransport(settings); + } + } + + /// + /// Native serial-транспорт для IL2CPP-билдов, где System.IO.Ports.SerialPort может быть недоступен. + /// + internal sealed class NativeComTransport : IComTransport + { + private const int ReadBufferSize = 4096; + private const int MaxFlushLines = 100; + + private readonly SerialSettings settings; + private int handleId; + + public NativeComTransport(SerialSettings settings) + { + this.settings = settings; + } + + public bool IsOpen => handleId > 0; + + public void Open() + { + if (IsOpen) + { + return; + } + + handleId = ComNativeMethods.Serial_Open( + settings.PortName, + settings.BaudRate, + settings.DataBits, + ConvertParity(settings.Parity), + ConvertStopBits(settings.StopBits), + settings.ReadTimeoutMs, + settings.WriteTimeoutMs); + + if (handleId <= 0) + { + string nativeError = ComNativeMethods.GetLastError(); + handleId = 0; + throw new IOException($"ComNative failed to open {settings.PortName}: {nativeError}"); + } + + Debug.Log($"[ComSystem] ComNative opened {settings.PortName} with handle {handleId}."); + } + + public void Close() + { + if (handleId <= 0) + { + return; + } + + int closingHandle = handleId; + handleId = 0; + int result = ComNativeMethods.Serial_Close(closingHandle); + if (result < 0) + { + Debug.LogWarning($"[ComSystem] ComNative close failed for handle {closingHandle}: {ComNativeMethods.GetLastError()}"); + } + } + + public void DiscardInBuffer() + { + EnsureOpen(); + + byte[] buffer = new byte[ReadBufferSize]; + for (int i = 0; i < MaxFlushLines; i++) + { + int read = ComNativeMethods.Serial_ReadLineUTF8_bytes(handleId, buffer, buffer.Length); + if (read <= 0) + { + return; + } + } + } + + public void DiscardOutBuffer() + { + // Native plugin has no explicit outgoing-buffer flush API. + } + + public string ReadLine() + { + EnsureOpen(); + + byte[] buffer = new byte[ReadBufferSize]; + int read = ComNativeMethods.Serial_ReadLineUTF8_bytes(handleId, buffer, buffer.Length); + if (read == 0) + { + throw new TimeoutException(); + } + + if (read < 0) + { + throw new IOException($"ComNative read failed on {settings.PortName}: {ComNativeMethods.GetLastError()}"); + } + + int count = Math.Min(read, buffer.Length); + return Encoding.UTF8.GetString(buffer, 0, count).TrimEnd('\r', '\n'); + } + + public void WriteLine(string value) + { + EnsureOpen(); + + int result = ComNativeMethods.Serial_WriteStringUTF8(handleId, value ?? "", appendNewline: 1); + if (result < 0) + { + throw new IOException($"ComNative write failed on {settings.PortName}: {ComNativeMethods.GetLastError()}"); + } + } + + public void Dispose() + { + Close(); + } + + private void EnsureOpen() + { + if (!IsOpen) + { + throw new InvalidOperationException($"ComNative port {settings.PortName} is not open."); + } + } + + private static int ConvertParity(SerialParity parity) + { + return parity switch + { + SerialParity.Odd => 1, + SerialParity.Even => 2, + SerialParity.Mark => 3, + SerialParity.Space => 4, + _ => 0 + }; + } + + private static int ConvertStopBits(SerialStopBits stopBits) + { + return stopBits switch + { + SerialStopBits.Two => 2, + SerialStopBits.OnePointFive => 3, + _ => 1 + }; + } + } + + internal static class ComNativeMethods + { + private const string Dll = "ComNative"; + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_ListPortsLength(); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_ListPorts(StringBuilder buffer, int bufferChars); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_Open( + string portName, + int baud, + int dataBits, + int parity, + int stopBits, + int readTimeoutMs, + int writeTimeoutMs); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Serial_Close(int handleId); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Serial_CloseAll(); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern int Serial_WriteStringUTF8(int handleId, string utf8, int appendNewline); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Serial_ReadLineUTF8")] + public static extern int Serial_ReadLineUTF8_bytes(int handleId, byte[] buffer, int bufferSize); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_GetLastErrorW(StringBuilder buffer, int bufferChars); + + public static string GetLastError() + { + int requiredLength = Serial_GetLastErrorW(null, 0); + if (requiredLength < 0) + { + return $"Serial_GetLastErrorW failed with code {requiredLength}."; + } + + var buffer = new StringBuilder(Math.Max(requiredLength, 1)); + int result = Serial_GetLastErrorW(buffer, buffer.Capacity); + return result < 0 ? $"Serial_GetLastErrorW failed with code {result}." : buffer.ToString(); + } + + public static string[] ListPorts() + { + int requiredLength = Serial_ListPortsLength(); + if (requiredLength <= 1) + { + return Array.Empty(); + } + + var buffer = new StringBuilder(requiredLength); + int result = Serial_ListPorts(buffer, buffer.Capacity); + if (result < 0) + { + throw new IOException($"ComNative port listing failed: {GetLastError()}"); + } + + string joined = buffer.ToString(); + if (string.IsNullOrWhiteSpace(joined)) + { + return Array.Empty(); + } + + string[] rawPorts = joined.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < rawPorts.Length; i++) + { + rawPorts[i] = rawPorts[i].Trim(); + } + + return rawPorts; + } + } +} diff --git a/Assets/Scripts/Runtime/Hardware/Com/Transports/NativeComTransport.cs.meta b/Assets/Scripts/Runtime/Hardware/Com/Transports/NativeComTransport.cs.meta new file mode 100644 index 0000000..168ea28 --- /dev/null +++ b/Assets/Scripts/Runtime/Hardware/Com/Transports/NativeComTransport.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 19898e41b209f744f91b180136b8f18c \ No newline at end of file diff --git a/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionComPortProvider.cs b/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionComPortProvider.cs index 457ed4e..f68cbdb 100644 --- a/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionComPortProvider.cs +++ b/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionComPortProvider.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using System.Reflection; using UnityEngine; @@ -9,29 +10,189 @@ namespace DanroJump.Hardware.Com /// public sealed class ReflectionComPortProvider : IComPortProvider { - private static readonly Type SerialPortType = Type.GetType("System.IO.Ports.SerialPort, System"); + private static readonly Type SerialPortType = FindSerialPortType(); + private static bool diagnosticLogged; /// /// Возвращает имена доступных serial-портов или пустой список, если API недоступен. /// public string[] GetPortNames() { + LogSerialPortDiagnosticOnce(); + + string[] registryPorts = GetWindowsRegistryPortNames(); if (SerialPortType == null) { Debug.LogWarning("[ComSystem] System.IO.Ports.SerialPort is not available in this Unity profile."); - return Array.Empty(); + return registryPorts; } try { - return (string[])SerialPortType.GetMethod("GetPortNames", BindingFlags.Public | BindingFlags.Static) - ?.Invoke(null, null) ?? Array.Empty(); + var getPortNamesMethod = SerialPortType.GetMethod("GetPortNames", BindingFlags.Public | BindingFlags.Static); + if (getPortNamesMethod == null) + { + Debug.LogWarning("[ComSystem] System.IO.Ports.SerialPort.GetPortNames() is not available."); + return registryPorts; + } + + var ports = (string[])getPortNamesMethod.Invoke(null, null) ?? Array.Empty(); + Debug.Log($"[ComSystem] SerialPort.GetPortNames returned: {(ports.Length > 0 ? string.Join(", ", ports) : "")}"); + return ports.Length > 0 ? ports : registryPorts; } catch (Exception exception) { - Debug.LogWarning($"[ComSystem] Failed to list COM ports: {Unwrap(exception).Message}"); + Exception unwrapped = Unwrap(exception); + Debug.LogWarning($"[ComSystem] Failed to list COM ports via SerialPort.GetPortNames: {unwrapped.GetType().Name}: {unwrapped.Message}"); + return registryPorts; + } + } + + private static string[] GetWindowsRegistryPortNames() + { +#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN + try + { + Type registryType = FindType("Microsoft.Win32.Registry"); + Type registryKeyType = FindType("Microsoft.Win32.RegistryKey"); + if (registryType == null || registryKeyType == null) + { + Debug.Log("[ComSystem] Windows registry API is not available for COM fallback."); + return Array.Empty(); + } + + object localMachine = registryType.GetProperty("LocalMachine", BindingFlags.Public | BindingFlags.Static) + ?.GetValue(null); + if (localMachine == null) + { + localMachine = registryType.GetField("LocalMachine", BindingFlags.Public | BindingFlags.Static) + ?.GetValue(null); + } + + if (localMachine == null) + { + Debug.Log("[ComSystem] Windows registry fallback could not access LocalMachine."); + return Array.Empty(); + } + + MethodInfo openSubKey = registryKeyType.GetMethod( + "OpenSubKey", + BindingFlags.Public | BindingFlags.Instance, + null, + new[] { typeof(string) }, + null); + MethodInfo getValueNames = registryKeyType.GetMethod( + "GetValueNames", + BindingFlags.Public | BindingFlags.Instance, + null, + Type.EmptyTypes, + null); + MethodInfo getValue = registryKeyType.GetMethod( + "GetValue", + BindingFlags.Public | BindingFlags.Instance, + null, + new[] { typeof(string) }, + null); + + if (openSubKey == null || getValueNames == null || getValue == null) + { + Debug.Log("[ComSystem] Windows registry fallback methods are not available."); + return Array.Empty(); + } + + object serialCommKey = openSubKey.Invoke(localMachine, new object[] { @"HARDWARE\DEVICEMAP\SERIALCOMM" }); + if (serialCommKey == null) + { + Debug.Log("[ComSystem] Windows registry fallback did not find HARDWARE\\DEVICEMAP\\SERIALCOMM."); + return Array.Empty(); + } + + using (serialCommKey as IDisposable) + { + var valueNames = (string[])getValueNames.Invoke(serialCommKey, null) ?? Array.Empty(); + var ports = new List(); + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (string valueName in valueNames) + { + string portName = getValue.Invoke(serialCommKey, new object[] { valueName }) as string; + if (string.IsNullOrWhiteSpace(portName)) + { + continue; + } + + portName = portName.Trim(); + if (!seen.Add(portName)) + { + continue; + } + + ports.Add(portName); + } + + ports.Sort(StringComparer.OrdinalIgnoreCase); + Debug.Log($"[ComSystem] Windows registry COM fallback returned: {(ports.Count > 0 ? string.Join(", ", ports) : "")}"); + return ports.ToArray(); + } + } + catch (Exception exception) + { + Exception unwrapped = Unwrap(exception); + Debug.LogWarning($"[ComSystem] Windows registry COM fallback failed: {unwrapped.GetType().Name}: {unwrapped.Message}"); return Array.Empty(); } +#else + return Array.Empty(); +#endif + } + + private static Type FindSerialPortType() + { + var type = Type.GetType("System.IO.Ports.SerialPort, System", false) ?? + Type.GetType("System.IO.Ports.SerialPort, System.IO.Ports", false); + if (type != null) + { + return type; + } + + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + type = assembly.GetType("System.IO.Ports.SerialPort", false); + if (type != null) + { + return type; + } + } + + return null; + } + + private static void LogSerialPortDiagnosticOnce() + { + if (diagnosticLogged) + { + return; + } + + diagnosticLogged = true; + string message = SerialPortType == null + ? "SerialPort API resolution failed." + : $"SerialPort API resolved from assembly: {SerialPortType.Assembly.FullName}"; + Debug.Log($"[ComSystem] {message}"); + } + + private static Type FindType(string fullName) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + Type type = assembly.GetType(fullName, false); + if (type != null) + { + return type; + } + } + + return null; } /// diff --git a/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionSerialComTransport.cs b/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionSerialComTransport.cs index 8af8216..f7a4e81 100644 --- a/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionSerialComTransport.cs +++ b/Assets/Scripts/Runtime/Hardware/Com/Transports/ReflectionSerialComTransport.cs @@ -25,9 +25,9 @@ namespace DanroJump.Hardware.Com { private const BindingFlags InstancePublicFlags = BindingFlags.Public | BindingFlags.Instance; - private static readonly Type SerialPortType = Type.GetType("System.IO.Ports.SerialPort, System"); - private static readonly Type ParityType = Type.GetType("System.IO.Ports.Parity, System"); - private static readonly Type StopBitsType = Type.GetType("System.IO.Ports.StopBits, System"); + private static readonly Type SerialPortType = FindSystemIoPortsType("SerialPort"); + private static readonly Type ParityType = FindSystemIoPortsType("Parity"); + private static readonly Type StopBitsType = FindSystemIoPortsType("StopBits"); private static readonly PropertyInfo IsOpenProperty = SerialPortType?.GetProperty("IsOpen", InstancePublicFlags); private static readonly PropertyInfo DataBitsProperty = SerialPortType?.GetProperty("DataBits", InstancePublicFlags); private static readonly PropertyInfo ParityProperty = SerialPortType?.GetProperty("Parity", InstancePublicFlags); @@ -64,7 +64,9 @@ namespace DanroJump.Hardware.Com { if (SerialPortType == null) { - throw new InvalidOperationException("System.IO.Ports.SerialPort is not available."); + throw new InvalidOperationException( + "System.IO.Ports.SerialPort is not available. " + + "Check Unity API compatibility/scripting backend and whether System.IO.Ports is present in the player."); } EnsureRequiredMetadata(); @@ -214,5 +216,27 @@ namespace DanroJump.Hardware.Com return Enum.Parse(enumType, value); } + + private static Type FindSystemIoPortsType(string typeName) + { + string fullName = "System.IO.Ports." + typeName; + var type = Type.GetType(fullName + ", System", false) ?? + Type.GetType(fullName + ", System.IO.Ports", false); + if (type != null) + { + return type; + } + + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + type = assembly.GetType(fullName, false); + if (type != null) + { + return type; + } + } + + return null; + } } } diff --git a/Assets/Scripts/Runtime/Settings/Core/ServiceSettingsActionRouter.cs b/Assets/Scripts/Runtime/Settings/Core/ServiceSettingsActionRouter.cs index 4dfff72..c9c7f32 100644 --- a/Assets/Scripts/Runtime/Settings/Core/ServiceSettingsActionRouter.cs +++ b/Assets/Scripts/Runtime/Settings/Core/ServiceSettingsActionRouter.cs @@ -48,8 +48,8 @@ namespace DanroJump.Settings ServiceSettingsKeys.Currency), ServiceSettingsKeys.HopperTestButton => ServiceSettingsActionResult.Success( "Тест выдачи", - "Тест выдачи подготовлен. Аппаратная команда будет подключена через призовой контур.", - "Проверка hopper/выдачи"), + "Запускается тест хоппера: мотор выдачи будет включен до срабатывания датчика или таймаута.", + ServiceSettingsKeys.HopperTestButton), ServiceSettingsKeys.LevelsPrizesButton => ServiceSettingsActionResult.Success( "Настройки призов", PrizeInfoFormatter.Build(settings), diff --git a/Assets/Scripts/Runtime/UI/Gameplay/ContinueGameOverlayWindow.cs b/Assets/Scripts/Runtime/UI/Gameplay/ContinueGameOverlayWindow.cs index aba66a7..2eab619 100644 --- a/Assets/Scripts/Runtime/UI/Gameplay/ContinueGameOverlayWindow.cs +++ b/Assets/Scripts/Runtime/UI/Gameplay/ContinueGameOverlayWindow.cs @@ -43,6 +43,12 @@ namespace DanroJump.UI.Gameplay menuButton.RegisterCallback(HandleButtonFocusIn); menuButton.RegisterCallback(HandleButtonFocusOut); } + + if (root != null) + { + root.RegisterCallback(HandleKeyDown, TrickleDown.TrickleDown); + root.RegisterCallback(HandleNavigationMove, TrickleDown.TrickleDown); + } } public event Action ContinueRequested; @@ -124,6 +130,12 @@ namespace DanroJump.UI.Gameplay menuButton.UnregisterCallback(HandleButtonFocusIn); menuButton.UnregisterCallback(HandleButtonFocusOut); } + + if (root != null) + { + root.UnregisterCallback(HandleKeyDown, TrickleDown.TrickleDown); + root.UnregisterCallback(HandleNavigationMove, TrickleDown.TrickleDown); + } } private static readonly Color FocusedButtonTint = new Color32(0xCE, 0xFF, 0x1A, 0xFF); @@ -178,5 +190,102 @@ namespace DanroJump.UI.Gameplay { return Time.unscaledTime < inputEnabledAtTime; } + + private void HandleKeyDown(KeyDownEvent evt) + { + if (IsInputGuardActive()) + { + return; + } + + var focusedElement = root?.panel?.focusController?.focusedElement as VisualElement; + + var direction = 0; + switch (evt.keyCode) + { + case KeyCode.Tab: + direction = evt.shiftKey ? -1 : 1; + break; + case KeyCode.LeftArrow: + case KeyCode.UpArrow: + case KeyCode.A: + case KeyCode.W: + direction = -1; + break; + case KeyCode.RightArrow: + case KeyCode.DownArrow: + case KeyCode.D: + case KeyCode.S: + direction = 1; + break; + } + + if (direction != 0) + { + NavigateFocus(focusedElement, direction); + evt.StopPropagation(); + } + } + + private void HandleNavigationMove(NavigationMoveEvent evt) + { + if (IsInputGuardActive()) + { + return; + } + + var focusedElement = root?.panel?.focusController?.focusedElement as VisualElement; + var direction = 0; + switch (evt.direction) + { + case NavigationMoveEvent.Direction.Left: + case NavigationMoveEvent.Direction.Up: + direction = -1; + break; + case NavigationMoveEvent.Direction.Right: + case NavigationMoveEvent.Direction.Down: + direction = 1; + break; + } + + if (direction != 0) + { + NavigateFocus(focusedElement, direction); + evt.StopPropagation(); + } + } + + private void NavigateFocus(VisualElement current, int direction) + { + if (continueButton == null || menuButton == null) + { + return; + } + + if (!continueButton.enabledSelf) + { + menuButton.Focus(); + return; + } + + if (current == continueButton) + { + if (direction > 0) + { + menuButton.Focus(); + } + } + else if (current == menuButton) + { + if (direction < 0) + { + continueButton.Focus(); + } + } + else + { + FocusDefaultButton(); + } + } } } diff --git a/Assets/Scripts/Runtime/UI/Gameplay/GameplayHudController.cs b/Assets/Scripts/Runtime/UI/Gameplay/GameplayHudController.cs index 99961b7..f402f3e 100644 --- a/Assets/Scripts/Runtime/UI/Gameplay/GameplayHudController.cs +++ b/Assets/Scripts/Runtime/UI/Gameplay/GameplayHudController.cs @@ -21,7 +21,7 @@ namespace DanroJump.UI.Gameplay { [SerializeField] private VisualTreeAsset visualTreeAsset; [SerializeField] private PanelSettings panelSettings; - [SerializeField] private int sortingOrder = 10; + [SerializeField] private int sortingOrder = 5000; [SerializeField] private string scoreLabelName = "ScoreValue"; [SerializeField] private string heightLabelName = "HeightValue"; [SerializeField] private string bestHeightLabelName = "BestHeightValue"; @@ -59,6 +59,17 @@ namespace DanroJump.UI.Gameplay private bool isSubscribedToSession; private bool isSubscribedToPrizeFlow; + private int lastScore = -1; + private int lastHeight = -1; + private int lastRecord = -1; + private int lastLives = -1; + private int lastCoins = -1; + private int lastDeaths = -1; + private int lastCombo = -1; + private GameplaySessionState lastState = (GameplaySessionState)(-1); + private bool isSubscribedToSettings; + private bool enabledStatsInSettings; + /// /// Получает игровую сессию из DI-контейнера сцены. /// @@ -91,6 +102,7 @@ namespace DanroJump.UI.Gameplay BindPrizeOverlay(); BindContinueOverlay(); ShowLastPrizeResultIfNeeded(); + ResetCache(); Refresh(); } @@ -101,6 +113,7 @@ namespace DanroJump.UI.Gameplay { SubscribeToSession(); SubscribeToPrizeFlow(); + SubscribeToSettings(); if (refreshCancellation == null) { @@ -108,6 +121,7 @@ namespace DanroJump.UI.Gameplay RefreshContinuouslyAsync(refreshCancellation.Token).Forget(Debug.LogException); } + ResetCache(); Refresh(); } @@ -139,6 +153,7 @@ namespace DanroJump.UI.Gameplay UnsubscribeFromSession(); UnsubscribeFromPrizeFlow(); + UnsubscribeFromSettings(); prizeResultOverlay?.Dispose(); prizeResultOverlay = null; prizeOverlayRoot = null; @@ -162,9 +177,18 @@ namespace DanroJump.UI.Gameplay return; } - document.visualTreeAsset = visualTreeAsset; - document.panelSettings = panelSettings; - document.sortingOrder = sortingOrder; + if (document.visualTreeAsset != visualTreeAsset) + { + document.visualTreeAsset = visualTreeAsset; + } + if (document.panelSettings != panelSettings) + { + document.panelSettings = panelSettings; + } + if (document.sortingOrder != sortingOrder) + { + document.sortingOrder = sortingOrder; + } } /// @@ -191,6 +215,11 @@ namespace DanroJump.UI.Gameplay private void BindPrizeOverlay() { + if (prizeResultOverlay != null && prizeOverlayRoot != null && prizeOverlayRoot.panel != null) + { + return; + } + var root = document != null ? document.rootVisualElement : null; var overlayRoot = root?.Q("PrizeResultOverlay"); if (overlayRoot == null) @@ -198,12 +227,6 @@ namespace DanroJump.UI.Gameplay return; } - if (prizeResultOverlay != null && prizeOverlayRoot == overlayRoot) - { - return; - } - - prizeResultOverlay?.Dispose(); prizeOverlayRoot = overlayRoot; prizeResultOverlay = new PrizeResultOverlayWindow(overlayRoot, audioService); prizeResultOverlay.ReturnRequested += HandlePrizeReturnRequested; @@ -211,6 +234,11 @@ namespace DanroJump.UI.Gameplay private void BindContinueOverlay() { + if (continueGameOverlay != null && continueOverlayRoot != null && continueOverlayRoot.panel != null) + { + return; + } + var root = document != null ? document.rootVisualElement : null; var overlayRoot = root?.Q("ContinueGameOverlay"); if (overlayRoot == null) @@ -218,12 +246,6 @@ namespace DanroJump.UI.Gameplay return; } - if (continueGameOverlay != null && continueOverlayRoot == overlayRoot) - { - return; - } - - continueGameOverlay?.Dispose(); continueOverlayRoot = overlayRoot; continueGameOverlay = new ContinueGameOverlayWindow(overlayRoot, audioService); continueGameOverlay.ContinueRequested += HandleContinueRequested; @@ -243,29 +265,76 @@ namespace DanroJump.UI.Gameplay return; } - SetLabel(scoreLabel, session.Score.ToString()); - SetLabel(heightLabel, $"{session.MaxRunHeight:0} м"); - SetLabel(bestHeightLabel, $"{session.RecordHeight:0} м"); - SetLabel(livesLabel, session.RemainingLives.ToString()); - SetLabel(coinsLabel, session.Coins.ToString()); - SetLabel(deathsLabel, session.DeathCount.ToString()); - SetLabel(stateLabel, FormatState(session.State)); + int currentScore = session.Score; + if (currentScore != lastScore) + { + SetLabel(scoreLabel, currentScore.ToString()); + lastScore = currentScore; + } + + int currentHeight = Mathf.FloorToInt(session.MaxRunHeight); + if (currentHeight != lastHeight) + { + SetLabel(heightLabel, $"{currentHeight} м"); + lastHeight = currentHeight; + } + + int currentRecord = Mathf.FloorToInt(session.RecordHeight); + if (currentRecord != lastRecord) + { + SetLabel(bestHeightLabel, $"{currentRecord} м"); + lastRecord = currentRecord; + } + + int currentLives = session.RemainingLives; + if (currentLives != lastLives) + { + SetLabel(livesLabel, currentLives.ToString()); + lastLives = currentLives; + } + + int currentCoins = session.Coins; + if (currentCoins != lastCoins) + { + SetLabel(coinsLabel, currentCoins.ToString()); + lastCoins = currentCoins; + } + + int currentDeaths = session.DeathCount; + if (currentDeaths != lastDeaths) + { + SetLabel(deathsLabel, currentDeaths.ToString()); + lastDeaths = currentDeaths; + } + + GameplaySessionState currentState = session.State; + if (currentState != lastState) + { + SetLabel(stateLabel, FormatState(currentState)); + lastState = currentState; + } if (comboLabel != null) { - if (session.CurrentCombo > 1) + int currentCombo = session.CurrentCombo; + if (currentCombo > 1) { - var comboText = $"x{session.CurrentCombo} COMBO!"; - if (comboLabel.text != comboText) + if (currentCombo != lastCombo) { + var comboText = $"x{currentCombo} COMBO!"; comboLabel.text = comboText; comboLabel.style.display = DisplayStyle.Flex; PulseComboLabel(); + lastCombo = currentCombo; } } else { - comboLabel.style.display = DisplayStyle.None; + if (lastCombo != currentCombo) + { + comboLabel.style.display = DisplayStyle.None; + lastCombo = currentCombo; + } } } @@ -320,12 +389,14 @@ namespace DanroJump.UI.Gameplay return; } - developmentStatsRoot.style.display = ShouldShowDevelopmentStats - ? DisplayStyle.Flex - : DisplayStyle.None; + var targetDisplay = ShouldShowDevelopmentStats ? DisplayStyle.Flex : DisplayStyle.None; + if (developmentStatsRoot.style.display != targetDisplay) + { + developmentStatsRoot.style.display = targetDisplay; + } } - private static bool ShouldShowDevelopmentStats => Debug.isDebugBuild || Application.isEditor; + private bool ShouldShowDevelopmentStats => enabledStatsInSettings && (Debug.isDebugBuild || Application.isEditor); /// /// Подстраховывает HUD от событий, которые могли прийти до подписки. @@ -368,10 +439,60 @@ namespace DanroJump.UI.Gameplay UnsubscribeFromSession(); session = injectedSession; + ResetCache(); SubscribeToSession(); Refresh(); } + private void ResetCache() + { + lastScore = -1; + lastHeight = -1; + lastRecord = -1; + lastLives = -1; + lastCoins = -1; + lastDeaths = -1; + lastCombo = -1; + lastState = (GameplaySessionState)(-1); + } + + private void SubscribeToSettings() + { + if (serviceSettings == null || isSubscribedToSettings) + { + return; + } + + serviceSettings.SettingChanged += HandleSettingChanged; + isSubscribedToSettings = true; + UpdateStatsVisibilityCache(); + } + + private void UnsubscribeFromSettings() + { + if (serviceSettings == null || !isSubscribedToSettings) + { + return; + } + + serviceSettings.SettingChanged -= HandleSettingChanged; + isSubscribedToSettings = false; + } + + private void HandleSettingChanged(string key, DanroJump.Settings.ServiceSettingValue value) + { + if (key == DanroJump.Settings.ServiceSettingsKeys.StatisticsButton) + { + UpdateStatsVisibilityCache(); + RefreshDevelopmentStatsVisibility(); + } + } + + private void UpdateStatsVisibilityCache() + { + enabledStatsInSettings = serviceSettings != null && serviceSettings.GetBool(DanroJump.Settings.ServiceSettingsKeys.StatisticsButton); + } + private void SubscribeToSession() { if (!isActiveAndEnabled || session == null || isSubscribedToSession) @@ -612,10 +733,9 @@ namespace DanroJump.UI.Gameplay if (result.RewardKind == PrizeRewardKind.Hopper) { - success = await DispenseHopperAsync(comSystem, token); - detailMessage = success - ? "Капсула выдана успешно!" - : "Ошибка выдачи: датчик хоппера не сработал за 10 секунд."; + var hopperResult = await ComHopperDispenser.DispenseAsync(comSystem, token); + success = hopperResult.IsSuccess; + detailMessage = hopperResult.Message; } else if (result.RewardKind == PrizeRewardKind.PrizeStand) { @@ -676,35 +796,5 @@ namespace DanroJump.UI.Gameplay } } - private async UniTask DispenseHopperAsync(IComSystem com, CancellationToken token) - { - if (com == null) - { - return false; - } - - if (!com.SendToMain("301")) - { - return false; - } - - var tcs = new UniTaskCompletionSource(); - UnityEngine.Events.UnityAction onTriggered = () => tcs.TrySetResult(); - com.HopperSensorTriggered.AddListener(onTriggered); - - try - { - await UniTask.WhenAny( - tcs.Task, - UniTask.Delay(TimeSpan.FromSeconds(10), cancellationToken: token) - ); - return tcs.Task.Status == UniTaskStatus.Succeeded; - } - finally - { - com.HopperSensorTriggered.RemoveListener(onTriggered); - com.SendToMain("300"); - } - } } } diff --git a/Assets/Scripts/Runtime/UI/MainMenu/MainMenuController.cs b/Assets/Scripts/Runtime/UI/MainMenu/MainMenuController.cs index 337c41f..cde3d5f 100644 --- a/Assets/Scripts/Runtime/UI/MainMenu/MainMenuController.cs +++ b/Assets/Scripts/Runtime/UI/MainMenu/MainMenuController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System; using System.Threading; using Cysharp.Threading.Tasks; using DanroJump.Audio; @@ -66,6 +67,7 @@ namespace DanroJump.UI.MainMenu private IReadOnlyServiceSettings serviceSettings; private IServiceSettingsService serviceSettingsService; private ISceneFlowService sceneFlow; + private IComSystem comSystem; private IComCreditWallet creditWallet; private IGameAudioService audioService; private IPrizeSessionStateService prizeSessionState; @@ -92,6 +94,7 @@ namespace DanroJump.UI.MainMenu serviceSettingsService = injectedSettingsService; serviceSettings = injectedServiceSettings ?? injectedSettingsService; sceneFlow = injectedSceneFlow; + comSystem = injectedComSystem; creditWallet = injectedComSystem; audioService = injectedAudioService; prizeSessionState = injectedPrizeSessionState; @@ -253,6 +256,11 @@ namespace DanroJump.UI.MainMenu return; } + if (TryRunHopperTest(result)) + { + return; + } + if (serviceInfoModal == null || serviceInfoModal.Root == null) { return; @@ -262,6 +270,64 @@ namespace DanroJump.UI.MainMenu modalStack?.Open(serviceInfoModal); } + private bool TryRunHopperTest(ServiceSettingsActionResult result) + { + if (!string.Equals(result.FocusKey, ServiceSettingsKeys.HopperTestButton, StringComparison.Ordinal)) + { + return false; + } + + if (serviceInfoModal == null || serviceInfoModal.Root == null) + { + return false; + } + + serviceInfoModal.SetContent( + "Тест выдачи", + "Запускается тест хоппера...", + "Хоппер"); + modalStack?.Open(serviceInfoModal); + RunHopperTestAsync(this.GetCancellationTokenOnDestroy()).Forget(Debug.LogException); + return true; + } + + private async UniTask RunHopperTestAsync(CancellationToken cancellationToken) + { + ResolveComSystemIfNeeded(); + + if (comSystem == null) + { + audioService?.Play(GameAudioEvent.UiError); + serviceInfoModal?.SetContent( + "Тест выдачи", + "Ошибка: COM-система недоступна. Проверь подключение платы и запуск COM-сервиса.", + "Хоппер"); + return; + } + + try + { + serviceInfoModal?.SetContent( + "Тест выдачи", + "Хоппер включен. Ожидаю срабатывание датчика выдачи...", + "Хоппер"); + + var result = await ComHopperDispenser.DispenseAsync(comSystem, cancellationToken); + audioService?.Play(result.IsSuccess ? GameAudioEvent.UiSubmit : GameAudioEvent.UiError); + serviceInfoModal?.SetContent( + "Тест выдачи", + result.Message, + "Хоппер"); + } + catch (OperationCanceledException) + { + serviceInfoModal?.SetContent( + "Тест выдачи", + "Тест хоппера отменен.", + "Хоппер"); + } + } + private bool TryOpenServiceTextInput(ServiceSettingsActionResult result) { if (serviceSettingsService == null || @@ -659,7 +725,13 @@ namespace DanroJump.UI.MainMenu if (creditWallet == null && projectContext.Container.HasBinding()) { - creditWallet = projectContext.Container.Resolve(); + comSystem = projectContext.Container.Resolve(); + creditWallet = comSystem; + } + + if (comSystem == null && projectContext.Container.HasBinding()) + { + comSystem = projectContext.Container.Resolve(); } if (creditWallet == null && projectContext.Container.HasBinding()) @@ -693,7 +765,18 @@ namespace DanroJump.UI.MainMenu } ResolveServiceSettingsIfNeeded(); - creditWallet ??= ComSystem.Instance; + creditWallet ??= comSystem ?? ComSystem.Instance; + } + + private void ResolveComSystemIfNeeded() + { + if (comSystem != null) + { + return; + } + + ResolveServiceSettingsIfNeeded(); + comSystem ??= ComSystem.Instance; } private void RegisterCreditCallbacks() diff --git a/Assets/Tests/EditMode/Editor/ComHopperDispenserTests.cs b/Assets/Tests/EditMode/Editor/ComHopperDispenserTests.cs new file mode 100644 index 0000000..7274c44 --- /dev/null +++ b/Assets/Tests/EditMode/Editor/ComHopperDispenserTests.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Cysharp.Threading.Tasks; +using DanroJump.Hardware.Com; +using NUnit.Framework; + +namespace DanroJump.Tests.EditMode +{ + public sealed class ComHopperDispenserTests + { + [Test] + public async Task DispenseAsync_StartsAndStopsMotorOnTimeout() + { + var com = new StubComSystem { IsRunningValue = true }; + + var result = await ComHopperDispenser + .DispenseAsync(com, TimeSpan.Zero, CancellationToken.None) + .AsTask(); + + Assert.That(result.IsSuccess, Is.False); + Assert.That(result.Message, Does.Contain("Датчик хоппера")); + Assert.That(com.SentCommands, Is.EqualTo(new[] + { + ComHopperDispenser.StartMotorCommand, + ComHopperDispenser.StopMotorCommand + })); + } + + [Test] + public async Task DispenseAsync_NotRunningComReturnsFailureWithoutCommands() + { + var com = new StubComSystem { IsRunningValue = false }; + + var result = await ComHopperDispenser + .DispenseAsync(com, TimeSpan.Zero, CancellationToken.None) + .AsTask(); + + Assert.That(result.IsSuccess, Is.False); + Assert.That(result.Message, Does.Contain("не запущена")); + Assert.That(com.SentCommands, Is.Empty); + } + + private sealed class StubComSystem : IComSystem + { + public readonly List SentCommands = new(); + + public bool IsRunningValue { get; set; } + public bool IsRunning => IsRunningValue; + public string MainDeviceId => "main"; + public string PrizeStandDeviceId => "stand"; + public IReadOnlyCollection ConnectedDeviceIds => new[] { "main" }; + public UnityEngine.Events.UnityEvent DeviceAuthorized { get; } = new(); + public UnityEngine.Events.UnityEvent DeviceDisconnected { get; } = new(); + public UnityEngine.Events.UnityEvent PrizeBoxOpened { get; } = new(); + public int Credits => 0; + public UnityEngine.Events.UnityEvent CreditAdded { get; } = new(); + public UnityEngine.Events.UnityEvent CreditRemoved { get; } = new(); + public UnityEngine.Events.UnityEvent CreditsChanged { get; } = new(); + public string LastMainInputBits => string.Empty; + public string LastMainAdcValues => string.Empty; + public UnityEngine.Events.UnityEvent MainInputReceived { get; } = new(); + public UnityEngine.Events.UnityEvent MainAdcReceived { get; } = new(); + public UnityEngine.Events.UnityEvent HopperSensorTriggered { get; } = new(); + + public void StartSystem() { } + public void StopSystem(bool sendShutdownCommands) { } + public bool SendToMain(string command) + { + SentCommands.Add(command); + return true; + } + public bool SendToDevice(string deviceId, string command) => true; + public void SendToAll(string command) { } + public string GetDeviceRole(string deviceId) => string.Empty; + public string GetDeviceMac(string deviceId) => string.Empty; + public UniTask> GetPrizeBoxStatusesAsync(CancellationToken cancellationToken = default) + { + return UniTask.FromResult>(new Dictionary()); + } + public UniTask GetReadyPrizeBoxesAsync(CancellationToken cancellationToken = default) + { + return UniTask.FromResult(Array.Empty()); + } + public UniTask OpenPrizeBoxAsync(int boxNumber, CancellationToken cancellationToken = default) + { + return UniTask.FromResult(PrizeOpenResult.Fail(boxNumber, "stub")); + } + public UniTask ForceOpenPrizeBoxAsync(int boxNumber, CancellationToken cancellationToken = default) + { + return UniTask.FromResult(PrizeOpenResult.Fail(boxNumber, "stub")); + } + public bool SetPrizeStandIdleLighting() => true; + public bool HighlightPrizeBox(int boxNumber) => true; + public void AddCredit(int amount = 1) { } + public bool TrySpendCredits(int amount = 1) => false; + public void SetCredits(int value) { } + public string GetLastInputBits(string deviceId) => string.Empty; + public string GetLastAdcValues(string deviceId) => string.Empty; + public void PlayBoardLight(LightMode mode, float cycles = -1f) { } + public void PlayBoardLightSequence(IReadOnlyList steps, bool loop) { } + public void PlayDefaultBoardLight() { } + public void StopBoardLightSequence() { } + public void StopAllBoardEffects() { } + public void BlinkHat() { } + public void BlinkHat(float intervalSeconds) { } + public void StopHatBlink(bool leaveHatEnabled = true) { } + public void PauseLighting() { } + public void ResumeLighting() { } + } + } +} diff --git a/Assets/Tests/EditMode/Editor/ComHopperDispenserTests.cs.meta b/Assets/Tests/EditMode/Editor/ComHopperDispenserTests.cs.meta new file mode 100644 index 0000000..e9af5e1 --- /dev/null +++ b/Assets/Tests/EditMode/Editor/ComHopperDispenserTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d77880810c968aa408b9b36b0ee7e9ea \ No newline at end of file diff --git a/Assets/Tests/EditMode/Editor/ComTransportFallbackTests.cs b/Assets/Tests/EditMode/Editor/ComTransportFallbackTests.cs new file mode 100644 index 0000000..0b159be --- /dev/null +++ b/Assets/Tests/EditMode/Editor/ComTransportFallbackTests.cs @@ -0,0 +1,168 @@ +using System; +using DanroJump.Hardware.Com; +using NUnit.Framework; + +namespace DanroJump.Tests.EditMode +{ + public sealed class ComTransportFallbackTests + { + private static readonly SerialSettings TestSettings = new( + "COM_TEST", + 115200, + 8, + SerialParity.None, + SerialStopBits.One, + 50, + 100); + + [Test] + public void FallbackComTransportFactory_Open_UsesPrimaryWhenPrimarySucceeds() + { + var primaryTransport = new FakeComTransport(); + var fallbackTransport = new FakeComTransport(); + var factory = new FallbackComTransportFactory( + new FakeComTransportFactory(primaryTransport), + new FakeComTransportFactory(fallbackTransport)); + + using IComTransport transport = factory.Create(TestSettings); + transport.Open(); + + Assert.That(primaryTransport.OpenCount, Is.EqualTo(1)); + Assert.That(fallbackTransport.OpenCount, Is.EqualTo(0)); + Assert.That(transport.IsOpen, Is.True); + } + + [Test] + public void FallbackComTransportFactory_Open_UsesFallbackWhenPrimaryOpenFails() + { + var primaryTransport = new FakeComTransport(openException: new InvalidOperationException("SerialPort failed")); + var fallbackTransport = new FakeComTransport(); + var factory = new FallbackComTransportFactory( + new FakeComTransportFactory(primaryTransport), + new FakeComTransportFactory(fallbackTransport)); + + using IComTransport transport = factory.Create(TestSettings); + transport.Open(); + + Assert.That(primaryTransport.OpenCount, Is.EqualTo(1)); + Assert.That(primaryTransport.DisposeCount, Is.EqualTo(1)); + Assert.That(fallbackTransport.OpenCount, Is.EqualTo(1)); + Assert.That(transport.IsOpen, Is.True); + } + + [Test] + public void FallbackComTransportFactory_DiscardInBuffer_UsesFallbackWhenPrimaryOperationFails() + { + var primaryTransport = new FakeComTransport(discardInException: new NotSupportedException("Discard failed")); + var fallbackTransport = new FakeComTransport(); + var factory = new FallbackComTransportFactory( + new FakeComTransportFactory(primaryTransport), + new FakeComTransportFactory(fallbackTransport)); + + using IComTransport transport = factory.Create(TestSettings); + transport.Open(); + transport.DiscardInBuffer(); + + Assert.That(primaryTransport.OpenCount, Is.EqualTo(1)); + Assert.That(primaryTransport.DisposeCount, Is.EqualTo(1)); + Assert.That(fallbackTransport.OpenCount, Is.EqualTo(1)); + Assert.That(fallbackTransport.DiscardInBufferCount, Is.EqualTo(1)); + Assert.That(transport.IsOpen, Is.True); + } + + [Test] + public void FallbackComPortProvider_GetPortNames_UsesFallbackWhenPrimaryReturnsEmpty() + { + var provider = new FallbackComPortProvider( + new FakeComPortProvider(Array.Empty()), + new FakeComPortProvider(new[] { "COM3" })); + + string[] ports = provider.GetPortNames(); + + Assert.That(ports, Is.EqualTo(new[] { "COM3" })); + } + + private sealed class FakeComPortProvider : IComPortProvider + { + private readonly string[] ports; + + public FakeComPortProvider(string[] ports) + { + this.ports = ports; + } + + public string[] GetPortNames() + { + return ports; + } + } + + private sealed class FakeComTransportFactory : IComTransportFactory + { + private readonly IComTransport transport; + + public FakeComTransportFactory(IComTransport transport) + { + this.transport = transport; + } + + public IComTransport Create(SerialSettings settings) + { + return transport; + } + } + + private sealed class FakeComTransport : IComTransport + { + private readonly Exception openException; + private readonly Exception discardInException; + private bool isOpen; + + public FakeComTransport(Exception openException = null, Exception discardInException = null) + { + this.openException = openException; + this.discardInException = discardInException; + } + + public int OpenCount { get; private set; } + public int DiscardInBufferCount { get; private set; } + public int DisposeCount { get; private set; } + public bool IsOpen => isOpen; + + public void Open() + { + OpenCount++; + if (openException != null) + { + throw openException; + } + + isOpen = true; + } + + public void Close() + { + isOpen = false; + } + + public void DiscardInBuffer() + { + DiscardInBufferCount++; + if (discardInException != null) + { + throw discardInException; + } + } + + public void DiscardOutBuffer() { } + public string ReadLine() => ""; + public void WriteLine(string value) { } + + public void Dispose() + { + DisposeCount++; + Close(); + } + } + } +} diff --git a/Assets/Tests/EditMode/Editor/ComTransportFallbackTests.cs.meta b/Assets/Tests/EditMode/Editor/ComTransportFallbackTests.cs.meta new file mode 100644 index 0000000..2763cbc --- /dev/null +++ b/Assets/Tests/EditMode/Editor/ComTransportFallbackTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 58c3e63a06e9fc04eac935acec081a3c \ No newline at end of file diff --git a/Assets/Tests/EditMode/Editor/GameplayHudControllerTests.cs b/Assets/Tests/EditMode/Editor/GameplayHudControllerTests.cs index 8c31264..6fa67ec 100644 --- a/Assets/Tests/EditMode/Editor/GameplayHudControllerTests.cs +++ b/Assets/Tests/EditMode/Editor/GameplayHudControllerTests.cs @@ -21,7 +21,7 @@ namespace DanroJump.Tests.EditMode try { var controller = gameObject.AddComponent(); - var session = new GameplaySession(null, new StubSettings(canContinue: false)); + var session = new GameplaySession(null, new StubSettings(canContinue: true)); TestObjectFactory.SetPrivateField(session, "k__BackingField", GameplaySessionState.GameOver); var overlayRoot = CreateContinueOverlayRoot(); @@ -76,7 +76,7 @@ namespace DanroJump.Tests.EditMode public IReadOnlyDictionary Snapshot() => new Dictionary(); public ServiceSettingValue Get(string key) => default; public bool GetBool(string key) => key == ServiceSettingsKeys.CanContinueGame && canContinue; - public int GetInt(string key) => 0; + public int GetInt(string key) => key == ServiceSettingsKeys.ContinueGameCost ? 1 : 0; public float GetFloat(string key) => 0f; public string GetString(string key) => string.Empty; public int GetOptionIndex(string key) => 0; @@ -118,6 +118,41 @@ namespace DanroJump.Tests.EditMode } } + [Test] + public void RefreshDevelopmentStatsVisibility_RequiresServiceSetting() + { + var gameObject = new GameObject("GameplayHudControllerTest"); + try + { + var controller = gameObject.AddComponent(); + var statsRoot = new VisualElement { name = "RightStats" }; + + TestObjectFactory.SetPrivateField(controller, "developmentStatsRoot", statsRoot); + TestObjectFactory.SetPrivateField(controller, "enabledStatsInSettings", false); + + InvokeRefreshDevelopmentStatsVisibility(controller); + + Assert.That(statsRoot.style.display.value, Is.EqualTo(DisplayStyle.None)); + + TestObjectFactory.SetPrivateField(controller, "enabledStatsInSettings", true); + + InvokeRefreshDevelopmentStatsVisibility(controller); + + Assert.That(statsRoot.style.display.value, Is.EqualTo(DisplayStyle.Flex)); + } + finally + { + UnityEngine.Object.DestroyImmediate(gameObject); + } + } + + private static void InvokeRefreshDevelopmentStatsVisibility(GameplayHudController controller) + { + typeof(GameplayHudController) + .GetMethod("RefreshDevelopmentStatsVisibility", BindingFlags.Instance | BindingFlags.NonPublic) + ?.Invoke(controller, null); + } + private sealed class StubComSystem : IComSystem { public List SentCommands = new List(); diff --git a/Assets/Tests/EditMode/Editor/OneWayPlatformColliderTests.cs b/Assets/Tests/EditMode/Editor/OneWayPlatformColliderTests.cs new file mode 100644 index 0000000..4e695e5 --- /dev/null +++ b/Assets/Tests/EditMode/Editor/OneWayPlatformColliderTests.cs @@ -0,0 +1,79 @@ +using DanroJump.Gameplay; +using NUnit.Framework; +using UnityEngine; + +namespace DanroJump.Tests.EditMode +{ + public sealed class OneWayPlatformColliderTests + { + private GameObject playerObject; + private GameObject platformObject; + + [TearDown] + public void TearDown() + { + if (playerObject != null) + { + Object.DestroyImmediate(playerObject); + } + + if (platformObject != null) + { + Object.DestroyImmediate(platformObject); + } + } + + [Test] + public void ShouldCollideWith_ReturnsFalseWhenPlayerBottomIsBelowPlatformTop() + { + var player = CreatePlayer(centerY: 0.45f, previousGroundCheckY: 0.5f); + var oneWayPlatform = CreatePlatform(topY: 0f); + + Assert.That(oneWayPlatform.ShouldCollideWith(player), Is.False); + } + + [Test] + public void ShouldCollideWith_ReturnsTrueWhenPlayerBottomIsAtPlatformTop() + { + var player = CreatePlayer(centerY: 0.5f, previousGroundCheckY: 0.5f); + var oneWayPlatform = CreatePlatform(topY: 0f); + + Assert.That(oneWayPlatform.ShouldCollideWith(player), Is.True); + } + + private JumpPlayerController CreatePlayer(float centerY, float previousGroundCheckY) + { + playerObject = new GameObject("OneWay Platform Test Player"); + playerObject.transform.position = new Vector3(0f, centerY, 0f); + + var groundCheckObject = new GameObject("GroundCheck"); + groundCheckObject.transform.SetParent(playerObject.transform, false); + groundCheckObject.transform.localPosition = new Vector3(0f, -0.5f, 0f); + + var body = playerObject.AddComponent(); + body.gravityScale = 0f; + body.linearVelocity = Vector2.down; + playerObject.AddComponent(); + + var player = playerObject.AddComponent(); + TestObjectFactory.SetPrivateField(player, "k__BackingField", previousGroundCheckY); + TestObjectFactory.SetPrivateField(player, "k__BackingField", -1f); + return player; + } + + private OneWayPlatformCollider CreatePlatform(float topY) + { + platformObject = new GameObject("OneWay Platform Test Platform"); + platformObject.transform.position = Vector3.zero; + + var platformCollider = platformObject.AddComponent(); + platformCollider.size = new Vector2(2f, 0.2f); + platformCollider.offset = new Vector2(0f, topY - platformCollider.size.y * 0.5f); + + var oneWayPlatform = platformObject.AddComponent(); + oneWayPlatform.Construct(playerObject.GetComponent()); + oneWayPlatform.UpdateCachedTopY(); + return oneWayPlatform; + } + } +} diff --git a/Assets/Tests/EditMode/Editor/OneWayPlatformColliderTests.cs.meta b/Assets/Tests/EditMode/Editor/OneWayPlatformColliderTests.cs.meta new file mode 100644 index 0000000..3830bab --- /dev/null +++ b/Assets/Tests/EditMode/Editor/OneWayPlatformColliderTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6eedb3e116ba1504b8694f7757c6f212 \ No newline at end of file diff --git a/Assets/Tests/EditMode/Editor/ServiceSettingsActionRouterTests.cs b/Assets/Tests/EditMode/Editor/ServiceSettingsActionRouterTests.cs index f033525..612320b 100644 --- a/Assets/Tests/EditMode/Editor/ServiceSettingsActionRouterTests.cs +++ b/Assets/Tests/EditMode/Editor/ServiceSettingsActionRouterTests.cs @@ -140,6 +140,22 @@ namespace DanroJump.Tests.EditMode Assert.That(result.Status, Does.Contain("малый приз включён")); } + [Test] + public void HopperTestButton_ReturnsHopperTestFocusKey() + { + var definition = TestObjectFactory.CreateSettingDefinition( + ServiceSettingsKeys.HopperTestButton, + ServiceSettingValueType.Button, + ServiceSettingValue.Button()); + + var result = ServiceSettingsActionRouter.Execute(definition, new StubReadOnlySettings()); + + Assert.That(result.Handled, Is.True); + Assert.That(result.Title, Is.EqualTo("Тест выдачи")); + Assert.That(result.FocusKey, Is.EqualTo(ServiceSettingsKeys.HopperTestButton)); + Assert.That(result.Status, Does.Contain("тест хоппера")); + } + [Test] public void UnknownButton_ReturnsSafeFallback() diff --git a/Assets/UI/Gameplay/GameplayHud.uss b/Assets/UI/Gameplay/GameplayHud.uss index 4a2c8d8..07652a5 100644 --- a/Assets/UI/Gameplay/GameplayHud.uss +++ b/Assets/UI/Gameplay/GameplayHud.uss @@ -3,31 +3,52 @@ left: 0; right: 0; top: 0; + bottom: 0; padding-left: 24px; padding-right: 24px; padding-top: 20px; - flex-direction: row; - justify-content: space-between; - align-items: flex-start; + flex-direction: column; } .hud-column { flex-direction: column; + flex-grow: 0; + flex-shrink: 0; } .hud-column-left { - min-width: 460px; + position: absolute; + left: 24px; + top: 20px; + width: 240px; + min-width: 0; + max-width: 240px; } .hud-column-right { - min-width: 500px; + position: absolute; + right: 24px; + top: 190px; + width: 360px; + min-width: 0; + max-width: 360px; align-items: flex-end; } .hud-center { - flex-grow: 1; + position: absolute; + left: 50%; + top: 20px; + width: 500px; + height: 130px; + min-width: 0; + max-width: 500px; + margin-left: -250px; + flex-grow: 0; + flex-shrink: 0; align-items: center; justify-content: flex-start; + background-size: 100% 100%; } .hud-plaque, @@ -45,20 +66,22 @@ } .hud-plaque-score { - width: 450px; - min-height: 154px; - padding-left: 64px; - padding-right: 64px; - padding-top: 24px; - padding-bottom: 22px; + width: 240px; + min-height: 104px; + padding-left: 14px; + padding-right: 24px; + padding-top: 16px; + padding-bottom: 14px; background-image: url("project://database/Assets/Sprites/UI/Components/plashka_side.png"); } .hud-height-plaque { - min-width: 560px; - height: 156px; - padding-left: 68px; - padding-right: 68px; + width: 500px; + min-width: 0; + max-width: 500px; + height: 130px; + padding-left: 82px; + padding-right: 82px; flex-direction: row; align-items: center; justify-content: center; @@ -66,7 +89,10 @@ } .hud-height-text { - min-width: 280px; + width: 280px; + min-width: 0; + max-width: 280px; + margin-top: 0; align-items: center; } @@ -86,45 +112,45 @@ } .hud-score-value { - margin-top: -6px; + margin-top: -14px; color: rgb(31, 79, 63); - font-size: 66px; + font-size: 36px; -unity-font-style: bold; } .hud-height-value { margin-top: -2px; color: rgb(31, 79, 63); - font-size: 58px; + font-size: 32px; -unity-font-style: bold; -unity-text-align: middle-center; } .hud-stat-list { - width: 500px; + width: 360px; align-items: stretch; } .hud-stat-row { - height: 86px; - margin-bottom: 10px; - padding-left: 34px; - padding-right: 42px; + height: 72px; + margin-bottom: 8px; + padding-left: 24px; + padding-right: 28px; flex-direction: row; align-items: center; background-image: url("project://database/Assets/Sprites/UI/Components/plashka_small.png"); } .hud-stat-caption { - width: 156px; - margin-left: 14px; - font-size: 20px; + width: 124px; + margin-left: 10px; + font-size: 18px; } .hud-stat-value { flex-grow: 1; color: rgb(31, 79, 63); - font-size: 34px; + font-size: 28px; -unity-font-style: bold; -unity-text-align: middle-right; } @@ -149,8 +175,8 @@ } .hud-mini-icon { - width: 42px; - height: 42px; + width: 36px; + height: 36px; -unity-background-scale-mode: scale-to-fit; background-size: contain; } @@ -209,8 +235,8 @@ .overlay-panel { max-width: 1180px; - min-width: 760px; - min-height: 430px; + min-width: 900px; + min-height: 520px; padding-left: 86px; padding-right: 86px; padding-top: 72px; @@ -222,7 +248,8 @@ .prize-panel { max-width: 1280px; - min-height: 500px; + min-width: 1100px; + min-height: 650px; } .overlay-title { @@ -299,18 +326,17 @@ } .overlay-button { - min-width: 360px; - height: 106px; + min-width: 420px; + height: 120px; color: rgb(31, 79, 63); - font-size: 36px; + font-size: 32px; -unity-font-style: bold; background-image: url("project://database/Assets/Sprites/UI/Components/button_white.png"); -unity-font-definition: url("project://database/Assets/Fonts/RifficFree-Bold.ttf?fileID=12800000&guid=63991fec8a53abe4d9a7907ef7f0db77&type=3#RifficFree-Bold"); } .overlay-button-primary { - min-width: 430px; - background-image: url("project://database/Assets/Sprites/UI/Components/button.png"); + min-width: 480px; } .overlay-button-secondary { @@ -319,7 +345,7 @@ .overlay-button-single { margin-top: 66px; - min-width: 430px; + min-width: 480px; } .prize-stand-container { diff --git a/Assets/UI/Gameplay/GameplayHud.uxml b/Assets/UI/Gameplay/GameplayHud.uxml index 777c914..98ac704 100644 --- a/Assets/UI/Gameplay/GameplayHud.uxml +++ b/Assets/UI/Gameplay/GameplayHud.uxml @@ -2,22 +2,22 @@