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