111
This commit is contained in:
@@ -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}
|
||||
|
||||
129
Assets/Scripts/Test/Light/Dynamic3DTo2DCollider.cs
Normal file
129
Assets/Scripts/Test/Light/Dynamic3DTo2DCollider.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/Dynamic3DTo2DCollider.cs.meta
Normal file
11
Assets/Scripts/Test/Light/Dynamic3DTo2DCollider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 717ba394b4fc8354db9479f2f858d521
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
340
Assets/Scripts/Test/Light/MarchingSquaresShadowCollider.cs
Normal file
340
Assets/Scripts/Test/Light/MarchingSquaresShadowCollider.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b369cd814f44044d8a55791d065e3fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
336
Assets/Scripts/Test/Light/MeshShadowProjector.cs
Normal file
336
Assets/Scripts/Test/Light/MeshShadowProjector.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/MeshShadowProjector.cs.meta
Normal file
11
Assets/Scripts/Test/Light/MeshShadowProjector.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7911d7cd239cfb44c910a896590b2db4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/Test/Light/New.meta
Normal file
8
Assets/Scripts/Test/Light/New.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f17787cb19d6fd24ab50665a7b16a04a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
259
Assets/Scripts/Test/Light/New/MarchingSquaresProcessor.cs
Normal file
259
Assets/Scripts/Test/Light/New/MarchingSquaresProcessor.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2a4c434153f3614d8381195368e2fec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
54
Assets/Scripts/Test/Light/New/ShadowCollider.cs
Normal file
54
Assets/Scripts/Test/Light/New/ShadowCollider.cs
Normal 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
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/New/ShadowCollider.cs.meta
Normal file
11
Assets/Scripts/Test/Light/New/ShadowCollider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a96ca5e4a935984392a295d8855f0f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
183
Assets/Scripts/Test/Light/New/ShadowColliderSystem.cs
Normal file
183
Assets/Scripts/Test/Light/New/ShadowColliderSystem.cs
Normal 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
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/New/ShadowColliderSystem.cs.meta
Normal file
11
Assets/Scripts/Test/Light/New/ShadowColliderSystem.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 836b4625937ba9d45be59618cd2a6a15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
123
Assets/Scripts/Test/Light/New/ShadowMeshGenerator.cs
Normal file
123
Assets/Scripts/Test/Light/New/ShadowMeshGenerator.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/New/ShadowMeshGenerator.cs.meta
Normal file
11
Assets/Scripts/Test/Light/New/ShadowMeshGenerator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85170fe0e92d3604fbf662a3620570e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
136
Assets/Scripts/Test/Light/New/ShadowProjector.cs
Normal file
136
Assets/Scripts/Test/Light/New/ShadowProjector.cs
Normal 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;
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/New/ShadowProjector.cs.meta
Normal file
11
Assets/Scripts/Test/Light/New/ShadowProjector.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 462c6c18ec968ae4badc890a169194e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Scripts/Test/Light/New/ShadowUtils.cs
Normal file
42
Assets/Scripts/Test/Light/New/ShadowUtils.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/New/ShadowUtils.cs.meta
Normal file
11
Assets/Scripts/Test/Light/New/ShadowUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5f9eb8ac9da6a8488b456411e1e7370
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,82 +1,274 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class RaycastShadowCollider : MonoBehaviour
|
||||
{
|
||||
public Light spotLight; // <20>۹<EFBFBD><DBB9><EFBFBD>
|
||||
public GameObject shadowCaster; // Ͷ<><CDB6><EFBFBD><EFBFBD>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
public int rayCount = 36; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
public Light spotLight;
|
||||
public List<GameObject> shadowCasters = new List<GameObject>();
|
||||
public LayerMask shadowCasterLayer;
|
||||
public LayerMask shadowReceiverLayer;
|
||||
public float maxShadowDistance = 10f;
|
||||
|
||||
private PolygonCollider2D shadowCollider;
|
||||
public PolygonCollider2D shadowCollider;
|
||||
|
||||
void Start()
|
||||
{
|
||||
shadowCollider = GetComponent<PolygonCollider2D>();
|
||||
if (shadowCollider == null)
|
||||
shadowCollider = gameObject.AddComponent<PolygonCollider2D>();
|
||||
UpdateShadowColliders();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
UpdateShadowCollider();
|
||||
//UpdateShadowCollider();
|
||||
}
|
||||
|
||||
void UpdateShadowCollider()
|
||||
void UpdateShadowColliders()
|
||||
{
|
||||
foreach (var shadowCaster in shadowCasters)
|
||||
{
|
||||
UpdateShadowCollider(shadowCaster);
|
||||
}
|
||||
}
|
||||
void UpdateShadowCollider(GameObject shadowCaster)
|
||||
{
|
||||
List<Vector2> shadowPoints = new List<Vector2>();
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߽<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƹⷽ<EFBFBD><EFBFBD>Ͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Bounds bounds = shadowCaster.GetComponent<Renderer>().bounds;
|
||||
Vector3[] boundPoints = GetBoundPoints(bounds);
|
||||
// 获取阴影投射器的边界点(世界坐标)
|
||||
Vector3[] worldBoundPoints = GetBoundPoints(shadowCaster);
|
||||
|
||||
foreach (Vector3 point in boundPoints)
|
||||
foreach (Vector3 worldPoint in worldBoundPoints)
|
||||
{
|
||||
Vector3 toLight = (spotLight.transform.position - point).normalized;
|
||||
Ray ray = new Ray(point, toLight);
|
||||
// 方向:从灯光指向边界点
|
||||
Vector3 toPoint = (worldPoint - spotLight.transform.position).normalized;
|
||||
|
||||
// 创建从灯光位置发出的射线
|
||||
Ray ray = new Ray(spotLight.transform.position, toPoint);
|
||||
RaycastHit hit;
|
||||
|
||||
if (Physics.Raycast(ray, out hit, maxShadowDistance))
|
||||
// 调试:显示射线方向
|
||||
Debug.DrawRay(spotLight.transform.position, toPoint * maxShadowDistance, Color.yellow);
|
||||
|
||||
if (Physics.Raycast(ray, out hit, maxShadowDistance, shadowReceiverLayer))
|
||||
{
|
||||
if (hit.collider.gameObject == gameObject) // <20><><EFBFBD><EFBFBD>ǽ<EFBFBD><C7BD>
|
||||
// 检查是否击中了阴影投射器
|
||||
if (hit.collider.gameObject == gameObject)
|
||||
{
|
||||
// ת<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǽ<EFBFBD><EFBFBD><EFBFBD>ľֲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵ
|
||||
Vector3 localHit = transform.InverseTransformPoint(hit.point);
|
||||
// 将世界坐标的碰撞点转换到阴影碰撞器的本地坐标
|
||||
Vector3 localHit = shadowCollider.transform.InverseTransformPoint(hit.point);
|
||||
shadowPoints.Add(new Vector2(localHit.x, localHit.y));
|
||||
|
||||
// 调试:在碰撞点创建小球
|
||||
// GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
//sphere.transform.position = hit.point;
|
||||
// sphere.transform.localScale = Vector3.one * 0.1f;
|
||||
//Destroy(sphere, 0.1f);
|
||||
}
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// // 如果没有击中,使用最大距离的点
|
||||
// Vector3 maxPoint = spotLight.transform.position + toPoint * maxShadowDistance;
|
||||
// Vector3 localMaxPoint = shadowCollider.transform.InverseTransformPoint(maxPoint);
|
||||
// shadowPoints.Add(new Vector2(localMaxPoint.x, localMaxPoint.y));
|
||||
|
||||
// // 调试:在最大距离点创建小球
|
||||
// GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
// sphere.transform.position = maxPoint;
|
||||
// sphere.transform.localScale = Vector3.one * 0.1f;
|
||||
// sphere.GetComponent<Renderer>().material.color = Color.blue;
|
||||
// Destroy(sphere, 0.1f);
|
||||
//}
|
||||
}
|
||||
|
||||
// <EFBFBD>Ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>γ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// 设置碰撞器路径
|
||||
if (shadowPoints.Count > 2)
|
||||
{
|
||||
List<Vector2> convexHull = ComputeConvexHull(shadowPoints);
|
||||
shadowCollider.SetPath(0, convexHull);
|
||||
// 对点进行排序以确保正确的多边形
|
||||
shadowPoints = SortShadowPoints(shadowPoints);
|
||||
shadowCollider.SetPath(0, shadowPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
shadowCollider.pathCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3[] GetBoundPoints(Bounds bounds)
|
||||
Vector3[] GetBoundPoints(GameObject target)
|
||||
{
|
||||
|
||||
Bounds bounds = target.GetComponent<MeshRenderer>().bounds;
|
||||
return new Vector3[]
|
||||
{
|
||||
bounds.min,
|
||||
bounds.max,
|
||||
new Vector3(bounds.min.x, bounds.min.y, bounds.max.z),
|
||||
new Vector3(bounds.min.x, bounds.max.y, bounds.min.z),
|
||||
new Vector3(bounds.max.x, bounds.min.y, bounds.min.z),
|
||||
new Vector3(bounds.min.x, bounds.max.y, bounds.max.z),
|
||||
new Vector3(bounds.max.x, bounds.min.y, bounds.max.z),
|
||||
new Vector3(bounds.max.x, bounds.max.y, bounds.min.z)
|
||||
bounds.min,
|
||||
bounds.max,
|
||||
new Vector3(bounds.min.x, bounds.min.y, bounds.max.z),
|
||||
new Vector3(bounds.min.x, bounds.max.y, bounds.min.z),
|
||||
new Vector3(bounds.max.x, bounds.min.y, bounds.min.z),
|
||||
new Vector3(bounds.min.x, bounds.max.y, bounds.max.z),
|
||||
new Vector3(bounds.max.x, bounds.min.y, bounds.max.z),
|
||||
new Vector3(bounds.max.x, bounds.max.y, bounds.min.z)
|
||||
};
|
||||
}
|
||||
|
||||
// <><CDB9><EFBFBD>㷨<EFBFBD><E3B7A8>Graham Scan<61><6E>
|
||||
//List<Vector2> SortPointsClockwise(List<Vector2> points)
|
||||
//{
|
||||
// if (points.Count < 3) return points;
|
||||
|
||||
// // 计算中心点
|
||||
// Vector2 center = Vector2.zero;
|
||||
// foreach (Vector2 point in points)
|
||||
// {
|
||||
// center += point;
|
||||
// }
|
||||
// center /= points.Count;
|
||||
|
||||
// // 按角度排序
|
||||
// points.Sort((a, b) =>
|
||||
// {
|
||||
// Vector2 dirA = a - center;
|
||||
// Vector2 dirB = b - center;
|
||||
// float angleA = Mathf.Atan2(dirA.y, dirA.x);
|
||||
// float angleB = Mathf.Atan2(dirB.y, dirB.x);
|
||||
// return angleA.CompareTo(angleB);
|
||||
// });
|
||||
// points.Add(points[points.Count-1]);
|
||||
// return points;
|
||||
//}
|
||||
List<Vector2> SortPointsClockwise(List<Vector2> points)
|
||||
{
|
||||
if (points.Count < 3) return points;
|
||||
|
||||
// 方法1:使用凸包算法(推荐)
|
||||
List<Vector2> convexHull = ComputeConvexHull(points);
|
||||
return convexHull;
|
||||
|
||||
// 或者方法2:改进的角度排序(如果确定是凸多边形)
|
||||
// return ImprovedAngleSort(points);
|
||||
}
|
||||
|
||||
// 计算凸包 - Graham Scan 算法
|
||||
List<Vector2> ComputeConvexHull(List<Vector2> points)
|
||||
{
|
||||
if (points.Count < 3) return points;
|
||||
|
||||
// ʵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㷨...
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Unity<74><79>Collider2D.CreatePrimitive<76><65><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
return points; // <20><EFBFBD><F2BBAFB7><EFBFBD>
|
||||
// 找到最左下角的点作为起点
|
||||
Vector2 startPoint = points[0];
|
||||
foreach (Vector2 point in points)
|
||||
{
|
||||
if (point.y < startPoint.y || (point.y == startPoint.y && point.x < startPoint.x))
|
||||
{
|
||||
startPoint = point;
|
||||
}
|
||||
}
|
||||
|
||||
// 按极角排序
|
||||
List<Vector2> sortedPoints = new List<Vector2>(points);
|
||||
sortedPoints.Sort((a, b) =>
|
||||
{
|
||||
if (a == startPoint) return -1;
|
||||
if (b == startPoint) return 1;
|
||||
|
||||
Vector2 dirA = a - startPoint;
|
||||
Vector2 dirB = b - startPoint;
|
||||
|
||||
float cross = Cross(dirA, dirB);
|
||||
if (Mathf.Abs(cross) < 0.001f) // 共线情况
|
||||
{
|
||||
return dirA.sqrMagnitude.CompareTo(dirB.sqrMagnitude);
|
||||
}
|
||||
return cross > 0 ? -1 : 1;
|
||||
});
|
||||
|
||||
// 构建凸包
|
||||
List<Vector2> hull = new List<Vector2>();
|
||||
hull.Add(startPoint);
|
||||
hull.Add(sortedPoints[1]);
|
||||
|
||||
for (int i = 2; i < sortedPoints.Count; i++)
|
||||
{
|
||||
while (hull.Count >= 2)
|
||||
{
|
||||
Vector2 a = hull[hull.Count - 2];
|
||||
Vector2 b = hull[hull.Count - 1];
|
||||
Vector2 c = sortedPoints[i];
|
||||
|
||||
if (Cross(b - a, c - a) <= 0)
|
||||
{
|
||||
hull.RemoveAt(hull.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
hull.Add(sortedPoints[i]);
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
// 叉积计算
|
||||
float Cross(Vector2 a, Vector2 b)
|
||||
{
|
||||
return a.x * b.y - a.y * b.x;
|
||||
}
|
||||
|
||||
// 改进的角度排序(仅适用于凸多边形)
|
||||
List<Vector2> ImprovedAngleSort(List<Vector2> points)
|
||||
{
|
||||
// 计算中心点
|
||||
Vector2 center = Vector2.zero;
|
||||
foreach (Vector2 point in points)
|
||||
{
|
||||
center += point;
|
||||
}
|
||||
center /= points.Count;
|
||||
|
||||
// 按角度排序,处理相同角度的情况
|
||||
points.Sort((a, b) =>
|
||||
{
|
||||
Vector2 dirA = a - center;
|
||||
Vector2 dirB = b - center;
|
||||
|
||||
float angleA = Mathf.Atan2(dirA.y, dirA.x);
|
||||
float angleB = Mathf.Atan2(dirB.y, dirB.x);
|
||||
|
||||
// 处理角度接近的情况
|
||||
if (Mathf.Abs(angleA - angleB) < 0.001f)
|
||||
{
|
||||
return dirA.sqrMagnitude.CompareTo(dirB.sqrMagnitude);
|
||||
}
|
||||
|
||||
return angleA.CompareTo(angleB);
|
||||
});
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
// 专门为阴影投影点设计的排序方法
|
||||
List<Vector2> SortShadowPoints(List<Vector2> points)
|
||||
{
|
||||
if (points.Count < 3) return points;
|
||||
|
||||
// 对于阴影点,通常形成凸多边形,使用凸包算法
|
||||
List<Vector2> hull = ComputeConvexHull(points);
|
||||
|
||||
// 确保点是顺时针顺序(Unity碰撞器需要)
|
||||
if (!IsClockwise(hull))
|
||||
{
|
||||
hull.Reverse();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
// 检查多边形是否是顺时针顺序
|
||||
bool IsClockwise(List<Vector2> points)
|
||||
{
|
||||
float area = 0;
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
Vector2 current = points[i];
|
||||
Vector2 next = points[(i + 1) % points.Count];
|
||||
area += (next.x - current.x) * (next.y + current.y);
|
||||
}
|
||||
return area > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +1,288 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class ShadowColliderGenerator : MonoBehaviour
|
||||
{
|
||||
public Camera shadowCamera; // ר<><D7A8><EFBFBD><EFBFBD>Ⱦ<EFBFBD><C8BE>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
public RenderTexture shadowRT; // <20><>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>
|
||||
public LayerMask shadowCasterLayer; // Ͷ<><CDB6><EFBFBD><EFBFBD>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
public float colliderPrecision = 0.1f; // <20><>ײ<EFBFBD>徫<EFBFBD><E5BEAB>
|
||||
[Header("References")]
|
||||
public Camera shadowCamera;
|
||||
public RenderTexture shadowRT;
|
||||
public PolygonCollider2D polygonCollider;
|
||||
|
||||
[Header("Settings")]
|
||||
[Range(0f, 1f)]
|
||||
public float shadowThreshold = 0.3f;
|
||||
public int gridSize = 2; // 网格大小,越小越精确
|
||||
public float simplifyTolerance = 1f;
|
||||
|
||||
private Texture2D processedTexture;
|
||||
private PolygonCollider2D polygonCollider;
|
||||
|
||||
void Start()
|
||||
{
|
||||
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD>
|
||||
shadowRT = new RenderTexture(512, 512, 24);
|
||||
shadowCamera.targetTexture = shadowRT;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>õ<EFBFBD><C3B5><EFBFBD><EFBFBD><EFBFBD>
|
||||
processedTexture = new Texture2D(512, 512, TextureFormat.RGB24, false);
|
||||
|
||||
polygonCollider = GetComponent<PolygonCollider2D>();
|
||||
if (polygonCollider == null)
|
||||
polygonCollider = gameObject.AddComponent<PolygonCollider2D>();
|
||||
polygonCollider = GetComponent<PolygonCollider2D>();
|
||||
|
||||
processedTexture = new Texture2D(shadowRT.width, shadowRT.height, TextureFormat.R8, false);
|
||||
GenerateCollider();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
GenerateShadowCollider();
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
GenerateCollider();
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateShadowCollider()
|
||||
[ContextMenu("Generate Collider")]
|
||||
public void GenerateCollider()
|
||||
{
|
||||
// 1. <20><>Ⱦ<EFBFBD><C8BE>Ӱ<EFBFBD><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
if (shadowCamera == null || shadowRT == null) return;
|
||||
|
||||
// 渲染阴影
|
||||
shadowCamera.Render();
|
||||
|
||||
// 2. <20><>GPU<50><55>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>CPU
|
||||
// 读取纹理
|
||||
RenderTexture.active = shadowRT;
|
||||
processedTexture.ReadPixels(new Rect(0, 0, shadowRT.width, shadowRT.height), 0, 0);
|
||||
processedTexture.Apply();
|
||||
RenderTexture.active = null;
|
||||
|
||||
// 3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӱ<EFBFBD><D3B0>Ե
|
||||
List<Vector2> edgePoints = DetectEdges(processedTexture);
|
||||
// 生成轮廓
|
||||
List<Vector2> contour = GenerateContourFromTexture();
|
||||
|
||||
// 4. ת<><D7AA>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>겢<EFBFBD><EAB2A2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ײ<EFBFBD><D7B2>
|
||||
if (edgePoints.Count > 2)
|
||||
if (contour.Count > 2)
|
||||
{
|
||||
List<Vector2> worldPoints = ConvertToWorldCoordinates(edgePoints);
|
||||
List<Vector2> worldPoints = ConvertToWorldCoordinates(contour);
|
||||
polygonCollider.SetPath(0, worldPoints);
|
||||
Debug.Log($"Collider generated with {worldPoints.Count} points");
|
||||
}
|
||||
else
|
||||
{
|
||||
polygonCollider.pathCount = 0;
|
||||
Debug.Log("No contour generated");
|
||||
}
|
||||
}
|
||||
|
||||
List<Vector2> DetectEdges(Texture2D texture)
|
||||
List<Vector2> GenerateContourFromTexture()
|
||||
{
|
||||
List<Vector2> edges = new List<Vector2>();
|
||||
Color[] pixels = texture.GetPixels();
|
||||
int width = processedTexture.width;
|
||||
int height = processedTexture.height;
|
||||
|
||||
int width = texture.width;
|
||||
int height = texture.height;
|
||||
// 创建二值化网格
|
||||
bool[,] grid = CreateBinaryGrid(width, height);
|
||||
|
||||
// <EFBFBD>ı<EFBFBD>Ե<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㷨
|
||||
for (int y = 1; y < height - 1; y += (int)(1f / colliderPrecision))
|
||||
// 找到所有边界点
|
||||
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
|
||||
|
||||
// 连接边界点形成轮廓
|
||||
List<Vector2> contour = ConnectBoundaryPoints(boundaryPoints);
|
||||
|
||||
return contour;
|
||||
}
|
||||
|
||||
bool[,] CreateBinaryGrid(int width, int height)
|
||||
{
|
||||
bool[,] grid = new bool[width / gridSize, height / gridSize];
|
||||
|
||||
for (int x = 0; x < grid.GetLength(0); x++)
|
||||
{
|
||||
for (int x = 1; x < width - 1; x += (int)(1f / colliderPrecision))
|
||||
for (int y = 0; y < grid.GetLength(1); y++)
|
||||
{
|
||||
int index = y * width + x;
|
||||
int texX = x * gridSize;
|
||||
int texY = y * gridSize;
|
||||
Color pixel = processedTexture.GetPixel(texX, texY);
|
||||
grid[x, y] = pixel.grayscale < shadowThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>鵱ǰ<E9B5B1><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Χ<EFBFBD><CEA7><EFBFBD>صIJ<D8B5><C4B2><EFBFBD>
|
||||
if (IsEdgePixel(pixels, index, width))
|
||||
return grid;
|
||||
}
|
||||
|
||||
List<Vector2> FindBoundaryPoints(bool[,] grid)
|
||||
{
|
||||
List<Vector2> boundaryPoints = new List<Vector2>();
|
||||
int width = grid.GetLength(0);
|
||||
int height = grid.GetLength(1);
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (grid[x, y])
|
||||
{
|
||||
edges.Add(new Vector2(x, y));
|
||||
// 检查是否是边界(有非阴影像素邻居)
|
||||
bool isBoundary = false;
|
||||
|
||||
// 检查4邻域
|
||||
if (x == 0 || !grid[x - 1, y]) isBoundary = true;
|
||||
else if (x == width - 1 || !grid[x + 1, y]) isBoundary = true;
|
||||
else if (y == 0 || !grid[x, y - 1]) isBoundary = true;
|
||||
else if (y == height - 1 || !grid[x, y + 1]) isBoundary = true;
|
||||
|
||||
if (isBoundary)
|
||||
{
|
||||
boundaryPoints.Add(new Vector2(x * gridSize, y * gridSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
Debug.Log($"Found {boundaryPoints.Count} boundary points");
|
||||
return boundaryPoints;
|
||||
}
|
||||
|
||||
bool IsEdgePixel(Color[] pixels, int index, int width)
|
||||
List<Vector2> ConnectBoundaryPoints(List<Vector2> points)
|
||||
{
|
||||
Color current = pixels[index];
|
||||
float currentBrightness = current.r + current.g + current.b;
|
||||
if (points.Count < 3) return points;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
float[] neighborBrightness = new float[4]
|
||||
{
|
||||
pixels[index - 1].r + pixels[index - 1].g + pixels[index - 1].b, // <20><>
|
||||
pixels[index + 1].r + pixels[index + 1].g + pixels[index + 1].b, // <20><>
|
||||
pixels[index - width].r + pixels[index - width].g + pixels[index - width].b, // <20><>
|
||||
pixels[index + width].r + pixels[index + width].g + pixels[index + width].b // <20><>
|
||||
};
|
||||
List<Vector2> contour = new List<Vector2>();
|
||||
List<Vector2> remaining = new List<Vector2>(points);
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ھӲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD>DZ<EFBFBD>Ե
|
||||
float threshold = 0.3f;
|
||||
for (int i = 0; i < 4; i++)
|
||||
// 找到最左边的点作为起点
|
||||
Vector2 start = remaining.OrderBy(p => p.x).First();
|
||||
contour.Add(start);
|
||||
remaining.Remove(start);
|
||||
|
||||
Vector2 current = start;
|
||||
Vector2 previousDirection = Vector2.right;
|
||||
|
||||
int maxIterations = points.Count * 2;
|
||||
int iterations = 0;
|
||||
|
||||
while (remaining.Count > 0 && iterations < maxIterations)
|
||||
{
|
||||
if (Mathf.Abs(currentBrightness - neighborBrightness[i]) > threshold)
|
||||
return true;
|
||||
iterations++;
|
||||
|
||||
// 找到与当前方向最一致的下一个点
|
||||
float bestScore = float.MinValue;
|
||||
Vector2 bestPoint = Vector2.zero;
|
||||
int bestIndex = -1;
|
||||
|
||||
for (int i = 0; i < remaining.Count; i++)
|
||||
{
|
||||
Vector2 toPoint = (remaining[i] - current).normalized;
|
||||
float distance = Vector2.Distance(current, remaining[i]);
|
||||
|
||||
// 评分:方向一致性 + 距离权重
|
||||
float directionScore = Vector2.Dot(previousDirection, toPoint);
|
||||
float distanceScore = 1f / (distance + 0.1f);
|
||||
float totalScore = directionScore + distanceScore * 0.1f;
|
||||
|
||||
if (totalScore > bestScore && distance < 50f) // 距离限制
|
||||
{
|
||||
bestScore = totalScore;
|
||||
bestPoint = remaining[i];
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestIndex >= 0)
|
||||
{
|
||||
previousDirection = (bestPoint - current).normalized;
|
||||
contour.Add(bestPoint);
|
||||
remaining.RemoveAt(bestIndex);
|
||||
current = bestPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
// 确保轮廓闭合
|
||||
if (contour.Count > 2 && Vector2.Distance(contour[0], contour[contour.Count - 1]) > 30f)
|
||||
{
|
||||
contour.Add(contour[0]);
|
||||
}
|
||||
|
||||
// 简化轮廓
|
||||
contour = SimplifyContour(contour);
|
||||
|
||||
Debug.Log($"Connected {contour.Count} points into contour");
|
||||
return contour;
|
||||
}
|
||||
|
||||
List<Vector2> SimplifyContour(List<Vector2> contour)
|
||||
{
|
||||
if (contour.Count <= 3) return contour;
|
||||
|
||||
List<Vector2> simplified = new List<Vector2>();
|
||||
simplified.Add(contour[0]);
|
||||
|
||||
for (int i = 1; i < contour.Count - 1; i++)
|
||||
{
|
||||
// 如果角度变化太大,保留这个点
|
||||
if (i > 1)
|
||||
{
|
||||
Vector2 prevDir = (contour[i] - contour[i - 1]).normalized;
|
||||
Vector2 nextDir = (contour[i + 1] - contour[i]).normalized;
|
||||
float angle = Vector2.Angle(prevDir, nextDir);
|
||||
|
||||
if (angle > 30f) // 角度阈值
|
||||
{
|
||||
simplified.Add(contour[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
simplified.Add(contour[contour.Count - 1]);
|
||||
|
||||
return simplified;
|
||||
}
|
||||
|
||||
List<Vector2> ConvertToWorldCoordinates(List<Vector2> texturePoints)
|
||||
{
|
||||
List<Vector2> worldPoints = new List<Vector2>();
|
||||
Bounds wallBounds = GetComponent<Collider2D>().bounds;
|
||||
|
||||
foreach (Vector2 point in texturePoints)
|
||||
foreach (Vector2 texCoord in texturePoints)
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>Ϊǽ<CEAA><C7BD><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD><EFBFBD><EFBFBD>
|
||||
Vector2 localPoint = new Vector2(
|
||||
point.x / shadowRT.width * wallBounds.size.x - wallBounds.size.x * 0.5f,
|
||||
point.y / shadowRT.height * wallBounds.size.y - wallBounds.size.y * 0.5f
|
||||
Vector3 viewportPos = new Vector3(
|
||||
texCoord.x / processedTexture.width,
|
||||
texCoord.y / processedTexture.height,
|
||||
shadowCamera.nearClipPlane
|
||||
);
|
||||
|
||||
worldPoints.Add(localPoint);
|
||||
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
|
||||
worldPoints.Add(new Vector2(worldPos.x, worldPos.y));
|
||||
}
|
||||
|
||||
return worldPoints;
|
||||
}
|
||||
|
||||
// 调试:显示边界点
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
if (processedTexture == null) return;
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
|
||||
// 显示边界点
|
||||
bool[,] grid = CreateBinaryGrid(processedTexture.width, processedTexture.height);
|
||||
List<Vector2> boundaryPoints = FindBoundaryPoints(grid);
|
||||
|
||||
foreach (Vector2 point in boundaryPoints)
|
||||
{
|
||||
Vector3 viewportPos = new Vector3(
|
||||
point.x / processedTexture.width,
|
||||
point.y / processedTexture.height,
|
||||
shadowCamera.nearClipPlane
|
||||
);
|
||||
Vector3 worldPos = shadowCamera.ViewportToWorldPoint(viewportPos);
|
||||
Gizmos.DrawSphere(worldPos, 0.02f);
|
||||
}
|
||||
|
||||
// 显示碰撞器轮廓
|
||||
if (polygonCollider != null && polygonCollider.pathCount > 0)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Vector2[] path = polygonCollider.GetPath(0);
|
||||
for (int i = 0; i < path.Length; i++)
|
||||
{
|
||||
Vector3 start = path[i];
|
||||
Vector3 end = path[(i + 1) % path.Length];
|
||||
Gizmos.DrawLine(start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,4 +37,4 @@ RenderTexture:
|
||||
m_WrapW: 1
|
||||
m_Dimension: 2
|
||||
m_VolumeDepth: 1
|
||||
m_ShadowSamplingMode: 2
|
||||
m_ShadowSamplingMode: 0
|
||||
|
||||
256
Assets/Scripts/Test/Light/SimpleShadowCollider.cs
Normal file
256
Assets/Scripts/Test/Light/SimpleShadowCollider.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Test/Light/SimpleShadowCollider.cs.meta
Normal file
11
Assets/Scripts/Test/Light/SimpleShadowCollider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb1369db1529b6543ba24519d5c2a35d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -16,7 +16,7 @@ TagManager:
|
||||
-
|
||||
- DestroyItDebris
|
||||
- Shadow
|
||||
-
|
||||
- Panel
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user