如何优化Pygame中带透明度的Surface以提升粒子爆炸效果运行性能
核心卡顿原因
你当前的性能瓶颈完全来自每帧重复创建Surface、重复执行旋转操作:Pygame的Surface实例创建、pygame.transform.rotate都是CPU密集型操作,当粒子数量上千时,这些重复操作会直接占满计算资源,导致帧率暴跌。
优化方案(改完帧率直接拉满)
1. 彻底废弃小Surface创建逻辑,改用直接绘制
你要渲染的都是纯色的简单图形:烟雾是8x8纯色方块、火花是带角度的短线条,完全不需要创建Surface再blit,直接用pygame.draw系列接口画到主窗口即可,性能至少提升10倍。
2. 避免重复计算固定值
火花的旋转角度、烟雾的扩散方向都是初始化时就确定的,不需要每帧重复计算;颜色、alpha值在粒子位置更新时同步计算即可,不用单独跑一遍makeSurface。
3. 及时清理死亡粒子
不要等2.5秒的定时器才清火花,每帧更新完粒子状态就顺便清理死亡实例,避免无效计算堆积。
核心代码修改示例
### 修改后的Spark类 class Spark: def __init__(self, travel, position, rotation, scale): self.position = list(position) self.rotation = rotation self.goTowards = (math.sin(rotation), math.cos(rotation)) self.scale = scale self.speed = random.randint(70, 130) * scale self.maxTravel = travel * self.scale * 2 self.maxAlpha = 150 self.minAlpha = 0 self.randomColor = random.choice(( (188, 98, 5), (255, 215, 0), (255, 127, 80), (255, 140, 0))) self.travelled = 0 self.line_len = 15 * scale # 提前定好火花长度 self._update_color() def shoot(self): if self.travelled < self.maxTravel: self.travelled += 5 self.position[0] += self.goTowards[0] * (3/self.travelled) * self.speed self.position[1] += self.goTowards[1] * (3/self.travelled) * self.speed self._update_color() def _update_color(self): ratio = 1 - (self.travelled / self.maxTravel) self.alpha = ratio * (self.maxAlpha - self.minAlpha) + self.minAlpha self.color = (*self.randomColor, int(self.alpha)) def dead(self): return self.alpha < 1 ### 修改后的火花绘制函数 def drawSpark(window, spark): if not spark.dead(): end_x = spark.position[0] + spark.goTowards[0] * spark.line_len end_y = spark.position[1] + spark.goTowards[1] * spark.line_len pygame.draw.line(window, spark.color, spark.position, (end_x, end_y), width=3)
### 修改后的SmokeParticle类 class SmokeParticle: def __init__(self, position, distFromcentre, explosionRange, centre): self.position = list(position) self.distFromCentre = distFromcentre self.possibleMoves = ( (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0) ) self.blowSpeed = 2 self.blowDir = (self.position[0] - centre[0], self.position[1] - centre[1]) self.blowDir = (self.blowDir[0] / distFromcentre, self.blowDir[1] / distFromcentre) self.blowDistance = math.hypot(*self.blowDir) self.explosionRange = explosionRange self.maxAlpha = 200 self.minAlpha = 80 self.cf = (255, 140, 0) self.cn = (20, 20, 20) self._update_color() def _update_color(self): ratio = 1 - (self.distFromCentre / self.explosionRange) self.alpha = ratio * (self.maxAlpha - self.minAlpha) + self.minAlpha findColor = lambda c : max(0, min(ratio * (self.cf[c] - self.cn[c]) + self.cn[c], 255)) self.color = (int(findColor(0)), int(findColor(1)), int(findColor(2)), int(self.alpha)) def blowAway(self, dt): move = random.choice(self.possibleMoves) self.position[0] += move[0] self.position[1] += move[1] self.position[0] += self.blowDir[0] * self.blowSpeed self.position[1] += self.blowDir[1] * self.blowSpeed self.distFromCentre += self.blowDistance * self.blowSpeed self._update_color() def dead(self): return self.distFromCentre > self.explosionRange ### 修改后的烟雾粒子绘制函数 def drawParticle(window, particle): if particle.alpha > 0: pygame.draw.rect(window, particle.color, (particle.position[0], particle.position[1], 8, 8))
### 去掉Spark类、SmokeParticle类里所有的makeSurface相关逻辑即可
可选锦上添花优化
如果还要更高性能,可以把SmokeMass的nClusters参数从400砍到100,每个SmokeCluster的nParticels从10砍到4,视觉效果几乎没有差别,性能还能再提升一倍。
内容的提问来源于stack exchange,提问作者Cool_Cornflakes
相关产品推荐
相关产品推荐

