import json import math import xml.etree.ElementTree as ET import re import os from logging import setLoggerClass # 使用python3.x # 2024-11-01 盘子按层分组,每层可以有一个或者多个盘子,一个CCNode就表示一层。 # 钉子的图片名-ID映射表 #screw_pic_to_id_map = {"sg_screw1.png":1,"sg_screw2.png":2,"sg_screw3.png":3} # 盘子的图片名-ID映射表 #plate_pic_to_id_map = {"sg_plant1.png":1,"sg_plant0.png":2} # 目标盒子的图片名-ID映射表 #target_pic_to_id_map = {"sg-目标容器-1.png":1,"sg-目标容器-2.png":2} def xml_to_dict(root: ET.Element): """将XML文件解析为python dict""" ret_arr = [] ret_dict = {} if root.tag == "dict": idx = 0 while idx < len(root): if root[idx].tag == "key": ret_dict[root[idx].text] = xml_to_dict(root[idx + 1]) idx += 1 idx += 1 return ret_dict elif root.tag == "array": idx = 0 while idx < len(root): ret_arr.append(xml_to_dict(root[idx])) idx += 1 return ret_arr elif root.tag == "plist": for child in root: ret_dict["plist"] = xml_to_dict(child) break return ret_dict else: return root.text def get_ccnode_list(conf_dict: dict): """从node_graph字典下获取target(任务目标)和element(元素)的list""" node_graph_dict = conf_dict["plist"]["nodeGraph"]["children"] return node_graph_dict[0]["children"] def get_all_target(target_list: list): """获取所有target的list""" all_target_list = [] for dic in target_list: propertieIdx = 0 while True: propertie = dic["properties"][propertieIdx] if propertie["name"] == "displayFrame": displayFrame = propertie["value"][1] break propertieIdx = propertieIdx+1 displayFrame = os.path.basename(displayFrame) resTagrgetId = target_pic_to_id_map[displayFrame] screwIds = [] for target_dict in dic["children"]: # 展示的目标名称可能和采用的图片不一样 所以先不用这个 # display_name_str = target_dict["displayName"] target_properties_list = target_dict["properties"] propertieIdx = 0 while True: propertie = target_properties_list[propertieIdx] if propertie["name"] == "displayFrame": target_pic_name = propertie["value"][1] break propertieIdx = propertieIdx+1 target_pic_name = os.path.basename(target_pic_name) # print (target_pic_name) if target_pic_name not in screw_pic_to_id_map.keys(): screw_pic_to_id_map[target_pic_name] = len(screw_pic_to_id_map) + 1 screwIds.append(screw_pic_to_id_map[target_pic_name]) all_target_list.append({"targetResId":resTagrgetId, "screwIds":screwIds}) return all_target_list def get_node_position( elem ): for ppt in elem["properties"]: if( ppt["name"] == "position"): return ppt["value"][0],ppt["value"][1] return 0,0 def is_point_in_plane(point, plane): a, b, c = plane x, y = point return a * x + b * y + c > 0 def is_intersect(p, a, b): px, py = p x1, y1 = a x2, y2 = b if (py < y1 and py < y2) or (py > y1 and py > y2): return False if px > max(x1, x2): return False if x1 == x2: return True k = (y2 - y1) / (x2 - x1) x = (py - y1) / k + x1 if x > px: return True else: return False # 判断点是否落在多边形内部 def isPointInsidePolygon(point , polygon_points): intersect_cnt = 0 for i in range(len(polygon_points)-1) : p = polygon_points[i] next_p = polygon_points[i+1] if is_intersect(point, p, next_p): intersect_cnt += 1 return intersect_cnt % 2 == 1 # 读取盘子轮廓多边形点数据 def readPlatePolygonPoints( jsonFilename ): points = [] with open(jsonFilename,'r') as json_file: points = json.load(json_file) return points # 单点坐标变换 def pointTransform(point, scalex, scaley, parent_posi , rotate_deg ): radians = - rotate_deg * math.pi / 180 point[0] = point[0] * scalex point[1] = point[1] * scaley tempx = point[0]*math.cos(radians) - point[1]*math.sin(radians) tempy = point[0]*math.sin(radians) + point[1]*math.cos(radians) point[0] = tempx + parent_posi[0] point[1] = tempy + parent_posi[1] return point # 全部点坐标变换 def pointsTransform( points, scalex, scaley, parent_posi , rotate_deg ): new_points = [] for pt in points: pt = pointTransform(pt,scalex,scaley,parent_posi , rotate_deg) new_points.append(pt) return new_points def get_plate_data_list(elem_list: list): """获取盘子信息列表""" layers_data = [] plateId = 1 screwId = 1 # for each layer for dic in elem_list: # dic 为一个 CCNode plates_data = [] screws_data = [] ccnodex, ccnodey = get_node_position( dic ) ccnodex = float(ccnodex) ccnodey = float(ccnodey) # for each srew and plate for elem_dict in dic["children"]: # elem_dict 为一个钉子或者盘子 # CCNode 下面盘子和钉子由关卡编辑人员摆放,不在人工添加层级信息, # 由程序负责判断钉子归宿某个盘子,如果钉子不属于任何盘子那么丢弃。 if elem_dict["displayName"] == "盘子": plate_property = elem_dict["properties"] # 处理盘子的信息 one_plate_data = {} one_plate_data["rotate"] = 0.0 one_plate_data["scale_x"] = 1.0 one_plate_data["scale_y"] = 1.0 spriteFrameName = "" for ele in plate_property: dict_values = ele.values() if "displayFrame" in dict_values: spriteFrameName = ele["value"][1] one_plate_data["sprite_frame_name"] = spriteFrameName # new added. elif "rotation" in dict_values: one_plate_data["rotate"] = round(float(ele["value"]), 1) elif "scale" in dict_values: one_plate_data["scale"] = round(float(ele["value"][0]), 1) # deprecated one_plate_data["scale_x"] = round(float(ele["value"][0]), 1) one_plate_data["scale_y"] = round(float(ele["value"][1]), 1) elif "position" in dict_values: one_plate_data["position"] = [round(ccnodex + float(ele["value"][0])), round(ccnodey + float(ele["value"][1]), 1)] # print("plate:"+spriteFrameName) one_plate_data["plateId"] = plateId # this is useless. plateId += 1 one_plate_data["zorder"] = len(plates_data) + 1 # the plates appeared in plist are in bottom first order. plates_data.append(one_plate_data) else: # 钉子 screw_property = elem_dict["properties"] one_screw_dict = {} one_screw_dict["rotate"] = 0.0 one_screw_dict["scale_x"] = 1.0 one_screw_dict["scale_y"] = 1.0 for ele in screw_property: dict_values = ele.values() if "displayFrame" in dict_values: spriteFrameName = ele["value"][1] one_screw_dict["sprite_frame_name"] = spriteFrameName # new added. elif "rotation" in dict_values: one_screw_dict["rotate"] = round(float(ele["value"]), 1) elif "scale" in dict_values: one_screw_dict["scale"] = round(float(ele["value"][0]), 1) one_screw_dict["scale_x"] = round(float(ele["value"][0]), 1) one_screw_dict["scale_y"] = round(float(ele["value"][1]), 1) elif "position" in dict_values: one_screw_dict["position"] = [round(ccnodex + float(ele["value"][0])), round(ccnodey + float(ele["value"][1]), 1)] # print(" --> screw:" + one_screw_dict["sprite_frame_name"]) one_screw_dict["screwId"] = screwId # this is useless. screwId=screwId+1 screws_data.append(one_screw_dict) #end if...else... # endfor each screw and plate # 判断钉子属于哪个盘子,然后把钉子放到盘子里 for plateX in plates_data: plateX["screws"] = [] # read polygon points plate_points =readPlatePolygonPoints( "../轮廓/sg_盘子轮廓/" + plateX["sprite_frame_name"] + ".json" ) # read from file plate_points = pointsTransform(plate_points, plateX["scale_x"], plateX["scale_y"], plateX["position"] , plateX["rotate"] ) for screwX in screws_data: screw_posi = screwX["position"] if( isPointInsidePolygon(screw_posi, plate_points) ): plateX["screws"].append( screwX ) print( "debug lyr ", len(layers_data) + 1, "-> plate ", plateX["plateId"] , "--> screw " , screwX["screwId"] ) # 构建一个层对象 layer1 = {} layer1["layerId"] = len(layers_data) + 1 layer1["plates"] = plates_data layers_data.append(layer1) # endfor each layer return layers_data def py_dict_to_json_file(py_dict: dict, json_path: str): """将python dict写到JSON文件中""" #file_name = re.findall(r"(?=/).*(?=.red)", xml_path)[0] #file_name = re.findall(r"[^/]+$", file_name)[0] #file_name += ".json" j_data = json.dumps(py_dict) #jsonFilename="../config/" + file_name print("output: " + json_path) with open(json_path, "w") as file: file.write(j_data) if __name__ == "__main__": # 遍历关卡目录(../关卡)下全部 *关卡*.red 文件,将其转换为json格式,存储在 ../config 目录下。 for (root,dirs,files) in os.walk('../关卡',topdown=True): #print("Directory path: %s"%root) #print("Directory Names: %s"%dirs) #print("Files Names: %s"%files) for fname in files: if( "关卡" in fname and ".red" in fname and ( "测试" in fname or "old" in fname ) ): jsonName = fname.replace(".red",".json") xml_path = root+"/"+fname out_json_path = "../config/"+jsonName print("input: "+xml_path) element_tree = ET.parse(xml_path) conf_dict = xml_to_dict(element_tree.getroot()) ccnode_list = get_ccnode_list(conf_dict) target_and_plateData_dict = { "target": {}, "layerData": get_plate_data_list(ccnode_list) } py_dict_to_json_file(target_and_plateData_dict, out_json_path)