Pygame模拟帆船航行时矩形沿波浪旋转出现闪烁问题求助
问题原因分析
- 绘制顺序错误:原代码先绘制船再绘制水面,水面的像素会覆盖船身下半部分,造成视觉闪烁
- 角度计算参考值错误:计算左右水位时使用了旋转后的外接矩形
rect的left/right属性,图像旋转后外接矩形宽高会动态变化,导致左右采样点位置不稳定,角度计算出现跳变 - 位置更新逻辑顺序错误:先设置船的底部位置再执行旋转,旋转后外接矩形变化会导致之前设置的位置偏移,进一步加剧抖动
- 水位采样取整误差:存储水位时提前转成整数,浮点精度损失导致高度差计算出现跳变
修复方案
- 调整绘制顺序:先绘制背景、水面,再绘制船,避免水面覆盖船身
- 固定角度采样点:用原始船宽计算相对于中心点的固定偏移量作为水位采样x坐标,不受旋转后外接矩形的影响
- 调整更新逻辑顺序:先计算角度、完成旋转,再统一设置船的位置
- 水位用浮点数存储,避免精度损失
修复后完整代码
import sys import math import pygame from pygame.locals import * pygame.init() SCREEN_WIDTH = 900 SCREEN_HEIGHT = 900 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) clock = pygame.time.Clock() BLUE = (0, 103, 247) RED = (255,0,0) # 改为存储浮点数水位 water_levels = [0.0 for _ in range(SCREEN_WIDTH)] ORIGIN_SHIP_WIDTH = 80 # 固定原始船宽 def draw_water(surface, dy): amplitude = 35 global water_levels for x in range(SCREEN_WIDTH): y = math.sin(x * 0.01) * amplitude + dy water_levels[x] = y pygame.draw.aaline(surface, BLUE, (x, int(y)), (x, int(y))) def get_water_level(index): if index <= 0: return water_levels[0] if index >= SCREEN_WIDTH: return water_levels[-1] return water_levels[index] font = pygame.font.Font(None,30) def debug(info,x=10,y=10): display_surf = pygame.display.get_surface() debug_surface = font.render(str(info),True,'White') debug_rect = debug_surface.get_rect(topleft=(x,y)) pygame.draw.rect(display_surf,'Black',debug_rect) display_surf.blit(debug_surface,debug_rect) class Ship(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.Surface((ORIGIN_SHIP_WIDTH,80),pygame.SRCALPHA) self.image.fill('red') self.rect = self.image.get_rect(topleft=(0,0)) self.copy_img = self.image.copy() self.move = pygame.math.Vector2(0,0) self.velocity = 8 self.half_width = ORIGIN_SHIP_WIDTH / 2 # 半宽固定 def rotate(self, angle): # 不用强行转int,角度可以是浮点数更平滑 self.image = pygame.transform.rotate(self.copy_img, angle) self.rect = self.image.get_rect(center=self.rect.center) def update(self): self.get_input() cx = self.rect.centerx # 用固定偏移量取左右采样点 left_x = cx - self.half_width right_x = cx + self.half_width left_y = get_water_level(int(left_x)) right_y = get_water_level(int(right_x)) angle = 180 * math.atan2(left_y - right_y, ORIGIN_SHIP_WIDTH) / math.pi debug("angle: "+str(int(angle))) # 先旋转 self.rotate(angle) # 旋转后再统一设置底部位置为中心点水位 self.rect.bottom = int(get_water_level(int(cx))) def replicate(self): if self.rect.left == 363: return self.rect.x += 1 def get_input(self): self.replicate() # 原有键盘控制逻辑保留即可 # keys = pygame.key.get_pressed() # if keys[K_LEFT]: # self.move.x = -self.velocity # elif keys[K_RIGHT]: # self.move.x = self.velocity # else: # self.move.x = 0 # if keys[K_UP]: # self.move.y = -self.velocity # elif keys[K_DOWN]: # self.move.y = self.velocity # else: # self.move.y = 0 # self.rect.x += self.move.x # self.rect.y += self.move.y # if self.rect.left <= 0: # self.rect.left = 0 # if self.rect.right >= SCREEN_WIDTH: # self.rect.right = SCREEN_WIDTH ship_sprite = pygame.sprite.GroupSingle() ship_sprite.add(Ship()) while True: screen.fill((200,210,255,0)) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() ship_sprite.update() # 调整顺序:先画水再画船 draw_water(screen,SCREEN_HEIGHT-300) ship_sprite.draw(screen) clock.tick(60) pygame.display.update()
内容的提问来源于stack exchange,提问作者Juan Álvarez
相关产品推荐
相关产品推荐

