此文會透過GitHub@TankMan 坦克大作戰描述,如何透過讀取用Tiled這個軟體製作的地圖,來建立遊戲物件的程式碼
Tiled繪製時的畫面

遊戲讀取地圖後的畫面(草地是另外在遊戲的程式碼中,隨機產生的)

這是tmx檔案中的樣子
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,0,0,0,5,0,0,0,0,3,3,0,0,0,0,4,0,0,0,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,2,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,1,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,0,0,0,4,0,0,0,0,3,3,0,0,0,0,5,0,0,0,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
可以發現:
- 為1P玩家
- 為2P玩家
- 為牆壁
- 彈藥補給站
- 油料補給站
跟圖塊集的順序一樣
__init__()BattleMode透過將地圖路徑傳入TiledMap,建立TiledMap的實例
class BattleMode:
	def __init__(self, is_manual: bool, map_path: str, frame_limit: int, sound_path: str, play_rect_area: pygame.Rect):
        self.map = TiledMap(map_path)
TiledMap透過pytmx這個套件裡的TiledMap類別,傳入地圖路徑,建立地圖物件
class TiledMap:
    def __init__(self, filepath: str):
        self.tm = pytmx.TiledMap(filepath)
即可透過地圖物件,獲取地圖資訊
class TiledMap:
    def __init__(self, filepath: str):
        self.tile_width = tm.tilewidth
        self.tile_height = tm.tileheight
        self.width = tm.width
        self.height = tm.height
        self.map_width = self.tile_width * self.width
        self.map_height = self.tile_height * self.height
tm.tilewidth 獲取地圖上每格(tile)的寬(px),例:50 pxtm.width 獲取地圖有幾格(tile)寬,例:20格self.map_width  為地圖的寬(px)= 每格的寬 * 共有幾格寬,例: 1000 px = 50 px * 20格BattleMode呼叫TiledMap的add_init_obj_data(image_id, class_name, obj_other_params)函式,傳入初始化物件的資料add_init_obj_data()遊戲呼叫地圖的添加初始化物件資料的函式
class BattleMode:
	def __init__(self, is_manual: bool, map_path: str, frame_limit: int, sound_path: str, play_rect_area: pygame.Rect):
        # init obj data
        self.map.add_init_obj_data(PLAYER_1_IMG_NO, Player, act_cd=act_cd, play_rect_area=self.play_rect_area)
        self.map.add_init_obj_data(PLAYER_2_IMG_NO, Player, act_cd=act_cd, play_rect_area=self.play_rect_area)
        self.map.add_init_obj_data(WALL_IMG_NO, Wall, margin=8, spacing=8)
        self.map.add_init_obj_data(BULLET_STATION_IMG_NO, Station, margin=2, spacing=2, capacity=5, quadrant=1)
        self.map.add_init_obj_data(OIL_STATION_IMG_NO, Station, margin=2, spacing=2, capacity=30, quadrant=1)
將收到的物件初始化資料,以image_id為key,value為class_name和obj_other_params,更新到all_obj_data_dict儲存,並在all_obj初始化key為image_id的value為一空陣列
class TiledMap:
    def add_init_obj_data(self, img_id: int, cls, **kwargs):
        obj_data = {img_id: {"cls": cls,
                             "kwargs": kwargs
                             }
                    }
        self.all_obj_data_dict.update(obj_data)
        self.all_obj[img_id] = []
create_init_obj_dict()BattleMode呼叫地圖的create_init_obj_dict()函式,獲取TiledMap的有初始化資料的遊戲物件字典
class BattleMode:
	def __init__(self, is_manual: bool, map_path: str, frame_limit: int, sound_path: str, play_rect_area: pygame.Rect):
        # create obj
        all_obj = self.map.create_init_obj_dict()
TiledMap開始讀取地圖上的每一格,若有物件,則從all_obj_data_dict獲取初始化物件資料,打包地圖該格的資料,初始化物件後,儲存進all_obj[img_id]內,最後回傳以image_id為key,value為object_list的all_obj所有物件字典
class TiledMap:
    def create_init_obj_dict(self) -> dict:
        obj_no = 0
        for layer in self.tmx_data.visible_layers:
            for x, y, gid, in layer:
                if isinstance(layer, pytmx.TiledTileLayer):
                    pos = (x * self.tile_width, y * self.tile_height)
                    if gid:# 0代表空格,無圖塊
                        img_id = layer.parent.tiledgidmap[gid]
                        kwargs = self.all_obj_data_dict[img_id]["kwargs"]
                        obj_no += 1
                        img_info = {"_id": img_id, "_no": obj_no
                            , "_init_pos": pos
                            , "_init_size": (self.tile_width, self.tile_height)
                                    }
                        self.all_obj[img_id].append(self.all_obj_data_dict[img_id]["cls"](img_info, **kwargs))
        return self.all_obj
最後BattleMode透過all_obj,以image_id為key索引,即可獲取其value的已初始化完成的物件清單
class BattleMode:
	def __init__(self, is_manual: bool, map_path: str, frame_limit: int, sound_path: str, play_rect_area: pygame.Rect):
	    # init player
        self.player_1P = all_obj[PLAYER_1_IMG_NO][0]
        self.player_2P = all_obj[PLAYER_2_IMG_NO][0]
        # init walls
        self.walls.add(all_obj[WALL_IMG_NO])
        # init bullet stations
        self.bullet_stations.add(all_obj[BULLET_STATION_IMG_NO])
        # init oil stations
        self.oil_stations.add(all_obj[OIL_STATION_IMG_NO])
如何透過地圖物件,獲取遊戲物件的程式碼,完整程式碼請看@TankMan/src/BattleMode.py ,__init__函式的部分
如何讀取地圖,一次產生所有遊戲物件的程式碼,完整程式碼請看@TankMan/src/game_module/TiledMap.py