如何在高效viewport滚动中正确适配右侧菜单,避免全量Blit?
优化平滑滚动时右侧菜单的绘制问题
我写了一个函数实现平滑滚动,不想每帧全量blit整个屏幕。这个函数通过blit滚动到viewport(位于(0,0),尺寸640×640像素)内的右侧菜单部分来做补偿。现在用的Surface是680×680的,出于性能考虑不能每帧都blit整个viewport,请问怎么正确处理右侧菜单?
用户提供的原始代码:
def optimized_blit(source, dest, position,frametime,xt=0,xtd=0): global phash,phashx # # if viewportx.__class__.__name__ == 'str': # viewportx = viewport.Viewport(position[0],position[1],source,dest) # viewportx.sc(position[0],position[1]) if (frametime%4)==1 or xt==1: source.set_alpha(255) #dest.blit(source,position) phash = position dest.blit(source,position) elif not xt==1: if not str(phash) == str(position): #new_position = ( phash[0]-position[0] , position[1] - phash[1]) #visible_width = min(680 - new_position[0], 680) #ufr = 640-(position[0]-phash[0]) #sca = position[0] - phash[0] x_p = min((640-position[0])-5,679) y_p = 0 rect = (x_p,y_p,5,640) dest.scroll(position[0] - phash[0], position[1] - phash[1]) print(rect) #print if x_p>0 and y_p>0: #time.sleep(0.1) print(rect) dest.blit(source.subsurface(rect),(635,0)) print() phash = position xtract = [640-position[0],680-phash[0]] #print(xtract) #print(phash) #positionx = [int(i) for i in position] #if not phas if xtd: phashx = position phash = position #phash = position #print(position) dest.blit(source,position) #viewportx.draw(dest)
问题分析与修正方案
你的核心思路没问题:用dest.scroll()移动已有内容,只补画新露出的区域来节省性能。但当前实现存在滚动方向搞反、区域计算错误、无效条件判断等问题,修正后可以正确处理右侧菜单:
关键修正点
修正滚动方向
pygame的Surface.scroll(dx, dy)中,dx为正代表内容向左移动(右侧留空),dx为负代表内容向右移动(左侧留空)。你的代码里滚动方向和position偏移逻辑相反,需要调整为dest.scroll(-dx, -dy)(dx是当前position与上一帧position的差值)。准确计算补画区域
右侧菜单是source中x从640到680的40像素宽度区域,向左滚动时,viewport右侧会露出新的菜单内容,需要截取对应宽度的菜单区域补画到dest右侧;向右滚动时则补画source左侧区域。去掉无效判断
不要用str(phash) == str(position)比较位置,直接用元组相等判断phash != position更高效;同时删除x_p>0 and y_p>0的错误条件(菜单y从0开始,y_p=0时应该正常绘制)。
修正后的完整代码
def optimized_blit(source, dest, position, frametime, xt=0, xtd=0): global phash, phashx # 初始化上一帧位置(第一次调用时) if 'phash' not in globals(): phash = position # 全量绘制触发条件:定时刷新、强制刷新标记、xtd标记 if (frametime % 4) == 1 or xt == 1 or xtd: phash = position dest.blit(source, position) if xtd: phashx = position return # 位置未变化时无需操作 if phash == position: return # 计算滚动偏移量 dx = position[0] - phash[0] dy = position[1] - phash[1] # 滚动已有内容,方向与position偏移相反 dest.scroll(-dx, -dy) # 处理水平滚动的补画(重点处理右侧菜单) if dx < 0: # 向左滚动:补画source右侧的菜单区域 blit_width = abs(dx) # 限制补画宽度不超过菜单总宽度40px blit_width = min(blit_width, 40) # 截取source中对应的菜单区域 menu_subsurface = source.subsurface((640, 0, blit_width, 640)) # 补画到viewport右侧的对应位置 dest.blit(menu_subsurface, (640 - blit_width, 0)) elif dx > 0: # 向右滚动:补画source左侧的内容区域(如果需要) blit_width = dx left_subsurface = source.subsurface((0, 0, blit_width, 640)) dest.blit(left_subsurface, (0, 0)) # 处理垂直滚动的补画(可选,根据需求调整) if dy < 0: blit_height = abs(dy) blit_height = min(blit_height, 40) bottom_subsurface = source.subsurface((0, 640, 640, blit_height)) dest.blit(bottom_subsurface, (0, 640 - blit_height)) elif dy > 0: blit_height = dy top_subsurface = source.subsurface((0, 0, 640, blit_height)) dest.blit(top_subsurface, (0, 0)) # 更新上一帧位置 phash = position
额外优化建议
- 避免使用全局变量
phash,可以将其封装为类属性或函数的默认参数(如phash=None),减少副作用。 - 提前缓存右侧菜单Surface:
menu_surface = source.subsurface((640, 0, 40, 640)),每次补画直接使用缓存的Surface,避免重复截取subsurface。 - 当滚动偏移量超过菜单宽度(40px)时,触发一次全量blit,避免多次补画累积显示错误。
内容的提问来源于stack exchange,提问作者internet incorporated
相关产品推荐
相关产品推荐

