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