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