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,62 +1,99 @@
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,
@@ -70,13 +107,168 @@ public class RaycastShadowCollider : MonoBehaviour
};
}
// ͹<><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))
{
for (int x = 1; x < width - 1; x += (int)(1f / colliderPrecision))
{
int index = y * width + x;
// 找到所有边界点
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
// <EFBFBD><EFBFBD><EFBFBD>鵱ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Χ<EFBFBD><EFBFBD><EFBFBD>صIJ<EFBFBD><EFBFBD><EFBFBD>
if (IsEdgePixel(pixels, index, width))
// 连接边界点形成轮廓
List<Vector2> contour = ConnectBoundaryPoints(boundaryPoints);
return contour;
}
bool[,] CreateBinaryGrid(int width, int height)
{
edges.Add(new Vector2(x, y));
bool[,] grid = new bool[width / gridSize, height / gridSize];
for (int x = 0; x < grid.GetLength(0); x++)
{
for (int y = 0; y < grid.GetLength(1); y++)
{
int texX = x * gridSize;
int texY = y * gridSize;
Color pixel = processedTexture.GetPixel(texX, texY);
grid[x, y] = pixel.grayscale < shadowThreshold;
}
}
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])
{
// 检查是否是边界(有非阴影像素邻居)
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;
}
}
return false;
if (bestIndex >= 0)
{
previousDirection = (bestPoint - current).normalized;
contour.Add(bestPoint);
remaining.RemoveAt(bestIndex);
current = bestPoint;
}
else
{
break;
}
}
// 确保轮廓闭合
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: