diff --git a/Assets/Canto URP.asset b/Assets/Canto URP.asset index 9645d3f..0169bb9 100644 --- a/Assets/Canto URP.asset +++ b/Assets/Canto URP.asset @@ -25,7 +25,7 @@ MonoBehaviour: m_SupportsTerrainHoles: 1 m_SupportsHDR: 1 m_HDRColorBufferPrecision: 0 - m_MSAA: 1 + m_MSAA: 2 m_RenderScale: 1 m_UpscalingFilter: 0 m_FsrOverrideSharpness: 0 diff --git a/Assets/Resources/Glock17.fbx b/Assets/Resources/Glock17.fbx new file mode 100644 index 0000000..11d5008 Binary files /dev/null and b/Assets/Resources/Glock17.fbx differ diff --git a/Assets/Resources/Glock17.fbx.meta b/Assets/Resources/Glock17.fbx.meta new file mode 100644 index 0000000..f4ec38d --- /dev/null +++ b/Assets/Resources/Glock17.fbx.meta @@ -0,0 +1,107 @@ +fileFormatVersion: 2 +guid: bf9d862a19647f28795df2e666425003 +ModelImporter: + serializedVersion: 22200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 0 + motionNodeName: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importPhysicalCameras: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 1 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + strictVertexDataChecks: 0 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + importBlendShapeDeformPercent: 1 + remapMaterialsIfMaterialImportModeIsNone: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Main.glb b/Assets/Resources/Main.glb new file mode 100644 index 0000000..0bfa787 Binary files /dev/null and b/Assets/Resources/Main.glb differ diff --git a/Assets/Resources/Main.glb.meta b/Assets/Resources/Main.glb.meta new file mode 100644 index 0000000..920b6de --- /dev/null +++ b/Assets/Resources/Main.glb.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 16d8ce8164e4d9a59bd9c28737489417 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SOAP/Items/DebugKey.asset b/Assets/SOAP/Items/DebugKey.asset index 6a38ed1..39d524d 100644 --- a/Assets/SOAP/Items/DebugKey.asset +++ b/Assets/SOAP/Items/DebugKey.asset @@ -19,6 +19,6 @@ MonoBehaviour: type: 0 stackable: 0 maxStackCount: 10 - icon: {fileID: 0} + icon: {fileID: -1304905567622442630, guid: 4e01e0da3979f115d951c3cb798e4313, type: 3} instancePrefab: {fileID: 2892703864492016621, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} previewPrefab: {fileID: 0} diff --git a/Assets/SOAP/Items/TestInventory.asset b/Assets/SOAP/Items/TestInventory.asset index 4d26a01..80aeecc 100644 --- a/Assets/SOAP/Items/TestInventory.asset +++ b/Assets/SOAP/Items/TestInventory.asset @@ -12,5 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 9702da9b93bb25bf089c54dc37cc44e7, type: 3} m_Name: TestInventory m_EditorClassIdentifier: - serializableItems: [] + serializableItems: + - Item: {fileID: 11400000, guid: 3a9b5fc9b523890cf8abd310dbc64940, type: 2} + count: 1 capacity: 8 diff --git a/Assets/Scenes/RecycleView.unity b/Assets/Scenes/RecycleView.unity new file mode 100644 index 0000000..1f5080d --- /dev/null +++ b/Assets/Scenes/RecycleView.unity @@ -0,0 +1,406 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 1 + m_PVRFilteringGaussRadiusAO: 1 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1223357529 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1223357531} + - component: {fileID: 1223357530} + - component: {fileID: 1223357532} + m_Layer: 5 + m_Name: UIDocument + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1223357530 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223357529} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_PanelSettings: {fileID: 11400000, guid: d8894ba51a816c451b072d957a1cfe45, type: 2} + m_ParentUI: {fileID: 0} + sourceAsset: {fileID: 9197481963319205126, guid: 0c1a0528290b37272b20d94b19311768, type: 3} + m_SortingOrder: 0 + m_WorldSpaceSizeMode: 1 + m_WorldSpaceWidth: 1920 + m_WorldSpaceHeight: 1080 +--- !u!4 &1223357531 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223357529} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1223357532 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1223357529} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a819c4e399f6cc6c84ace2a31f18d56, type: 3} + m_Name: + m_EditorClassIdentifier: + uiDocument: {fileID: 1223357530} +--- !u!1 &1411077289 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1411077291} + - component: {fileID: 1411077290} + - component: {fileID: 1411077292} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1411077290 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1411077289} + m_Enabled: 1 + serializedVersion: 11 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ForceVisible: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 + m_LightUnit: 1 + m_LuxAtDistance: 1 + m_EnableSpotReflector: 1 +--- !u!4 &1411077291 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1411077289} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &1411077292 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1411077289} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Version: 3 + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_RenderingLayers: 1 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_ShadowRenderingLayers: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 0 +--- !u!1 &1889780342 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1889780345} + - component: {fileID: 1889780344} + - component: {fileID: 1889780343} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1889780343 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889780342} + m_Enabled: 1 +--- !u!20 &1889780344 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889780342} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1889780345 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889780342} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1889780345} + - {fileID: 1411077291} + - {fileID: 1223357531} diff --git a/Assets/Scenes/RecycleView.unity.meta b/Assets/Scenes/RecycleView.unity.meta new file mode 100644 index 0000000..7d06e44 --- /dev/null +++ b/Assets/Scenes/RecycleView.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 374b635465e0bf8888b5381eb4a6d9b6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 85d2cd5..c9ab256 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -527,7 +527,7 @@ Camera: m_Depth: -1 m_CullingMask: serializedVersion: 2 - m_Bits: 4294967295 + m_Bits: 255 m_RenderingPath: -1 m_TargetTexture: {fileID: 0} m_TargetDisplay: 0 @@ -2755,7 +2755,7 @@ GameObject: m_Layer: 6 m_Name: Interaction Collider m_TagString: Untagged - m_Icon: {fileID: -5397416234189338067, guid: 0000000000000000d000000000000000, type: 0} + m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 diff --git a/Assets/Scenes/UI.unity b/Assets/Scenes/UI.unity new file mode 100644 index 0000000..f729ce1 --- /dev/null +++ b/Assets/Scenes/UI.unity @@ -0,0 +1,518 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 1 + m_PVRFilteringGaussRadiusAO: 1 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1001 &344266626 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalPosition.y + value: -5 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalRotation.w + value: 0.8535535 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalRotation.x + value: -0.35355338 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalRotation.y + value: -0.35355338 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalRotation.z + value: -0.1464466 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: -45 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: -45 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -5922622823359544968, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: -5568371526223589888, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: -2407699025559542186, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: -2059808378567916338, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: -1919110505097373512, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: 919132149155446097, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Name + value: Glock17 + objectReference: {fileID: 0} + - target: {fileID: 919132149155446097, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: 6619225641134156614, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + - target: {fileID: 7556193500268387829, guid: bf9d862a19647f28795df2e666425003, type: 3} + propertyPath: m_Layer + value: 8 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: bf9d862a19647f28795df2e666425003, type: 3} +--- !u!1 &955626052 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 955626054} + - component: {fileID: 955626053} + - component: {fileID: 955626055} + m_Layer: 8 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &955626053 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 955626052} + m_Enabled: 1 + serializedVersion: 11 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 128 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ForceVisible: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 + m_LightUnit: 1 + m_LuxAtDistance: 1 + m_EnableSpotReflector: 1 +--- !u!4 &955626054 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 955626052} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &955626055 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 955626052} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Version: 3 + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_RenderingLayers: 128 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_ShadowRenderingLayers: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 0 +--- !u!1 &1368180367 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1368180370} + - component: {fileID: 1368180369} + - component: {fileID: 1368180371} + m_Layer: 8 + m_Name: Preview Camera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &1368180369 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1368180367} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 4 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 256 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 8400000, guid: fc19a022409ab0d408fa0e78cfeab827, type: 2} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1368180370 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1368180367} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: -3} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1368180371 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1368180367} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 + m_TaaSettings: + m_Quality: 3 + m_FrameInfluence: 0.1 + m_JitterScale: 1 + m_MipBias: 0 + m_VarianceClampScale: 0.9 + m_ContrastAdaptiveSharpening: 0 +--- !u!1 &2064440267 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2064440269} + - component: {fileID: 2064440268} + m_Layer: 8 + m_Name: UIDocument + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2064440268 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2064440267} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_PanelSettings: {fileID: 11400000, guid: d8894ba51a816c451b072d957a1cfe45, type: 2} + m_ParentUI: {fileID: 0} + sourceAsset: {fileID: 9197481963319205126, guid: deb1ce82aafbc3b128a0ec5a8cc03439, type: 3} + m_SortingOrder: 0 + m_WorldSpaceSizeMode: 1 + m_WorldSpaceWidth: 1920 + m_WorldSpaceHeight: 1080 +--- !u!4 &2064440269 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2064440267} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 955626054} + - {fileID: 2064440269} + - {fileID: 1368180370} + - {fileID: 344266626} diff --git a/Assets/Scenes/UI.unity.meta b/Assets/Scenes/UI.unity.meta new file mode 100644 index 0000000..977e508 --- /dev/null +++ b/Assets/Scenes/UI.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f04d37138af3f5f50aaec2e381a77f12 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/PropertyDrawers/ConditionalDisplayPropertyDrawer.cs b/Assets/Scripts/Editor/PropertyDrawers/ConditionalDisplayPropertyDrawer.cs index 1ecd814..64fb700 100644 --- a/Assets/Scripts/Editor/PropertyDrawers/ConditionalDisplayPropertyDrawer.cs +++ b/Assets/Scripts/Editor/PropertyDrawers/ConditionalDisplayPropertyDrawer.cs @@ -1,9 +1,9 @@ using System; using KitsuneCafe.System.Attributes; -using Unity.Properties; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; +using Debug = UnityEngine.Debug; [CustomPropertyDrawer(typeof(ConditionalDisplayAttribute), true)] public class ConditionalDisplayPropertyDrawer : PropertyDrawer @@ -11,17 +11,36 @@ public class ConditionalDisplayPropertyDrawer : PropertyDrawer public override VisualElement CreatePropertyGUI(SerializedProperty property) { var attribute = this.attribute as ConditionalDisplayAttribute; - var serializedObject = property.serializedObject; - var names = attribute.Properties; - var length = names.Length; - var properties = new SerializedProperty[length]; + var properties = GetPropertiesFrom(property, attribute.Properties); + return new SimpleConditionPropertyField(properties, attribute, property); + } - for (int i = 0; i < length; i++) + private SerializedProperty[] GetPropertiesFrom(SerializedProperty property, string[] propertyNames) + { + var len = propertyNames.Length; + var properties = new SerializedProperty[len]; + var path = property.propertyPath; + var index = path.LastIndexOf('.'); + var obj = property.serializedObject; + + if (index > -1) { - properties[i] = serializedObject.FindProperty(names[i]); + var parentPath = path[..path.LastIndexOf('.')]; + var parent = obj.FindProperty(parentPath); + for (int i = 0; i < len; i++) + { + properties[i] = parent.FindPropertyRelative(propertyNames[i]); + } + } + else + { + for (int i = 0; i < len; i++) + { + properties[i] = obj.FindProperty(propertyNames[i]); + } } - return new BoolConditionPropertyField(properties, attribute, property); + return properties; } public abstract class ConditionalPropertyField : PropertyField @@ -57,13 +76,13 @@ public class ConditionalDisplayPropertyDrawer : PropertyDrawer protected abstract void OnTrackedValueChanged(SerializedProperty property); } - public class BoolConditionPropertyField : ConditionalPropertyField + public class SimpleConditionPropertyField : ConditionalPropertyField { - public BoolConditionPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property) : base(properties, attribute, property) + public SimpleConditionPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property) : base(properties, attribute, property) { } - public BoolConditionPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property, string label) : base(properties, attribute, property, label) + public SimpleConditionPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property, string label) : base(properties, attribute, property, label) { } @@ -76,16 +95,105 @@ public class ConditionalDisplayPropertyDrawer : PropertyDrawer _ => false }; - for (int i = 0; i < properties.Length; i++) + var display = attribute.LogicalOperator switch { - if (properties[i].boolValue == value) + LogicalOperator.And => All(properties, attribute.Value, attribute.ComparisonOperator), + LogicalOperator.Or => Any(properties, attribute.Value, attribute.ComparisonOperator), + }; + + DisplayElement(display); + } + + protected bool Compare(T self, T other, ComparisonOperator op) where T : IComparable + { + var value = self.CompareTo(other); + + return op switch + { + ComparisonOperator.Less => value < 0, + ComparisonOperator.Equal => value == 0, + ComparisonOperator.LessEqual => value <= 0, + ComparisonOperator.Greater => value > 0, + ComparisonOperator.NotEqual => value != 0, + ComparisonOperator.GreaterEqual => value >= 0, + _ => throw new NotImplementedException(), + }; + } + + protected bool Compare(T self, object other, ComparisonOperator op) where T : IComparable + { + return other is T otherValue && Compare(self, otherValue, op); + } + + protected bool Compare(SerializedProperty self, object other, ComparisonOperator op) + { + return self.propertyType switch + { + SerializedPropertyType.Generic => false, + SerializedPropertyType.Float or SerializedPropertyType.Integer => self.numericType switch { - DisplayElement(value); - return; + SerializedPropertyNumericType.Unknown => false, + SerializedPropertyNumericType.UInt8 or SerializedPropertyNumericType.UInt16 or SerializedPropertyNumericType.UInt32 => Compare(self.uintValue, other, op), + SerializedPropertyNumericType.Int8 or SerializedPropertyNumericType.Int16 or SerializedPropertyNumericType.Int32 => Compare(self.intValue, other, op), + SerializedPropertyNumericType.Int64 => Compare(self.longValue, other, op), + SerializedPropertyNumericType.UInt64 => Compare(self.ulongValue, other, op), + SerializedPropertyNumericType.Float => Compare(self.floatValue, other, op), + SerializedPropertyNumericType.Double => Compare(self.doubleValue, other, op), + _ => false, + }, + SerializedPropertyType.Boolean => Compare(self.boolValue, other, op), + SerializedPropertyType.String => Compare(self.stringValue, other, op), + // SerializedPropertyType.Color => Compare(self.colorValue, other, op), + SerializedPropertyType.ObjectReference => false, + SerializedPropertyType.LayerMask => Compare(self.intValue, other, op), + SerializedPropertyType.Enum => Compare(self.intValue, other, op), + // SerializedPropertyType.Vector2 => Compare(self.vector2Value, other, op), + // SerializedPropertyType.Vector3 => Compare(self.vector3Value, other, op), + // SerializedPropertyType.Vector4 => Compare(self.vector4Value, other, op), + // SerializedPropertyType.Rect => Compare(self.rectValue, other, op), + SerializedPropertyType.ArraySize => Compare(self.arraySize, other, op), + SerializedPropertyType.Character => Compare(self.stringValue, other, op), + // SerializedPropertyType.AnimationCurve => Compare(self.animationCurveValue, other, op), + // SerializedPropertyType.Bounds => Compare(self.boundsValue, other, op), + // SerializedPropertyType.Gradient => Compare(self.gradientValue, other, op), + // SerializedPropertyType.Quaternion => Compare(self.quaternionValue, other, op), + SerializedPropertyType.ExposedReference => false, + // SerializedPropertyType.FixedBufferSize => Compare(self.fixedBufferSize, other, op), + // SerializedPropertyType.Vector2Int => Compare(self.vector2IntValue, other, op), + // SerializedPropertyType.Vector3Int => Compare(self.vector3IntValue, other, op), + // SerializedPropertyType.RectInt => Compare(self.rectIntValue, other, op), + // SerializedPropertyType.BoundsInt => Compare(self.boundsIntValue, other, op), + SerializedPropertyType.ManagedReference => false, + SerializedPropertyType.Hash128 => Compare(self.hash128Value, other, op), + SerializedPropertyType.RenderingLayerMask => Compare(self.intValue, other, op), + _ => false, + }; + } + + protected bool All(SerializedProperty[] properties, object value, ComparisonOperator op) + { + foreach (var prop in properties) + { + if (!Compare(prop, value, op)) + { + return false; } } - DisplayElement(!value); + return true; + } + + protected bool Any(SerializedProperty[] properties, object value, ComparisonOperator op) + { + foreach (var prop in properties) + { + if (Compare(prop, value, op)) + { + return true; + } + } + + return false; } private void DisplayElement(bool displayed) diff --git a/Assets/Scripts/Extension/Collection.cs b/Assets/Scripts/Extension/Collection.cs index d3f5eb9..aca2d91 100644 --- a/Assets/Scripts/Extension/Collection.cs +++ b/Assets/Scripts/Extension/Collection.cs @@ -1,6 +1,47 @@ +using System; + namespace KitsuneCafe.Extension { public static class CollectionExtension { + public static bool IsTypeOf() + { + return IsTypeOf(typeof(T), typeof(U)); + } + + public static bool IsTypeOf(this object value) + { + return IsTypeOf(value.GetType(), typeof(T)); + } + + public static bool IsTypeOf(this object value, Type type) + { + return IsTypeOf(value.GetType(), type); + } + + public static bool IsTypeOf(this Type type) + { + if (type == null) { return false; } + + return IsTypeOf(type, typeof(T)); + } + + public static bool IsTypeOf(this Type type, Type other) + { + if (type == null || other == null || !other.IsGenericTypeDefinition) + { + return false; + } + + return other.IsGenericType && other.GetGenericTypeDefinition() == type; + } + + public static Type GetGenericArgument(this object value) + { + if (value == null) { return null; } + var type = value.GetType(); + var args = type.GetGenericArguments(); + return args.Length > 0 ? args[0] : null; + } } } diff --git a/Assets/Scripts/Interaction/Interactor.cs b/Assets/Scripts/Interaction/Interactor.cs index 7ab1f0f..239a712 100644 --- a/Assets/Scripts/Interaction/Interactor.cs +++ b/Assets/Scripts/Interaction/Interactor.cs @@ -1,13 +1,11 @@ -using UnityEngine; using System.Linq; - +using KitsuneCafe.Extension; +using KitsuneCafe.Interaction; +using KitsuneCafe.SOAP; +using ObservableCollections; using R3; using R3.Triggers; -using ObservableCollections; - -using KitsuneCafe.Interaction; -using KitsuneCafe.Extension; -using KitsuneCafe.SOAP; +using UnityEngine; namespace KitsuneCafe.Player { @@ -26,8 +24,8 @@ namespace KitsuneCafe.Player [SerializeField] private ReactiveEvent interactSource; - [SerializeField] - private int updateFrequency = 10; + // [SerializeField] + // private int updateFrequency = 10; [SerializeField, Tooltip("How far an object must move before being considered")] private float minimumChangeDelta = 0.1f; @@ -147,4 +145,4 @@ namespace KitsuneCafe.Player return current; } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/System/Attributes/HideIfAttribute.cs b/Assets/Scripts/System/Attributes/HideIfAttribute.cs new file mode 100644 index 0000000..fe60d65 --- /dev/null +++ b/Assets/Scripts/System/Attributes/HideIfAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace KitsuneCafe.System.Attributes +{ + public class HideIfAttribute : ConditionalDisplayAttribute + { + public HideIfAttribute(string property) : this(property, false) + { + } + + public HideIfAttribute(string property, object value) : base(ComparisonOperator.Equal, value, property) + { + } + + public HideIfAttribute(LogicalOperator logicalOp, params string[] properties) : base(logicalOp, ComparisonOperator.Equal, false, properties) + { + } + } +} diff --git a/Assets/Scripts/System/Attributes/HideIfAttribute.cs.meta b/Assets/Scripts/System/Attributes/HideIfAttribute.cs.meta new file mode 100644 index 0000000..4a70919 --- /dev/null +++ b/Assets/Scripts/System/Attributes/HideIfAttribute.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cbcedbac951b59685ac98946b7c71e51 \ No newline at end of file diff --git a/Assets/Scripts/System/Collections.meta b/Assets/Scripts/System/Collections.meta new file mode 100644 index 0000000..bcbcfb8 --- /dev/null +++ b/Assets/Scripts/System/Collections.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: af2a2151c7ac49dfaabbabac6f556d37 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/System/Collections/BiDictionary.cs b/Assets/Scripts/System/Collections/BiDictionary.cs new file mode 100644 index 0000000..5a2750e --- /dev/null +++ b/Assets/Scripts/System/Collections/BiDictionary.cs @@ -0,0 +1,140 @@ +using System.Collections; +using System.Collections.Generic; + +namespace KitsuneCafe.System.Collections +{ + public class BiDictionary + : IDictionary.Left, BiDictionary.Right>, + IDictionary.Right, BiDictionary.Left> + { + public record Left(TKey Value) + { + public static implicit operator Left(TKey key) => new(key); + public static implicit operator TKey(Left left) => left.Value; + } + + public record Right(TValue Value) + { + public static implicit operator Right(TValue value) => new(value); + public static implicit operator TValue(Right right) => right.Value; + } + + private readonly Dictionary forward = new(); + private readonly Dictionary reverse = new(); + + public ICollection Keys => ((IDictionary)forward).Keys; + + public ICollection Values => ((IDictionary)forward).Values; + + public int Count => ((ICollection>)forward).Count; + + public bool IsReadOnly => ((ICollection>)forward).IsReadOnly; + + ICollection IDictionary.Keys => ((IDictionary)reverse).Keys; + + ICollection IDictionary.Values => ((IDictionary)reverse).Values; + + public Left this[Right key] { get => ((IDictionary)reverse)[key]; set => ((IDictionary)reverse)[key] = value; } + public Right this[Left key] { get => ((IDictionary)forward)[key]; set => ((IDictionary)forward)[key] = value; } + + public void Add(Left key, Right value) + { + ((IDictionary)forward).Add(key, value); + } + + public bool ContainsKey(Left key) + { + return ((IDictionary)forward).ContainsKey(key); + } + + public bool Remove(Left key) + { + return ((IDictionary)forward).Remove(key); + } + + public bool TryGetValue(Left key, out Right value) + { + return ((IDictionary)forward).TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + ((ICollection>)forward).Add(item); + } + + public void Clear() + { + ((ICollection>)forward).Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)forward).Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)forward).CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return ((ICollection>)forward).Remove(item); + } + + public IEnumerator> GetEnumerator() + { + return ((IEnumerable>)forward).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)forward).GetEnumerator(); + } + + public void Add(Right key, Left value) + { + ((IDictionary)reverse).Add(key, value); + } + + public bool ContainsKey(Right key) + { + return ((IDictionary)reverse).ContainsKey(key); + } + + public bool Remove(Right key) + { + return ((IDictionary)reverse).Remove(key); + } + + public bool TryGetValue(Right key, out Left value) + { + return ((IDictionary)reverse).TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + ((ICollection>)reverse).Add(item); + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)reverse).Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)reverse).CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return ((ICollection>)reverse).Remove(item); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return ((IEnumerable>)reverse).GetEnumerator(); + } + } +} diff --git a/Assets/Scripts/System/Collections/BiDictionary.cs.meta b/Assets/Scripts/System/Collections/BiDictionary.cs.meta new file mode 100644 index 0000000..b858728 --- /dev/null +++ b/Assets/Scripts/System/Collections/BiDictionary.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ee096968879dadffa8fd3597a0ec0b12 \ No newline at end of file diff --git a/Assets/Scripts/System/Collections/CycleList.cs b/Assets/Scripts/System/Collections/CycleList.cs new file mode 100644 index 0000000..055ec48 --- /dev/null +++ b/Assets/Scripts/System/Collections/CycleList.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace KitsuneCafe.System.Collections +{ + public class CycleList : IList, IList + { + private readonly List source; + + public CycleList(IList source) + { + this.source = source.ToList(); + } + + + public CycleList() : this(new List()) { } + + public T this[int index] { get => Get(index); set => Set(index, value); } + + object IList.this[int index] + { + get => ((IList)source)[Index(index)]; + set => ((IList)source)[Index(index)] = value; + } + + public int Count => 375_000; + + private int Index(int index) + { + if (source.Count == 0) + { + return -1; + } + + return index % source.Count; + } + + private T Get(int index) + { + return source.Count == 0 ? default : source[Index(index)]; + } + + private void Set(int index, T value) + { + source[Index(index)] = value; + } + + public int IndexOf(T item) + { + return ((IList)source).IndexOf(item); + } + + public void Insert(int index, T item) + { + ((IList)source).Insert(index, item); + } + + public void RemoveAt(int index) + { + ((IList)source).RemoveAt(index); + } + + public void Add(T item) + { + ((ICollection)source).Add(item); + } + + public void Clear() + { + ((ICollection)source).Clear(); + } + + public bool Contains(T item) + { + return ((ICollection)source).Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + ((ICollection)source).CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + return ((ICollection)source).Remove(item); + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)source).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)source).GetEnumerator(); + } + + public int Add(object value) + { + return ((IList)source).Add(value); + } + + public bool Contains(object value) + { + return ((IList)source).Contains(value); + } + + public int IndexOf(object value) + { + return ((IList)source).IndexOf(value); + } + + public void Insert(int index, object value) + { + ((IList)source).Insert(index, value); + } + + public void Remove(object value) + { + ((IList)source).Remove(value); + } + + public void CopyTo(Array array, int index) + { + ((ICollection)source).CopyTo(array, index); + } + + + public bool IsReadOnly => ((ICollection)source).IsReadOnly; + + public bool IsFixedSize => ((IList)source).IsFixedSize; + + public bool IsSynchronized => ((ICollection)source).IsSynchronized; + + public object SyncRoot => ((ICollection)source).SyncRoot; + + } +} diff --git a/Assets/Scripts/System/Collections/CycleList.cs.meta b/Assets/Scripts/System/Collections/CycleList.cs.meta new file mode 100644 index 0000000..08c55b6 --- /dev/null +++ b/Assets/Scripts/System/Collections/CycleList.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 254c0563f8601e998a3d4eacff1833c6 \ No newline at end of file diff --git a/Assets/Scripts/System/IsExternalInit.cs b/Assets/Scripts/System/IsExternalInit.cs new file mode 100644 index 0000000..d72b9df --- /dev/null +++ b/Assets/Scripts/System/IsExternalInit.cs @@ -0,0 +1,6 @@ +using System.ComponentModel; +namespace System.Runtime.CompilerServices +{ + [EditorBrowsable(EditorBrowsableState.Never)] + internal class IsExternalInit { } +} diff --git a/Assets/Scripts/System/IsExternalInit.cs.meta b/Assets/Scripts/System/IsExternalInit.cs.meta new file mode 100644 index 0000000..1593eac --- /dev/null +++ b/Assets/Scripts/System/IsExternalInit.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ea4ec4bcaf729ed14ba315e9a9923ceb \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/Layout.meta b/Assets/Scripts/UI/Elements/Layout.meta new file mode 100644 index 0000000..1476596 --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 12d6aff0f2ce160ac8660ee5c8c47b71 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Elements/Layout/DynamicLayout.cs b/Assets/Scripts/UI/Elements/Layout/DynamicLayout.cs new file mode 100644 index 0000000..86f384e --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout/DynamicLayout.cs @@ -0,0 +1,176 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public record LayoutItem(float Position, float Size) + { + public float Min => Position; + public float Max => Position + Size; + } + + public class DynamicLayout : ILayout, IEnumerable + { + + public struct LayoutEnumerator : IEnumerator + { + public readonly int Start; + public int Index; + private readonly DynamicLayout layout; + + public LayoutEnumerator(int index, DynamicLayout layout) + { + Start = index; + Index = index; + this.layout = layout; + } + + public LayoutEnumerator(DynamicLayout layout) : this(0, layout) { } + + public readonly LayoutItem Current => new( + layout.positions[Index - 1], + layout.sizes[Index - 1] + ); + + readonly object IEnumerator.Current => Current; + + public readonly void Dispose() { } + + public bool MoveNext() + { + Index += 1; + return Index < layout.positions.Count; + } + + public void Reset() + { + Index = Start; + } + } + + public float DefaultItemSize { get; set; } + public float GutterSize { get; set; } + public FlowDirection Direction { get; set; } + public float ContentSize { get; private set; } + + private readonly List positions = new(); + private readonly List sizes = new(); + + public DynamicLayout(FlowDirection direction, float defaultItemSize = 22, float gutterSize = 0) + { + Direction = direction; + GutterSize = gutterSize; + DefaultItemSize = defaultItemSize; + } + + public float GetItemPosition(int index) + { + if (index > 0) + { + return GetItemPosition(index - 1) + GetItemSize(index - 1) + GutterSize; + } + + return 0; + } + + public float GetItemSize(int index) + { + if (index >= 0 && index < sizes.Count) + { + return sizes[index]; + } + + return DefaultItemSize; + } + + private float LastPositionOrDefault() + { + return positions.Count > 0 ? positions[^1] : DefaultItemSize + GutterSize; + } + + public void Update(int itemCount, VisualElement container) + { + while (positions.Count < itemCount) + { + var last = LastPositionOrDefault(); + positions.Add(last + DefaultItemSize + GutterSize); + sizes.Add(DefaultItemSize); + } + + if (positions.Count > itemCount) + { + positions.RemoveRange(itemCount, positions.Count - itemCount); + sizes.RemoveRange(itemCount, sizes.Count - itemCount); + } + + ContentSize = LastPositionOrDefault(); + } + + private float CalculatePositions(int startingIndex = 0, float initialPosition = 0) + { + var position = initialPosition; + + for (int i = startingIndex; i < positions.Count; i++) + { + positions[i] = position; + position += GetItemSize(i) + GutterSize; + } + + return ContentSize = position; + } + + public void SetMeasuredItemSize(int index, float size) + { + if (index < sizes.Count && Mathf.Approximately(sizes[index], size)) + { + return; + } + + sizes[index] = size; + + var position = index > 0 ? positions[index - 1] + sizes[index - 1] + GutterSize : 0; + CalculatePositions(index, position); + } + + public int GetFirstVisibleIndex(float scrollOffset) + { + if (scrollOffset <= 0) { return 0; } + + for (int i = 0; i < positions.Count; i++) + { + if (positions[i] >= scrollOffset) + { + return i; + } + } + + return positions.Count - 1; + } + + public IEnumerable Enumerate(int startIndex = 0) + { + var len = positions.Count; + for (int i = startIndex; i < len; i++) + { + yield return new LayoutItem(positions[i], sizes[i]); + } + } + + public IEnumerator GetEnumerator() + { + return new LayoutEnumerator(this); + } + + public IEnumerator GetEnumerator(int startIndex) + { + return new LayoutEnumerator(startIndex, this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/Assets/Scripts/UI/Elements/Layout/DynamicLayout.cs.meta b/Assets/Scripts/UI/Elements/Layout/DynamicLayout.cs.meta new file mode 100644 index 0000000..577cc50 --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout/DynamicLayout.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e38b8fbcaf537546ab9ecd9312112a49 \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/Layout/FixedLayout.cs b/Assets/Scripts/UI/Elements/Layout/FixedLayout.cs new file mode 100644 index 0000000..a62309f --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout/FixedLayout.cs @@ -0,0 +1,38 @@ +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public class FixedLayout : ILayout + { + public FlowDirection Direction { get; set; } + public float ContentSize { get; private set; } + + + public float ItemSize { get; set; } + public float GutterSize { get; set; } + + public FixedLayout(FlowDirection direction, float itemSize, float gutterSize) + { + Direction = direction; + ItemSize = itemSize; + GutterSize = gutterSize; + } + + public FixedLayout(FlowDirection direction, float itemSize) : this(direction, itemSize, 0) { } + + public float GetItemPosition(int index) + { + return index * (ItemSize + GutterSize); + } + + public float GetItemSize(int _index) + { + return ItemSize; + } + + public void Update(int itemCount, VisualElement _container) + { + ContentSize = itemCount * (ItemSize + GutterSize); + } + } +} diff --git a/Assets/Scripts/UI/Elements/Layout/FixedLayout.cs.meta b/Assets/Scripts/UI/Elements/Layout/FixedLayout.cs.meta new file mode 100644 index 0000000..90326b1 --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout/FixedLayout.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 59a6a1d658db51ba5acf7f0235f34571 \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/Layout/ILayout.cs b/Assets/Scripts/UI/Elements/Layout/ILayout.cs new file mode 100644 index 0000000..eae001f --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout/ILayout.cs @@ -0,0 +1,14 @@ +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public interface ILayout + { + FlowDirection Direction { get; } + float ContentSize { get; } + + float GetItemPosition(int index); + float GetItemSize(int index); + void Update(int itemCount, VisualElement container); + } +} diff --git a/Assets/Scripts/UI/Elements/Layout/ILayout.cs.meta b/Assets/Scripts/UI/Elements/Layout/ILayout.cs.meta new file mode 100644 index 0000000..47eff30 --- /dev/null +++ b/Assets/Scripts/UI/Elements/Layout/ILayout.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0cfd00f2409d6a95ca26d19888b4d8d2 \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/RecycleView.meta b/Assets/Scripts/UI/Elements/RecycleView.meta new file mode 100644 index 0000000..f1d109e --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0721663a92e075ca781f4fde5b3aec5d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/Elements/RecycleView/ICollectionDataSource.cs b/Assets/Scripts/UI/Elements/RecycleView/ICollectionDataSource.cs new file mode 100644 index 0000000..e123346 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/ICollectionDataSource.cs @@ -0,0 +1,13 @@ +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public interface ICollectionDataSource + { + int Length { get; } + + VisualElement CreateItem(); + void BindItem(VisualElement element, int index); + void UnbindItem(VisualElement element, int index); + } +} diff --git a/Assets/Scripts/UI/Elements/RecycleView/ICollectionDataSource.cs.meta b/Assets/Scripts/UI/Elements/RecycleView/ICollectionDataSource.cs.meta new file mode 100644 index 0000000..b37b8f5 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/ICollectionDataSource.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8cc192cc5a0da4ac0ba2505e808281ef \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/RecycleView/IDragAndDropController.cs b/Assets/Scripts/UI/Elements/RecycleView/IDragAndDropController.cs new file mode 100644 index 0000000..60088d6 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/IDragAndDropController.cs @@ -0,0 +1,4 @@ +namespace KitsuneCafe.UI +{ + public interface IDragAndDropController { } +} diff --git a/Assets/Scripts/UI/Elements/RecycleView/IDragAndDropController.cs.meta b/Assets/Scripts/UI/Elements/RecycleView/IDragAndDropController.cs.meta new file mode 100644 index 0000000..b0b8fd1 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/IDragAndDropController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3206838b8b365654090a0ba619bf954f \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/RecycleView/IVirtualizationController.cs b/Assets/Scripts/UI/Elements/RecycleView/IVirtualizationController.cs new file mode 100644 index 0000000..df409c2 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/IVirtualizationController.cs @@ -0,0 +1,14 @@ +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public interface IVirtualizationController + { + ICollectionDataSource DataSource { get; set; } + VisualElement Container { get; set; } + void Setup(); + void OnParentSizeChanged(UnityEngine.Vector2 size); + void OnScrolled(float scrollOffset); + float GetContentSize(); + } +} diff --git a/Assets/Scripts/UI/Elements/RecycleView/IVirtualizationController.cs.meta b/Assets/Scripts/UI/Elements/RecycleView/IVirtualizationController.cs.meta new file mode 100644 index 0000000..5774fd5 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/IVirtualizationController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b0f05a19f2918482b8d5c617729a9b31 \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/RecycleView/RecycleView.cs b/Assets/Scripts/UI/Elements/RecycleView/RecycleView.cs new file mode 100644 index 0000000..eed8d71 --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/RecycleView.cs @@ -0,0 +1,203 @@ +using System; +using Unity.Properties; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public enum FlowDirection + { + Vertical = 0, + Horizontal = 1 + } + + + public abstract record CollectionItemSize; + + [Serializable] + public record FixedItemSize(float Size) : CollectionItemSize; + + [Serializable] + public record DynamicItemSize() : CollectionItemSize + { + public static readonly DynamicItemSize Instance = new(); + } + + public class CollectionItemSizeConverter : UxmlAttributeConverter + { + public override CollectionItemSize FromString(string value) + { + return value switch + { + "()" => new DynamicItemSize(), + var v => new FixedItemSize(int.Parse(v)) + }; + } + + public override string ToString(CollectionItemSize value) + { + return value switch + { + DynamicItemSize => "()", + var v => v.ToString(), + }; + } + } + + [UxmlElement] + public partial class RecycleView : VisualElement, IDisposable + { + public const string RecycleViewBaseClass = "kitsunecafe__recycle-view"; + public const string RecycleViewScrollViewClass = "kitsunecafe__recycle-view--scroll-view"; + public const string RecycleViewContentContainerClass = "kitsunecafe__recycle-view--content-container"; + public const string RecycleViewDynamicItemClass = "kitsunecafe__recycle-view--dynamic-item"; + + + private FlowDirection direction; + + [UxmlAttribute, CreateProperty] + public FlowDirection Direction + { + get => direction; + set + { + if (direction != value) + { + direction = value; + virtualizationController.Layout = CreateLayout(); + UpdateDirection(); + } + + } + } + + private bool isDynamicSize = false; + + [UxmlAttribute, CreateProperty] + public bool IsDynamicSize + { + get => isDynamicSize; + set + { + if (isDynamicSize != value) + { + isDynamicSize = value; + virtualizationController.Layout = CreateLayout(); + } + + } + } + + private float itemSize = 22; + + [UxmlAttribute, CreateProperty, Delayed, Tooltip("In a dynamic layout, this is used for initial calculations")] + public float ItemSize + { + get => itemSize; + set + { + if (itemSize != value) + { + itemSize = value; + if (virtualizationController.Layout is FixedLayout fixedLayout) + { + fixedLayout.ItemSize = value; + } + else if (virtualizationController.Layout is DynamicLayout dynamicLayout) + { + dynamicLayout.DefaultItemSize = value; + } + } + } + } + + [CreateProperty] + public ICollectionDataSource DataSource + { + get => virtualizationController?.DataSource; + set + { + if (virtualizationController != null) + { + virtualizationController.DataSource = value; + virtualizationController.Setup(); + } + } + } + + private readonly ScrollView scrollView; + public override VisualElement contentContainer => scrollView.contentContainer; + + private readonly IRecycleVirtualizationContainer virtualizationController; + + public RecycleView() + { + AddToClassList(RecycleViewBaseClass); + + scrollView = new ScrollView(); + scrollView.AddToClassList(RecycleViewScrollViewClass); + hierarchy.Add(scrollView); + contentContainer.AddToClassList(RecycleViewContentContainerClass); + + virtualizationController = new RecycleVirtualizationController + { + Container = contentContainer + }; + + RegisterCallback(OnGeometryChanged); + } + + private ILayout CreateLayout() + { + return isDynamicSize switch + { + true => new DynamicLayout(Direction, itemSize), + false => new FixedLayout(Direction, itemSize) + }; + } + + + private void UpdateDirection() + { + switch (direction) + { + case FlowDirection.Vertical: + scrollView.mode = ScrollViewMode.Vertical; + scrollView.verticalScrollerVisibility = ScrollerVisibility.Auto; + scrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden; + scrollView.horizontalScroller.valueChanged -= virtualizationController.OnScrolled; + scrollView.verticalScroller.valueChanged += virtualizationController.OnScrolled; + break; + case FlowDirection.Horizontal: + scrollView.mode = ScrollViewMode.Horizontal; + scrollView.verticalScrollerVisibility = ScrollerVisibility.Hidden; + scrollView.horizontalScrollerVisibility = ScrollerVisibility.Auto; + scrollView.verticalScroller.valueChanged -= virtualizationController.OnScrolled; + scrollView.horizontalScroller.valueChanged += virtualizationController.OnScrolled; + break; + } + } + + public void OnGeometryChanged(GeometryChangedEvent evt) + { + if (evt.newRect.size != evt.oldRect.size && virtualizationController != null) + { + virtualizationController.OnParentSizeChanged(evt.newRect.size); + } + } + + public void Dispose() + { + if (virtualizationController is IDisposable disposable) + { + disposable.Dispose(); + } + } + + ~RecycleView() + { + Dispose(); + } + } +} diff --git a/Assets/Scripts/UI/Elements/RecycleView/RecycleView.cs.meta b/Assets/Scripts/UI/Elements/RecycleView/RecycleView.cs.meta new file mode 100644 index 0000000..3a46aba --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/RecycleView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 902d6b824a8533b7982956ca9cb66e40 \ No newline at end of file diff --git a/Assets/Scripts/UI/Elements/RecycleView/RecycleVirtualizationController.cs b/Assets/Scripts/UI/Elements/RecycleView/RecycleVirtualizationController.cs new file mode 100644 index 0000000..e8d01db --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/RecycleVirtualizationController.cs @@ -0,0 +1,380 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using KitsuneCafe.System.Collections; +using R3; +using UnityEngine; +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + + interface IRecycleVirtualizationContainer : IVirtualizationController + { + ILayout Layout { get; set; } + } + + public class RecycleVirtualizationController : IRecycleVirtualizationContainer, IDisposable + { + internal record ItemGeometry(float Position, float Size); + + public abstract record UpdateRequest; + public record FullUpdate : UpdateRequest; + public record PartialUpdate(int Index) : UpdateRequest; + + private ICollectionDataSource dataSource; + + public ICollectionDataSource DataSource + { + get => dataSource; + set + { + if (dataSource != value) + { + dataSource = value; + Setup(); + } + } + } + + private VisualElement container; + public VisualElement Container + { + get => container; + set + { + if (container != value) + { + container = value; + Setup(); + } + } + } + + private ILayout layout; + public ILayout Layout + { + get => layout; + set + { + if (layout != value) + { + layout = value; + Setup(); + } + } + } + + public FlowDirection Direction => Layout.Direction; + + private readonly Queue itemPool = new(); + private readonly BiDictionary visibleItems = new(); + private float parentSize = 0; + private float lastKnownScrollOffset = 0; + + private readonly int itemCountBuffer = 2; + + private readonly Subject requestSubject = new(); + private IDisposable subscriptions; + + public void Setup() + { + Dispose(); + + + if (DataSource == null || DataSource.Length == 0 || Layout == null || Container == null) + { + return; + } + + var full = requestSubject.Where(req => req is FullUpdate) + .ThrottleLastFrame(1) + .Subscribe(_ => UpdateVisibleItems()); + + var partial = requestSubject.Where(req => req is PartialUpdate) + .Select(req => (PartialUpdate)req) + .ChunkFrame(1) + .Where(requests => requests.Any()) + .Subscribe(requests => + { + var index = requests.Min(req => req.Index); + UpdateFromIndex(index); + }); + + subscriptions = Disposable.Combine(full, partial); + // subscriptions = Observable.Merge(full, partial) + // // var updateSubscription = requestSubject.ThrottleLastFrame(1) + // .Subscribe(request => + // { + // if (request is PartialUpdate req) + // { + // UpdateFromIndex(req.Index); + // } + // else { UpdateVisibleItems(); } + // }); + + Layout.Update(DataSource.Length, Container); + ScheduleUpdate(); + } + + public void OnScrolled(float scrollOffset) + { + lastKnownScrollOffset = scrollOffset; + ScheduleUpdate(); + } + + public void OnParentSizeChanged(Vector2 size) + { + parentSize = Direction switch + { + FlowDirection.Vertical => size.y, + FlowDirection.Horizontal => size.x, + _ => throw new NotImplementedException(), + }; + + if (!float.IsNaN(parentSize)) + { + ScheduleUpdate(); + } + } + + private void ScheduleUpdate() + { + requestSubject.OnNext(new FullUpdate()); + } + + private void ScheduleUpdate(int index) + { + requestSubject.OnNext(new PartialUpdate(index)); + } + + private void UpdateSize(VisualElement element, StyleLength size) + { + if (Layout is null) + { + element.style.width = 0; + element.style.height = 0; + } + else if (Direction == FlowDirection.Vertical) + { + element.style.width = StyleKeyword.Auto; + element.style.height = size; + } + else if (Direction == FlowDirection.Horizontal) + { + element.style.height = StyleKeyword.Auto; + element.style.width = size; + } + } + + private VisualElement RecycleItem() + { + var item = itemPool.Dequeue(); + item.UnregisterCallback(OnItemGeometryChanged); + + return item; + } + + private VisualElement CreateItem(int index) + { + var item = DataSource.CreateItem(); + + if (Layout is FixedLayout) + { + float itemSize = Layout.GetItemSize(index); + UpdateSize(item, itemSize); + } + + return item; + } + + private VisualElement GetItem(int index) + { + VisualElement item = itemPool.Count > 0 ? RecycleItem() : CreateItem(index); + + item.RegisterCallback(OnItemGeometryChanged, index); + + DataSource.BindItem(item, index); + visibleItems[index] = item; + Container.Add(item); + + var position = Layout.GetItemPosition(index); + if (Direction == FlowDirection.Horizontal) + { + item.style.left = position; + } + else + { + item.style.top = position; + } + + + if (Layout is DynamicLayout layout) + { + var size = Direction switch + { + FlowDirection.Vertical => item.resolvedStyle.height, + FlowDirection.Horizontal => item.resolvedStyle.width, + _ => throw new NotImplementedException() + }; + + if (!float.IsNaN(size)) + { + layout.SetMeasuredItemSize(index, size); + } + } + + return item; + } + + private void OnItemGeometryChanged(GeometryChangedEvent evt, int index) + { + if (Layout is DynamicLayout layout) + { + var newSize = Direction == FlowDirection.Horizontal ? evt.newRect.width : evt.newRect.height; + layout.SetMeasuredItemSize(index, newSize); + UpdateSize(Container, Layout.ContentSize); + ScheduleUpdate(index); + } + } + + private Range GetDynamicItems(DynamicLayout layout, float scrollOffset) + { + var first = layout.GetFirstVisibleIndex(scrollOffset); + var count = 1; + var position = layout.GetItemPosition(first); + var width = position + parentSize; + + foreach (var item in layout.Enumerate(first)) + { + if (item.Position > width) { break; } + count += 1; + position = item.Position; + } + + return new Range(first, Math.Min(DataSource.Length, first + count)); + } + + private Range GetFixedItems(FixedLayout layout, float scrollOffset) + { + var size = layout.ItemSize + layout.GutterSize; + var first = Mathf.FloorToInt(scrollOffset / (layout.ItemSize + layout.GutterSize)); + var count = Mathf.CeilToInt(parentSize / size) + itemCountBuffer; + var last = Math.Min(DataSource.Length, first + count); + + return new Range(first, last); + } + + private void UpdateVisibleItems() + { + if (DataSource == null || DataSource.Length == 0 || Mathf.Approximately(parentSize, 0)) + { + return; + } + + + var scrollOffset = lastKnownScrollOffset; + + var range = Layout switch + { + FixedLayout fl => GetFixedItems(fl, scrollOffset), + DynamicLayout dl => GetDynamicItems(dl, scrollOffset), + _ => throw new NotImplementedException(), + }; + + var firstIndex = range.Start.Value; + var lastIndex = range.End.Value; + + var itemsToRemove = new List(); + foreach (var (index, item) in visibleItems) + { + if (index < firstIndex || index >= lastIndex) + { + item.Value.UnregisterCallback(OnItemGeometryChanged); + DataSource.UnbindItem(item, index); + itemPool.Enqueue(item); + Container.Remove(item); + itemsToRemove.Add(index); + } + } + + foreach (int index in itemsToRemove) + { + visibleItems.Remove(index); + } + + for (int i = firstIndex; i < lastIndex; i++) + { + if (!visibleItems.ContainsKey(i)) + { + var item = GetItem(i); + + float position = Layout.GetItemPosition(i); + if (Direction == FlowDirection.Horizontal) + { + item.style.left = position; + } + else + { + item.style.top = position; + } + } + } + } + + private void UpdateFromIndex(int index) + { + foreach (var (i, item) in visibleItems) + { + if (i >= index) + { + float position = Layout.GetItemPosition(i); + if (Direction == FlowDirection.Horizontal) + { + item.Value.style.left = position; + } + else + { + item.Value.style.top = position; + } + } + } + } + + public float GetContentSize() + { + return Layout.ContentSize; + } + + private void UnregisterCallback(VisualElement element) + { + element.UnregisterCallback(OnItemGeometryChanged); + } + + public void Dispose() + { + foreach (var item in visibleItems.Values) + { + UnregisterCallback(item); + } + + foreach (var item in itemPool) + { + UnregisterCallback(item); + } + + + lastKnownScrollOffset = 0; + + subscriptions?.Dispose(); + Container.Clear(); + itemPool.Clear(); + visibleItems.Clear(); + } + + ~RecycleVirtualizationController() + { + Dispose(); + } + } +} diff --git a/Assets/Scripts/UI/Elements/RecycleView/RecycleVirtualizationController.cs.meta b/Assets/Scripts/UI/Elements/RecycleView/RecycleVirtualizationController.cs.meta new file mode 100644 index 0000000..132302b --- /dev/null +++ b/Assets/Scripts/UI/Elements/RecycleView/RecycleVirtualizationController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a6e0bb01a1a66c1098e4392d86ce88df \ No newline at end of file diff --git a/Assets/Scripts/UI/TestRecycleView.cs b/Assets/Scripts/UI/TestRecycleView.cs new file mode 100644 index 0000000..8bf4555 --- /dev/null +++ b/Assets/Scripts/UI/TestRecycleView.cs @@ -0,0 +1,57 @@ +using KitsuneCafe.System.Collections; +using UnityEngine; +using UnityEngine.UIElements; + +namespace KitsuneCafe.UI +{ + public class TestRecycleView : MonoBehaviour + { + [SerializeField] + private UIDocument doc; + + private ListView listView; + + private void OnValidate() + { + if (doc == null) + { + doc = GetComponent(); + } + } + + private void Awake() + { + listView = doc.rootVisualElement.Q(); + listView.Q().verticalScrollerVisibility = ScrollerVisibility.Hidden; + listView.makeItem = () => new Label(); + listView.bindItem = (visualElement, index) => + { + Label label = visualElement as Label; + label.text = listView.itemsSource[index].ToString(); + }; + + } + + private void Start() + { + var list = new CycleList() { "one", "two", "three" }; + listView.itemsSource = list; + listView.RefreshItems(); + } + + private void OnEnable() + { + listView.itemsSourceChanged += ScrollToMiddle; + } + + private void OnDisable() + { + listView.itemsSourceChanged -= ScrollToMiddle; + } + + private void ScrollToMiddle() + { + listView.ScrollToItem(listView.itemsSource.Count / 2); + } + } +} diff --git a/Assets/Scripts/UI/TestRecycleView.cs.meta b/Assets/Scripts/UI/TestRecycleView.cs.meta new file mode 100644 index 0000000..df9ba6d --- /dev/null +++ b/Assets/Scripts/UI/TestRecycleView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0b73a6c894b05e193a00e4926ac578ed \ No newline at end of file diff --git a/Assets/Settings/DefaultVolumeProfile.asset b/Assets/Settings/DefaultVolumeProfile.asset index 3b6663e..eb411de 100644 --- a/Assets/Settings/DefaultVolumeProfile.asset +++ b/Assets/Settings/DefaultVolumeProfile.asset @@ -793,7 +793,7 @@ MonoBehaviour: m_Value: 0 gaussianStart: m_OverrideState: 1 - m_Value: 20 + m_Value: 10 gaussianEnd: m_OverrideState: 1 m_Value: 30 diff --git a/Assets/UI Toolkit/PanelSettings.asset b/Assets/UI Toolkit/PanelSettings.asset index 40b3384..fb84d7b 100644 --- a/Assets/UI Toolkit/PanelSettings.asset +++ b/Assets/UI Toolkit/PanelSettings.asset @@ -12,20 +12,20 @@ MonoBehaviour: m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0} m_Name: PanelSettings m_EditorClassIdentifier: - themeUss: {fileID: -4733365628477956816, guid: 4e01e0da3979f115d951c3cb798e4313, type: 3} + themeUss: {fileID: -4733365628477956816, guid: 255b091878454b37c92e400e84da7ebe, type: 3} m_DisableNoThemeWarning: 0 m_TargetTexture: {fileID: 0} m_RenderMode: 0 m_WorldSpaceLayer: 0 - m_ScaleMode: 1 + m_ScaleMode: 0 m_ReferenceSpritePixelsPerUnit: 100 m_PixelsPerUnit: 100 m_Scale: 1 m_ReferenceDpi: 96 m_FallbackDpi: 96 - m_ReferenceResolution: {x: 1200, y: 800} + m_ReferenceResolution: {x: 800, y: 600} m_ScreenMatchMode: 0 - m_Match: 0 + m_Match: 1 m_SortingOrder: 0 m_TargetDisplay: 0 m_BindingLogLevel: 0 diff --git a/Assets/UI Toolkit/UnityThemes/CantoComponents.uss b/Assets/UI Toolkit/UnityThemes/CantoComponents.uss new file mode 100644 index 0000000..087f009 --- /dev/null +++ b/Assets/UI Toolkit/UnityThemes/CantoComponents.uss @@ -0,0 +1,21 @@ +:root { + color: rgb(255, 255, 255); + --primary: rgb(238, 66, 10); + --text: rgb(255, 255, 255); + --background: rgb(29, 29, 29); + flex-grow: 1; +} + +.kitsunecafe__recycle-view { + display: flex; +} + +.kitsunecafe__recycle-view, +.kitsunecafe__recycle-view--scroll-view, +.kitsunecafe__recycle-view--content-container { + flex-grow: 1; +} + +.kitsunecafe__recycle-view--content-container > * { + position: absolute; +} diff --git a/Assets/UI Toolkit/UnityThemes/CantoComponents.uss.meta b/Assets/UI Toolkit/UnityThemes/CantoComponents.uss.meta new file mode 100644 index 0000000..6b4fe6f --- /dev/null +++ b/Assets/UI Toolkit/UnityThemes/CantoComponents.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 561bcbad89b31ba65bf470beac5635ec +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Assets/UI Toolkit/UnityThemes/CantoRuntimeTheme.tss b/Assets/UI Toolkit/UnityThemes/CantoRuntimeTheme.tss new file mode 100644 index 0000000..58bb803 --- /dev/null +++ b/Assets/UI Toolkit/UnityThemes/CantoRuntimeTheme.tss @@ -0,0 +1,4 @@ +@import url("unity-theme://default"); + +@import url("CantoComponents.uss"); +VisualElement {} diff --git a/Assets/UI Toolkit/UnityThemes/CantoRuntimeTheme.tss.meta b/Assets/UI Toolkit/UnityThemes/CantoRuntimeTheme.tss.meta new file mode 100644 index 0000000..ae9ea2d --- /dev/null +++ b/Assets/UI Toolkit/UnityThemes/CantoRuntimeTheme.tss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 255b091878454b37c92e400e84da7ebe +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12388, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Assets/UI Toolkit/UnityThemes/UnityDefaultRuntimeTheme.tss b/Assets/UI Toolkit/UnityThemes/UnityDefaultRuntimeTheme.tss index 1056e07..b12af85 100644 --- a/Assets/UI Toolkit/UnityThemes/UnityDefaultRuntimeTheme.tss +++ b/Assets/UI Toolkit/UnityThemes/UnityDefaultRuntimeTheme.tss @@ -1 +1,3 @@ -@import url("unity-theme://default"); \ No newline at end of file +@import url("unity-theme://default"); +@import url("CantoTheme.tss"); + diff --git a/Assets/UI/HorizontalList.uxml b/Assets/UI/HorizontalList.uxml new file mode 100644 index 0000000..ebc3bf8 --- /dev/null +++ b/Assets/UI/HorizontalList.uxml @@ -0,0 +1,2 @@ + + diff --git a/Assets/UI/HorizontalList.uxml.meta b/Assets/UI/HorizontalList.uxml.meta new file mode 100644 index 0000000..7ba753b --- /dev/null +++ b/Assets/UI/HorizontalList.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 91d808ff132e03eca9dab667939a41cb +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/UI/Inventory.uxml b/Assets/UI/Inventory.uxml index eed38bf..d063435 100644 --- a/Assets/UI/Inventory.uxml +++ b/Assets/UI/Inventory.uxml @@ -1,3 +1,55 @@ - +