Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / common / Utils.cs
  1. using System.Collections.Generic;
  2. using Godot;
  3. using UI.TileSetEditorCombination;
  4.  
  5. /// <summary>
  6. /// 常用函数工具类
  7. /// </summary>
  8. public static class Utils
  9. {
  10. /// <summary>
  11. /// 默认随机数对象
  12. /// </summary>
  13. public static SeedRandom Random { get; private set; }
  14. /// <summary>
  15. /// 初始化随机种子
  16. /// </summary>
  17. public static void InitRandom()
  18. {
  19. Random = new SeedRandom();
  20. Debug.Log("随机种子为: ", Random.Seed);
  21. }
  22.  
  23. /// <summary>
  24. /// 根据两个点计算出矩形
  25. /// </summary>
  26. public static Rect2 CalcRect(float start1, float end1, float start2, float end2)
  27. {
  28. return new Rect2(
  29. Mathf.Min(start1, start2), Mathf.Min(end1, end2),
  30. Mathf.Abs(start1 - start2), Mathf.Abs(end1 - end2)
  31. );
  32. }
  33. /// <summary>
  34. /// 根据两个点计算出矩形
  35. /// </summary>
  36. public static Rect2I CalcRect(int start1, int end1, int start2, int end2)
  37. {
  38. return new Rect2I(
  39. Mathf.Min(start1, start2), Mathf.Min(end1, end2),
  40. Mathf.Abs(start1 - start2), Mathf.Abs(end1 - end2)
  41. );
  42. }
  43. /// <summary>
  44. /// 返回碰撞层 mask 是否会检测 layer
  45. /// </summary>
  46. public static bool CollisionMaskWithLayer(uint mask, uint layer)
  47. {
  48. return (mask & layer) != 0;
  49. }
  50.  
  51. /// <summary>
  52. /// 使用定的 canvasItem 绘制导航区域, 注意, 该函数只能在 draw 函数中调用
  53. /// </summary>
  54. public static void DrawNavigationPolygon(CanvasItem canvasItem, NavigationPolygonData[] polygonData, float width = 1)
  55. {
  56. for (var i = 0; i < polygonData.Length; i++)
  57. {
  58. var item = polygonData[i];
  59. var points = item.GetPoints();
  60. if (points.Length>= 2)
  61. {
  62. var array = new Vector2[points.Length + 1];
  63. for (var j = 0; j < points.Length; j++)
  64. {
  65. array[j] = points[j];
  66. }
  67. array[array.Length - 1] = points[0];
  68. if (item.Type == NavigationPolygonType.In)
  69. {
  70. canvasItem.DrawPolyline(points, Colors.Orange, width);
  71. }
  72. else
  73. {
  74. canvasItem.DrawPolyline(points, Colors.Orange, width);
  75. }
  76. }
  77. }
  78. }
  79. /// <summary>
  80. /// 将一个任意角度转为0到360度
  81. /// </summary>
  82. public static float ConvertAngle(float angle)
  83. {
  84. angle %= 360; // 取余
  85.  
  86. if (angle < 0) // 如果角度为负数,转为正数
  87. {
  88. angle += 360;
  89. }
  90.  
  91. return angle;
  92. }
  93. /// <summary>
  94. /// 根据步长吸附值
  95. /// </summary>
  96. /// <param name="value">原数值</param>
  97. /// <param name="step">吸附步长</param>
  98. public static float Adsorption(this float value, float step)
  99. {
  100. var f = Mathf.Round(value / step);
  101. return f * step;
  102. }
  103. /// <summary>
  104. /// 根据步长吸附值
  105. /// </summary>
  106. /// <param name="value">原数值</param>
  107. /// <param name="step">吸附步长</param>
  108. public static int Adsorption(this float value, int step)
  109. {
  110. var f = Mathf.RoundToInt(value / step);
  111. return f * step;
  112. }
  113. /// <summary>
  114. /// 根据步长吸附值
  115. /// </summary>
  116. /// <param name="value">原数值</param>
  117. /// <param name="step">吸附步长</param>
  118. public static Vector2 Adsorption(this Vector2 value, Vector2 step)
  119. {
  120. var x = Mathf.Round(value.X / step.X);
  121. var y = Mathf.Round(value.Y / step.Y);
  122. return new Vector2(x * step.X, y * step.Y);
  123. }
  124. /// <summary>
  125. /// 根据步长吸附值
  126. /// </summary>
  127. /// <param name="value">原数值</param>
  128. /// <param name="step">吸附步长</param>
  129. public static Vector2I Adsorption(this Vector2 value, Vector2I step)
  130. {
  131. var x = Mathf.RoundToInt(value.X / step.X);
  132. var y = Mathf.RoundToInt(value.Y / step.Y);
  133. return new Vector2I(x * step.X, y * step.Y);
  134. }
  135. /// <summary>
  136. /// 根据步长按照 Floor() 函数吸附值
  137. /// </summary>
  138. /// <param name="value">原数值</param>
  139. /// <param name="step">吸附步长</param>
  140. public static float FloorAdsorption(this float value, float step)
  141. {
  142. var f = Mathf.Floor(value / step);
  143. return f * step;
  144. }
  145. /// <summary>
  146. /// 根据步长按照 Floor() 函数吸附值
  147. /// </summary>
  148. /// <param name="value">原数值</param>
  149. /// <param name="step">吸附步长</param>
  150. public static int FloorAdsorption(this float value, int step)
  151. {
  152. var f = Mathf.FloorToInt(value / step);
  153. return f * step;
  154. }
  155.  
  156. /// <summary>
  157. /// 根据步长按照 Floor() 函数吸附值
  158. /// </summary>
  159. /// <param name="value">原数值</param>
  160. /// <param name="step">吸附步长</param>
  161. public static Vector2 FloorAdsorption(this Vector2 value, Vector2 step)
  162. {
  163. var x = Mathf.Floor(value.X / step.X);
  164. var y = Mathf.Floor(value.Y / step.Y);
  165. return new Vector2(x * step.X, y * step.Y);
  166. }
  167. /// <summary>
  168. /// 根据步长按照 Floor() 函数吸附值
  169. /// </summary>
  170. /// <param name="value">原数值</param>
  171. /// <param name="step">吸附步长</param>
  172. public static Vector2I FloorAdsorption(this Vector2 value, Vector2I step)
  173. {
  174. var x = Mathf.FloorToInt(value.X / step.X);
  175. var y = Mathf.FloorToInt(value.Y / step.Y);
  176. return new Vector2I(x * step.X, y * step.Y);
  177. }
  178.  
  179. /// <summary>
  180. /// 字符串首字母小写
  181. /// </summary>
  182. public static string FirstToLower(this string str)
  183. {
  184. return str.Substring(0, 1).ToLower() + str.Substring(1);
  185. }
  186. /// <summary>
  187. /// 字符串首字母大写
  188. /// </summary>
  189. public static string FirstToUpper(this string str)
  190. {
  191. return str.Substring(0, 1).ToUpper() + str.Substring(1);
  192. }
  193.  
  194. /// <summary>
  195. /// 将 Vector2 类型转为 Vector2I 类型
  196. /// </summary>
  197. public static Vector2I AsVector2I(this Vector2 vector2)
  198. {
  199. return new Vector2I((int)vector2.X, (int)vector2.Y);
  200. }
  201.  
  202. /// <summary>
  203. /// 判断点是否在区域内
  204. /// </summary>
  205. public static bool IsPositionInRect(Vector2 pos, Rect2 rect2)
  206. {
  207. return pos.X >= rect2.Position.X && pos.X <= rect2.Position.X + rect2.Size.X &&
  208. pos.Y >= rect2.Position.Y && pos.Y <= rect2.Position.Y + rect2.Size.Y;
  209. }
  210.  
  211. /// <summary>
  212. /// 返回区域起始值, 用于获取配置表范围配置数据
  213. /// </summary>
  214. public static int GetConfigRangeStart(int[] range)
  215. {
  216. return range[0];
  217. }
  218. /// <summary>
  219. /// 返回区域结束值, 用于获取配置表范围配置数据
  220. /// </summary>
  221. public static int GetConfigRangeEnd(int[] range)
  222. {
  223. if (range.Length > 1)
  224. {
  225. return range[1];
  226. }
  227.  
  228. return range[0];
  229. }
  230. /// <summary>
  231. /// 返回区域起始值, 用于获取配置表范围配置数据
  232. /// </summary>
  233. public static float GetConfigRangeStart(float[] range)
  234. {
  235. return range[0];
  236. }
  237. /// <summary>
  238. /// 返回区域结束值, 用于获取配置表范围配置数据
  239. /// </summary>
  240. public static float GetConfigRangeEnd(float[] range)
  241. {
  242. if (range.Length > 1)
  243. {
  244. return range[1];
  245. }
  246.  
  247. return range[0];
  248. }
  249.  
  250. /// <summary>
  251. /// 创建扇形多边形区域数据, 返回坐标点
  252. /// </summary>
  253. /// <param name="centerAngle">中心角度, 角度制</param>
  254. /// <param name="radius">扇形半径</param>
  255. /// <param name="range">扇形开口角度, 角度制</param>
  256. /// <param name="edgesCount">扇形弧度边的数量</param>
  257. /// <param name="offset">整体偏移坐标, 默认0</param>
  258. public static Vector2[] CreateSectorPolygon(float centerAngle, float radius, float range, uint edgesCount, Vector2? offset = null)
  259. {
  260. var point = new Vector2[edgesCount + 2];
  261. var edgesAngle = range / edgesCount;
  262. var startAngle = centerAngle - range * 0.5f;
  263. var temp = new Vector2(radius, 0);
  264.  
  265. for (var i = 0; i <= edgesCount; i++)
  266. {
  267. if (offset == null)
  268. {
  269. point[i] = temp.Rotated(Mathf.DegToRad(startAngle + edgesAngle * i));
  270. }
  271. else
  272. {
  273. point[i] = temp.Rotated(Mathf.DegToRad(startAngle + edgesAngle * i)) + offset.Value;
  274. }
  275. }
  276.  
  277. if (offset == null)
  278. {
  279. point[point.Length - 1] = Vector2.Zero;
  280. }
  281. else
  282. {
  283. point[point.Length - 1] = offset.Value;
  284. }
  285. return point;
  286. }
  287.  
  288. /// <summary>
  289. /// 将 point 位置限制在 anchor 的周围, 最大距离为 distance, 并返回新的位置
  290. /// </summary>
  291. public static Vector2 ConstrainDistance(Vector2 point, Vector2 anchor, float distance)
  292. {
  293. return (point - anchor).Normalized() * distance + anchor;
  294. }
  295. /// <summary>
  296. /// 返回一个点是否在 Polygon 内部
  297. /// </summary>
  298. /// <param name="polygon">多边形顶点</param>
  299. /// <param name="point">目标点</param>
  300. public static bool IsPointInPolygon(Vector2[] polygon, Vector2 point)
  301. {
  302. var isInside = false;
  303. for (int i = 0, j = polygon.Length - 1; i < polygon.Length; j = i++)
  304. {
  305. if ((polygon[i].Y > point.Y) != (polygon[j].Y > point.Y) &&
  306. point.X < (polygon[j].X - polygon[i].X) * (point.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) +
  307. polygon[i].X)
  308. {
  309. isInside = !isInside;
  310. }
  311. }
  312.  
  313. return isInside;
  314. }
  315. /// <summary>
  316. /// 返回一个点是否在 Polygon 内部
  317. /// </summary>
  318. /// <param name="polygon">多边形顶点</param>
  319. /// <param name="point">目标点</param>
  320. public static bool IsPointInPolygon(List<Vector2> polygon, Vector2 point)
  321. {
  322. var isInside = false;
  323. for (int i = 0, j = polygon.Count - 1; i < polygon.Count; j = i++)
  324. {
  325. if ((polygon[i].Y > point.Y) != (polygon[j].Y > point.Y) &&
  326. point.X < (polygon[j].X - polygon[i].X) * (point.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) +
  327. polygon[i].X)
  328. {
  329. isInside = !isInside;
  330. }
  331. }
  332.  
  333. return isInside;
  334. }
  335.  
  336. /// <summary>
  337. /// 根据法线翻转向量
  338. /// </summary>
  339. public static Vector2 ReflectByNormal(Vector2 vector, Vector2 normal)
  340. {
  341. return vector.Reflect(normal.Rotated(Mathf.Pi * 0.5f));
  342. }
  343. /// <summary>
  344. /// 根据法线翻转角度, 弧度制
  345. /// </summary>
  346. public static float ReflectByNormal(float rotation, Vector2 normal)
  347. {
  348. return ReflectByNormal(Vector2.FromAngle(rotation), normal).Angle();
  349. }
  350.  
  351. /// <summary>
  352. /// 计算TileSet Cell所占用的区域
  353. /// </summary>
  354. public static Rect2I CalcTileRect(IEnumerable<Vector2I> cells)
  355. {
  356. //单位: 像素
  357. var canvasXStart = int.MaxValue;
  358. var canvasYStart = int.MaxValue;
  359. var canvasXEnd = int.MinValue;
  360. var canvasYEnd = int.MinValue;
  361.  
  362. foreach (var pos in cells)
  363. {
  364. canvasXStart = Mathf.Min(pos.X, canvasXStart);
  365. canvasYStart = Mathf.Min(pos.Y, canvasYStart);
  366. canvasXEnd = Mathf.Max(pos.X + GameConfig.TileCellSize, canvasXEnd);
  367. canvasYEnd = Mathf.Max(pos.Y + GameConfig.TileCellSize, canvasYEnd);
  368. }
  369.  
  370. return new Rect2I(
  371. canvasXStart,
  372. canvasYStart,
  373. canvasXEnd - canvasXStart,
  374. canvasYEnd - canvasYStart
  375. );
  376. }
  377. /// <summary>
  378. /// 计算TileSet Cell所占用的区域
  379. /// </summary>
  380. public static Rect2I CalcTileRect(IEnumerable<SerializeVector2> cells)
  381. {
  382. //单位: 像素
  383. var canvasXStart = float.MaxValue;
  384. var canvasYStart = float.MaxValue;
  385. var canvasXEnd = float.MinValue;
  386. var canvasYEnd = float.MinValue;
  387.  
  388. foreach (var pos in cells)
  389. {
  390. canvasXStart = Mathf.Min(pos.X, canvasXStart);
  391. canvasYStart = Mathf.Min(pos.Y, canvasYStart);
  392. canvasXEnd = Mathf.Max(pos.X + GameConfig.TileCellSize, canvasXEnd);
  393. canvasYEnd = Mathf.Max(pos.Y + GameConfig.TileCellSize, canvasYEnd);
  394. }
  395.  
  396. return new Rect2I(
  397. (int)canvasXStart,
  398. (int)canvasYStart,
  399. (int)(canvasXEnd - canvasXStart),
  400. (int)(canvasYEnd - canvasYStart)
  401. );
  402. }
  403.  
  404. /// <summary>
  405. /// 根据鼠标位置执行单步放大逻辑
  406. /// </summary>
  407. public static bool DoMagnifyByMousePosition(Control control, float maxXScale)
  408. {
  409. var offset = control.GetLocalMousePosition();
  410. var prevScale = control.Scale;
  411. var newScale = prevScale * 1.1f;
  412. if (newScale.X <= maxXScale)
  413. {
  414. control.Scale = newScale;
  415. var position = control.Position - offset * 0.1f * prevScale;
  416. control.Position = position;
  417. return true;
  418. }
  419.  
  420. return false;
  421. }
  422. /// <summary>
  423. /// 根据鼠标位置执行单步放大逻辑
  424. /// </summary>
  425. public static bool DoMagnifyByMousePosition(Node2D node, float maxXScale)
  426. {
  427. var offset = node.GetLocalMousePosition();
  428. var prevScale = node.Scale;
  429. var newScale = prevScale * 1.1f;
  430. if (newScale.X <= maxXScale)
  431. {
  432. node.Scale = newScale;
  433. var position = node.Position - offset * 0.1f * prevScale;
  434. node.Position = position;
  435. return true;
  436. }
  437.  
  438. return false;
  439. }
  440.  
  441. /// <summary>
  442. /// 根据鼠标位置执行单步缩小逻辑
  443. /// </summary>
  444. public static bool DoShrinkByMousePosition(Control control, float minXScale)
  445. {
  446. var offset = control.GetLocalMousePosition();
  447. var prevScale = control.Scale;
  448. var newScale = prevScale / 1.1f;
  449. if (newScale.X >= minXScale)
  450. {
  451. control.Scale = newScale;
  452. var position = control.Position + offset * 0.1f * newScale;
  453. control.Position = position;
  454. return true;
  455. }
  456.  
  457. return false;
  458. }
  459. /// <summary>
  460. /// 根据鼠标位置执行单步缩小逻辑
  461. /// </summary>
  462. public static bool DoShrinkByMousePosition(Node2D node, float minXScale)
  463. {
  464. var offset = node.GetLocalMousePosition();
  465. var prevScale = node.Scale;
  466. var newScale = prevScale / 1.1f;
  467. if (newScale.X >= minXScale)
  468. {
  469. node.Scale = newScale;
  470. var position = node.Position + offset * 0.1f * newScale;
  471. node.Position = position;
  472. return true;
  473. }
  474.  
  475. return false;
  476. }
  477.  
  478. /// <summary>
  479. /// 聚焦Ui节点
  480. /// </summary>
  481. /// <param name="control">需要聚焦的节点</param>
  482. /// <param name="parentSize">父节点容器大小</param>
  483. /// <param name="selfSize">当前节点容器大小</param>
  484. public static void DoFocusNode(Control control, Vector2 parentSize, Vector2 selfSize)
  485. {
  486. control.Position = parentSize / 2 - selfSize * 0.5f * control.Scale;
  487. }
  488.  
  489. /// <summary>
  490. /// 返回鼠标所在的单元格位置, 相对于Ui节点左上角
  491. /// </summary>
  492. public static Vector2I GetMouseCellPosition(CanvasItem control)
  493. {
  494. var pos = control.GetLocalMousePosition() / GameConfig.TileCellSize;
  495. return pos.AsVector2I();
  496. }
  497.  
  498. /// <summary>
  499. /// 创建一个数组, 并填充该对象
  500. /// </summary>
  501. public static T[] MakeArray<T>(this T data, int len)
  502. {
  503. var arr = new T[len];
  504. for (var i = 0; i < len; i++)
  505. {
  506. arr[i] = data;
  507. }
  508. return arr;
  509. }
  510. }