You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame从瓦片集blit子表面渲染瓦片地图出现异常

Pygame瓦片地图渲染错位问题

实现逻辑

  • 瓦片集加载:读取.png格式瓦片图源,切割后将所有独立瓦片纹理存入tileset.tiles列表
  • 地图存储:使用三层.csv文件存储瓦片排布信息,每个网格位置对应一个瓦片纹理id
  • 渲染优化:加载房间时将三层瓦片一次性烘焙到map表面,游戏主循环每帧直接blit该表面到屏幕,避免逐瓦片渲染的性能开销

异常现象

  • 瓦片无法正确绘制到目标表面:Room类load方法第二次blit同一子表面时触发异常,初始排查未定位到直接诱因
  • 渲染结果受csv排布影响:任意调整csv内的瓦片编号(包括跨层调整)都会导致最终渲染结果错乱,出现随机错贴瓦片的情况;测试用例中id=8的瓦片为天空蓝色方块,其余id对应不同地形、装饰纹理
  • 局部修改无对应反馈:修改中层第一个瓦片(id=0为花朵纹理)时渲染结果无变化;修改底层前几个瓦片同样出现渲染错位
  • 已排除瓦片集类故障:绕过load方法直接从Tileset实例提取子表面,可在屏幕任意位置正常绘制瓦片

排查留存素材

  • 三层csv排布文件:底层、中层、顶层瓦片配置
  • 异常渲染结果截图
  • 中层瓦片调整后的配置文件、对应渲染结果截图
  • 底层瓦片调整后的配置文件、对应渲染结果截图

复现代码

import pygame as pg

class Tileset:

    def __init__(self,img:pg.Surface):
        self.tiles = []
        self.img = img
        self.loadtiles()

    def loadtiles(self):
        for i in range(16):
            for n in range(16):
                currentimg = self.img.subsurface(32*n,32*i,32,32)
                self.tiles.append(currentimg.copy())

class Tile(pg.sprite.Sprite):

    def __init__(self,image:pg.Surface,position:tuple):
        self.img = image
        self.pos = position

    def draw (self,surface:pg.Surface): 
        surface.blit(self.img,self.pos)


class Room:
    def __init__(self,id,size:tuple):
        self.id = id
        self.layers = [[],[],[]]
        self.size = (size[0]*32,size[1]*32)


    # 加载新房间时调用:读取房间csv,存储瓦片信息,烘焙新的地图表面
    def load(self,map):

        map = pg.Surface(self.size)

        for layer in self.layers:
            with open ('levels/final/room'+str(self.id)+'_'+str(self.layers.index(layer))+'.csv') as file:
                data = file.readlines()

            for rrrow in data:                           # 原始行数据
                rrow = rrrow.strip('\n').split(',')      # 半处理行数据
                row = []                                 # 处理完成的行数据

                for rtile in rrow:                       # 行内未处理瓦片

                    if rtile != -1:

                        tileimg = tileset.tiles[int(rtile)]     
                        tile = Tile(tileimg,(rrow.index(rtile)*32, data.index(rrrow)*32))   # 生成瓦片实例
                        tile.draw(map)                                                      # 绘制瓦片到当前地图表面
                        row.append(tile)                                                    # 将瓦片存入行列表

                layer.append(row)                                                       # 将行存入层列表,后续用于碰撞检测

        return map
        

# pygame主循环初始化(非完整代码,不影响问题复现)
res  = (32*30,32*20)
scr = pg.display.set_mode(res)
tileset = Tileset(pg.image.load('graphics\stock.png'))
room0 = Room(0,(30,20))
map = pg.Surface((0,0))
map = room0.load(map)
running  = True

while running:

    scr.fill((0,0,0))

    scr.blit(map,(0,0))
    pg.display.flip()

故障原因

代码中四处逻辑错误直接导致渲染异常:

  1. 层索引获取错误:遍历self.layers时用self.layers.index(layer)拼接csv文件名,初始化时三个层均为空列表,index()方法永远返回第一个匹配空列表的索引0,导致中层、顶层加载时全部读取底层csv文件,三层内容完全重复,修改中层文件自然不会有渲染变化
  2. 瓦片坐标计算错误:用rrow.index(rtile)计算瓦片x坐标、data.index(rrrow)计算瓦片y坐标,list.index()只会返回列表中第一个匹配值的索引:同一行存在多个相同id的瓦片时,所有同id瓦片的x坐标都会被错误设置为第一个同id瓦片的位置;csv中存在内容完全相同的行时,所有重复行的y坐标都会被错误设置为第一个匹配行的位置,直接导致瓦片错位
  3. 空瓦片判断逻辑失效:从csv读取的rtile是字符串类型,和整数-1比较永远为真,会尝试将空字符串/字符串格式的"-1"转为整数索引瓦片列表,容易触发索引越界错误
  4. Windows路径转义风险:graphics\stock.png中的\s会被Python识别为转义字符,可能导致瓦片集图片加载失败

修复方案

  • 遍历层、行、列时统一使用enumerate()直接获取索引值,禁止用list.index()方法查找位置,避免重复值导致的索引错误
  • 修正空瓦片判断逻辑,先把读取到的字符串转为整数,再判断是否为空瓦片(约定空瓦片id为-1)
  • 修正文件路径写法,使用raw字符串或者正斜杠规避转义字符问题
    修复后的load方法核心逻辑参考:
def load(self,map):
    map = pg.Surface(self.size)
    # 按层索引遍历,避免用list.index找层位置
    for layer_idx in range(len(self.layers)):
        with open(f'levels/final/room{self.id}_{layer_idx}.csv') as file:
            data = file.readlines()
        # 直接拿行号作为y坐标索引
        for y_idx, rrrow in enumerate(data):
            rrow = rrrow.strip('\n').split(',')
            row = []
            # 直接拿列号作为x坐标索引
            for x_idx, rtile in enumerate(rrow):
                tile_id = int(rtile)
                # 正确判断空瓦片
                if tile_id == -1:
                    continue
                tileimg = tileset.tiles[tile_id]
                tile = Tile(tileimg, (x_idx*32, y_idx*32))
                tile.draw(map)
                row.append(tile)
            self.layers[layer_idx].append(row)
    return map

同时把瓦片集加载路径改成pg.image.load(r'graphics\stock.png')或者pg.image.load('graphics/stock.png')即可。

内容的提问来源于stack exchange,提问作者royalmk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:33:16