This commit is contained in:
2025-10-15 20:13:48 +08:00
parent 0fd74a1a45
commit 1b17489c11
27 changed files with 3196 additions and 302 deletions

View File

@@ -122,6 +122,78 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &6388584
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6388586}
- component: {fileID: 6388585}
- component: {fileID: 6388587}
m_Layer: 0
m_Name: ShadowColliderSystem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &6388585
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6388584}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 836b4625937ba9d45be59618cd2a6a15, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowLight: {fileID: 354864581}
shadowWall: {fileID: 1371778128}
shadowLayerName: Shadow
obstacleLayerMask:
serializedVersion: 2
m_Bits: 1024
generateOnStart: 1
updateInRealTime: 0
updateInterval: 0.1
shadowColliders: []
--- !u!4 &6388586
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6388584}
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 &6388587
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6388584}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7911d7cd239cfb44c910a896590b2db4, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowLight: {fileID: 354864581}
shadowWall: {fileID: 1371778128}
shadowLayerName: Shadow
keepOriginalMesh: 1
shadowIntensity: 1
--- !u!1 &282165411
GameObject:
m_ObjectHideFlags: 0
@@ -133,9 +205,6 @@ GameObject:
- component: {fileID: 282165415}
- component: {fileID: 282165414}
- component: {fileID: 282165412}
- component: {fileID: 282165416}
- component: {fileID: 282165417}
- component: {fileID: 282165418}
m_Layer: 0
m_Name: ShadowCamera
m_TagString: Untagged
@@ -222,11 +291,11 @@ Camera:
far clip plane: 1000
field of view: 60
orthographic: 1
orthographic size: 3
orthographic size: 5
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 512
m_Bits: 1536
m_RenderingPath: -1
m_TargetTexture: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
m_TargetDisplay: 0
@@ -247,78 +316,12 @@ Transform:
m_GameObject: {fileID: 282165411}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 5, z: 0}
m_LocalPosition: {x: 0, y: 5, z: 4.51}
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 &282165416
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 282165411}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 45374fb251a6d21488433ebd2dcd2203, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowLight: {fileID: 354864581}
obstacleLayer:
m_Bits: 512
realtimeUpdate: 1
use2DMode: 1
rayDistance: 10
rayCount: 100
shadowCamera: {fileID: 282165414}
shadowTexture: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
shadowIntensityThreshold: 0.3
simplificationThreshold: 0.1
updateFrameInterval: 3
minPointDistance: 0.1
edgeCollider2D: {fileID: 0}
polygonCollider2D: {fileID: 0}
meshCollider3D: {fileID: 0}
shadowMesh: {fileID: 0}
frameCount: 0
--- !u!114 &282165417
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 282165411}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ac3308532951bea4fa59009510cc632d, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowCamera: {fileID: 282165414}
shadowRenderTexture: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
wall: {fileID: 767553978}
colliderHeight: 0.1
shadowTexture: {fileID: 0}
shadowColliders: []
--- !u!114 &282165418
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 282165411}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4c8c04e11b9681342bda2a7a35b300b8, type: 3}
m_Name:
m_EditorClassIdentifier:
directionalLight: {fileID: 354864581}
wall: {fileID: 767553978}
casterLayer:
m_Bits: 512
colliderHeight: 0.1
shadowColliders: []
--- !u!1 &354864579
GameObject:
m_ObjectHideFlags: 0
@@ -372,13 +375,13 @@ Light:
m_Type: 0
m_Shape: 0
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 20
m_Range: 10
m_Intensity: 200
m_Range: 20
m_SpotAngle: 52.683002
m_InnerSpotAngle: 44.48507
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Type: 1
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
@@ -414,7 +417,7 @@ Light:
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_BounceIntensity: 10
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
@@ -431,7 +434,7 @@ Transform:
m_GameObject: {fileID: 354864579}
serializedVersion: 2
m_LocalRotation: {x: -0.10489331, y: 0.14298823, z: 0.015222758, w: 0.9840325}
m_LocalPosition: {x: -1.1246443, y: 4.4885726, z: -0.15423861}
m_LocalPosition: {x: -2.271, y: 3.618, z: -4.017}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
@@ -574,6 +577,127 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &507957216
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 507957221}
- component: {fileID: 507957220}
- component: {fileID: 507957219}
- component: {fileID: 507957218}
- component: {fileID: 507957217}
m_Layer: 9
m_Name: Cube (2)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &507957217
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 507957216}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 717ba394b4fc8354db9479f2f858d521, type: 3}
m_Name:
m_EditorClassIdentifier:
mainCamera: {fileID: 0}
model3D: {fileID: 507957216}
edgeSegments: 16
--- !u!65 &507957218
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 507957216}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &507957219
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 507957216}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &507957220
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 507957216}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &507957221
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 507957216}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0.54599994, y: 5.58, z: 2.41}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &767553978
GameObject:
m_ObjectHideFlags: 0
@@ -586,14 +710,14 @@ GameObject:
- component: {fileID: 767553981}
- component: {fileID: 767553980}
- component: {fileID: 767553983}
- component: {fileID: 767553984}
- component: {fileID: 767553985}
m_Layer: 0
m_Name: Plane
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!23 &767553980
MeshRenderer:
m_ObjectHideFlags: 0
@@ -654,7 +778,7 @@ Transform:
serializedVersion: 2
m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 5, z: 5}
m_LocalScale: {x: 10, y: 10, z: 10}
m_LocalScale: {x: 2, y: 2, z: 2}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
@@ -673,26 +797,31 @@ MonoBehaviour:
m_EditorClassIdentifier:
shadowCamera: {fileID: 282165414}
shadowRT: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
shadowCasterLayer:
serializedVersion: 2
m_Bits: 512
colliderPrecision: 0.1
--- !u!114 &767553984
MonoBehaviour:
polygonCollider: {fileID: 0}
shadowThreshold: 0.3
gridSize: 2
simplifyTolerance: 2
--- !u!65 &767553985
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 767553978}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 21f7e71103c1a5941af3223174264656, type: 3}
m_Name:
m_EditorClassIdentifier:
spotLight: {fileID: 354864581}
shadowCaster: {fileID: 1455178660}
rayCount: 36
maxShadowDistance: 10
serializedVersion: 3
m_Size: {x: 10, y: 1.2295777, z: 10.000002}
m_Center: {x: 0, y: -0.61478895, z: 1.6312508e-15}
--- !u!1 &1283729761
GameObject:
m_ObjectHideFlags: 0
@@ -811,6 +940,194 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!1 &1371778123
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1371778128}
- component: {fileID: 1371778127}
- component: {fileID: 1371778126}
- component: {fileID: 1371778125}
- component: {fileID: 1371778130}
- component: {fileID: 1371778129}
- component: {fileID: 1371778124}
- component: {fileID: 1371778131}
m_Layer: 10
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1371778124
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 21f7e71103c1a5941af3223174264656, type: 3}
m_Name:
m_EditorClassIdentifier:
spotLight: {fileID: 354864581}
shadowCasters: []
shadowCasterLayer:
serializedVersion: 2
m_Bits: 0
shadowReceiverLayer:
serializedVersion: 2
m_Bits: 0
maxShadowDistance: 10
shadowCollider: {fileID: 1974410756}
--- !u!65 &1371778125
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &1371778126
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &1371778127
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &1371778128
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
serializedVersion: 2
m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 5, z: 6}
m_LocalScale: {x: 10, y: 1, z: 10}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1974410755}
- {fileID: 1835604431}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1371778129
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 28f43899f7559ca4e915497eaf23cc4f, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowCamera: {fileID: 282165414}
shadowRT: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
polygonCollider: {fileID: 1974410756}
shadowThreshold: 0
gridSize: 2
simplifyTolerance: 0.1
--- !u!114 &1371778130
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7b369cd814f44044d8a55791d065e3fb, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowCamera: {fileID: 282165414}
shadowRT: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
polygonCollider: {fileID: 1974410756}
shadowThreshold: 0.1
gridResolution: 64
minContourArea: 10
maxGapDistance: 20
--- !u!114 &1371778131
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1371778123}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: eb1369db1529b6543ba24519d5c2a35d, type: 3}
m_Name:
m_EditorClassIdentifier:
shadowCamera: {fileID: 282165414}
shadowRT: {fileID: 8400000, guid: 3746a9ea25b5f084f963f36250ae89a2, type: 2}
polygonCollider: {fileID: 1974410756}
shadowThreshold: 0.1
downSample: 4
minShadowSize: 0.1
--- !u!1 &1455178660
GameObject:
m_ObjectHideFlags: 0
@@ -823,6 +1140,7 @@ GameObject:
- component: {fileID: 1455178663}
- component: {fileID: 1455178662}
- component: {fileID: 1455178661}
- component: {fileID: 1455178665}
m_Layer: 9
m_Name: Cube
m_TagString: Untagged
@@ -910,19 +1228,263 @@ Transform:
m_GameObject: {fileID: 1455178660}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 5, z: 2.41}
m_LocalPosition: {x: -0.787, y: 5.58, z: 2.41}
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 &1455178665
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1455178660}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 717ba394b4fc8354db9479f2f858d521, type: 3}
m_Name:
m_EditorClassIdentifier:
mainCamera: {fileID: 0}
model3D: {fileID: 1455178660}
edgeSegments: 16
--- !u!1 &1835604430
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1835604431}
- component: {fileID: 1835604433}
- component: {fileID: 1835604432}
- component: {fileID: 1835604434}
m_Layer: 10
m_Name: GameObject (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &1835604431
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1835604430}
serializedVersion: 2
m_LocalRotation: {x: 0.7071068, y: -0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0.181, y: 0.5, z: 0.5159}
m_LocalScale: {x: 0.1, y: 0.09999998, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1371778128}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!50 &1835604432
Rigidbody2D:
serializedVersion: 4
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1835604430}
m_BodyType: 0
m_Simulated: 1
m_UseFullKinematicContacts: 0
m_UseAutoMass: 0
m_Mass: 1
m_LinearDrag: 0
m_AngularDrag: 0.05
m_GravityScale: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 0
--- !u!212 &1835604433
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1835604430}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_Sprite: {fileID: 21300000, guid: 3d67b5c335d66e74d81f6f34263a17be, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 1, y: 1}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!58 &1835604434
CircleCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1835604430}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0.0000009536743}
serializedVersion: 2
m_Radius: 2.5600011
--- !u!1 &1974410754
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1974410755}
- component: {fileID: 1974410756}
m_Layer: 10
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1974410755
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1974410754}
serializedVersion: 2
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1371778128}
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!60 &1974410756
PolygonCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1974410754}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_SpriteTilingProperty:
border: {x: 0.049999997, y: 0.049999997, z: 0.049999997, w: 0.049999997}
pivot: {x: 0.5, y: 0.5}
oldSize: {x: 0.16, y: 0.16}
newSize: {x: 0.16, y: 0.16}
adaptiveTilingThreshold: 0.5
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
m_Points:
m_Paths:
- []
m_UseDelaunayMesh: 0
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 354864582}
- {fileID: 1371778128}
- {fileID: 767553982}
- {fileID: 488143182}
- {fileID: 1283729764}
- {fileID: 282165415}
- {fileID: 1455178664}
- {fileID: 507957221}
- {fileID: 6388586}

View File

