CurvedUISettingsEditor.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEditor;
  5. using UnityEditor.UI;
  6. using UnityEngine.EventSystems;
  7. #if CURVEDUI_TMP || TMP_PRESENT
  8. using TMPro;
  9. #endif
  10. #if CURVEDUI_STEAMVR_2
  11. using Valve.VR;
  12. using System.Reflection;
  13. using System;
  14. #endif
  15. namespace CurvedUI {
  16. [CustomEditor(typeof(CurvedUISettings))]
  17. [ExecuteInEditMode]
  18. public class CurvedUISettingsEditor : Editor {
  19. #pragma warning disable 414
  20. bool ShowRemoveCurvedUI = false;
  21. static bool ShowAdvaced = false;
  22. bool loadingCustomDefine = false;
  23. static bool CUIeventSystemPresent = false;
  24. private bool inPrefabMode = false;
  25. [SerializeField][HideInInspector]
  26. Dictionary<CurvedUIInputModule.CUIControlMethod, string> m_controlMethodDefineDict;
  27. #if CURVEDUI_STEAMVR_2
  28. SteamVR_Action_Boolean[] steamVRActions;
  29. string[] steamVRActionsPaths;
  30. #endif
  31. #pragma warning restore 414
  32. #region LIFECYCLE
  33. void Awake()
  34. {
  35. AddCurvedUIComponents();
  36. }
  37. void OnEnable()
  38. {
  39. //if we're firing OnEnable, this means any compilation has ended. We're good!
  40. loadingCustomDefine = false;
  41. //look for CurvedUI custom EventSystem -> we'll check later if it makes sense to have one.
  42. CUIeventSystemPresent = (FindObjectsOfType(typeof(CurvedUIEventSystem)).Length > 0);
  43. //hacky way to make sure event is connected only once, but it works!
  44. #if UNITY_2018_1_OR_NEWER
  45. EditorApplication.hierarchyChanged -= AddCurvedUIComponents;
  46. EditorApplication.hierarchyChanged -= AddCurvedUIComponents;
  47. EditorApplication.hierarchyChanged += AddCurvedUIComponents;
  48. #else
  49. //hacky way to make sure event is connected only once, but it works!
  50. EditorApplication.hierarchyWindowChanged -= AddCurvedUIComponents;
  51. EditorApplication.hierarchyWindowChanged -= AddCurvedUIComponents;
  52. EditorApplication.hierarchyWindowChanged += AddCurvedUIComponents;
  53. #endif
  54. //Warnings-------------------------------------//
  55. if (Application.isPlaying)
  56. {
  57. CurvedUISettings myTarget = (CurvedUISettings)target;
  58. //Canvas' layer not included in RaycastLayerMask warning
  59. if (!IsInLayerMask(myTarget.gameObject.layer, CurvedUIInputModule.Instance.RaycastLayerMask) &&
  60. myTarget.Interactable)
  61. {
  62. Debug.LogError("CURVEDUI: " + WarningLayerNotIncluded, myTarget.gameObject);
  63. }
  64. //check if the currently selected control method is enabled in editor.
  65. //Otherwise, show error.
  66. string define = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
  67. foreach(var key in ControlMethodDefineDict.Keys)
  68. {
  69. if(myTarget.ControlMethod == key && !define.Contains(ControlMethodDefineDict[key]))
  70. Debug.LogError("CURVEDUI: Selected control method (" + key.ToString() + ") is not enabled. " +
  71. "Enable it on CurvedUISettings component", myTarget.gameObject);
  72. }
  73. }
  74. #if CURVEDUI_STEAMVR_2
  75. //Get action and their paths to show in the popup.
  76. steamVRActions = SteamVR_Input.GetActions<SteamVR_Action_Boolean>();
  77. steamVRActionsPaths = new string[] { "None" };
  78. if (steamVRActions != null && steamVRActions.Length > 0)
  79. {
  80. List<string> enumList = new List<string>();
  81. //add all action paths to list.
  82. for (int i = 0; i < steamVRActions.Length; i++)
  83. enumList.Add(steamVRActions[i].fullPath);
  84. enumList.Add("None"); //need a way to null that field, so add None as last pick.
  85. //replace forward slashes with backslack instead. Otherwise they will not show up.
  86. for (int index = 0; index < enumList.Count; index++)
  87. enumList[index] = enumList[index].Replace('/', '\\');
  88. steamVRActionsPaths = enumList.ToArray();
  89. }
  90. #endif
  91. }
  92. #endregion
  93. public override void OnInspectorGUI()
  94. {
  95. //initial settings------------------------------------//
  96. CurvedUISettings myTarget = (CurvedUISettings)target;
  97. if (target == null) return;
  98. GUI.changed = false;
  99. EditorGUIUtility.labelWidth = 150;
  100. #if UNITY_2018_3_OR_NEWER
  101. inPrefabMode = (UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null);
  102. #endif
  103. //Version----------------------------------------------//
  104. GUILayout.Label("Version " + CurvedUISettings.Version, EditorStyles.miniLabel);
  105. //Warnings------------------------------------------//
  106. //Canvas is on layer that is not part of RaycastLayerMask
  107. if(!IsInLayerMask(myTarget.gameObject.layer, CurvedUIInputModule.Instance.RaycastLayerMask) && myTarget.Interactable)
  108. {
  109. EditorGUILayout.HelpBox(WarningLayerNotIncluded, MessageType.Error);
  110. GUILayout.Space(30);
  111. }
  112. //Improper event system warning
  113. if (CUIeventSystemPresent == false)
  114. {
  115. EditorGUILayout.HelpBox("Unity UI may become unresponsive in VR if game window loses focus. " +
  116. "Use CurvedUIEventSystem instead of standard EventSystem component to solve this issue.",
  117. MessageType.Warning);
  118. GUILayout.BeginHorizontal();
  119. GUILayout.Space(146);
  120. if (GUILayout.Button("Use CurvedUI Event System")) SwapEventSystem();
  121. GUILayout.EndHorizontal();
  122. GUILayout.Space(30);
  123. }
  124. //-----------------------------------------------------//
  125. //Control methods--------------------------------------//
  126. DrawControlMethods();
  127. //shape settings----------------------------------------//
  128. GUILayout.Label("Shape", EditorStyles.boldLabel);
  129. myTarget.Shape = (CurvedUISettings.CurvedUIShape)EditorGUILayout.EnumPopup("Canvas Shape", myTarget.Shape);
  130. switch (myTarget.Shape)
  131. {
  132. case CurvedUISettings.CurvedUIShape.CYLINDER:
  133. {
  134. myTarget.Angle = EditorGUILayout.IntSlider("Angle", myTarget.Angle, -360, 360);
  135. myTarget.PreserveAspect = EditorGUILayout.Toggle("Preserve Aspect", myTarget.PreserveAspect);
  136. break;
  137. }
  138. case CurvedUISettings.CurvedUIShape.CYLINDER_VERTICAL:
  139. {
  140. myTarget.Angle = EditorGUILayout.IntSlider("Angle", myTarget.Angle, -360, 360);
  141. myTarget.PreserveAspect = EditorGUILayout.Toggle("Preserve Aspect", myTarget.PreserveAspect);
  142. break;
  143. }
  144. case CurvedUISettings.CurvedUIShape.RING:
  145. {
  146. myTarget.RingExternalDiameter = Mathf.Clamp(EditorGUILayout.IntField("External Diameter", myTarget.RingExternalDiameter), 1, 100000);
  147. myTarget.Angle = EditorGUILayout.IntSlider("Angle", myTarget.Angle, 0, 360);
  148. myTarget.RingFill = EditorGUILayout.Slider("Fill", myTarget.RingFill, 0.0f, 1.0f);
  149. myTarget.RingFlipVertical = EditorGUILayout.Toggle("Flip Canvas Vertically", myTarget.RingFlipVertical);
  150. break;
  151. }
  152. case CurvedUISettings.CurvedUIShape.SPHERE:
  153. {
  154. GUILayout.BeginHorizontal();
  155. GUILayout.Space(150);
  156. EditorGUILayout.HelpBox("Sphere shape is more expensive than a Cylinder shape. Keep this in mind when working on mobile VR.", MessageType.Info);
  157. GUILayout.EndHorizontal();
  158. GUILayout.Space(10);
  159. if (myTarget.PreserveAspect)
  160. {
  161. myTarget.Angle = EditorGUILayout.IntSlider("Angle", myTarget.Angle, -360, 360);
  162. }
  163. else {
  164. myTarget.Angle = EditorGUILayout.IntSlider("Horizontal Angle", myTarget.Angle, 0, 360);
  165. myTarget.VerticalAngle = EditorGUILayout.IntSlider("Vertical Angle", myTarget.VerticalAngle, 0, 180);
  166. }
  167. myTarget.PreserveAspect = EditorGUILayout.Toggle("Preserve Aspect", myTarget.PreserveAspect);
  168. break;
  169. }
  170. }//end of shape settings-------------------------------//
  171. //180 degree warning ----------------------------------//
  172. if ((myTarget.Shape != CurvedUISettings.CurvedUIShape.RING && myTarget.Angle.Abs() > 180) ||
  173. (myTarget.Shape == CurvedUISettings.CurvedUIShape.SPHERE && myTarget.VerticalAngle > 180))
  174. Draw180DegreeWarning();
  175. //advanced settings------------------------------------//
  176. GUILayout.Space(30);
  177. if (!ShowAdvaced)
  178. {
  179. if (GUILayout.Button("Show Advanced Settings"))
  180. {
  181. ShowAdvaced = true;
  182. loadingCustomDefine = false;
  183. }
  184. }
  185. else
  186. {
  187. //hide advances settings button.
  188. if (GUILayout.Button("Hide Advanced Settings")) ShowAdvaced = false;
  189. GUILayout.Space(20);
  190. //InputModule Options - only if we're not in prefab mode.
  191. if (!inPrefabMode)
  192. {
  193. CurvedUIInputModule.Instance.RaycastLayerMask = LayerMaskField("Raycast Layer Mask",
  194. CurvedUIInputModule.Instance.RaycastLayerMask);
  195. //pointer override
  196. GUILayout.Space(20);
  197. CurvedUIInputModule.Instance.PointerTransformOverride = (Transform)EditorGUILayout.ObjectField("Pointer Override", CurvedUIInputModule.Instance.PointerTransformOverride, typeof(Transform), true);
  198. GUILayout.BeginHorizontal();
  199. GUILayout.Space(150);
  200. GUILayout.Label("(Optional) If set, its position and forward (blue) direction will be used to point at canvas.", EditorStyles.helpBox);
  201. GUILayout.EndHorizontal();
  202. }
  203. else
  204. {
  205. EditorGUILayout.HelpBox("Some settings are hidden in Prefab Mode", MessageType.Warning);
  206. }
  207. //quality
  208. GUILayout.Space(20);
  209. myTarget.Quality = EditorGUILayout.Slider("Quality", myTarget.Quality, 0.1f, 3.0f);
  210. GUILayout.BeginHorizontal();
  211. GUILayout.Space(150);
  212. GUILayout.Label("Smoothness of the curve. Bigger values mean more subdivisions. Decrease for better performance. Default 1", EditorStyles.helpBox);
  213. GUILayout.EndHorizontal();
  214. //common options
  215. myTarget.Interactable = EditorGUILayout.Toggle("Interactable", myTarget.Interactable);
  216. myTarget.BlocksRaycasts = EditorGUILayout.Toggle("Blocks Raycasts", myTarget.BlocksRaycasts);
  217. if (myTarget.Shape != CurvedUISettings.CurvedUIShape.SPHERE) myTarget.ForceUseBoxCollider =
  218. EditorGUILayout.Toggle("Force Box Colliders Use", myTarget.ForceUseBoxCollider);
  219. //add components button
  220. GUILayout.Space(20);
  221. GUILayout.BeginHorizontal();
  222. GUILayout.Label("Components", GUILayout.Width(146));
  223. if (GUILayout.Button("Add Curved Effect To Children")) AddCurvedUIComponents();
  224. GUILayout.EndHorizontal();
  225. //remove components button
  226. GUILayout.BeginHorizontal();
  227. GUILayout.Label("", GUILayout.Width(146));
  228. if (!ShowRemoveCurvedUI)
  229. {
  230. if (GUILayout.Button("Remove CurvedUI from Canvas")) ShowRemoveCurvedUI = true;
  231. }
  232. else {
  233. if (GUILayout.Button("Remove CurvedUI")) RemoveCurvedUIComponents();
  234. if (GUILayout.Button("Cancel")) ShowRemoveCurvedUI = false;
  235. }
  236. GUILayout.EndHorizontal();
  237. //documentation link
  238. GUILayout.Space(20);
  239. GUILayout.BeginHorizontal();
  240. GUILayout.Label("Documentation", GUILayout.Width(146));
  241. if (GUILayout.Button("Open in web browser")) Help.BrowseURL("https://docs.google.com/document/d/10hNcvOMissNbGgjyFyV1MS7HwkXXE6270A6Ul8h8pnQ/edit");
  242. GUILayout.EndHorizontal();
  243. }//end of Advanced settings---------------------------//
  244. GUILayout.Space(20);
  245. //final settings
  246. if (GUI.changed && myTarget != null)
  247. EditorUtility.SetDirty(myTarget);
  248. }
  249. #region CUSTOM GUI ELEMENTS
  250. void DrawControlMethods()
  251. {
  252. GUILayout.Label("Global Settings", EditorStyles.boldLabel);
  253. //Do not allow to change Global Settings in Prefab Mode
  254. //These are stored in CurvedUInputModule
  255. if (inPrefabMode)
  256. {
  257. EditorGUILayout.HelpBox(
  258. "Some global settings (including Control Method) are hidden in Prefab Mode. These are stored on CurvedUIInputModule component which cannot be accessed now.",
  259. MessageType.Warning);
  260. return;
  261. }
  262. //Control Method dropdown--------------------------------//
  263. CurvedUIInputModule.ControlMethod = (CurvedUIInputModule.CUIControlMethod)EditorGUILayout.EnumPopup("Control Method", CurvedUIInputModule.ControlMethod);
  264. GUILayout.BeginHorizontal();
  265. GUILayout.Space(150);
  266. GUILayout.BeginVertical();
  267. //Custom Settings for each Control Method---------------//
  268. switch (CurvedUIInputModule.ControlMethod)
  269. {
  270. case CurvedUIInputModule.CUIControlMethod.MOUSE:
  271. {
  272. #if CURVEDUI_GOOGLEVR
  273. EditorGUILayout.HelpBox("Enabling this control method will disable GoogleVR support.", MessageType.Warning);
  274. DrawCustomDefineSwitcher("");
  275. #else
  276. GUILayout.Label("Basic Controller. Mouse on screen", EditorStyles.helpBox);
  277. #endif
  278. break;
  279. }// end of MOUSE
  280. case CurvedUIInputModule.CUIControlMethod.GAZE:
  281. {
  282. #if CURVEDUI_GOOGLEVR
  283. EditorGUILayout.HelpBox("Enabling this control method will disable GoogleVR support.", MessageType.Warning);
  284. DrawCustomDefineSwitcher("");
  285. #else
  286. GUILayout.Label("Center of Canvas's Event Camera acts as a pointer. Can be used with any headset. If you're on cardboard, you can use it instead of GOOGLEVR control method.", EditorStyles.helpBox);
  287. CurvedUIInputModule.Instance.GazeUseTimedClick = EditorGUILayout.Toggle("Use Timed Click", CurvedUIInputModule.Instance.GazeUseTimedClick);
  288. if (CurvedUIInputModule.Instance.GazeUseTimedClick)
  289. {
  290. GUILayout.Label("Clicks a button if player rests his gaze on it for a period of time. You can assign an image to be used as a progress bar.", EditorStyles.helpBox);
  291. CurvedUIInputModule.Instance.GazeClickTimer = EditorGUILayout.FloatField("Click Timer (seconds)", CurvedUIInputModule.Instance.GazeClickTimer);
  292. CurvedUIInputModule.Instance.GazeClickTimerDelay = EditorGUILayout.FloatField("Timer Start Delay", CurvedUIInputModule.Instance.GazeClickTimerDelay);
  293. CurvedUIInputModule.Instance.GazeTimedClickProgressImage = (UnityEngine.UI.Image)EditorGUILayout.ObjectField("Progress Image To FIll", CurvedUIInputModule.Instance.GazeTimedClickProgressImage, typeof(UnityEngine.UI.Image), true);
  294. }
  295. #endif
  296. break;
  297. }// end of GAZE
  298. case CurvedUIInputModule.CUIControlMethod.WORLD_MOUSE:
  299. {
  300. #if CURVEDUI_GOOGLEVR
  301. EditorGUILayout.HelpBox("Enabling this control method will disable GoogleVR support.", MessageType.Warning);
  302. DrawCustomDefineSwitcher("");
  303. #else
  304. GUILayout.Label("Mouse controller that is independent of the camera view. Use WorldSpaceMouseOnCanvas function to get its position.", EditorStyles.helpBox);
  305. CurvedUIInputModule.Instance.WorldSpaceMouseSensitivity = EditorGUILayout.FloatField("Mouse Sensitivity", CurvedUIInputModule.Instance.WorldSpaceMouseSensitivity);
  306. #endif
  307. break;
  308. }// end of WORLD_MOUSE
  309. case CurvedUIInputModule.CUIControlMethod.CUSTOM_RAY:
  310. {
  311. #if CURVEDUI_GOOGLEVR
  312. EditorGUILayout.HelpBox("Enabling this control method will disable GoogleVR support.", MessageType.Warning);
  313. DrawCustomDefineSwitcher("");
  314. #else
  315. GUILayout.Label("Set a ray used to interact with canvas using CustomControllerRay function. Use CustomControllerButtonState bool to set button pressed state. Find both in CurvedUIInputModule class", EditorStyles.helpBox);
  316. GUILayout.BeginHorizontal();
  317. //GUILayout.Space(20);
  318. GUILayout.FlexibleSpace();
  319. if (GUILayout.Button("View code snippet")) Help.BrowseURL("https://docs.google.com/document/d/10hNcvOMissNbGgjyFyV1MS7HwkXXE6270A6Ul8h8pnQ/edit#heading=h.b164qm67xp15");
  320. GUILayout.EndHorizontal();
  321. #endif
  322. break;
  323. }// end of CUSTOM_RAY
  324. case CurvedUIInputModule.CUIControlMethod.STEAMVR_LEGACY:
  325. {
  326. #if CURVEDUI_STEAMVR_LEGACY
  327. // vive enabled, we can show settings
  328. GUILayout.Label("Use with SteamVR plugin 1.2 or below. Trigger acts a button", EditorStyles.helpBox);
  329. CurvedUIInputModule.Instance.UsedHand = (CurvedUIInputModule.Hand)EditorGUILayout.EnumPopup("Used Controller", CurvedUIInputModule.Instance.UsedHand);
  330. #else
  331. GUILayout.Label("For SteamVR plugin 1.2 or below. Make sure you imported that SDK before enabling.", EditorStyles.helpBox);
  332. DrawCustomDefineSwitcher(ControlMethodDefineDict[CurvedUIInputModule.CUIControlMethod.STEAMVR_LEGACY]);
  333. #endif
  334. break;
  335. }// end of STEAMVR_LEGACY
  336. case CurvedUIInputModule.CUIControlMethod.STEAMVR_2:
  337. {
  338. #if CURVEDUI_STEAMVR_2
  339. GUILayout.Label("Use SteamVR controllers to interact with canvas. Requires SteamVR Plugin 2.0 or later.", EditorStyles.helpBox);
  340. if(steamVRActions != null)
  341. {
  342. CurvedUIInputModule.Instance.UsedHand = (CurvedUIInputModule.Hand)EditorGUILayout.EnumPopup("Hand", CurvedUIInputModule.Instance.UsedHand);
  343. //Find currently selected action in CurvedUIInputModule
  344. int curSelected = steamVRActionsPaths.Length - 1;
  345. for (int i = 0; i < steamVRActions.Length; i++)
  346. {
  347. //no action selected? select one that most likely deals with UI
  348. if(CurvedUIInputModule.Instance.SteamVRClickAction == null && steamVRActions[i].GetShortName().Contains("UI"))
  349. CurvedUIInputModule.Instance.SteamVRClickAction = steamVRActions[i];
  350. //otherwise show currently selected
  351. if (steamVRActions[i] == CurvedUIInputModule.Instance.SteamVRClickAction) //otherwise show selected
  352. curSelected = i;
  353. }
  354. //Show popup
  355. int newSelected = EditorGUILayout.Popup("Click With", curSelected, steamVRActionsPaths, EditorStyles.popup);
  356. //assign selected SteamVR Action to CurvedUIInputMOdule
  357. if (curSelected != newSelected)
  358. {
  359. //none has been selected
  360. if (newSelected >= steamVRActions.Length)
  361. CurvedUIInputModule.Instance.SteamVRClickAction = null;
  362. else
  363. CurvedUIInputModule.Instance.SteamVRClickAction = steamVRActions[newSelected];
  364. }
  365. }
  366. else
  367. {
  368. //draw error
  369. EditorGUILayout.HelpBox("No SteamVR Actions set up. Configure your SteamVR plugin first in Window > Steam VR Input", MessageType.Error);
  370. }
  371. #else
  372. GUILayout.Label("For SteamVR plugin 2.0 or above. Make sure you imported that SDK before enabling.", EditorStyles.helpBox);
  373. DrawCustomDefineSwitcher(ControlMethodDefineDict[CurvedUIInputModule.CUIControlMethod.STEAMVR_2]);
  374. #endif
  375. break;
  376. }// end of STEAMVR_2
  377. case CurvedUIInputModule.CUIControlMethod.OCULUSVR:
  378. {
  379. #if CURVEDUI_OCULUSVR
  380. // oculus enabled, we can show settings
  381. GUILayout.Label("Use Rift, Quest, Go, or GearVR controller to interact with your canvas.", EditorStyles.helpBox);
  382. //hand property
  383. CurvedUIInputModule.Instance.UsedHand = (CurvedUIInputModule.Hand)EditorGUILayout.EnumPopup("Hand", CurvedUIInputModule.Instance.UsedHand);
  384. //button property
  385. CurvedUIInputModule.Instance.OculusTouchInteractionButton = (OVRInput.Button)EditorGUILayout.EnumPopup("Interaction Button", CurvedUIInputModule.Instance.OculusTouchInteractionButton);
  386. #else
  387. GUILayout.Label("Make sure you imported the SDK before enabling.", EditorStyles.helpBox);
  388. DrawCustomDefineSwitcher(ControlMethodDefineDict[CurvedUIInputModule.CUIControlMethod.OCULUSVR]);
  389. #endif
  390. break;
  391. }// end of OCULUSVR
  392. case CurvedUIInputModule.CUIControlMethod.UNITY_XR:
  393. {
  394. #if CURVEDUI_UNITY_XR
  395. // oculus enabled, we can show settings
  396. GUILayout.Label("Use Unity XR Toolkit to interact with the canvas. You can choose UI button on the XRController itself.", EditorStyles.helpBox);
  397. //hand property
  398. CurvedUIInputModule.Instance.UsedHand = (CurvedUIInputModule.Hand)EditorGUILayout.EnumPopup("Hand", CurvedUIInputModule.Instance.UsedHand);
  399. //assign controllers
  400. CurvedUIInputModule.Instance.RightXRController = (UnityEngine.XR.Interaction.Toolkit.XRController)EditorGUILayout.ObjectField("Right Controller",
  401. CurvedUIInputModule.Instance.RightXRController, typeof(UnityEngine.XR.Interaction.Toolkit.XRController), true);
  402. CurvedUIInputModule.Instance.LeftXRController = (UnityEngine.XR.Interaction.Toolkit.XRController)EditorGUILayout.ObjectField("Left Controller",
  403. CurvedUIInputModule.Instance.LeftXRController, typeof(UnityEngine.XR.Interaction.Toolkit.XRController), true);
  404. #else
  405. GUILayout.Label("Make sure you imported Unity XR Toolkit before enabling.", EditorStyles.helpBox);
  406. DrawCustomDefineSwitcher(ControlMethodDefineDict[CurvedUIInputModule.CUIControlMethod.UNITY_XR]);
  407. #endif
  408. break;
  409. }// end of UNITY_XR
  410. case CurvedUIInputModule.CUIControlMethod.GOOGLEVR:
  411. {
  412. #if CURVEDUI_GOOGLEVR
  413. GUILayout.Label("Use GoogleVR Reticle to interact with canvas. Requires GoogleVR SDK 1.200.1 or later. Make sure you imported the SDK before enabling.", EditorStyles.helpBox);
  414. #else
  415. GUILayout.Label("Make sure you imported the GoogleVR SDK before enabling.", EditorStyles.helpBox);
  416. DrawCustomDefineSwitcher(ControlMethodDefineDict[CurvedUIInputModule.CUIControlMethod.GOOGLEVR]);
  417. #endif
  418. break;
  419. }// end of GOOGLEVR
  420. }//end of CUIControlMethod Switch
  421. GUILayout.EndVertical();
  422. GUILayout.EndHorizontal();
  423. GUILayout.Space(20);
  424. }
  425. /// <summary>
  426. /// Draws the define switcher for different control methods.
  427. /// Because different control methods use different API's that may not always be available,
  428. /// CurvedUI needs to be recompile with different custom defines to fix this. This method
  429. /// manages the defines.
  430. /// </summary>
  431. /// <param name="defineToSet">Switcho.</param>
  432. void DrawCustomDefineSwitcher(string defineToSet)
  433. {
  434. GUILayout.BeginVertical();
  435. GUILayout.Label("Press the [Enable] button to recompile scripts for this control method. Afterwards, you'll see more settings here.", EditorStyles.helpBox);
  436. GUILayout.BeginHorizontal();
  437. GUILayout.Space(50);
  438. if (GUILayout.Button(loadingCustomDefine ? "Please wait..." : "Enable."))
  439. {
  440. loadingCustomDefine = true;
  441. //retrieve current defines
  442. string str = "";
  443. str += PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
  444. //remove unused curvedui defines - dictionary based.
  445. string define = "";
  446. foreach (var key in ControlMethodDefineDict.Keys)
  447. {
  448. define = ControlMethodDefineDict[key];
  449. if (str.Contains(define))
  450. {
  451. if (str.Contains((";" + define)))
  452. str = str.Replace((";" + define), "");
  453. else
  454. str = str.Replace(define, "");
  455. }
  456. }
  457. //add this one, if not present.
  458. if (defineToSet != "" && !str.Contains(defineToSet))
  459. str += ";" + defineToSet;
  460. //Submit defines. This will cause recompilation
  461. PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, str);
  462. }
  463. GUILayout.EndHorizontal();
  464. GUILayout.EndVertical();
  465. }
  466. void Draw180DegreeWarning()
  467. {
  468. GUILayout.BeginHorizontal();
  469. GUILayout.Space(150);
  470. EditorGUILayout.HelpBox("Cavas with angle bigger than 180 degrees will not be interactable. \n" +
  471. "This is caused by Unity Event System requirements. Use two canvases facing each other for fully interactive 360 degree UI.", MessageType.Warning);
  472. GUILayout.EndHorizontal();
  473. GUILayout.Space(10);
  474. }
  475. #endregion
  476. #region HELPER FUNCTIONS
  477. static List<string> layers;
  478. static string[] layerNames;
  479. public static LayerMask LayerMaskField (string label, LayerMask selected)
  480. {
  481. if (layers == null) {
  482. layers = new List<string>();
  483. layerNames = new string[4];
  484. } else {
  485. layers.Clear ();
  486. }
  487. int emptyLayers = 0;
  488. for (int i=0;i<32;i++) {
  489. string layerName = LayerMask.LayerToName (i);
  490. if (layerName != "") {
  491. for (;emptyLayers>0;emptyLayers--) layers.Add ("Layer "+(i-emptyLayers));
  492. layers.Add (layerName);
  493. } else {
  494. emptyLayers++;
  495. }
  496. }
  497. if (layerNames.Length != layers.Count) {
  498. layerNames = new string[layers.Count];
  499. }
  500. for (int i=0;i<layerNames.Length;i++) layerNames[i] = layers[i];
  501. selected.value = EditorGUILayout.MaskField (label,selected.value,layerNames);
  502. return selected;
  503. }
  504. bool IsInLayerMask(int layer, LayerMask layermask)
  505. {
  506. return layermask == (layermask | (1 << layer));
  507. }
  508. Dictionary<CurvedUIInputModule.CUIControlMethod, string> ControlMethodDefineDict {
  509. get
  510. {
  511. if (m_controlMethodDefineDict == null)
  512. {
  513. m_controlMethodDefineDict = new Dictionary<CurvedUIInputModule.CUIControlMethod, string>();
  514. m_controlMethodDefineDict.Add(CurvedUIInputModule.CUIControlMethod.GOOGLEVR, "CURVEDUI_GOOGLEVR");
  515. m_controlMethodDefineDict.Add(CurvedUIInputModule.CUIControlMethod.STEAMVR_LEGACY, "CURVEDUI_STEAMVR_LEGACY");
  516. m_controlMethodDefineDict.Add(CurvedUIInputModule.CUIControlMethod.STEAMVR_2, "CURVEDUI_STEAMVR_2");
  517. m_controlMethodDefineDict.Add(CurvedUIInputModule.CUIControlMethod.OCULUSVR, "CURVEDUI_OCULUSVR");
  518. m_controlMethodDefineDict.Add(CurvedUIInputModule.CUIControlMethod.UNITY_XR, "CURVEDUI_UNITY_XR");
  519. }
  520. return m_controlMethodDefineDict;
  521. }
  522. }
  523. void SwapEventSystem()
  524. {
  525. if (Application.isPlaying)
  526. {
  527. Debug.LogError("Cant do this in Play mode!");
  528. return;
  529. }
  530. EventSystem system = FindObjectOfType<EventSystem>();
  531. if (!(system is CurvedUIEventSystem))
  532. {
  533. system.AddComponentIfMissing<CurvedUIEventSystem>();
  534. DestroyImmediate(system);
  535. }
  536. CUIeventSystemPresent = true;
  537. }
  538. /// <summary>
  539. ///Travel the hierarchy and add CurvedUIVertexEffect to every gameobject that can be bent.
  540. /// </summary>
  541. private void AddCurvedUIComponents()
  542. {
  543. if (target == null) return;
  544. (target as CurvedUISettings).AddEffectToChildren();
  545. }
  546. /// <summary>
  547. /// Removes all CurvedUI components from this canvas.
  548. /// </summary>
  549. private void RemoveCurvedUIComponents()
  550. {
  551. if (target == null) return;
  552. //destroy TMP objects
  553. List<CurvedUITMP> tmps = new List<CurvedUITMP>();
  554. tmps.AddRange((target as CurvedUISettings).GetComponentsInChildren<CurvedUITMP>(true));
  555. for (int i = 0; i < tmps.Count; i++)
  556. {
  557. DestroyImmediate(tmps[i]);
  558. }
  559. List<CurvedUITMPSubmesh> submeshes = new List<CurvedUITMPSubmesh>();
  560. submeshes.AddRange((target as CurvedUISettings).GetComponentsInChildren<CurvedUITMPSubmesh>(true));
  561. for (int i = 0; i < submeshes.Count; i++)
  562. {
  563. DestroyImmediate(submeshes[i]);
  564. }
  565. //destroy curving componenets
  566. List<CurvedUIVertexEffect> comps = new List<CurvedUIVertexEffect>();
  567. comps.AddRange((target as CurvedUISettings).GetComponentsInChildren<CurvedUIVertexEffect>(true));
  568. for (int i = 0; i < comps.Count; i++)
  569. {
  570. if (comps[i].GetComponent<UnityEngine.UI.Graphic>() != null) comps[i].GetComponent<UnityEngine.UI.Graphic>().SetAllDirty();
  571. DestroyImmediate(comps[i]);
  572. }
  573. //destroy raycasters
  574. List<CurvedUIRaycaster> raycasters = new List<CurvedUIRaycaster>();
  575. raycasters.AddRange((target as CurvedUISettings).GetComponents<CurvedUIRaycaster>());
  576. for (int i = 0; i < raycasters.Count; i++)
  577. {
  578. DestroyImmediate(raycasters[i]);
  579. }
  580. DestroyImmediate(target);
  581. }
  582. #endregion
  583. #region STRINGS
  584. private static string WarningLayerNotIncluded = "This Canvas' layer " +
  585. "is not included in the RaycastLayerMask. User will not be able to interact with it. " +
  586. "Add its layer to RaycastLayerMask below to fix it, or set the " +
  587. "Interactable property to False to dismiss this message.";
  588. #endregion
  589. }
  590. }