Shuvit game master repo. http://shuvit.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

astar.py 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import bge, json, mathutils, math
  2. from timeit import default_timer as timer
  3. def chrono(function):
  4. def wrapper(*args, **kargs):
  5. before = timer()
  6. value = function(*args, **kargs)
  7. print(function.__qualname__, 'took', (timer() - before) * 100, 'to return', value)
  8. return value
  9. return wrapper
  10. def draw(locs, self):
  11. for x in locs:
  12. b = bge.logic.getCurrentScene().addObject('ball')
  13. b.worldPosition = self.points[x].loc
  14. b.color = [1,0,0,1]
  15. def draw_mesh(self):
  16. self.searched = set(self.searched)
  17. for x in self.searched:
  18. b = bge.logic.getCurrentScene().addObject('ball')
  19. b.worldPosition = x.loc
  20. b.color = [0,0,1,1]
  21. b.worldScale = [.5, .5, .5]
  22. def draw_searched(self):
  23. pass
  24. def draw_se(self):
  25. b = bge.logic.getCurrentScene().addObject('ball')
  26. b.worldPosition = self.start.loc
  27. b.color = [0,1,0,1]
  28. b.worldScale = [.75, .75, .75]
  29. #----
  30. b = bge.logic.getCurrentScene().addObject('ball')
  31. b.worldPosition = self.end.loc
  32. b.color = [1,1,0,1]
  33. b.worldScale = [2, 2, 2]
  34. def draw_all(self):
  35. for x in self.points:
  36. pm = bge.logic.getCurrentScene().addObject('path_marker')
  37. pm.worldPosition = x.loc
  38. pm.color = [1,1,0,1]
  39. pm.worldScale = [.25, .26, .25]
  40. class IterRegistry(type):
  41. def __iter__(cls):
  42. return iter(cls._registry)
  43. def load_points(points_file):
  44. #mainDir = bge.logic.expandPath("//")
  45. mainDir = bge.logic.expandPath("//assets/")
  46. fileName = mainDir + points_file
  47. with open(fileName, 'r') as filehandle:
  48. return json.load(filehandle)
  49. def build_tree(points):
  50. kd = mathutils.kdtree.KDTree(len(points))
  51. id = 0
  52. for p in points:
  53. p = mathutils.Vector(p[0])
  54. kd.insert(p, id)
  55. id += 1
  56. kd.balance()
  57. return kd
  58. def get_distance(start, end):
  59. length = (end - start).magnitude
  60. return length
  61. class Point(object):
  62. __metaclass__ = IterRegistry
  63. _registry = []
  64. def __init__(self, list_point, parent, iter):
  65. self.l = list_point[0]
  66. self.id = iter
  67. self.parent = parent
  68. self.loc = mathutils.Vector(list_point[0])
  69. self.lneighbors = list_point[1]
  70. self.neighbors = []
  71. self._registry.append(self)
  72. self.g = 100000
  73. self.h = 100000
  74. self.f = 100000
  75. self.prev = None
  76. def get_neighbors(self):
  77. for p in self.lneighbors:
  78. for p_ in self.parent.points:
  79. if p_.loc == mathutils.Vector(p):
  80. self.neighbors.append(p_)
  81. self.neighbors = set(self.neighbors)
  82. def init_points(self):
  83. points = []
  84. iter = 0
  85. for x in self.points_list:
  86. name = 'p' + str(iter)
  87. y = Point(x, self, iter)
  88. points.append(y)
  89. iter += 1
  90. return points
  91. class Astar:
  92. def __init__(self, points_file, messenger):
  93. self.state = 'idle'
  94. self.points_list = load_points(points_file)
  95. self.points = init_points(self)
  96. self.start = []
  97. self.end = []
  98. self.kdtree = build_tree(self.points_list)
  99. self.life = 0
  100. self.open = []
  101. self.closed = []
  102. self.searched = []
  103. self.current = []
  104. self.fscores = []
  105. self.gscores = []
  106. self.lps = []
  107. self.messenger = messenger
  108. self.queue = []
  109. self.current_searcher = None
  110. for x in self.points: x.get_neighbors()
  111. def update(self):
  112. self.life += 1
  113. if self.state == 'searching':
  114. self.search()
  115. #print('life', self.life)
  116. if self.life > 5000:
  117. #print('path failed')
  118. self.state = 'idle'
  119. else:
  120. #print('updating pathfinder queue')
  121. self.update_queue()
  122. def get_results(self):
  123. result_locs = []
  124. results = []
  125. #print('self.end', self.end, self.end.id)
  126. cp = self.end
  127. breaker = 0
  128. while cp != self.start:
  129. result_locs.append(cp.id)
  130. results.append(cp.loc)
  131. if cp.prev == None:
  132. cp = self.current
  133. else:
  134. cp = cp.prev
  135. breaker += 1
  136. if breaker > 2000:
  137. #print('breaking results')
  138. break
  139. #if cp == self.start:
  140. #print('start found!!')
  141. #print('sp', self.start.id, 'ep', self.end.id)
  142. #print('final path', result_locs, len(result_locs))
  143. #draw_mesh(self)
  144. #draw(result_locs, self)
  145. #publish
  146. results.reverse()
  147. self.messenger.dispatch('path found', ['found path for object', self.current_searcher, results])
  148. #draw_all(self)
  149. def smallest_f(self):
  150. if len(self.open) > 1:
  151. l = []
  152. lfs = []
  153. for x in self.open:
  154. l.append(x)
  155. lfs.append(x.f)
  156. ind = lfs.index(min(lfs))
  157. #print(ind, lfs, l[ind])
  158. return l[ind]
  159. else:
  160. return self.start
  161. #@chrono
  162. def search(self):
  163. #Loop until you find the end
  164. tries = 0
  165. if self.open == []:
  166. draw_mesh(self)
  167. self.state = 'inactive'
  168. while self.open != []:
  169. #Get the current node from open list with smallest fval
  170. try:
  171. self.open.remove(self.current)
  172. except:
  173. pass
  174. #print('current is not in open, cant remove')
  175. current_node = self.smallest_f()
  176. self.current = current_node
  177. self.searched.append(current_node)
  178. #remove from open
  179. self.closed.append(current_node) #add to closed
  180. if current_node == self.end:
  181. #print('path found')
  182. path = self.get_results()
  183. #print('p', path)
  184. self.state = 'inactive'
  185. self.open = []
  186. break
  187. else:
  188. children = current_node.neighbors
  189. for child in children:
  190. self.searched.append(child)
  191. p = current_node
  192. g = get_distance(current_node.loc, child.loc) + current_node.g
  193. h = get_distance(child.loc, self.end.loc)
  194. f = g + h
  195. if child not in self.open and child not in self.closed:
  196. if g >= child.g:
  197. pass
  198. else:
  199. child.g = g
  200. child.h = h
  201. child.f = f
  202. child.prev = p
  203. self.open[:] = [x for x in self.open if x != child]
  204. self.closed[:] = [x for x in self.closed if x != child]
  205. self.open.append(child)
  206. # print('trying', current_node.id, current_node.f, len(self.open))
  207. # nei = []
  208. # for z in current_node.neighbors:
  209. # nei.append(z.id)
  210. tries += 1
  211. if tries >= 5:
  212. break
  213. def get_neighbor_id(self, id):
  214. iter = 0
  215. for p in self.points:
  216. if p[0] == id:
  217. num = iter
  218. else:
  219. pass
  220. #print('cant get navmesh neighbor id')
  221. iter += 1
  222. #print(num, 'id')
  223. def set_gscore(self):
  224. self.gscores = []
  225. self.fscores = []
  226. self.lps = []
  227. for x in self.points:
  228. self.gscores.append(100000)
  229. self.fscores.append(100000)
  230. self.lps.append(None)
  231. x.f = 100000
  232. x.h = 100000
  233. x.g = 100000
  234. def set_fscore(self):
  235. self.fscores = []
  236. for x in self.points:
  237. self.fscores.append(100000)
  238. def queue_path(self, start_in, end_in, obj):
  239. self.queue.append([start_in, end_in, obj])
  240. #obj.manager.pub.register("path found", obj)
  241. #print('queueing path', len(self.queue))
  242. def update_queue(self):
  243. if self.queue != []:
  244. #print('--getting next path in queue')
  245. self.get_path(self.queue[0][0], self.queue[0][1], self.queue[0][2])
  246. self.queue.remove(self.queue[0])
  247. def get_path(self, start_in, end_in, obj):
  248. stree = self.kdtree.find_n(start_in, 1)
  249. start_out = self.points[stree[0][1]]
  250. otree = self.kdtree.find_n(end_in, 1)
  251. end_out = self.points[otree[0][1]]
  252. self.life = 0
  253. self.current_searcher = obj
  254. self.start = start_out
  255. self.end = end_out
  256. self.state = 'searching'
  257. self.open.append(self.start)
  258. self.set_gscore()
  259. self.current = self.start
  260. #beginning search
  261. #Initialize both open and closed list
  262. self.open = []
  263. self.closed = []
  264. self.searched = []
  265. #Add the start node
  266. self.open.append(self.start)
  267. self.current = self.start
  268. self.current.g = 0
  269. self.current.f = 0
  270. self.current.h = get_distance(self.current.loc, self.end.loc)
  271. #draw_se(self)
  272. print('start', self.start.id, 'end', self.end.id)
  273. # def main(cont):
  274. # own = cont.owner
  275. # if 'inited' not in own:
  276. # own['inited'] = True
  277. # own['ped_astar'] = Astar('nav_points')
  278. # own['ped_astar'].get_path([5,-12,1] , [16,16,1])
  279. # own['ped_astar'].update()