@@ -0,0 +1,129 @@
using UnityEngine;
using System.Collections.Generic;
public class Dynamic3DTo2DCollider : MonoBehaviour
{
public Camera mainCamera;
public GameObject model3D;
[Range(4, 32)] public int edgeSegments = 16;
void Start()
{
if (mainCamera == null)
mainCamera = Camera.main;
Convert3DTo2DCollider();
}
void Convert3DTo2DCollider()
{
MeshFilter meshFilter = model3D.GetComponent<MeshFilter>();
if (meshFilter == null) return;
// 获取网格的所有顶点
Vector3[] vertices = meshFilter.mesh.vertices;
int[] triangles = meshFilter.mesh.triangles;
// 收集所有在轮廓边缘的顶点
List<Vector2> silhouettePoints = GetSilhouettePoints(vertices, triangles);
if (silhouettePoints.Count > 2)
{
// 创建2D碰撞器
GameObject obj = new GameObject(gameObject.name + "2D");
PolygonCollider2D collider2D = obj.AddComponent<PolygonCollider2D>();
collider2D.points = silhouettePoints.ToArray();
// 禁用3D碰撞器
Collider collider3D = model3D.GetComponent<Collider>();
if (collider3D != null) collider3D.enabled = false;
}
}
List<Vector2> GetSilhouettePoints(Vector3[] vertices, int[] triangles)
{
List<Vector2> screenPoints = new List<Vector2>();
HashSet<Vector2> uniquePoints = new HashSet<Vector2>();
// 转换所有顶点到屏幕空间
for (int i = 0; i < vertices.Length; i++)
{
Vector3 worldPos = model3D.transform.TransformPoint(vertices[i]);
Vector2 screenPos = mainCamera.WorldToScreenPoint(worldPos);
// 只添加在相机前方的点
if (IsInFrontOfCamera(worldPos))
{
uniquePoints.Add(screenPos);
}
}
screenPoints.AddRange(uniquePoints);
// 计算凸包来获得轮廓
return CalculateConvexHull(screenPoints);
}
bool IsInFrontOfCamera(Vector3 worldPos)
{
Vector3 viewportPos = mainCamera.WorldToViewportPoint(worldPos);
return viewportPos.z > 0;
}
// 计算凸包的Graham Scan算法
List<Vector2> CalculateConvexHull(List<Vector2> points)
{
if (points.Count < 3) return points;
// 找到最左下角的点
Vector2 pivot = points[0];
for (int i = 1; i < points.Count; i++)
{
if (points[i].y < pivot.y || (points[i].y == pivot.y && points[i].x < pivot.x))
pivot = points[i];
}
// 按极角排序
points.Sort((a, b) =>
{
float angleA = Mathf.Atan2(a.y - pivot.y, a.x - pivot.x);
float angleB = Mathf.Atan2(b.y - pivot.y, b.x - pivot.x);
if (angleA < angleB) return -1;
if (angleA > angleB) return 1;
// 如果角度相同,按距离排序
float distA = (a - pivot).sqrMagnitude;
float distB = (b - pivot).sqrMagnitude;
return distA.CompareTo(distB);
});
// Graham Scan算法
List<Vector2> hull = new List<Vector2>();
hull.Add(points[0]);
hull.Add(points[1]);
for (int i = 2; i < points.Count; i++)
{
while (hull.Count >= 2)
{
Vector2 a = hull[hull.Count - 2];
Vector2 b = hull[hull.Count - 1];
Vector2 c = points[i];
if (Cross(b - a, c - b) <= 0)
hull.RemoveAt(hull.Count - 1);
else
break;
}
hull.Add(points[i]);
}
return hull;
}
float Cross(Vector2 a, Vector2 b)
{
return a.x * b.y - a.y * b.x;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 717ba394b4fc8354db9479f2f858d521
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,340 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class MarchingSquaresShadowCollider : MonoBehaviour
{
[Header("References")]
public Camera shadowCamera;
public RenderTexture shadowRT;
public PolygonCollider2D polygonCollider;
[Header("Settings")]
[Range(0f, 1f)]
public float shadowThreshold = 0.5f;
public int gridResolution = 40;
public float minContourArea = 50f;
public float maxGapDistance = 20f;
private Texture2D processedTexture;
private List<List<Vector2>> separateContours = new List<List<Vector2>>();
void Start()
{
if (polygonCollider == null)
polygonCollider = GetComponent<PolygonCollider2D>();
processedTexture = new Texture2D(shadowRT.width, shadowRT.height, TextureFormat.RGBA32, false);
GenerateCollider();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GenerateCollider();
}
}
[ContextMenu("Generate Collider")]
public void GenerateCollider()
{
if (shadowCamera == null || shadowRT == null) return;
// 渲染并读取纹理
shadowCamera.Render();
RenderTexture.active = shadowRT;
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
processedTexture.Apply();
RenderTexture.active = null;
// 找到所有独立的轮廓
separateContours = FindSeparateContours();
// 设置多边形碰撞器路径
if (separateContours.Count > 0)
{
polygonCollider.pathCount = separateContours.Count;
for (int i = 0; i < separateContours.Count; i++)
{
List<Vector2> worldPoints = ConvertToWorldCoordinates(separateContours[i]);
polygonCollider.SetPath(i, worldPoints);
}
Debug.Log($"Created {separateContours.Count} separate collider paths");
}
else
{
polygonCollider.pathCount = 0;
Debug.LogWarning("No contours found");
}
}
List<List<Vector2>> FindSeparateContours()
{
List<Vector2> allEdgePoints = ExtractEdgePoints();
List<List<Vector2>> contours = new List<List<Vector2>>();
// 使用区域生长算法找到独立的轮廓
List<Vector2> unassignedPoints = new List<Vector2>(allEdgePoints);
while (unassignedPoints.Count > 0)
{
List<Vector2> currentContour = new List<Vector2>();
Queue<Vector2> pointsToProcess = new Queue<Vector2>();
// 从第一个未分配的点开始
pointsToProcess.Enqueue(unassignedPoints[0]);
unassignedPoints.RemoveAt(0);
while (pointsToProcess.Count > 0)
{
Vector2 current = pointsToProcess.Dequeue();
currentContour.Add(current);
// 查找附近的点
for (int i = unassignedPoints.Count - 1; i >= 0; i--)
{
if (Vector2.Distance(current, unassignedPoints[i]) <= maxGapDistance)
{
pointsToProcess.Enqueue(unassignedPoints[i]);
unassignedPoints.RemoveAt(i);
}
}
}
// 连接轮廓点并过滤小区域
if (currentContour.Count > 3)
{
List<Vector2> connectedContour = ConnectContourPoints(currentContour);
float area = CalculateContourArea(connectedContour);
if (area >= minContourArea)
{
contours.Add(connectedContour);
}
}
}
Debug.Log($"Found {contours.Count} separate contours");
return contours;
}
List<Vector2> ExtractEdgePoints()
{
List<Vector2> points = new List<Vector2>();
int width = processedTexture.width;
int height = processedTexture.height;
int step = Mathf.Max(width, height) / gridResolution;
// 扫描整个纹理寻找阴影边缘
for (int x = step; x < width - step; x += step)
{
for (int y = step; y < height - step; y += step)
{
if (IsEdgePoint(x, y, step))
{
points.Add(new Vector2(x, y));
}
}
}
Debug.Log($"Found {points.Count} edge points");
return points;
}
bool IsEdgePoint(int x, int y, int step)
{
Color center = processedTexture.GetPixel(x, y);
bool isShadow = center.grayscale < shadowThreshold;
if (!isShadow) return false;
// 检查8邻域是否有非阴影点
for (int dx = -step; dx <= step; dx += step)
{
for (int dy = -step; dy <= step; dy += step)
{
if (dx == 0 && dy == 0) continue;
int nx = x + dx;
int ny = y + dy;
if (nx >= 0 && nx < processedTexture.width && ny >= 0 && ny < processedTexture.height)
{
Color neighbor = processedTexture.GetPixel(nx, ny);
if (neighbor.grayscale >= shadowThreshold)
{
return true;
}
}
}
}
return false;
}
List<Vector2> ConnectContourPoints(List<Vector2> points)
{
if (points.Count < 3) return points;
List<Vector2> connected = new List<Vector2>();
List<Vector2> remaining = new List<Vector2>(points);
// 找到最左边的点作为起点
Vector2 start = remaining.OrderBy(p => p.x).First();
connected.Add(start);
remaining.Remove(start);
Vector2 current = start;
// 按顺时针方向连接点
while (remaining.Count > 0)
{
// 找到与当前点角度变化最小的点
float bestAngle = float.MaxValue;
Vector2 bestPoint = remaining[0];
int bestIndex = 0;
Vector2 prevDir = connected.Count > 1 ?
(current - connected[connected.Count - 2]).normalized :
Vector2.right;
for (int i = 0; i < remaining.Count; i++)
{
Vector2 toNext = (remaining[i] - current).normalized;
float angle = Vector2.SignedAngle(prevDir, toNext);
// 优先选择向右转的点(顺时针)
if (angle < bestAngle && angle >= 0)
{
bestAngle = angle;
bestPoint = remaining[i];
bestIndex = i;
}
}
// 如果没有找到合适的点,选择最近的点
if (bestAngle == float.MaxValue)
{
bestPoint = remaining.OrderBy(p => Vector2.Distance(current, p)).First();
bestIndex = remaining.IndexOf(bestPoint);
}
connected.Add(bestPoint);
remaining.RemoveAt(bestIndex);
current = bestPoint;
}
// 确保轮廓闭合
if (Vector2.Distance(connected[0], connected[connected.Count - 1]) > maxGapDistance)
{
connected.Add(connected[0]);
}
return connected;
}
float CalculateContourArea(List<Vector2> points)
{
if (points.Count < 3) return 0f;
float area = 0f;
int n = points.Count;
for (int i = 0; i < n; i++)
{
Vector2 current = points[i];
Vector2 next = points[(i + 1) % n];
area += (current.x * next.y) - (next.x * current.y);
}
return Mathf.Abs(area / 2f);
}
List<Vector2> ConvertToWorldCoordinates(List<Vector2> texturePoints)
{
List<Vector2> worldPoints = new List<Vector2>();
foreach (Vector2 texCoord in texturePoints)
{
Vector3 viewportPos = new Vector3(
texCoord.x / processedTexture.width,
texCoord.y / processedTexture.height,
shadowCamera.nearClipPlane
);
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
worldPoints.Add(new Vector2(worldPos.x, worldPos.y));
}
return worldPoints;
}
// 调试可视化
void OnDrawGizmosSelected()
{
if (separateContours == null) return;
Color[] colors = { Color.red, Color.green, Color.blue, Color.yellow, Color.cyan, Color.magenta };
for (int i = 0; i < separateContours.Count; i++)
{
Gizmos.color = colors[i % colors.Length];
List<Vector2> contour = separateContours[i];
foreach (Vector2 point in contour)
{
Vector3 viewportPos = new Vector3(
point.x / processedTexture.width,
point.y / processedTexture.height,
shadowCamera.nearClipPlane
);
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
Gizmos.DrawSphere(worldPos, 0.05f);
}
// 绘制轮廓线
if (contour.Count > 1)
{
for (int j = 0; j < contour.Count; j++)
{
Vector2 start = contour[j];
Vector2 end = contour[(j + 1) % contour.Count];
Vector3 startWorld = shadowCamera.ViewportToWorldPoint(new Vector3(
start.x / processedTexture.width,
start.y / processedTexture.height,
shadowCamera.nearClipPlane
));
Vector3 endWorld = shadowCamera.ViewportToWorldPoint(new Vector3(
end.x / processedTexture.width,
end.y / processedTexture.height,
shadowCamera.nearClipPlane
));
Gizmos.DrawLine(startWorld, endWorld);
}
}
}
}
[ContextMenu("Debug Contour Info")]
void DebugContourInfo()
{
shadowCamera.Render();
RenderTexture.active = shadowRT;
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
processedTexture.Apply();
RenderTexture.active = null;
List<List<Vector2>> contours = FindSeparateContours();
for (int i = 0; i < contours.Count; i++)
{
float area = CalculateContourArea(contours[i]);
Debug.Log($"Contour {i}: {contours[i].Count} points, area: {area}");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b369cd814f44044d8a55791d065e3fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,336 @@
using UnityEngine;
using System.Collections.Generic;
public class MeshShadowProjector : MonoBehaviour
{
[Header("投影设置")]
public Light shadowLight;
public Transform shadowWall;
public string shadowLayerName = "Shadow";
[Header("Mesh设置")]
public bool keepOriginalMesh = true; // 是否保留原始mesh
public float shadowIntensity = 0.5f; // 阴影强度
private List<GameObject> modifiedObjects = new List<GameObject>();
private Vector3 wallNormal;
private Vector3 wallPosition;
void Start()
{
if (shadowLight == null) shadowLight = FindObjectOfType<Light>();
if (shadowWall == null) shadowWall = FindMainWall();
CalculateWallPlane();
GenerateAllShadowMeshes();
}
[ContextMenu("生成所有阴影Mesh")]
public void GenerateAllShadowMeshes()
{
ClearAllShadowMeshes();
var shadowObjects = FindObjectsInLayer(shadowLayerName);
foreach (GameObject obj in shadowObjects)
{
GenerateShadowMesh(obj);
}
Debug.Log($"为 {modifiedObjects.Count} 个物体生成了阴影Mesh");
}
void GenerateShadowMesh(GameObject shadowCaster)
{
MeshFilter meshFilter = shadowCaster.GetComponent<MeshFilter>();
if (meshFilter == null) return;
// 获取原始mesh
Mesh originalMesh = meshFilter.mesh;
// 创建新的mesh组合原始mesh + 投影mesh
Mesh combinedMesh = CreateCombinedMesh(originalMesh, shadowCaster.transform);
// 应用新mesh
if (keepOriginalMesh)
{
// 创建新物体来存放阴影mesh
CreateShadowObject(shadowCaster, combinedMesh);
}
else
{
// 直接修改原物体
meshFilter.mesh = combinedMesh;
UpdateMeshCollider(shadowCaster, combinedMesh);
modifiedObjects.Add(shadowCaster);
}
}
Mesh CreateCombinedMesh(Mesh originalMesh, Transform objectTransform)
{
// 创建新mesh
Mesh newMesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
List<Vector3> normals = new List<Vector3>();
List<Vector2> uv = new List<Vector2>();
// 1. 添加原始mesh的顶点和三角形
vertices.AddRange(originalMesh.vertices);
triangles.AddRange(originalMesh.triangles);
// 复制原始法线和UV
if (originalMesh.normals.Length == originalMesh.vertices.Length)
normals.AddRange(originalMesh.normals);
else
for (int i = 0; i < originalMesh.vertices.Length; i++)
normals.Add(Vector3.up);
if (originalMesh.uv.Length == originalMesh.vertices.Length)
uv.AddRange(originalMesh.uv);
else
for (int i = 0; i < originalMesh.vertices.Length; i++)
uv.Add(Vector2.zero);
// 2. 添加投影到墙面的顶点
int originalVertexCount = vertices.Count;
List<Vector3> projectedVertices = new List<Vector3>();
List<int> projectedTriangles = new List<int>();
// 投影每个顶点到墙面
foreach (Vector3 localVertex in originalMesh.vertices)
{
Vector3 worldVertex = objectTransform.TransformPoint(localVertex);
Vector3 projectedWorld = ProjectPointToWall(worldVertex);
Vector3 projectedLocal = objectTransform.InverseTransformPoint(projectedWorld);
projectedVertices.Add(projectedLocal);
}
// 添加投影顶点
vertices.AddRange(projectedVertices);
// 为投影顶点添加法线和UV
for (int i = 0; i < projectedVertices.Count; i++)
{
normals.Add(-objectTransform.forward); // 面向墙面
uv.Add(new Vector2(1, 1)); // 使用不同的UV区域
}
// 3. 创建连接原始mesh和投影mesh的侧面三角形
for (int i = 0; i < originalMesh.triangles.Length; i += 3)
{
int v1 = originalMesh.triangles[i];
int v2 = originalMesh.triangles[i + 1];
int v3 = originalMesh.triangles[i + 2];
// 对应的投影顶点索引
int p1 = v1 + originalVertexCount;
int p2 = v2 + originalVertexCount;
int p3 = v3 + originalVertexCount;
// 创建侧面四边形(两个三角形)
// 三角形1
projectedTriangles.Add(v1);
projectedTriangles.Add(p1);
projectedTriangles.Add(v2);
projectedTriangles.Add(v2);
projectedTriangles.Add(p1);
projectedTriangles.Add(p2);
// 三角形2
projectedTriangles.Add(v2);
projectedTriangles.Add(p2);
projectedTriangles.Add(v3);
projectedTriangles.Add(v3);
projectedTriangles.Add(p2);
projectedTriangles.Add(p3);
// 三角形3
projectedTriangles.Add(v3);
projectedTriangles.Add(p3);
projectedTriangles.Add(v1);
projectedTriangles.Add(v1);
projectedTriangles.Add(p3);
projectedTriangles.Add(p1);
}
// 4. 添加投影面的三角形(反转原始三角形)
int[] originalTriangles = originalMesh.triangles;
for (int i = 0; i < originalTriangles.Length; i += 3)
{
// 反转 winding order
projectedTriangles.Add(originalTriangles[i + 2] + originalVertexCount);
projectedTriangles.Add(originalTriangles[i + 1] + originalVertexCount);
projectedTriangles.Add(originalTriangles[i] + originalVertexCount);
}
// 5. 合并所有三角形
triangles.AddRange(projectedTriangles);
// 6. 应用到新mesh
newMesh.vertices = vertices.ToArray();
newMesh.triangles = triangles.ToArray();
newMesh.normals = normals.ToArray();
newMesh.uv = uv.ToArray();
newMesh.RecalculateBounds();
newMesh.RecalculateTangents();
return newMesh;
}
void CreateShadowObject(GameObject original, Mesh shadowMesh)
{
GameObject shadowObject = new GameObject($"{original.name}_ShadowMesh");
shadowObject.transform.SetParent(original.transform);
shadowObject.transform.localPosition = Vector3.zero;
shadowObject.transform.localRotation = Quaternion.identity;
shadowObject.transform.localScale = Vector3.one;
// 添加Mesh Filter
MeshFilter meshFilter = shadowObject.AddComponent<MeshFilter>();
meshFilter.mesh = shadowMesh;
// 添加Mesh Collider
MeshCollider meshCollider = shadowObject.AddComponent<MeshCollider>();
meshCollider.sharedMesh = shadowMesh;
meshCollider.convex = false; // 对于复杂mesh使用非凸体
// 可选:添加材质用于可视化
MeshRenderer renderer = shadowObject.AddComponent<MeshRenderer>();
renderer.material = original.GetComponent<MeshRenderer>().material;
modifiedObjects.Add(shadowObject);
}
void UpdateMeshCollider(GameObject obj, Mesh mesh)
{
MeshCollider meshCollider = obj.GetComponent<MeshCollider>();
if (meshCollider == null)
meshCollider = obj.AddComponent<MeshCollider>();
meshCollider.sharedMesh = mesh;
meshCollider.convex = false;
}
Material CreateShadowMaterial()
{
Material material = new Material(Shader.Find("Standard"));
material.color = new Color(0, 0, 0, shadowIntensity);
material.SetFloat("_Mode", 2); // Fade mode
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = 3000;
return material;
}
Vector3 ProjectPointToWall(Vector3 worldPoint)
{
Vector3 lightDirection = GetLightDirection(worldPoint);
return IntersectWithWall(worldPoint, lightDirection);
}
Vector3 GetLightDirection(Vector3 targetPoint)
{
if (shadowLight.type == LightType.Directional)
{
return -shadowLight.transform.forward;
}
else
{
return (targetPoint - shadowLight.transform.position).normalized;
}
}
Vector3 IntersectWithWall(Vector3 point, Vector3 direction)
{
float denominator = Vector3.Dot(direction, wallNormal);
if (Mathf.Abs(denominator) < 0.0001f)
return point;
float t = Vector3.Dot(wallPosition - point, wallNormal) / denominator;
return point + direction * t;
}
void CalculateWallPlane()
{
if (shadowWall != null)
{
wallPosition = shadowWall.position;
wallNormal = -shadowWall.forward;
}
}
Transform FindMainWall()
{
BoxCollider[] colliders = FindObjectsOfType<BoxCollider>();
return colliders.Length > 0 ? colliders[0].transform : transform;
}
List<GameObject> FindObjectsInLayer(string layerName)
{
int layer = LayerMask.NameToLayer(layerName);
List<GameObject> result = new List<GameObject>();
foreach (GameObject obj in FindObjectsOfType<GameObject>())
{
if (obj.activeInHierarchy && obj.layer == layer)
{
result.Add(obj);
}
}
return result;
}
[ContextMenu("清空所有阴影Mesh")]
void ClearAllShadowMeshes()
{
foreach (GameObject obj in modifiedObjects)
{
if (obj != null)
{
if (obj.name.EndsWith("_ShadowMesh"))
DestroyImmediate(obj);
else
ResetOriginalMesh(obj);
}
}
modifiedObjects.Clear();
}
void ResetOriginalMesh(GameObject obj)
{
MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.mesh != null)
{
// 重新加载原始mesh
Mesh originalMesh = Instantiate(Resources.Load<Mesh>(obj.name)) ?? CreateSimpleMesh();
meshFilter.mesh = originalMesh;
MeshCollider collider = obj.GetComponent<MeshCollider>();
if (collider != null)
collider.sharedMesh = originalMesh;
}
}
Mesh CreateSimpleMesh()
{
// 创建默认的立方体mesh
return GameObject.CreatePrimitive(PrimitiveType.Cube).GetComponent<MeshFilter>().sharedMesh;
}
void OnDestroy()
{
ClearAllShadowMeshes();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7911d7cd239cfb44c910a896590b2db4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,259 @@
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class MarchingSquaresProcessor
{
[Header("Marching Squares设置")]
public float isoLevel = 0.5f;
public bool smoothEdges = true;
public float smoothness = 0.1f;
// Marching Squares查找表
private static readonly int[,] transitionTable = new int[,]
{
{-1, -1, -1, -1}, // 0: 空单元格
{ 0, 3, -1, -1}, // 1: 只有左下角
{ 1, 0, -1, -1}, // 2: 只有右下角
{ 1, 3, -1, -1}, // 3: 下边
{ 2, 1, -1, -1}, // 4: 只有右上角
{ 0, 3, 2, 1}, // 5: S形状
{ 2, 0, -1, -1}, // 6: 右边
{ 2, 3, -1, -1}, // 7: 右下三角
{ 3, 2, -1, -1}, // 8: 只有左上角
{ 0, 2, -1, -1}, // 9: 左边
{ 0, 1, 3, 2}, // 10: S形状反转
{ 1, 2, -1, -1}, // 11: 左下三角
{ 3, 1, -1, -1}, // 12: 上边
{ 3, 0, -1, -1}, // 13: 右上三角
{ 1, 3, -1, -1}, // 14: 左上三角
{-1, -1, -1, -1} // 15: 全满
};
public List<List<Vector2>> ExtractContours(DensityGrid grid)
{
var contours = new List<List<Vector2>>();
bool[,] visited = new bool[grid.Width, grid.Height];
for (int x = 0; x < grid.Width - 1; x++)
{
for (int y = 0; y < grid.Height - 1; y++)
{
if (!visited[x, y] && IsBoundaryCell(grid, x, y))
{
var contour = MarchCell(grid, x, y, visited);
if (contour != null && contour.Count >= 3)
{
if (smoothEdges)
contour = SmoothContour(contour);
contours.Add(contour);
}
}
}
}
return contours;
}
List<Vector2> MarchCell(DensityGrid grid, int startX, int startY, bool[,] visited)
{
var contour = new List<Vector2>();
int x = startX, y = startY;
int step = 0;
int maxSteps = grid.Width * grid.Height; // 防止无限循环
do
{
// 边界检查
if (x < 0 || x >= grid.Width - 1 || y < 0 || y >= grid.Height - 1)
break;
if (visited[x, y])
break;
visited[x, y] = true;
int config = GetCellConfiguration(grid, x, y);
var edgePoints = GetEdgePoints(grid, x, y, config);
// 只添加有效的点
foreach (var point in edgePoints)
{
if (!contour.Contains(point)) // 简单的去重
contour.Add(point);
}
// 移动到下一个单元格
if (!MoveToNextCell(config, ref x, ref y))
break;
step++;
} while ((x != startX || y != startY) && step < maxSteps);
return contour;
}
int GetCellConfiguration(DensityGrid grid, int x, int y)
{
int config = 0;
// 安全地获取值,处理边界情况
float a = GetSafeValue(grid, x, y);
float b = GetSafeValue(grid, x + 1, y);
float c = GetSafeValue(grid, x + 1, y + 1);
float d = GetSafeValue(grid, x, y + 1);
if (a >= isoLevel) config |= 1;
if (b >= isoLevel) config |= 2;
if (c >= isoLevel) config |= 4;
if (d >= isoLevel) config |= 8;
return config;
}
float GetSafeValue(DensityGrid grid, int x, int y)
{
if (x < 0 || x >= grid.Width || y < 0 || y >= grid.Height)
return 0f;
return grid.GetValue(x, y);
}
Vector2[] GetEdgePoints(DensityGrid grid, int x, int y, int config)
{
var points = new List<Vector2>();
// 根据配置添加边点
switch (config)
{
case 1:
case 14:
points.Add(InterpolateEdge(grid, x, y, x, y + 1)); // 左边
points.Add(InterpolateEdge(grid, x, y, x + 1, y)); // 底边
break;
case 2:
case 13:
points.Add(InterpolateEdge(grid, x, y, x + 1, y)); // 底边
points.Add(InterpolateEdge(grid, x + 1, y, x + 1, y + 1)); // 右边
break;
case 3:
case 12:
points.Add(InterpolateEdge(grid, x, y, x, y + 1)); // 左边
points.Add(InterpolateEdge(grid, x + 1, y, x + 1, y + 1)); // 右边
break;
case 4:
case 11:
points.Add(InterpolateEdge(grid, x + 1, y, x + 1, y + 1)); // 右边
points.Add(InterpolateEdge(grid, x, y + 1, x + 1, y + 1)); // 顶边
break;
case 5:
points.Add(InterpolateEdge(grid, x, y, x, y + 1)); // 左边
points.Add(InterpolateEdge(grid, x, y, x + 1, y)); // 底边
points.Add(InterpolateEdge(grid, x + 1, y, x + 1, y + 1)); // 右边
points.Add(InterpolateEdge(grid, x, y + 1, x + 1, y + 1)); // 顶边
break;
case 6:
case 9:
points.Add(InterpolateEdge(grid, x, y, x + 1, y)); // 底边
points.Add(InterpolateEdge(grid, x, y + 1, x + 1, y + 1)); // 顶边
break;
case 7:
case 8:
points.Add(InterpolateEdge(grid, x, y, x, y + 1)); // 左边
points.Add(InterpolateEdge(grid, x, y + 1, x + 1, y + 1)); // 顶边
break;
case 10:
points.Add(InterpolateEdge(grid, x, y, x, y + 1)); // 左边
points.Add(InterpolateEdge(grid, x, y, x + 1, y)); // 底边
points.Add(InterpolateEdge(grid, x + 1, y, x + 1, y + 1)); // 右边
points.Add(InterpolateEdge(grid, x, y + 1, x + 1, y + 1)); // 顶边
break;
}
return points.ToArray();
}
Vector2 InterpolateEdge(DensityGrid grid, int x1, int y1, int x2, int y2)
{
// 安全获取值
float v1 = GetSafeValue(grid, x1, y1);
float v2 = GetSafeValue(grid, x2, y2);
if (Mathf.Abs(v1 - v2) < 0.001f)
return grid.GridToLocalPosition(x1, y1);
float t = (isoLevel - v1) / (v2 - v1);
t = Mathf.Clamp01(t);
Vector2 p1 = grid.GridToLocalPosition(x1, y1);
Vector2 p2 = grid.GridToLocalPosition(x2, y2);
return Vector2.Lerp(p1, p2, t);
}
bool MoveToNextCell(int config, ref int x, ref int y)
{
// 简化的移动逻辑 - 根据配置决定下一个单元格
switch (config)
{
case 1:
case 3:
case 7:
case 5:
y++; break; // 向上移动
case 2:
case 6:
case 10:
case 14:
x++; break; // 向右移动
case 4:
case 12:
case 13:
case 15:
y--; break; // 向下移动
case 8:
case 9:
case 11:
x--; break; // 向左移动
default:
return false; // 无法移动
}
return true;
}
List<Vector2> SmoothContour(List<Vector2> contour)
{
if (contour.Count < 4) return contour;
var smoothed = new List<Vector2>();
for (int i = 0; i < contour.Count; i++)
{
Vector2 prev = contour[(i - 1 + contour.Count) % contour.Count];
Vector2 current = contour[i];
Vector2 next = contour[(i + 1) % contour.Count];
Vector2 smoothedPoint = (prev + current * 2f + next) / 4f;
smoothed.Add(smoothedPoint);
}
return smoothed;
}
bool IsBoundaryCell(DensityGrid grid, int x, int y)
{
// 安全检查
if (x < 0 || x >= grid.Width - 1 || y < 0 || y >= grid.Height - 1)
return false;
float value = grid.GetValue(x, y);
return value >= isoLevel && value < 1.0f; // 边界单元格
}
public void DrawGridGizmos(Transform wall)
{
// 调试可视化代码可以留空或简化
#if UNITY_EDITOR
// 可选:添加网格可视化
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2a4c434153f3614d8381195368e2fec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using UnityEngine;
using System.Collections.Generic;
public class ShadowCollider : MonoBehaviour
{
[Header("阴影碰撞器")]
public GameObject sourceObject;
public PolygonCollider2D polygonCollider;
[Header("调试")]
public bool showGizmos = true;
public Color gizmoColor = new Color(1, 0, 0, 0.3f);
public void Initialize(List<List<Vector2>> contours)
{
polygonCollider = GetComponent<PolygonCollider2D>();
if (polygonCollider == null)
polygonCollider = gameObject.AddComponent<PolygonCollider2D>();
UpdateCollider(contours);
}
public void UpdateCollider(List<List<Vector2>> contours)
{
if (polygonCollider == null) return;
polygonCollider.pathCount = contours.Count;
for (int i = 0; i < contours.Count; i++)
{
polygonCollider.SetPath(i, contours[i].ToArray());
}
}
#if UNITY_EDITOR
void OnDrawGizmos()
{
if (!showGizmos || polygonCollider == null) return;
Gizmos.color = gizmoColor;
Vector3 offset = transform.forward * 0.01f;
for (int pathIndex = 0; pathIndex < polygonCollider.pathCount; pathIndex++)
{
var path = polygonCollider.GetPath(pathIndex);
for (int i = 0; i < path.Length; i++)
{
Vector3 point1 = transform.TransformPoint(path[i]);
Vector3 point2 = transform.TransformPoint(path[(i + 1) % path.Length]);
Gizmos.DrawLine(point1 + offset, point2 + offset);
}
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a96ca5e4a935984392a295d8855f0f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,183 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Rendering.Universal;
public class ShadowColliderSystem : MonoBehaviour
{
[Header("系统设置")]
public Light shadowLight;
public Transform shadowWall;
public string shadowLayerName = "Shadow";
public LayerMask obstacleLayerMask = -1;
[Header("生成设置")]
public bool generateOnStart = true;
public bool updateInRealTime = false;
public float updateInterval = 0.1f;
[Header("阴影碰撞器")]
public List<ShadowCollider> shadowColliders = new List<ShadowCollider>();
private ShadowProjector projector;
private ShadowMeshGenerator meshGenerator;
private MarchingSquaresProcessor marchingSquares;
private float updateTimer;
void Start()
{
InitializeComponents();
if (generateOnStart)
{
GenerateAllShadowColliders();
}
}
void Update()
{
if (updateInRealTime)
{
updateTimer += Time.deltaTime;
if (updateTimer >= updateInterval)
{
UpdateAllShadowColliders();
updateTimer = 0f;
}
}
}
void InitializeComponents()
{
// 自动查找必要的组件
if (shadowLight == null) shadowLight = ShadowUtils.FindMainLight();
if (shadowWall == null) shadowWall = ShadowUtils.FindMainWall();
// 初始化子系统
projector = new ShadowProjector(shadowLight, shadowWall, obstacleLayerMask);
meshGenerator = new ShadowMeshGenerator();
marchingSquares = new MarchingSquaresProcessor();
}
[ContextMenu("生成所有阴影碰撞器")]
public void GenerateAllShadowColliders()
{
ClearAllShadowColliders();
var shadowObjects = ShadowUtils.FindObjectsInLayer(shadowLayerName);
foreach (GameObject obj in shadowObjects)
{
GenerateShadowCollider(obj);
}
Debug.Log($"生成了 {shadowColliders.Count} 个阴影碰撞器");
}
[ContextMenu("更新所有阴影碰撞器")]
public void UpdateAllShadowColliders()
{
foreach (var shadowCollider in shadowColliders)
{
if (shadowCollider != null)
{
UpdateShadowCollider(shadowCollider);
}
}
}
public void GenerateShadowCollider(GameObject shadowCaster)
{
if (!IsValidShadowCaster(shadowCaster)) return;
try
{
// 1. 投影网格到墙面
var projectedMesh = projector.ProjectMeshToWall(shadowCaster);
if (!projectedMesh.IsValid)
{
Debug.LogWarning($"无法为 {shadowCaster.name} 生成有效投影网格");
return;
}
// 2. 生成阴影密度网格
var densityGrid = meshGenerator.GenerateDensityGrid(projectedMesh, shadowWall);
// 3. 使用Marching Squares提取轮廓
var contours = marchingSquares.ExtractContours(densityGrid);
if (contours == null || contours.Count == 0)
{
Debug.LogWarning($"无法为 {shadowCaster.name} 提取轮廓");
return;
}
// 4. 创建阴影碰撞器对象
var shadowCollider = CreateShadowColliderObject(shadowCaster.name, contours);
shadowCollider.sourceObject = shadowCaster;
shadowColliders.Add(shadowCollider);
Debug.Log($"成功为 {shadowCaster.name} 生成阴影碰撞器,包含 {contours.Count} 个轮廓");
}
catch (System.Exception e)
{
Debug.LogError($"为 {shadowCaster.name} 生成阴影碰撞器时出错: {e.Message}");
}
}
public void UpdateShadowCollider(ShadowCollider shadowCollider)
{
if (shadowCollider.sourceObject == null) return;
var projectedMesh = projector.ProjectMeshToWall(shadowCollider.sourceObject);
var densityGrid = meshGenerator.GenerateDensityGrid(projectedMesh, shadowWall);
var contours = marchingSquares.ExtractContours(densityGrid);
shadowCollider.UpdateCollider(contours);
}
[ContextMenu("清空所有阴影碰撞器")]
public void ClearAllShadowColliders()
{
foreach (var collider in shadowColliders)
{
if (collider != null && collider.gameObject != null)
{
DestroyImmediate(collider.gameObject);
}
}
shadowColliders.Clear();
}
bool IsValidShadowCaster(GameObject obj)
{
return obj != null &&
obj.activeInHierarchy &&
obj.GetComponent<MeshFilter>() != null;
}
ShadowCollider CreateShadowColliderObject(string name, List<List<Vector2>> contours)
{
GameObject colliderObj = new GameObject($"{name}_ShadowCollider");
colliderObj.transform.SetParent(shadowWall);
colliderObj.transform.localPosition = Vector3.zero;
colliderObj.transform.localRotation = Quaternion.identity;
var shadowCollider = colliderObj.AddComponent<ShadowCollider>();
shadowCollider.Initialize(contours);
return shadowCollider;
}
void OnDestroy()
{
ClearAllShadowColliders();
}
#if UNITY_EDITOR
void OnDrawGizmosSelected()
{
if (marchingSquares != null && shadowWall != null)
{
marchingSquares.DrawGridGizmos(shadowWall);
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 836b4625937ba9d45be59618cd2a6a15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class ShadowMeshGenerator
{
[Header("网格设置")]
public int gridResolution = 64;
public float gridSize = 1.0f;
public float influenceRadius = 0.5f;
public DensityGrid GenerateDensityGrid(ProjectedMesh mesh, Transform wall)
{
var bounds = CalculateMeshBounds(mesh);
var grid = new DensityGrid(bounds, gridResolution, gridSize);
// 为每个网格点计算密度
for (int x = 0; x < grid.Width; x++)
{
for (int y = 0; y < grid.Height; y++)
{
Vector2 gridPos = grid.GridToLocalPosition(x, y);
float density = CalculateDensityAtPoint(gridPos, mesh);
grid.SetValue(x, y, density);
}
}
return grid;
}
float CalculateDensityAtPoint(Vector2 point, ProjectedMesh mesh)
{
float totalDensity = 0f;
int samples = 0;
// 采样网格点周围的顶点影响
foreach (var vertex in mesh.vertices)
{
Vector2 vertex2D = new Vector2(vertex.x, vertex.y);
float distance = Vector2.Distance(point, vertex2D);
if (distance < influenceRadius)
{
float influence = 1f - (distance / influenceRadius);
totalDensity += influence;
samples++;
}
}
return samples > 0 ? totalDensity / samples : 0f;
}
Bounds CalculateMeshBounds(ProjectedMesh mesh)
{
if (mesh.vertices.Count == 0)
return new Bounds(Vector3.zero, Vector3.one);
Vector3 min = mesh.vertices[0];
Vector3 max = mesh.vertices[0];
foreach (var vertex in mesh.vertices)
{
min = Vector3.Min(min, vertex);
max = Vector3.Max(max, vertex);
}
return new Bounds((min + max) * 0.5f, max - min);
}
}
[System.Serializable]
public class DensityGrid
{
public int Width { get; private set; }
public int Height { get; private set; }
public float CellSize { get; private set; }
public Bounds WorldBounds { get; private set; }
private float[,] values;
private Vector3 gridOrigin;
public DensityGrid(Bounds bounds, int resolution, float cellSize)
{
WorldBounds = bounds;
CellSize = cellSize;
Width = resolution;
Height = resolution;
gridOrigin = bounds.min;
values = new float[Width, Height];
}
public void SetValue(int x, int y, float value)
{
if (x >= 0 && x < Width && y >= 0 && y < Height)
{
values[x, y] = Mathf.Clamp01(value);
}
}
public float GetValue(int x, int y)
{
if (x >= 0 && x < Width && y >= 0 && y < Height)
{
return values[x, y];
}
return 0f;
}
public Vector2 GridToLocalPosition(int x, int y)
{
return new Vector2(
x * CellSize + gridOrigin.x,
y * CellSize + gridOrigin.y
);
}
public Vector3 GridToWorldPosition(int x, int y, Transform wall)
{
Vector2 localPos = GridToLocalPosition(x, y);
return wall.TransformPoint(new Vector3(localPos.x, localPos.y, 0));
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85170fe0e92d3604fbf662a3620570e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,136 @@
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class ShadowProjector
{
public Light lightSource;
public Transform projectionWall;
public LayerMask obstacleMask;
private Vector3 wallNormal;
private Vector3 wallPosition;
public ShadowProjector(Light light, Transform wall, LayerMask obstacleMask)
{
this.lightSource = light;
this.projectionWall = wall;
this.obstacleMask = obstacleMask;
CalculateWallPlane();
}
public ProjectedMesh ProjectMeshToWall(GameObject shadowCaster)
{
var meshFilter = shadowCaster.GetComponent<MeshFilter>();
if (meshFilter == null) return new ProjectedMesh();
var mesh = meshFilter.mesh;
var vertices = mesh.vertices;
var triangles = mesh.triangles;
List<Vector3> projectedVertices = new List<Vector3>();
List<int> projectedTriangles = new List<int>();
// 投影每个顶点
for (int i = 0; i < vertices.Length; i++)
{
Vector3 worldVertex = shadowCaster.transform.TransformPoint(vertices[i]);
Vector3 projectedVertex = ProjectPoint(worldVertex);
if (!IsObstructed(worldVertex, projectedVertex))
{
projectedVertices.Add(projectedVertex);
}
}
// 重新构建三角形(简化版本)
// 实际应该使用更复杂的三角化算法
for (int i = 0; i < triangles.Length; i += 3)
{
if (IsValidTriangle(projectedVertices, triangles, i))
{
projectedTriangles.Add(triangles[i]);
projectedTriangles.Add(triangles[i + 1]);
projectedTriangles.Add(triangles[i + 2]);
}
}
return new ProjectedMesh
{
vertices = projectedVertices,
triangles = projectedTriangles.ToArray(),
sourceObject = shadowCaster
};
}
Vector3 ProjectPoint(Vector3 worldPoint)
{
Vector3 lightDirection = GetLightDirection(worldPoint);
return IntersectWithWall(worldPoint, lightDirection);
}
Vector3 GetLightDirection(Vector3 targetPoint)
{
if (lightSource.type == LightType.Directional)
{
return -lightSource.transform.forward;
}
else
{
return (targetPoint - lightSource.transform.position).normalized;
}
}
Vector3 IntersectWithWall(Vector3 point, Vector3 direction)
{
float denominator = Vector3.Dot(direction, wallNormal);
if (Mathf.Abs(denominator) < 0.0001f)
return point;
float t = Vector3.Dot(wallPosition - point, wallNormal) / denominator;
return point + direction * t;
}
bool IsObstructed(Vector3 start, Vector3 end)
{
Vector3 direction = (end - start).normalized;
float distance = Vector3.Distance(start, end);
RaycastHit hit;
if (Physics.Raycast(start, direction, out hit, distance, obstacleMask))
{
return hit.collider.gameObject != projectionWall.gameObject;
}
return false;
}
bool IsValidTriangle(List<Vector3> vertices, int[] triangles, int startIndex)
{
int i1 = triangles[startIndex];
int i2 = triangles[startIndex + 1];
int i3 = triangles[startIndex + 2];
return i1 < vertices.Count && i2 < vertices.Count && i3 < vertices.Count;
}
void CalculateWallPlane()
{
if (projectionWall != null)
{
wallPosition = projectionWall.position;
wallNormal = -projectionWall.forward;
}
}
}
[System.Serializable]
public struct ProjectedMesh
{
public List<Vector3> vertices;
public int[] triangles;
public GameObject sourceObject;
public bool IsValid => vertices != null && vertices.Count >= 3;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 462c6c18ec968ae4badc890a169194e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public static class ShadowUtils
{
public static Light FindMainLight()
{
Light[] lights = Object.FindObjectsOfType<Light>();
return lights.FirstOrDefault(l => l.type == LightType.Directional && l.enabled) ??
lights.FirstOrDefault(l => l.type == LightType.Spot && l.enabled);
}
public static Transform FindMainWall()
{
var walls = Object.FindObjectsOfType<Collider>()
.Where(c => c is BoxCollider)
.OrderByDescending(c =>
{
var box = c as BoxCollider;
var size = Vector3.Scale(box.size, c.transform.lossyScale);
return size.x * size.y;
});
return walls.FirstOrDefault()?.transform;
}
public static List<GameObject> FindObjectsInLayer(string layerName)
{
int layer = LayerMask.NameToLayer(layerName);
return Object.FindObjectsOfType<GameObject>()
.Where(obj => obj.activeInHierarchy && obj.layer == layer)
.ToList();
}
public static List<GameObject> FindObjectsInLayer(int layer)
{
return Object.FindObjectsOfType<GameObject>()
.Where(obj => obj.activeInHierarchy && obj.layer == layer)
.ToList();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d5f9eb8ac9da6a8488b456411e1e7370
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,82 +1,274 @@
using UnityEngine;
using UnityEngine;
using System.Collections.Generic;
public class RaycastShadowCollider : MonoBehaviour
{
public Light spotLight; // <20>۹<EFBFBD><DBB9><EFBFBD>
public GameObject shadowCaster; // Ͷ<><CDB6><EFBFBD><EFBFBD>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public int rayCount = 36; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public Light spotLight;
public List<GameObject> shadowCasters = new List<GameObject>();
public LayerMask shadowCasterLayer;
public LayerMask shadowReceiverLayer;
public float maxShadowDistance = 10f;
private PolygonCollider2D shadowCollider;
public PolygonCollider2D shadowCollider;
void Start()
{
shadowCollider = GetComponent<PolygonCollider2D>();
if (shadowCollider == null)
shadowCollider = gameObject.AddComponent<PolygonCollider2D>();
UpdateShadowColliders();
}
void Update()
{
UpdateShadowCollider();
//UpdateShadowCollider();
}
void UpdateShadowCollider()
void UpdateShadowColliders()
{
foreach (var shadowCaster in shadowCasters)
{
UpdateShadowCollider(shadowCaster);
}
}
void UpdateShadowCollider(GameObject shadowCaster)
{
List<Vector2> shadowPoints = new List<Vector2>();
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߽<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƹⷽ<EFBFBD><EFBFBD>Ͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Bounds bounds = shadowCaster.GetComponent<Renderer>().bounds;
Vector3[] boundPoints = GetBoundPoints(bounds);
// 获取阴影投射器的边界点(世界坐标)
Vector3[] worldBoundPoints = GetBoundPoints(shadowCaster);
foreach (Vector3 point in boundPoints)
foreach (Vector3 worldPoint in worldBoundPoints)
{
Vector3 toLight = (spotLight.transform.position - point).normalized;
Ray ray = new Ray(point, toLight);
// 方向:从灯光指向边界点
Vector3 toPoint = (worldPoint - spotLight.transform.position).normalized;
// 创建从灯光位置发出的射线
Ray ray = new Ray(spotLight.transform.position, toPoint);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, maxShadowDistance))
// 调试:显示射线方向
Debug.DrawRay(spotLight.transform.position, toPoint * maxShadowDistance, Color.yellow);
if (Physics.Raycast(ray, out hit, maxShadowDistance, shadowReceiverLayer))
{
if (hit.collider.gameObject == gameObject) // <20><><EFBFBD><EFBFBD>ǽ<EFBFBD><C7BD>
// 检查是否击中了阴影投射器
if (hit.collider.gameObject == gameObject)
{
// ת<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǽ<EFBFBD><EFBFBD><EFBFBD>ľֲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵ
Vector3 localHit = transform.InverseTransformPoint(hit.point);
// 将世界坐标的碰撞点转换到阴影碰撞器的本地坐标
Vector3 localHit = shadowCollider.transform.InverseTransformPoint(hit.point);
shadowPoints.Add(new Vector2(localHit.x, localHit.y));
// 调试:在碰撞点创建小球
// GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
//sphere.transform.position = hit.point;
// sphere.transform.localScale = Vector3.one * 0.1f;
//Destroy(sphere, 0.1f);
}
}
//else
//{
// // 如果没有击中,使用最大距离的点
// Vector3 maxPoint = spotLight.transform.position + toPoint * maxShadowDistance;
// Vector3 localMaxPoint = shadowCollider.transform.InverseTransformPoint(maxPoint);
// shadowPoints.Add(new Vector2(localMaxPoint.x, localMaxPoint.y));
// // 调试:在最大距离点创建小球
// GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
// sphere.transform.position = maxPoint;
// sphere.transform.localScale = Vector3.one * 0.1f;
// sphere.GetComponent<Renderer>().material.color = Color.blue;
// Destroy(sphere, 0.1f);
//}
}
// <EFBFBD>Ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>γ<EFBFBD>͹<EFBFBD><EFBFBD>
// 设置碰撞器路径
if (shadowPoints.Count > 2)
{
List<Vector2> convexHull = ComputeConvexHull(shadowPoints);
shadowCollider.SetPath(0, convexHull);
// 对点进行排序以确保正确的多边形
shadowPoints = SortShadowPoints(shadowPoints);
shadowCollider.SetPath(0, shadowPoints);
}
else
{
shadowCollider.pathCount = 0;
}
}
Vector3[] GetBoundPoints(Bounds bounds)
Vector3[] GetBoundPoints(GameObject target)
{
Bounds bounds = target.GetComponent<MeshRenderer>().bounds;
return new Vector3[]
{
bounds.min,
bounds.max,
new Vector3(bounds.min.x, bounds.min.y, bounds.max.z),
new Vector3(bounds.min.x, bounds.max.y, bounds.min.z),
new Vector3(bounds.max.x, bounds.min.y, bounds.min.z),
new Vector3(bounds.min.x, bounds.max.y, bounds.max.z),
new Vector3(bounds.max.x, bounds.min.y, bounds.max.z),
new Vector3(bounds.max.x, bounds.max.y, bounds.min.z)
bounds.min,
bounds.max,
new Vector3(bounds.min.x, bounds.min.y, bounds.max.z),
new Vector3(bounds.min.x, bounds.max.y, bounds.min.z),
new Vector3(bounds.max.x, bounds.min.y, bounds.min.z),
new Vector3(bounds.min.x, bounds.max.y, bounds.max.z),
new Vector3(bounds.max.x, bounds.min.y, bounds.max.z),
new Vector3(bounds.max.x, bounds.max.y, bounds.min.z)
};
}
// ͹<><CDB9><EFBFBD><EFBFBD><E3B7A8>Graham Scan<61><6E>
//List<Vector2> SortPointsClockwise(List<Vector2> points)
//{
// if (points.Count < 3) return points;
// // 计算中心点
// Vector2 center = Vector2.zero;
// foreach (Vector2 point in points)
// {
// center += point;
// }
// center /= points.Count;
// // 按角度排序
// points.Sort((a, b) =>
// {
// Vector2 dirA = a - center;
// Vector2 dirB = b - center;
// float angleA = Mathf.Atan2(dirA.y, dirA.x);
// float angleB = Mathf.Atan2(dirB.y, dirB.x);
// return angleA.CompareTo(angleB);
// });
// points.Add(points[points.Count-1]);
// return points;
//}
List<Vector2> SortPointsClockwise(List<Vector2> points)
{
if (points.Count < 3) return points;
// 方法1使用凸包算法推荐
List<Vector2> convexHull = ComputeConvexHull(points);
return convexHull;
// 或者方法2改进的角度排序如果确定是凸多边形
// return ImprovedAngleSort(points);
}
// 计算凸包 - Graham Scan 算法
List<Vector2> ComputeConvexHull(List<Vector2> points)
{
if (points.Count < 3) return points;
// ʵ<EFBFBD><EFBFBD>͹<EFBFBD><EFBFBD><EFBFBD>㷨...
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Unity<74><79>Collider2D.CreatePrimitive<76><65><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
return points; // <20>򻯷<EFBFBD><F2BBAFB7><EFBFBD>
// 找到最左下角的点作为起点
Vector2 startPoint = points[0];
foreach (Vector2 point in points)
{
if (point.y < startPoint.y || (point.y == startPoint.y && point.x < startPoint.x))
{
startPoint = point;
}
}
// 按极角排序
List<Vector2> sortedPoints = new List<Vector2>(points);
sortedPoints.Sort((a, b) =>
{
if (a == startPoint) return -1;
if (b == startPoint) return 1;
Vector2 dirA = a - startPoint;
Vector2 dirB = b - startPoint;
float cross = Cross(dirA, dirB);
if (Mathf.Abs(cross) < 0.001f) // 共线情况
{
return dirA.sqrMagnitude.CompareTo(dirB.sqrMagnitude);
}
return cross > 0 ? -1 : 1;
});
// 构建凸包
List<Vector2> hull = new List<Vector2>();
hull.Add(startPoint);
hull.Add(sortedPoints[1]);
for (int i = 2; i < sortedPoints.Count; i++)
{
while (hull.Count >= 2)
{
Vector2 a = hull[hull.Count - 2];
Vector2 b = hull[hull.Count - 1];
Vector2 c = sortedPoints[i];
if (Cross(b - a, c - a) <= 0)
{
hull.RemoveAt(hull.Count - 1);
}
else
{
break;
}
}
hull.Add(sortedPoints[i]);
}
return hull;
}
// 叉积计算
float Cross(Vector2 a, Vector2 b)
{
return a.x * b.y - a.y * b.x;
}
// 改进的角度排序(仅适用于凸多边形)
List<Vector2> ImprovedAngleSort(List<Vector2> points)
{
// 计算中心点
Vector2 center = Vector2.zero;
foreach (Vector2 point in points)
{
center += point;
}
center /= points.Count;
// 按角度排序,处理相同角度的情况
points.Sort((a, b) =>
{
Vector2 dirA = a - center;
Vector2 dirB = b - center;
float angleA = Mathf.Atan2(dirA.y, dirA.x);
float angleB = Mathf.Atan2(dirB.y, dirB.x);
// 处理角度接近的情况
if (Mathf.Abs(angleA - angleB) < 0.001f)
{
return dirA.sqrMagnitude.CompareTo(dirB.sqrMagnitude);
}
return angleA.CompareTo(angleB);
});
return points;
}
// 专门为阴影投影点设计的排序方法
List<Vector2> SortShadowPoints(List<Vector2> points)
{
if (points.Count < 3) return points;
// 对于阴影点,通常形成凸多边形,使用凸包算法
List<Vector2> hull = ComputeConvexHull(points);
// 确保点是顺时针顺序Unity碰撞器需要
if (!IsClockwise(hull))
{
hull.Reverse();
}
return hull;
}
// 检查多边形是否是顺时针顺序
bool IsClockwise(List<Vector2> points)
{
float area = 0;
for (int i = 0; i < points.Count; i++)
{
Vector2 current = points[i];
Vector2 next = points[(i + 1) % points.Count];
area += (next.x - current.x) * (next.y + current.y);
}
return area > 0;
}
}

View File

@@ -1,124 +1,288 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class ShadowColliderGenerator : MonoBehaviour
{
public Camera shadowCamera; // ר<><D7A8><EFBFBD><EFBFBD>Ⱦ<EFBFBD><C8BE>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public RenderTexture shadowRT; // <20><>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>
public LayerMask shadowCasterLayer; // Ͷ<><CDB6><EFBFBD><EFBFBD>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public float colliderPrecision = 0.1f; // <20><>ײ<EFBFBD><EFBFBD><E5BEAB>
[Header("References")]
public Camera shadowCamera;
public RenderTexture shadowRT;
public PolygonCollider2D polygonCollider;
[Header("Settings")]
[Range(0f, 1f)]
public float shadowThreshold = 0.3f;
public int gridSize = 2; // 网格大小,越小越精确
public float simplifyTolerance = 1f;
private Texture2D processedTexture;
private PolygonCollider2D polygonCollider;
void Start()
{
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>
shadowRT = new RenderTexture(512, 512, 24);
shadowCamera.targetTexture = shadowRT;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>õ<EFBFBD><C3B5><EFBFBD><EFBFBD><EFBFBD>
processedTexture = new Texture2D(512, 512, TextureFormat.RGB24, false);
polygonCollider = GetComponent<PolygonCollider2D>();
if (polygonCollider == null)
polygonCollider = gameObject.AddComponent<PolygonCollider2D>();
polygonCollider = GetComponent<PolygonCollider2D>();
processedTexture = new Texture2D(shadowRT.width, shadowRT.height, TextureFormat.R8, false);
GenerateCollider();
}
void Update()
{
GenerateShadowCollider();
if (Input.GetKeyDown(KeyCode.Space))
{
GenerateCollider();
}
}
void GenerateShadowCollider()
[ContextMenu("Generate Collider")]
public void GenerateCollider()
{
// 1. <20><>Ⱦ<EFBFBD><C8BE>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (shadowCamera == null || shadowRT == null) return;
// 渲染阴影
shadowCamera.Render();
// 2. <20><>GPU<50><55>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>CPU
// 读取纹理
RenderTexture.active = shadowRT;
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
processedTexture.Apply();
RenderTexture.active = null;
// 3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӱ<EFBFBD><D3B0>Ե
List<Vector2> edgePoints = DetectEdges(processedTexture);
// 生成轮廓
List<Vector2> contour = GenerateContourFromTexture();
// 4. ת<><D7AA>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EAB2A2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ײ<EFBFBD><D7B2>
if (edgePoints.Count > 2)
if (contour.Count > 2)
{
List<Vector2> worldPoints = ConvertToWorldCoordinates(edgePoints);
List<Vector2> worldPoints = ConvertToWorldCoordinates(contour);
polygonCollider.SetPath(0, worldPoints);
Debug.Log($"Collider generated with {worldPoints.Count} points");
}
else
{
polygonCollider.pathCount = 0;
Debug.Log("No contour generated");
}
}
List<Vector2> DetectEdges(Texture2D texture)
List<Vector2> GenerateContourFromTexture()
{
List<Vector2> edges = new List<Vector2>();
Color[] pixels = texture.GetPixels();
int width = processedTexture.width;
int height = processedTexture.height;
int width = texture.width;
int height = texture.height;
// 创建二值化网格
bool[,] grid = CreateBinaryGrid(width, height);
// <EFBFBD>򵥵ı<EFBFBD>Ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (int y = 1; y < height - 1; y += (int)(1f / colliderPrecision))
// 找到所有边界点
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
// 连接边界点形成轮廓
List<Vector2> contour = ConnectBoundaryPoints(boundaryPoints);
return contour;
}
bool[,] CreateBinaryGrid(int width, int height)
{
bool[,] grid = new bool[width / gridSize, height / gridSize];
for (int x = 0; x < grid.GetLength(0); x++)
{
for (int x = 1; x < width - 1; x += (int)(1f / colliderPrecision))
for (int y = 0; y < grid.GetLength(1); y++)
{
int index = y * width + x;
int texX = x * gridSize;
int texY = y * gridSize;
Color pixel = processedTexture.GetPixel(texX, texY);
grid[x, y] = pixel.grayscale < shadowThreshold;
}
}
// <20><><EFBFBD>鵱ǰ<E9B5B1><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Χ<EFBFBD><CEA7><EFBFBD>صIJ<D8B5><C4B2><EFBFBD>
if (IsEdgePixel(pixels, index, width))
return grid;
}
List<Vector2> FindBoundaryPoints(bool[,] grid)
{
List<Vector2> boundaryPoints = new List<Vector2>();
int width = grid.GetLength(0);
int height = grid.GetLength(1);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (grid[x, y])
{
edges.Add(new Vector2(x, y));
// 检查是否是边界(有非阴影像素邻居)
bool isBoundary = false;
// 检查4邻域
if (x == 0 || !grid[x - 1, y]) isBoundary = true;
else if (x == width - 1 || !grid[x + 1, y]) isBoundary = true;
else if (y == 0 || !grid[x, y - 1]) isBoundary = true;
else if (y == height - 1 || !grid[x, y + 1]) isBoundary = true;
if (isBoundary)
{
boundaryPoints.Add(new Vector2(x * gridSize, y * gridSize));
}
}
}
}
return edges;
Debug.Log($"Found {boundaryPoints.Count} boundary points");
return boundaryPoints;
}
bool IsEdgePixel(Color[] pixels, int index, int width)
List<Vector2> ConnectBoundaryPoints(List<Vector2> points)
{
Color current = pixels[index];
float currentBrightness = current.r + current.g + current.b;
if (points.Count < 3) return points;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
float[] neighborBrightness = new float[4]
{
pixels[index - 1].r + pixels[index - 1].g + pixels[index - 1].b, // <20><>
pixels[index + 1].r + pixels[index + 1].g + pixels[index + 1].b, // <20><>
pixels[index - width].r + pixels[index - width].g + pixels[index - width].b, // <20><>
pixels[index + width].r + pixels[index + width].g + pixels[index + width].b // <20><>
};
List<Vector2> contour = new List<Vector2>();
List<Vector2> remaining = new List<Vector2>(points);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ھӲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD>DZ<EFBFBD>Ե
float threshold = 0.3f;
for (int i = 0; i < 4; i++)
// 找到最左边的点作为起点
Vector2 start = remaining.OrderBy(p => p.x).First();
contour.Add(start);
remaining.Remove(start);
Vector2 current = start;
Vector2 previousDirection = Vector2.right;
int maxIterations = points.Count * 2;
int iterations = 0;
while (remaining.Count > 0 && iterations < maxIterations)
{
if (Mathf.Abs(currentBrightness - neighborBrightness[i]) > threshold)
return true;
iterations++;
// 找到与当前方向最一致的下一个点
float bestScore = float.MinValue;
Vector2 bestPoint = Vector2.zero;
int bestIndex = -1;
for (int i = 0; i < remaining.Count; i++)
{
Vector2 toPoint = (remaining[i] - current).normalized;
float distance = Vector2.Distance(current, remaining[i]);
// 评分:方向一致性 + 距离权重
float directionScore = Vector2.Dot(previousDirection, toPoint);
float distanceScore = 1f / (distance + 0.1f);
float totalScore = directionScore + distanceScore * 0.1f;
if (totalScore > bestScore && distance < 50f) // 距离限制
{
bestScore = totalScore;
bestPoint = remaining[i];
bestIndex = i;
}
}
if (bestIndex >= 0)
{
previousDirection = (bestPoint - current).normalized;
contour.Add(bestPoint);
remaining.RemoveAt(bestIndex);
current = bestPoint;
}
else
{
break;
}
}
return false;
// 确保轮廓闭合
if (contour.Count > 2 && Vector2.Distance(contour[0], contour[contour.Count - 1]) > 30f)
{
contour.Add(contour[0]);
}
// 简化轮廓
contour = SimplifyContour(contour);
Debug.Log($"Connected {contour.Count} points into contour");
return contour;
}
List<Vector2> SimplifyContour(List<Vector2> contour)
{
if (contour.Count <= 3) return contour;
List<Vector2> simplified = new List<Vector2>();
simplified.Add(contour[0]);
for (int i = 1; i < contour.Count - 1; i++)
{
// 如果角度变化太大,保留这个点
if (i > 1)
{
Vector2 prevDir = (contour[i] - contour[i - 1]).normalized;
Vector2 nextDir = (contour[i + 1] - contour[i]).normalized;
float angle = Vector2.Angle(prevDir, nextDir);
if (angle > 30f) // 角度阈值
{
simplified.Add(contour[i]);
}
}
}
simplified.Add(contour[contour.Count - 1]);
return simplified;
}
List<Vector2> ConvertToWorldCoordinates(List<Vector2> texturePoints)
{
List<Vector2> worldPoints = new List<Vector2>();
Bounds wallBounds = GetComponent<Collider2D>().bounds;
foreach (Vector2 point in texturePoints)
foreach (Vector2 texCoord in texturePoints)
{
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>Ϊǽ<CEAA><C7BD><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD><EFBFBD><EFBFBD>
Vector2 localPoint = new Vector2(
point.x / shadowRT.width * wallBounds.size.x - wallBounds.size.x * 0.5f,
point.y / shadowRT.height * wallBounds.size.y - wallBounds.size.y * 0.5f
Vector3 viewportPos = new Vector3(
texCoord.x / processedTexture.width,
texCoord.y / processedTexture.height,
shadowCamera.nearClipPlane
);
worldPoints.Add(localPoint);
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
worldPoints.Add(new Vector2(worldPos.x, worldPos.y));
}
return worldPoints;
}
// 调试:显示边界点
void OnDrawGizmosSelected()
{
if (processedTexture == null) return;
Gizmos.color = Color.red;
// 显示边界点
bool[,] grid = CreateBinaryGrid(processedTexture.width, processedTexture.height);
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
foreach (Vector2 point in boundaryPoints)
{
Vector3 viewportPos = new Vector3(
point.x / processedTexture.width,
point.y / processedTexture.height,
shadowCamera.nearClipPlane
);
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
Gizmos.DrawSphere(worldPos, 0.02f);
}
// 显示碰撞器轮廓
if (polygonCollider != null && polygonCollider.pathCount > 0)
{
Gizmos.color = Color.green;
Vector2[] path = polygonCollider.GetPath(0);
for (int i = 0; i < path.Length; i++)
{
Vector3 start = path[i];
Vector3 end = path[(i + 1) % path.Length];
Gizmos.DrawLine(start, end);
}
}
}
}

View File

@@ -37,4 +37,4 @@ RenderTexture:
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
m_ShadowSamplingMode: 0

View File

@@ -0,0 +1,256 @@
using UnityEngine;
using System.Collections.Generic;
public class SimpleShadowCollider : MonoBehaviour
{
[Header("References")]
public Camera shadowCamera;
public RenderTexture shadowRT;
public PolygonCollider2D polygonCollider;
[Header("Settings")]
[Range(0f, 1f)]
public float shadowThreshold = 0.3f;
public int downSample = 4; // 降低采样率提高性能
public float minShadowSize = 0.1f; // 最小阴影尺寸
private Texture2D processedTexture;
private bool[,] shadowGrid;
void Start()
{
if (polygonCollider == null)
polygonCollider = GetComponent<PolygonCollider2D>();
processedTexture = new Texture2D(shadowRT.width, shadowRT.height, TextureFormat.RGBA32, false);
GenerateCollider();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GenerateCollider();
}
}
[ContextMenu("Generate Collider")]
public void GenerateCollider()
{
if (shadowCamera == null || shadowRT == null) return;
// 渲染阴影
shadowCamera.Render();
// 读取纹理
RenderTexture.active = shadowRT;
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
processedTexture.Apply();
RenderTexture.active = null;
// 生成碰撞体
GenerateColliderFromTexture();
}
void GenerateColliderFromTexture()
{
int width = processedTexture.width / downSample;
int height = processedTexture.height / downSample;
// 创建阴影网格
shadowGrid = new bool[width, height];
// 采样纹理
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int texX = x * downSample;
int texY = y * downSample;
Color pixel = processedTexture.GetPixel(texX, texY);
shadowGrid[x, y] = pixel.grayscale < shadowThreshold;
}
}
// 找到所有阴影区域
List<List<Vector2>> shadowRegions = FindShadowRegions();
// 设置碰撞器路径
if (shadowRegions.Count > 0)
{
polygonCollider.pathCount = shadowRegions.Count;
for (int i = 0; i < shadowRegions.Count; i++)
{
List<Vector2> worldPoints = ConvertToWorldCoordinates(shadowRegions[i]);
polygonCollider.SetPath(i, worldPoints);
}
Debug.Log($"Generated {shadowRegions.Count} shadow colliders");
}
else
{
polygonCollider.pathCount = 0;
Debug.Log("No shadow regions found");
}
}
List<List<Vector2>> FindShadowRegions()
{
List<List<Vector2>> regions = new List<List<Vector2>>();
bool[,] visited = new bool[shadowGrid.GetLength(0), shadowGrid.GetLength(1)];
for (int x = 0; x < shadowGrid.GetLength(0); x++)
{
for (int y = 0; y < shadowGrid.GetLength(1); y++)
{
if (shadowGrid[x, y] && !visited[x, y])
{
// 找到连续的阴影区域
List<Vector2Int> regionPixels = FloodFill(x, y, visited);
if (regionPixels.Count >= minShadowSize * (shadowGrid.GetLength(0) * shadowGrid.GetLength(1)))
{
// 转换为边界框
List<Vector2> bounds = GetBoundingBox(regionPixels);
regions.Add(bounds);
}
}
}
}
return regions;
}
List<Vector2Int> FloodFill(int startX, int startY, bool[,] visited)
{
List<Vector2Int> region = new List<Vector2Int>();
Queue<Vector2Int> queue = new Queue<Vector2Int>();
queue.Enqueue(new Vector2Int(startX, startY));
visited[startX, startY] = true;
while (queue.Count > 0)
{
Vector2Int current = queue.Dequeue();
region.Add(current);
// 检查4个方向的邻居
CheckNeighbor(current.x + 1, current.y, visited, queue);
CheckNeighbor(current.x - 1, current.y, visited, queue);
CheckNeighbor(current.x, current.y + 1, visited, queue);
CheckNeighbor(current.x, current.y - 1, visited, queue);
}
return region;
}
void CheckNeighbor(int x, int y, bool[,] visited, Queue<Vector2Int> queue)
{
if (x >= 0 && x < shadowGrid.GetLength(0) && y >= 0 && y < shadowGrid.GetLength(1))
{
if (shadowGrid[x, y] && !visited[x, y])
{
visited[x, y] = true;
queue.Enqueue(new Vector2Int(x, y));
}
}
}
List<Vector2> GetBoundingBox(List<Vector2Int> pixels)
{
if (pixels.Count == 0) return new List<Vector2>();
// 找到边界
int minX = int.MaxValue, maxX = int.MinValue;
int minY = int.MaxValue, maxY = int.MinValue;
foreach (Vector2Int pixel in pixels)
{
if (pixel.x < minX) minX = pixel.x;
if (pixel.x > maxX) maxX = pixel.x;
if (pixel.y < minY) minY = pixel.y;
if (pixel.y > maxY) maxY = pixel.y;
}
// 创建矩形边界
List<Vector2> bounds = new List<Vector2>
{
new Vector2(minX * downSample, minY * downSample),
new Vector2(maxX * downSample, minY * downSample),
new Vector2(maxX * downSample, maxY * downSample),
new Vector2(minX * downSample, maxY * downSample)
};
return bounds;
}
List<Vector2> ConvertToWorldCoordinates(List<Vector2> texturePoints)
{
List<Vector2> worldPoints = new List<Vector2>();
foreach (Vector2 texCoord in texturePoints)
{
Vector3 viewportPos = new Vector3(
texCoord.x / processedTexture.width,
texCoord.y / processedTexture.height,
shadowCamera.nearClipPlane
);
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
worldPoints.Add(new Vector2(worldPos.x, worldPos.y));
}
return worldPoints;
}
// 调试在Scene视图中显示阴影区域
void OnDrawGizmosSelected()
{
if (shadowGrid == null) return;
Gizmos.color = Color.red;
float cellSizeX = 1f / shadowGrid.GetLength(0);
float cellSizeY = 1f / shadowGrid.GetLength(1);
for (int x = 0; x < shadowGrid.GetLength(0); x++)
{
for (int y = 0; y < shadowGrid.GetLength(1); y++)
{
if (shadowGrid[x, y])
{
Vector3 viewportPos = new Vector3(
(x + 0.5f) * cellSizeX,
(y + 0.5f) * cellSizeY,
shadowCamera.nearClipPlane
);
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
Gizmos.DrawWireCube(worldPos, new Vector3(cellSizeX, cellSizeY, 0.1f));
}
}
}
}
[ContextMenu("Debug Shadow Info")]
void DebugShadowInfo()
{
shadowCamera.Render();
RenderTexture.active = shadowRT;
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
processedTexture.Apply();
RenderTexture.active = null;
int shadowPixels = 0;
int totalPixels = processedTexture.width * processedTexture.height;
for (int x = 0; x < processedTexture.width; x += 10)
{
for (int y = 0; y < processedTexture.height; y += 10)
{
if (processedTexture.GetPixel(x, y).grayscale < shadowThreshold)
shadowPixels++;
}
}
Debug.Log($"Shadow coverage: {shadowPixels}/{(totalPixels / 100)} ({(float)shadowPixels / (totalPixels / 100) * 100f:F1}%)");
Debug.Log($"Texture size: {processedTexture.width}x{processedTexture.height}");
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb1369db1529b6543ba24519d5c2a35d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -16,7 +16,7 @@ TagManager:
-
- DestroyItDebris
- Shadow
-
- Panel
-
-
-

View File

@@ -15,11 +15,11 @@ MonoBehaviour:
m_PixelRect:
serializedVersion: 2
x: 0
y: 43.2
width: 2048
height: 1188.8
y: 43
width: 2560
height: 1349
m_ShowMode: 4
m_Title: Scene
m_Title: Inspector
m_RootView: {fileID: 2}
m_MinSize: {x: 875, y: 721}
m_MaxSize: {x: 10000, y: 10000}
@@ -44,8 +44,8 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 2048
height: 1188.8
width: 2560
height: 1349
m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000}
m_UseTopView: 1
@@ -69,7 +69,7 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 2048
width: 2560
height: 30
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
@@ -90,8 +90,8 @@ MonoBehaviour:
m_Position:
serializedVersion: 2
x: 0
y: 1168.8
width: 2048
y: 1329
width: 2560
height: 20
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
@@ -114,12 +114,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 30
width: 2048
height: 1138.8
width: 2560
height: 1299
m_MinSize: {x: 300, y: 100}
m_MaxSize: {x: 24288, y: 16192}
vertical: 0
controlID: 42
controlID: 40
draggingID: 0
--- !u!114 &6
MonoBehaviour:
@@ -140,12 +140,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1501.6
height: 1138.8
width: 1877
height: 1299
m_MinSize: {x: 200, y: 100}
m_MaxSize: {x: 16192, y: 16192}
vertical: 1
controlID: 43
controlID: 41
draggingID: 0
--- !u!114 &7
MonoBehaviour:
@@ -166,12 +166,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1501.6
height: 758.4
width: 1877
height: 865
m_MinSize: {x: 200, y: 50}
m_MaxSize: {x: 16192, y: 8096}
vertical: 0
controlID: 44
controlID: 42
draggingID: 0
--- !u!114 &8
MonoBehaviour:
@@ -190,8 +190,8 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 325.6
height: 758.4
width: 407
height: 865
m_MinSize: {x: 201, y: 221}
m_MaxSize: {x: 4001, y: 4021}
m_ActualView: {fileID: 13}
@@ -214,10 +214,10 @@ MonoBehaviour:
m_Children: []
m_Position:
serializedVersion: 2
x: 325.6
x: 407
y: 0
width: 1176
height: 758.4
width: 1470
height: 865
m_MinSize: {x: 202, y: 221}
m_MaxSize: {x: 4002, y: 4021}
m_ActualView: {fileID: 14}
@@ -247,9 +247,9 @@ MonoBehaviour:
m_Position:
serializedVersion: 2
x: 0
y: 758.4
width: 1501.6
height: 380.40002
y: 865
width: 1877
height: 434
m_MinSize: {x: 101, y: 121}
m_MaxSize: {x: 4001, y: 4021}
m_ActualView: {fileID: 12}
@@ -276,12 +276,12 @@ MonoBehaviour:
m_Children: []
m_Position:
serializedVersion: 2
x: 1501.6
x: 1877
y: 0
width: 546.4
height: 1138.8
m_MinSize: {x: 275, y: 100}
m_MaxSize: {x: 4000, y: 4000}
width: 683
height: 1299
m_MinSize: {x: 276, y: 121}
m_MaxSize: {x: 4001, y: 4021}
m_ActualView: {fileID: 25}
m_Panes:
- {fileID: 25}
@@ -304,15 +304,15 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Console
m_Image: {fileID: -4950941429401207979, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -4327648978806127646, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 0
y: 832
width: 1500.6
height: 359.40002
y: 938
width: 1876
height: 413
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -339,15 +339,15 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Hierarchy
m_Image: {fileID: -3734745235275155857, guid: 0000000000000000d000000000000000,
m_Image: {fileID: 7966133145522015247, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 0
y: 73.6
width: 324.6
height: 737.4
y: 73
width: 406
height: 844
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -361,9 +361,9 @@ MonoBehaviour:
m_SceneHierarchy:
m_TreeViewState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: 46d1fbff
m_LastClickedID: -274106
m_ExpandedIDs: 8673fdff5ea9fdffd8cefdff22d1fdff80d3fdff3c85feff12f2ffffe0f7ffff18840000
m_SelectedIDs:
m_LastClickedID: 0
m_ExpandedIDs: 08fbffffe27c0000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
@@ -403,15 +403,15 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Scene
m_Image: {fileID: 8634526014445323508, guid: 0000000000000000d000000000000000,
m_Image: {fileID: 2593428753322112591, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 325.6
y: 73.6
width: 1174
height: 737.4
x: 407
y: 73
width: 1468
height: 844
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -783,9 +783,9 @@ MonoBehaviour:
m_PlayAudio: 0
m_AudioPlay: 0
m_Position:
m_Target: {x: -1.4945829, y: 36.10217, z: 125.86397}
m_Target: {x: -0.6338736, y: 2.8792658, z: 9.9932}
speed: 2
m_Value: {x: -1.4945829, y: 36.10217, z: 125.86397}
m_Value: {x: -0.6338736, y: 2.8792658, z: 9.9932}
m_RenderMode: 0
m_CameraMode:
drawMode: 0
@@ -831,13 +831,13 @@ MonoBehaviour:
m_GridAxis: 1
m_gridOpacity: 0.5
m_Rotation:
m_Target: {x: -0.11434219, y: 0.0062596397, z: 0.00068356807, w: 0.99344337}
m_Target: {x: 0.1730932, y: 0.018612249, z: -0.0032754778, w: 0.98472977}
speed: 2
m_Value: {x: -0.11434045, y: 0.005067412, z: 0.0005463472, w: 0.9934286}
m_Value: {x: 0.17309225, y: 0.018612146, z: -0.0032754599, w: 0.98472434}
m_Size:
m_Target: 80.36852
m_Target: 8.308297
speed: 2
m_Value: 76.90768
m_Value: 8.308297
m_Ortho:
m_Target: 0
speed: 2
@@ -855,7 +855,7 @@ MonoBehaviour:
m_FarClip: 10000
m_DynamicClip: 0
m_OcclusionCulling: 0
m_LastSceneViewRotation: {x: 0.07008733, y: -0.042786285, z: 0.0029896083, w: 0.99662733}
m_LastSceneViewRotation: {x: 0, y: 0, z: 0, w: 1}
m_LastSceneViewOrtho: 0
m_ReplacementShader: {fileID: 0}
m_ReplacementString:
@@ -878,15 +878,15 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Game
m_Image: {fileID: 4621777727084837110, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -6423792434712278376, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 325.6
y: 73.6
width: 1174
height: 737.4
x: 407
y: 73
width: 1468
height: 844
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -918,10 +918,10 @@ MonoBehaviour:
m_VRangeLocked: 0
hZoomLockedByDefault: 0
vZoomLockedByDefault: 0
m_HBaseRangeMin: -1024
m_HBaseRangeMax: 1024
m_VBaseRangeMin: -576
m_VBaseRangeMax: 576
m_HBaseRangeMin: -1280
m_HBaseRangeMax: 1280
m_VBaseRangeMin: -720
m_VBaseRangeMax: 720
m_HAllowExceedBaseRangeMin: 1
m_HAllowExceedBaseRangeMax: 1
m_VAllowExceedBaseRangeMin: 1
@@ -939,23 +939,23 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 21
width: 1174
height: 716.4
m_Scale: {x: 0.5732422, y: 0.5732422}
m_Translation: {x: 587, y: 358.2}
width: 1468
height: 823
m_Scale: {x: 0.5715278, y: 0.5715278}
m_Translation: {x: 733.99994, y: 411.5}
m_MarginLeft: 0
m_MarginRight: 0
m_MarginTop: 0
m_MarginBottom: 0
m_LastShownAreaInsideMargins:
serializedVersion: 2
x: -1024
y: -624.8668
width: 2048
height: 1249.7336
x: -1284.277
y: -720
width: 2568.554
height: 1440
m_MinimalGUI: 1
m_defaultScale: 0.5732422
m_LastWindowPixelSize: {x: 1467.5, y: 921.75}
m_defaultScale: 0.5715278
m_LastWindowPixelSize: {x: 1468, y: 844}
m_ClearInEditMode: 1
m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000001000000000000
@@ -978,7 +978,7 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Package Manager
m_Image: {fileID: 5076950121296946556, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -2824328813065806953, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
@@ -1088,7 +1088,7 @@ MonoBehaviour:
x: 407
y: 73
width: 1468
height: 832
height: 844
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -1117,15 +1117,15 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Asset Store
m_Image: {fileID: -7444545952099596278, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -8693916549880196297, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 325.6
y: 73.6
width: 1174
height: 737.4
x: 407
y: 73
width: 1468
height: 844
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -1152,15 +1152,15 @@ MonoBehaviour:
m_MaxSize: {x: 10000, y: 10000}
m_TitleContent:
m_Text: Project
m_Image: {fileID: -5179483145760003458, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -5467254957812901981, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 0
y: 832
width: 1500.6
height: 359.40002
y: 938
width: 1876
height: 413
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -1192,14 +1192,14 @@ MonoBehaviour:
m_LastFolders:
- Assets/Scripts/Test/Light
m_LastFoldersGridSize: 16
m_LastProjectPath: D:\UnityProject\BlueArchiveMiniGame
m_LastProjectPath: C:\UnityProject\BlueArchiveMiniGame
m_LockTracker:
m_IsLocked: 0
m_FolderTreeState:
scrollPos: {x: 0, y: 124.599976}
m_SelectedIDs: 568c0000
m_LastClickedID: 35926
m_ExpandedIDs: 0000000072770000648300007c83000082830000cc83000000ca9a3bffffff7f
scrollPos: {x: 0, y: 27}
m_SelectedIDs: ce7f0000
m_LastClickedID: 32718
m_ExpandedIDs: 00000000c47d0000c67d0000c87d0000ca7d0000cc7d0000ce7d0000d07d0000c47f0000ce7f000000ca9a3b
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
@@ -1227,7 +1227,7 @@ MonoBehaviour:
scrollPos: {x: 0, y: 0}
m_SelectedIDs:
m_LastClickedID: 0
m_ExpandedIDs: 0000000064830000
m_ExpandedIDs: 00000000c47d0000c67d0000c87d0000ca7d0000cc7d0000ce7d0000d07d0000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
@@ -1252,26 +1252,26 @@ MonoBehaviour:
m_Icon: {fileID: 0}
m_ResourceFile:
m_ListAreaState:
m_SelectedInstanceIDs: 8673fdff
m_LastClickedInstanceID: -167034
m_SelectedInstanceIDs:
m_LastClickedInstanceID: 0
m_HadKeyboardFocusLastEvent: 1
m_ExpandedInstanceIDs: c6230000068f1200828d00000c8d0000121d050042e40400b2930000c4fa0400ae0e0500f2ef0400682a4200802a42008a17420050a10000ee090500bc130500eccd000032030000bef80000c2a300000000000040f500007a860000647c0600f2b606001cb7060010170100b6290100e8ae00000aae0400447f0000d283000082ae0000d4beffff66c70000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name: RaycastShadowCollider
m_OriginalName: RaycastShadowCollider
m_Name:
m_OriginalName:
m_EditFieldRect:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
m_UserData: 38236
m_UserData: 0
m_IsWaitingForDelay: 0
m_IsRenaming: 0
m_OriginalEventType: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 1
m_ClientGUIView: {fileID: 10}
m_ClientGUIView: {fileID: 9}
m_CreateAssetUtility:
m_EndAction: {fileID: 0}
m_InstanceID: 0
@@ -1299,7 +1299,7 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Animator
m_Image: {fileID: 1711060831702674872, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -1673928668082335149, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
@@ -1423,7 +1423,7 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Animation
m_Image: {fileID: -3237396543322336831, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -8166618308981325432, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
@@ -1444,7 +1444,7 @@ MonoBehaviour:
m_OverlaysVisible: 1
m_LockTracker:
m_IsLocked: 0
m_LastSelectedObjectID: -167034
m_LastSelectedObjectID: -21542028
--- !u!114 &24
MonoBehaviour:
m_ObjectHideFlags: 52
@@ -1461,7 +1461,7 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Audio Mixer
m_Image: {fileID: -3283902137440876849, guid: 0000000000000000d000000000000000,
m_Image: {fileID: 2344599766593239149, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
@@ -1644,15 +1644,15 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Inspector
m_Image: {fileID: -440750813802333266, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -2667387946076563598, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos:
serializedVersion: 2
x: 1501.6
y: 73.6
width: 545.4
height: 1117.8
x: 1877
y: 73
width: 682
height: 1278
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
@@ -1692,7 +1692,7 @@ MonoBehaviour:
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Lighting
m_Image: {fileID: -1477008817101679558, guid: 0000000000000000d000000000000000,
m_Image: {fileID: -1347227620855488341, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_Pos: