如何在pygame中实现向鼠标位置以恒定速度发射子弹的功能
问题根源
- 核心问题是
pygame.Rect的所有坐标属性只支持整数,你传入的浮点坐标会被自动截断取整。你当前计算出的x_move、y_move是归一化后的单位向量,数值都小于等于1,累加后赋值给Rect会被直接舍去小数部分,子弹坐标几乎不会变化,看起来就卡住了。 - 你之前直接用鼠标坐标差作为移动量的写法,等于子弹速度和鼠标到坦克的距离成正比,自然会出现近慢远快的问题。
- 额外隐藏问题:在遍历子弹列表的过程中删除列表元素,会导致后续子弹被跳过遍历;每个子弹实例重复加载图片会拉低运行效率。
修复方案
你可以通过单独维护浮点类型的子弹坐标、添加固定速度系数、优化销毁逻辑来解决问题,修改后的完整代码如下:
import pygame import math pygame.init() # 全局常量定义 SCREEN_SIZE = 400 BULLET_SPEED = 5 # 可自行调整子弹速度 # 子弹图片提前加载一次即可 BULLET_IMG = pygame.image.load("Bullet.png") screen = pygame.display.set_mode((SCREEN_SIZE, SCREEN_SIZE)) pygame.display.set_caption("diep.io") auto_shoot = False class Bullet: def __init__(self, x_dir, y_dir, x_loc, y_loc): self.image = BULLET_IMG # 乘以速度系数得到每帧移动量 self.x_move = x_dir * BULLET_SPEED self.y_move = y_dir * BULLET_SPEED # 单独维护浮点类型的坐标,避免精度丢失 self.x_loc = float(x_loc) self.y_loc = float(y_loc) self.bullet_rect = self.image.get_rect() def update(self): # 先更新浮点坐标 self.x_loc += self.x_move self.y_loc += self.y_move # 再赋值给Rect,自动转整数用于渲染 self.bullet_rect.center = (self.x_loc, self.y_loc) screen.blit(self.image, self.bullet_rect) # 返回是否超出屏幕,用于后续销毁判断,避免遍历中删列表 return (self.x_loc > SCREEN_SIZE or self.x_loc < 0 or self.y_loc > SCREEN_SIZE or self.y_loc < 0) bullets = [] while True: screen.fill((255, 255, 255)) pygame.draw.rect(screen, (100, 100, 100), (205, 193, 25, 15)) pygame.draw.circle(screen, (82, 219, 255), (200, 200), 15) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() if event.type == pygame.MOUSEBUTTONUP: mouse_x, mouse_y = pygame.mouse.get_pos() dx = mouse_x - 200 dy = mouse_y - 200 distance = math.hypot(dx, dy) # 避免鼠标和坦克完全重合时除以0 if distance != 0: x_dir = dx / distance y_dir = dy / distance bullets.append(Bullet(x_dir, y_dir, 200, 200)) # 更新子弹并过滤销毁超出屏幕的子弹 bullets = [bullet for bullet in bullets if not bullet.update()] pygame.display.update() pygame.time.delay(10)
关键改动说明
- 新增了固定的子弹速度系数
BULLET_SPEED,归一化的方向向量乘以该系数后,不管鼠标距离坦克远近,子弹每帧移动的距离都固定,速度均匀。 - 单独用浮点变量存储子弹坐标,更新完成后再赋值给Rect用于渲染,避免了自动取整导致的位置不更新问题。
- 优化了子弹销毁逻辑,不在遍历过程中删除列表元素,避免遍历异常,同时补充了向左、向上飞出屏幕的销毁判断。
- 新增了窗口关闭事件的处理,避免程序无法正常退出;新增了鼠标和坦克重合时的除0判断,避免运行报错。
- 子弹图片统一提前加载一次,降低性能消耗。
内容的提问来源于stack exchange,提问作者ᴇɴᴅᴇʀᴍᴀɴ
相关产品推荐
相关产品推荐

