1+ using UnityEngine ;
2+ using UnityEditor ;
3+ using UnityEngine . AI ;
4+ using Unity . AI . Navigation ;
5+ using System . Collections . Generic ;
6+
7+ public class AutoRampConnector : EditorWindow
8+ {
9+ [ Header ( "Zorunlu Alan" ) ]
10+ GameObject linkPrefab ;
11+
12+ [ Header ( "Tarama Ayarları" ) ]
13+ float minHeight = 0.5f ;
14+ float maxHeight = 8.0f ;
15+ float density = 2.0f ;
16+ LayerMask groundLayer = ~ 0 ;
17+
18+ [ Header ( "Eğim Ayarı (Rampa Uzunluğu)" ) ]
19+ [ Tooltip ( "1.0 = 45 derece. Değer arttıkça rampa daha uzağa gider (daha yatık olur)." ) ]
20+ float slopeFactor = 1.5f ;
21+
22+ [ MenuItem ( "Tools/Auto Ramp Connector (High-to-Low)" ) ]
23+ public static void ShowWindow ( )
24+ {
25+ GetWindow ( typeof ( AutoRampConnector ) ) ;
26+ }
27+
28+ void OnGUI ( )
29+ {
30+ GUILayout . Label ( "Otomatik Rampa Bağlayıcı" , EditorStyles . boldLabel ) ;
31+ EditorGUILayout . HelpBox ( "Bu araç, yüksek zeminlerden alçak zeminlere otomatik olarak 'Döndürülmüş' (Rotated) prefablar yerleştirir." , MessageType . Info ) ;
32+
33+ EditorGUILayout . Space ( ) ;
34+
35+ linkPrefab = ( GameObject ) EditorGUILayout . ObjectField ( "Link Prefab" , linkPrefab , typeof ( GameObject ) , false ) ;
36+
37+ if ( linkPrefab == null )
38+ {
39+ EditorGUILayout . HelpBox ( "Lütfen Rampa/Link Prefab'ını buraya sürükleyin!" , MessageType . Error ) ;
40+ }
41+
42+ EditorGUILayout . Space ( ) ;
43+ GUILayout . Label ( "Ayarlar" , EditorStyles . label ) ;
44+ minHeight = EditorGUILayout . FloatField ( "Min Yükseklik" , minHeight ) ;
45+ maxHeight = EditorGUILayout . FloatField ( "Max Yükseklik" , maxHeight ) ;
46+ slopeFactor = EditorGUILayout . FloatField ( "Eğim Çarpanı (Slope)" , slopeFactor ) ;
47+ density = EditorGUILayout . FloatField ( "Sıklık (Density)" , density ) ;
48+
49+ EditorGUILayout . Space ( ) ;
50+
51+ GUI . enabled = linkPrefab != null ;
52+ if ( GUILayout . Button ( "Rampaları Otomatik Yerleştir" , GUILayout . Height ( 40 ) ) )
53+ {
54+ GenerateConnections ( ) ;
55+ }
56+ GUI . enabled = true ;
57+
58+ if ( GUILayout . Button ( "Sahnedeki Rampaları Temizle" ) )
59+ {
60+ ClearConnections ( ) ;
61+ }
62+ }
63+
64+ void GenerateConnections ( )
65+ {
66+ ClearConnections ( ) ;
67+
68+ NavMeshTriangulation triangulation = NavMesh . CalculateTriangulation ( ) ;
69+
70+ GameObject container = new GameObject ( "Auto_Connected_Ramps" ) ;
71+ Undo . RegisterCreatedObjectUndo ( container , "Auto Ramp Gen" ) ;
72+
73+ int count = 0 ;
74+
75+ Dictionary < string , int > edgeCounts = new Dictionary < string , int > ( ) ;
76+ List < Vector3 [ ] > edges = new List < Vector3 [ ] > ( ) ;
77+
78+ for ( int i = 0 ; i < triangulation . indices . Length ; i += 3 )
79+ {
80+ int i1 = triangulation . indices [ i ] ;
81+ int i2 = triangulation . indices [ i + 1 ] ;
82+ int i3 = triangulation . indices [ i + 2 ] ;
83+ AddEdge ( triangulation . vertices [ i1 ] , triangulation . vertices [ i2 ] , edgeCounts , edges ) ;
84+ AddEdge ( triangulation . vertices [ i2 ] , triangulation . vertices [ i3 ] , edgeCounts , edges ) ;
85+ AddEdge ( triangulation . vertices [ i3 ] , triangulation . vertices [ i1 ] , edgeCounts , edges ) ;
86+ }
87+
88+ foreach ( var edge in edges )
89+ {
90+ if ( edgeCounts [ GetEdgeKey ( edge [ 0 ] , edge [ 1 ] ) ] == 1 )
91+ {
92+ ProcessHighGroundEdge ( edge [ 0 ] , edge [ 1 ] , container . transform , ref count ) ;
93+ }
94+ }
95+
96+ Debug . Log ( $ "İşlem Tamamlandı! { count } adet rampa bağlantısı kuruldu.") ;
97+ }
98+
99+ void ProcessHighGroundEdge ( Vector3 v1 , Vector3 v2 , Transform parent , ref int count )
100+ {
101+ float edgeLen = Vector3 . Distance ( v1 , v2 ) ;
102+ int segments = Mathf . Max ( 1 , Mathf . FloorToInt ( edgeLen / density ) ) ;
103+ Vector3 edgeDir = ( v2 - v1 ) . normalized ;
104+
105+ Vector3 sideA = Vector3 . Cross ( edgeDir , Vector3 . up ) ;
106+ Vector3 sideB = - sideA ;
107+
108+ for ( int i = 0 ; i <= segments ; i ++ )
109+ {
110+ float t = ( float ) i / segments ;
111+ Vector3 startPos = Vector3 . Lerp ( v1 , v2 , t ) ;
112+
113+ Vector3 cliffDir = Vector3 . zero ;
114+ if ( IsCliff ( startPos , sideA ) ) cliffDir = sideA ;
115+ else if ( IsCliff ( startPos , sideB ) ) cliffDir = sideB ;
116+ else continue ;
117+
118+ RaycastHit hit ;
119+ Vector3 rayStart = startPos + ( cliffDir * 0.2f ) + Vector3 . up * 0.1f ;
120+
121+ if ( Physics . Raycast ( rayStart , Vector3 . down , out hit , maxHeight , groundLayer ) )
122+ {
123+ float dropHeight = hit . distance ;
124+ if ( dropHeight < minHeight ) continue ;
125+
126+ float horizontalDistance = dropHeight * slopeFactor ;
127+
128+ Vector3 targetPos = startPos + ( cliffDir * horizontalDistance ) ;
129+ targetPos . y = hit . point . y ;
130+
131+ NavMeshHit navHit ;
132+ Vector3 finalEndPos ;
133+
134+ if ( NavMesh . SamplePosition ( targetPos , out navHit , 2.0f , NavMesh . AllAreas ) )
135+ {
136+ finalEndPos = navHit . position ;
137+ }
138+ else
139+ {
140+ if ( NavMesh . SamplePosition ( hit . point , out navHit , horizontalDistance , NavMesh . AllAreas ) )
141+ {
142+ if ( Vector3 . Distance ( startPos , navHit . position ) > minHeight )
143+ finalEndPos = navHit . position ;
144+ else continue ;
145+ }
146+ else continue ;
147+ }
148+
149+ CreateRotatedRamp ( startPos , finalEndPos , parent ) ;
150+ count ++ ;
151+ }
152+ }
153+ }
154+
155+ void CreateRotatedRamp ( Vector3 start , Vector3 end , Transform parent )
156+ {
157+ Vector3 direction = end - start ;
158+ float distance = direction . magnitude ;
159+
160+ GameObject rampObj = ( GameObject ) PrefabUtility . InstantiatePrefab ( linkPrefab ) ;
161+ rampObj . transform . position = start ;
162+ rampObj . transform . SetParent ( parent ) ;
163+
164+ if ( direction != Vector3 . zero )
165+ {
166+ rampObj . transform . rotation = Quaternion . LookRotation ( direction ) ;
167+ }
168+
169+ NavMeshLink link = rampObj . GetComponent < NavMeshLink > ( ) ;
170+ if ( link == null ) link = rampObj . AddComponent < NavMeshLink > ( ) ;
171+
172+ link . startPoint = Vector3 . zero ;
173+ link . endPoint = new Vector3 ( 0 , 0 , distance ) ;
174+
175+ link . width = 1.0f ;
176+ link . bidirectional = true ;
177+ link . autoUpdate = true ;
178+ }
179+
180+ bool IsCliff ( Vector3 origin , Vector3 dir )
181+ {
182+ Vector3 checkPos = origin + ( dir * 0.5f ) + Vector3 . up * 0.5f ;
183+ return ! Physics . Raycast ( checkPos , Vector3 . down , 2.0f , groundLayer ) ;
184+ }
185+
186+ void AddEdge ( Vector3 v1 , Vector3 v2 , Dictionary < string , int > counts , List < Vector3 [ ] > edges )
187+ {
188+ string key = GetEdgeKey ( v1 , v2 ) ;
189+ if ( counts . ContainsKey ( key ) ) counts [ key ] ++ ;
190+ else { counts [ key ] = 1 ; edges . Add ( new Vector3 [ ] { v1 , v2 } ) ; }
191+ }
192+
193+ string GetEdgeKey ( Vector3 v1 , Vector3 v2 )
194+ {
195+ if ( v1 . x < v2 . x || ( v1 . x == v2 . x && v1 . z < v2 . z ) ) return $ "{ v1 } _{ v2 } ";
196+ else return $ "{ v2 } _{ v1 } ";
197+ }
198+
199+ void ClearConnections ( )
200+ {
201+ GameObject existing = GameObject . Find ( "Auto_Connected_Ramps" ) ;
202+ if ( existing != null ) Undo . DestroyObjectImmediate ( existing ) ;
203+ }
204+ }
0 commit comments