旧关卡red转json.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import json
  2. import math
  3. import xml.etree.ElementTree as ET
  4. import re
  5. import os
  6. from logging import setLoggerClass
  7. # 使用python3.x
  8. # 2024-11-01 盘子按层分组,每层可以有一个或者多个盘子,一个CCNode就表示一层。
  9. # 钉子的图片名-ID映射表
  10. #screw_pic_to_id_map = {"sg_screw1.png":1,"sg_screw2.png":2,"sg_screw3.png":3}
  11. # 盘子的图片名-ID映射表
  12. #plate_pic_to_id_map = {"sg_plant1.png":1,"sg_plant0.png":2}
  13. # 目标盒子的图片名-ID映射表
  14. #target_pic_to_id_map = {"sg-目标容器-1.png":1,"sg-目标容器-2.png":2}
  15. def xml_to_dict(root: ET.Element):
  16. """将XML文件解析为python dict"""
  17. ret_arr = []
  18. ret_dict = {}
  19. if root.tag == "dict":
  20. idx = 0
  21. while idx < len(root):
  22. if root[idx].tag == "key":
  23. ret_dict[root[idx].text] = xml_to_dict(root[idx + 1])
  24. idx += 1
  25. idx += 1
  26. return ret_dict
  27. elif root.tag == "array":
  28. idx = 0
  29. while idx < len(root):
  30. ret_arr.append(xml_to_dict(root[idx]))
  31. idx += 1
  32. return ret_arr
  33. elif root.tag == "plist":
  34. for child in root:
  35. ret_dict["plist"] = xml_to_dict(child)
  36. break
  37. return ret_dict
  38. else:
  39. return root.text
  40. def get_ccnode_list(conf_dict: dict):
  41. """从node_graph字典下获取target(任务目标)和element(元素)的list"""
  42. node_graph_dict = conf_dict["plist"]["nodeGraph"]["children"]
  43. return node_graph_dict[0]["children"]
  44. def get_all_target(target_list: list):
  45. """获取所有target的list"""
  46. all_target_list = []
  47. for dic in target_list:
  48. propertieIdx = 0
  49. while True:
  50. propertie = dic["properties"][propertieIdx]
  51. if propertie["name"] == "displayFrame":
  52. displayFrame = propertie["value"][1]
  53. break
  54. propertieIdx = propertieIdx+1
  55. displayFrame = os.path.basename(displayFrame)
  56. resTagrgetId = target_pic_to_id_map[displayFrame]
  57. screwIds = []
  58. for target_dict in dic["children"]:
  59. # 展示的目标名称可能和采用的图片不一样 所以先不用这个
  60. # display_name_str = target_dict["displayName"]
  61. target_properties_list = target_dict["properties"]
  62. propertieIdx = 0
  63. while True:
  64. propertie = target_properties_list[propertieIdx]
  65. if propertie["name"] == "displayFrame":
  66. target_pic_name = propertie["value"][1]
  67. break
  68. propertieIdx = propertieIdx+1
  69. target_pic_name = os.path.basename(target_pic_name)
  70. # print (target_pic_name)
  71. if target_pic_name not in screw_pic_to_id_map.keys():
  72. screw_pic_to_id_map[target_pic_name] = len(screw_pic_to_id_map) + 1
  73. screwIds.append(screw_pic_to_id_map[target_pic_name])
  74. all_target_list.append({"targetResId":resTagrgetId, "screwIds":screwIds})
  75. return all_target_list
  76. def get_node_position( elem ):
  77. for ppt in elem["properties"]:
  78. if( ppt["name"] == "position"):
  79. return ppt["value"][0],ppt["value"][1]
  80. return 0,0
  81. def is_point_in_plane(point, plane):
  82. a, b, c = plane
  83. x, y = point
  84. return a * x + b * y + c > 0
  85. def is_intersect(p, a, b):
  86. px, py = p
  87. x1, y1 = a
  88. x2, y2 = b
  89. if (py < y1 and py < y2) or (py > y1 and py > y2):
  90. return False
  91. if px > max(x1, x2):
  92. return False
  93. if x1 == x2:
  94. return True
  95. k = (y2 - y1) / (x2 - x1)
  96. x = (py - y1) / k + x1
  97. if x > px:
  98. return True
  99. else:
  100. return False
  101. # 判断点是否落在多边形内部
  102. def isPointInsidePolygon(point , polygon_points):
  103. intersect_cnt = 0
  104. for i in range(len(polygon_points)-1) :
  105. p = polygon_points[i]
  106. next_p = polygon_points[i+1]
  107. if is_intersect(point, p, next_p):
  108. intersect_cnt += 1
  109. return intersect_cnt % 2 == 1
  110. # 读取盘子轮廓多边形点数据
  111. def readPlatePolygonPoints( jsonFilename ):
  112. points = []
  113. with open(jsonFilename,'r') as json_file:
  114. points = json.load(json_file)
  115. return points
  116. # 单点坐标变换
  117. def pointTransform(point, scalex, scaley, parent_posi , rotate_deg ):
  118. radians = - rotate_deg * math.pi / 180
  119. point[0] = point[0] * scalex
  120. point[1] = point[1] * scaley
  121. tempx = point[0]*math.cos(radians) - point[1]*math.sin(radians)
  122. tempy = point[0]*math.sin(radians) + point[1]*math.cos(radians)
  123. point[0] = tempx + parent_posi[0]
  124. point[1] = tempy + parent_posi[1]
  125. return point
  126. # 全部点坐标变换
  127. def pointsTransform( points, scalex, scaley, parent_posi , rotate_deg ):
  128. new_points = []
  129. for pt in points:
  130. pt = pointTransform(pt,scalex,scaley,parent_posi , rotate_deg)
  131. new_points.append(pt)
  132. return new_points
  133. def get_plate_data_list(elem_list: list):
  134. """获取盘子信息列表"""
  135. layers_data = []
  136. plateId = 1
  137. screwId = 1
  138. # for each layer
  139. for dic in elem_list:
  140. # dic 为一个 CCNode
  141. plates_data = []
  142. screws_data = []
  143. ccnodex, ccnodey = get_node_position( dic )
  144. ccnodex = float(ccnodex)
  145. ccnodey = float(ccnodey)
  146. # for each srew and plate
  147. for elem_dict in dic["children"]:
  148. # elem_dict 为一个钉子或者盘子
  149. # CCNode 下面盘子和钉子由关卡编辑人员摆放,不在人工添加层级信息,
  150. # 由程序负责判断钉子归宿某个盘子,如果钉子不属于任何盘子那么丢弃。
  151. if elem_dict["displayName"] == "盘子":
  152. plate_property = elem_dict["properties"]
  153. # 处理盘子的信息
  154. one_plate_data = {}
  155. one_plate_data["rotate"] = 0.0
  156. one_plate_data["scale_x"] = 1.0
  157. one_plate_data["scale_y"] = 1.0
  158. spriteFrameName = ""
  159. for ele in plate_property:
  160. dict_values = ele.values()
  161. if "displayFrame" in dict_values:
  162. spriteFrameName = ele["value"][1]
  163. one_plate_data["sprite_frame_name"] = spriteFrameName # new added.
  164. elif "rotation" in dict_values:
  165. one_plate_data["rotate"] = round(float(ele["value"]), 1)
  166. elif "scale" in dict_values:
  167. one_plate_data["scale"] = round(float(ele["value"][0]), 1) # deprecated
  168. one_plate_data["scale_x"] = round(float(ele["value"][0]), 1)
  169. one_plate_data["scale_y"] = round(float(ele["value"][1]), 1)
  170. elif "position" in dict_values:
  171. one_plate_data["position"] = [round(ccnodex + float(ele["value"][0])),
  172. round(ccnodey + float(ele["value"][1]), 1)]
  173. # print("plate:"+spriteFrameName)
  174. one_plate_data["plateId"] = plateId # this is useless.
  175. plateId += 1
  176. one_plate_data["zorder"] = len(plates_data) + 1 # the plates appeared in plist are in bottom first order.
  177. plates_data.append(one_plate_data)
  178. else:
  179. # 钉子
  180. screw_property = elem_dict["properties"]
  181. one_screw_dict = {}
  182. one_screw_dict["rotate"] = 0.0
  183. one_screw_dict["scale_x"] = 1.0
  184. one_screw_dict["scale_y"] = 1.0
  185. for ele in screw_property:
  186. dict_values = ele.values()
  187. if "displayFrame" in dict_values:
  188. spriteFrameName = ele["value"][1]
  189. one_screw_dict["sprite_frame_name"] = spriteFrameName # new added.
  190. elif "rotation" in dict_values:
  191. one_screw_dict["rotate"] = round(float(ele["value"]), 1)
  192. elif "scale" in dict_values:
  193. one_screw_dict["scale"] = round(float(ele["value"][0]), 1)
  194. one_screw_dict["scale_x"] = round(float(ele["value"][0]), 1)
  195. one_screw_dict["scale_y"] = round(float(ele["value"][1]), 1)
  196. elif "position" in dict_values:
  197. one_screw_dict["position"] = [round(ccnodex + float(ele["value"][0])),
  198. round(ccnodey + float(ele["value"][1]), 1)]
  199. # print(" --> screw:" + one_screw_dict["sprite_frame_name"])
  200. one_screw_dict["screwId"] = screwId # this is useless.
  201. screwId=screwId+1
  202. screws_data.append(one_screw_dict)
  203. #end if...else...
  204. # endfor each screw and plate
  205. # 判断钉子属于哪个盘子,然后把钉子放到盘子里
  206. for plateX in plates_data:
  207. plateX["screws"] = []
  208. # read polygon points
  209. plate_points =readPlatePolygonPoints( "../轮廓/sg_盘子轮廓/" + plateX["sprite_frame_name"] + ".json" ) # read from file
  210. plate_points = pointsTransform(plate_points, plateX["scale_x"], plateX["scale_y"], plateX["position"] , plateX["rotate"] )
  211. for screwX in screws_data:
  212. screw_posi = screwX["position"]
  213. if( isPointInsidePolygon(screw_posi, plate_points) ):
  214. plateX["screws"].append( screwX )
  215. print( "debug lyr ", len(layers_data) + 1, "-> plate ", plateX["plateId"] , "--> screw " , screwX["screwId"] )
  216. # 构建一个层对象
  217. layer1 = {}
  218. layer1["layerId"] = len(layers_data) + 1
  219. layer1["plates"] = plates_data
  220. layers_data.append(layer1)
  221. # endfor each layer
  222. return layers_data
  223. def py_dict_to_json_file(py_dict: dict, json_path: str):
  224. """将python dict写到JSON文件中"""
  225. #file_name = re.findall(r"(?=/).*(?=.red)", xml_path)[0]
  226. #file_name = re.findall(r"[^/]+$", file_name)[0]
  227. #file_name += ".json"
  228. j_data = json.dumps(py_dict)
  229. #jsonFilename="../config/" + file_name
  230. print("output: " + json_path)
  231. with open(json_path, "w") as file:
  232. file.write(j_data)
  233. if __name__ == "__main__":
  234. # 遍历关卡目录(../关卡)下全部 *关卡*.red 文件,将其转换为json格式,存储在 ../config 目录下。
  235. for (root,dirs,files) in os.walk('../关卡',topdown=True):
  236. #print("Directory path: %s"%root)
  237. #print("Directory Names: %s"%dirs)
  238. #print("Files Names: %s"%files)
  239. for fname in files:
  240. if( "关卡" in fname and ".red" in fname and ( "测试" in fname or "old" in fname ) ):
  241. jsonName = fname.replace(".red",".json")
  242. xml_path = root+"/"+fname
  243. out_json_path = "../config/"+jsonName
  244. print("input: "+xml_path)
  245. element_tree = ET.parse(xml_path)
  246. conf_dict = xml_to_dict(element_tree.getroot())
  247. ccnode_list = get_ccnode_list(conf_dict)
  248. target_and_plateData_dict = {
  249. "target": {},
  250. "layerData": get_plate_data_list(ccnode_list)
  251. }
  252. py_dict_to_json_file(target_and_plateData_dict, out_json_path)