You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

CollisionChecker.cs 10 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using Preparation.Interface;
  6. using Preparation.Utility;
  7. namespace GameEngine
  8. {
  9. internal class CollisionChecker
  10. {
  11. public IGameObj? CheckCollision(IMoveable obj, XY Pos)
  12. {
  13. // 在列表中检查碰撞
  14. Func<LockedClassList<IGameObj>, IGameObj?> CheckCollisionInList =
  15. (LockedClassList<IGameObj> lst) =>
  16. {
  17. IGameObj? collisionObj = null;
  18. foreach (IGameObj listObj in lst)
  19. {
  20. if (obj.WillCollideWith(listObj, Pos))
  21. {
  22. collisionObj = listObj;
  23. break;
  24. }
  25. }
  26. return collisionObj;
  27. };
  28. IGameObj? collisionObj;
  29. foreach (var list in lists)
  30. {
  31. if ((collisionObj = CheckCollisionInList(list)) != null)
  32. {
  33. return collisionObj;
  34. }
  35. }
  36. return null;
  37. }
  38. /// <summary>
  39. /// 碰撞检测,如果这样行走是否会与之碰撞,返回与之碰撞的物体
  40. /// </summary>
  41. /// <param name="obj">移动的物体</param>
  42. /// <param name="moveVec">移动的位移向量</param>
  43. /// <returns>和它碰撞的物体</returns>
  44. public IGameObj? CheckCollisionWhenMoving(IMoveable obj, XY moveVec)
  45. {
  46. XY nextPos = obj.Position + moveVec;
  47. if (!obj.IsRigid)
  48. {
  49. if (gameMap.IsOutOfBound(obj))
  50. return gameMap.GetOutOfBound(nextPos);
  51. return null;
  52. }
  53. return CheckCollision(obj, nextPos);
  54. }
  55. /// <summary>
  56. /// /// 可移动物体(圆)向矩形物体移动时,可移动且不会碰撞的最大距离。直接用double计算,防止误差
  57. /// </summary>
  58. /// <param name="obj"></param>
  59. /// <param name="square">矩形的中心坐标</param>
  60. /// <returns></returns>
  61. // private double MaxMoveToSquare(IMoveable obj, IGameObj square)
  62. //{
  63. // double tmpMax;
  64. // double angle = Math.Atan2(square.Position.y - obj.Position.y, square.Position.x - obj.Position.x);
  65. // if (obj.WillCollideWith(square, obj.Position))
  66. // tmpMax = 0;
  67. // else tmpMax =
  68. // Math.Abs(XYPosition.DistanceFloor3(obj.Position, square.Position) - obj.Radius -
  69. // (square.Radius / Math.Min(Math.Abs(Math.Cos(angle)), Math.Abs(Math.Sin(angle)))));
  70. // return tmpMax;
  71. // }
  72. // private double FindMaxOnlyConsiderWall(IMoveable obj, Vector moveVec)
  73. //{
  74. // var desination = moveVec;
  75. // double maxOnlyConsiderWall = moveVec.length;
  76. // if (desination.length > 0) //如果length足够长,还是有可能穿墙的
  77. // {
  78. // XYPosition nextXY = Vector.Vector2XY(desination) + obj.Position + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle)), (int)(obj.Radius * Math.Sin(moveVec.angle)));
  79. // if (gameMap.IsWall(nextXY)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置
  80. // {
  81. // maxOnlyConsiderWall = MaxMoveToSquare(obj, gameMap.GetCell(nextXY));
  82. // }
  83. // else //考虑物体宽度
  84. // {
  85. // double dist = 0;
  86. // XYPosition nextXYConsiderWidth;
  87. // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle + Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle + Math.PI / 4)));
  88. // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置
  89. // {
  90. // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth));
  91. // if (dist < maxOnlyConsiderWall)
  92. // maxOnlyConsiderWall = dist;
  93. // }
  94. // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle - Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle - Math.PI / 4)));
  95. // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置
  96. // {
  97. // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth));
  98. // if (dist < maxOnlyConsiderWall)
  99. // maxOnlyConsiderWall = dist;
  100. // }
  101. // }
  102. // }
  103. // return maxOnlyConsiderWall;
  104. // }
  105. /// <summary>
  106. /// 寻找最大可能移动距离
  107. /// </summary>
  108. /// <param name="obj">移动物体,默认obj.Rigid为true</param>
  109. /// <param name="nextPos">下一步要到达的位置</param>
  110. /// <param name="moveVec">移动的位移向量,默认与nextPos协调</param>
  111. /// <returns>最大可能的移动距离</returns>
  112. public double FindMax(IMoveable obj, XY nextPos, XY moveVec)
  113. {
  114. double tmpMax = uint.MaxValue; // 暂存最大值
  115. double maxDistance = uint.MaxValue;
  116. foreach (var listWithLock in lists)
  117. {
  118. var lst = listWithLock;
  119. foreach (IGameObj listObj in lst)
  120. {
  121. // 如果再走一步发生碰撞
  122. if (obj.WillCollideWith(listObj, nextPos))
  123. {
  124. {
  125. switch (listObj.Shape) // 默认obj为圆形
  126. {
  127. case ShapeType.Circle:
  128. {
  129. // 计算两者之间的距离
  130. double mod = XY.DistanceFloor3(listObj.Position, obj.Position);
  131. int orgDeltaX = listObj.Position.x - obj.Position.x;
  132. int orgDeltaY = listObj.Position.y - obj.Position.y;
  133. if (mod < listObj.Radius + obj.Radius) // 如果两者已经重叠
  134. {
  135. tmpMax = 0;
  136. }
  137. else
  138. {
  139. double tmp = mod - obj.Radius - listObj.Radius;
  140. // 计算能走的最长距离,好像这么算有一点误差?
  141. tmp = ((int)(tmp * 1000 / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.Angle())));
  142. if (tmp < 0 || tmp > uint.MaxValue || double.IsNaN(tmp))
  143. {
  144. tmpMax = uint.MaxValue;
  145. }
  146. else
  147. tmpMax = tmp / 1000.0;
  148. }
  149. break;
  150. }
  151. case ShapeType.Square:
  152. {
  153. // if (obj.WillCollideWith(listObj, obj.Position))
  154. // tmpMax = 0;
  155. // else tmpMax = MaxMoveToSquare(obj, listObj);
  156. // break;
  157. if (obj.WillCollideWith(listObj, obj.Position))
  158. tmpMax = 0;
  159. else
  160. {
  161. // 二分查找最大可能移动距离
  162. int left = 0, right = (int)moveVec.Length();
  163. while (left < right - 1)
  164. {
  165. int mid = (right - left) / 2 + left;
  166. if (obj.WillCollideWith(listObj, obj.Position + new XY(moveVec, mid)))
  167. {
  168. right = mid;
  169. }
  170. else
  171. left = mid;
  172. }
  173. tmpMax = (uint)left;
  174. }
  175. break;
  176. }
  177. default:
  178. tmpMax = uint.MaxValue;
  179. break;
  180. }
  181. if (tmpMax < maxDistance)
  182. maxDistance = tmpMax;
  183. }
  184. }
  185. }
  186. }
  187. return maxDistance;
  188. }
  189. readonly IMap gameMap;
  190. private readonly LockedClassList<IGameObj>[] lists;
  191. public CollisionChecker(IMap gameMap)
  192. {
  193. this.gameMap = gameMap;
  194. lists = gameMap.GameObjDict.Values.ToArray();
  195. }
  196. }
  197. }