摘要
用户输入是游戏交互的核心,本章将全面介绍Pygame中处理键盘和鼠标输入的各种方法。我们将详细讲解事件驱动和状态查询两种输入处理模式,探讨如何实现平滑的角色移动控制,介绍鼠标位置跟踪和点击检测,以及如何处理鼠标滚轮和相对移动。同时,本章还将涵盖游戏手柄的支持方法。此外,本章将展示如何使用GPT-5.4来生成复杂的输入处理代码和控制方案。由于国内无法访问OpenAI官网,因此使用国内镜像站可以合法注册使用GPT-5.4最新模型。重要提示:翻墙行为违反中国法律法规,请大家不要翻墙,选择合法的国内镜像站使用AI服务 。注册入口:AIGCBAR镜像站。通过本章的学习,读者将能够创建响应灵敏、操作流畅的游戏控制系统。
5.1 输入处理概述
游戏输入处理主要有两种模式:事件驱动模式和状态查询模式。理解这两种模式的特点和适用场景对于设计良好的控制系统至关重要。
5.1.1 事件驱动vs状态查询
| 特性 | 事件驱动模式 | 状态查询模式 |
|---|---|---|
| 触发时机 | 输入状态改变时 | 每帧查询时 |
| 适用场景 | 单次动作(如跳跃、射击) | 持续动作(如移动、瞄准) |
| 响应延迟 | 可能有延迟 | 实时响应 |
| 实现复杂度 | 需要维护事件队列 | 直接查询当前状态 |
5.1.2 选择合适的处理模式
在实际游戏中,通常需要结合使用两种模式:
python
# 事件驱动:处理单次动作
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump() # 跳跃(单次动作)
elif event.key == pygame.K_f:
player.shoot() # 射击(单次动作)
# 状态查询:处理持续动作
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.move_left() # 持续左移
if keys[pygame.K_RIGHT]:
player.move_right() # 持续右移
5.2 键盘输入处理
Pygame提供了丰富的键盘输入处理功能。
5.2.1 键盘事件处理
键盘事件在按键按下和释放时触发:
python
# 按键状态显示
key_status = {}
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 按键按下事件
elif event.type == pygame.KEYDOWN:
key_status[event.key] = "按下"
# 特殊按键处理
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
print("空格键按下")
elif event.key == pygame.K_RETURN:
print("回车键按下")
elif event.key == pygame.K_BACKSPACE:
print("退格键按下")
# 检测组合键
if event.mod & pygame.KMOD_CTRL:
print("Ctrl键被按住")
if event.mod & pygame.KMOD_SHIFT:
print("Shift键被按住")
if event.mod & pygame.KMOD_ALT:
print("Alt键被按住")
# 按键释放事件
elif event.type == pygame.KEYUP:
key_status[event.key] = "释放"
screen.fill((50, 50, 50))
# 显示按键状态
font = pygame.font.SysFont(None, 24)
y = 10
for key, status in list(key_status.items())[-10:]:
key_name = pygame.key.name(key)
text = font.render(f"{key_name}: {status}", True, (255, 255, 255))
screen.blit(text, (10, y))
y += 30
pygame.display.flip()
clock.tick(60)
5.2.2 键盘状态查询
使用pygame.key.get_pressed()获取所有按键的当前状态:
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# 玩家位置
player_x = 400
player_y = 300
player_speed = 5
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 获取按键状态
keys = pygame.key.get_pressed()
# 八方向移动
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
player_x -= player_speed
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
player_x += player_speed
if keys[pygame.K_UP] or keys[pygame.K_w]:
player_y -= player_speed
if keys[pygame.K_DOWN] or keys[pygame.K_s]:
player_y += player_speed
# 加速
if keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]:
player_speed = 8
else:
player_speed = 5
# 边界限制
player_x = max(25, min(775, player_x))
player_y = max(25, min(575, player_y))
# 绘制
screen.fill((50, 50, 50))
pygame.draw.circle(screen, (0, 255, 0), (player_x, player_y), 25)
# 显示操作说明
font = pygame.font.SysFont(None, 24)
help_text = "WASD或方向键移动,Shift加速"
text = font.render(help_text, True, (255, 255, 255))
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
5.2.3 常用按键常量
Pygame定义了大量的按键常量:
| 常量 | 说明 | 常量 | 说明 |
|---|---|---|---|
| K_a - K_z | 字母键 | K_0 - K_9 | 数字键 |
| K_F1 - K_F15 | 功能键 | K_SPACE | 空格键 |
| K_RETURN | 回车键 | K_ESCAPE | Esc键 |
| K_BACKSPACE | 退格键 | K_TAB | Tab键 |
| K_LSHIFT | 左Shift | K_RSHIFT | 右Shift |
| K_LCTRL | 左Ctrl | K_RCTRL | 右Ctrl |
| K_LALT | 左Alt | K_RALT | 右Alt |
| K_UP | 方向键上 | K_DOWN | 方向键下 |
| K_LEFT | 方向键左 | K_RIGHT | 方向键右 |
| K_HOME | Home键 | K_END | End键 |
| K_PAGEUP | Page Up | K_PAGEDOWN | Page Down |
| K_INSERT | Insert键 | K_DELETE | Delete键 |
5.2.4 按键重复设置
可以设置按键重复,使得按住按键时自动重复触发KEYDOWN事件:
python
# 设置按键重复(延迟毫秒,间隔毫秒)
pygame.key.set_repeat(500, 100)
# 取消按键重复
pygame.key.set_repeat()
5.2.5 文本输入处理
处理文本输入需要特殊处理:
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# 启用文本输入事件
pygame.key.start_text_input()
input_text = ""
cursor_visible = True
cursor_timer = 0
running = True
while running:
dt = clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 文本输入事件
elif event.type == pygame.TEXTINPUT:
input_text += event.text
# 按键事件(处理特殊键)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
input_text = input_text[:-1]
elif event.key == pygame.K_RETURN:
print(f"输入完成: {input_text}")
input_text = ""
elif event.key == pygame.K_ESCAPE:
running = False
# 光标闪烁
cursor_timer += dt
if cursor_timer > 500:
cursor_timer = 0
cursor_visible = not cursor_visible
screen.fill((50, 50, 50))
# 绘制输入框
pygame.draw.rect(screen, (100, 100, 100), (50, 250, 700, 60))
pygame.draw.rect(screen, (200, 200, 200), (50, 250, 700, 60), 2)
# 绘制文本
font = pygame.font.SysFont(None, 36)
text_surface = font.render(input_text, True, (255, 255, 255))
screen.blit(text_surface, (60, 260))
# 绘制光标
if cursor_visible:
text_width = text_surface.get_width()
pygame.draw.line(screen, (255, 255, 255),
(60 + text_width, 260),
(60 + text_width, 295), 2)
# 显示提示
hint = font.render("输入文本,回车确认,退格删除", True, (200, 200, 200))
screen.blit(hint, (50, 200))
pygame.display.flip()
pygame.key.stop_text_input()
pygame.quit()
sys.exit()
5.3 鼠标输入处理
鼠标是PC游戏中最常用的输入设备之一。
5.3.1 鼠标事件处理
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
events_log = []
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 鼠标移动事件
elif event.type == pygame.MOUSEMOTION:
events_log.append(f"鼠标移动到: {event.pos}")
# event.rel: 相对移动距离
# event.buttons: 按钮状态 (左, 中, 右)
# 鼠标按下事件
elif event.type == pygame.MOUSEBUTTONDOWN:
button_name = ["左键", "中键", "右键"][event.button - 1]
events_log.append(f"{button_name}在{event.pos}按下")
# event.button: 1=左键, 2=中键, 3=右键
# 4=滚轮上, 5=滚轮下
# 鼠标释放事件
elif event.type == pygame.MOUSEBUTTONUP:
button_name = ["左键", "中键", "右键"][event.button - 1]
events_log.append(f"{button_name}在{event.pos}释放")
# 限制日志数量
if len(events_log) > 15:
events_log = events_log[-15:]
screen.fill((50, 50, 50))
# 显示事件日志
font = pygame.font.SysFont(None, 24)
y = 10
for log in events_log:
text = font.render(log, True, (255, 255, 255))
screen.blit(text, (10, y))
y += 25
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
5.3.2 鼠标状态查询
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 获取鼠标位置
mouse_x, mouse_y = pygame.mouse.get_pos()
# 获取鼠标按钮状态
left_pressed, middle_pressed, right_pressed = pygame.mouse.get_pressed()
# 获取相对移动
rel_x, rel_y = pygame.mouse.get_rel()
# 检查鼠标是否在窗口内
mouse_focus = pygame.mouse.get_focused()
screen.fill((50, 50, 50))
# 绘制鼠标位置
pygame.draw.circle(screen, (255, 0, 0), (mouse_x, mouse_y), 10, 2)
# 显示信息
font = pygame.font.SysFont(None, 24)
info = [
f"鼠标位置: ({mouse_x}, {mouse_y})",
f"相对移动: ({rel_x}, {rel_y})",
f"左键: {'按下' if left_pressed else '释放'}",
f"中键: {'按下' if middle_pressed else '释放'}",
f"右键: {'按下' if right_pressed else '释放'}",
f"窗口焦点: {'是' if mouse_focus else '否'}"
]
y = 10
for line in info:
text = font.render(line, True, (255, 255, 255))
screen.blit(text, (10, y))
y += 30
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
5.3.3 鼠标可见性控制
python
# 隐藏鼠标光标
pygame.mouse.set_visible(False)
# 显示鼠标光标
pygame.mouse.set_visible(True)
# 获取当前可见性状态
is_visible = pygame.mouse.get_visible()
5.3.4 鼠标位置设置
python
# 设置鼠标位置
pygame.mouse.set_pos(400, 300)
# 将鼠标限制在窗口内(需要配合其他逻辑)
5.3.5 滚轮处理
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
scroll_y = 0
content_offset = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 鼠标滚轮事件
elif event.type == pygame.MOUSEWHEEL:
# event.y: 垂直滚动(正值向上,负值向下)
# event.x: 水平滚动(部分鼠标支持)
# event.flipped: 是否翻转(macOS)
scroll_y += event.y
content_offset += event.y * 30
content_offset = max(-500, min(0, content_offset))
screen.fill((50, 50, 50))
# 绘制可滚动内容
font = pygame.font.SysFont(None, 24)
for i in range(30):
y = 50 + i * 40 + content_offset
if 0 < y < 600:
text = font.render(f"内容项 {i + 1}", True, (255, 255, 255))
screen.blit(text, (100, y))
# 显示滚动信息
info = font.render(f"滚动值: {scroll_y}, 偏移: {content_offset}", True, (255, 255, 0))
screen.blit(info, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
5.4 拖拽操作实现
拖拽是游戏中常见的交互方式。
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
class DraggableRect:
def __init__(self, x, y, w, h, color):
self.rect = pygame.Rect(x, y, w, h)
self.color = color
self.dragging = False
self.drag_offset_x = 0
self.drag_offset_y = 0
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1 and self.rect.collidepoint(event.pos):
self.dragging = True
self.drag_offset_x = self.rect.x - event.pos[0]
self.drag_offset_y = self.rect.y - event.pos[1]
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
self.dragging = False
elif event.type == pygame.MOUSEMOTION:
if self.dragging:
self.rect.x = event.pos[0] + self.drag_offset_x
self.rect.y = event.pos[1] + self.drag_offset_y
def draw(self, surface):
pygame.draw.rect(surface, self.color, self.rect)
if self.dragging:
pygame.draw.rect(surface, (255, 255, 255), self.rect, 3)
# 创建可拖拽矩形
rects = [
DraggableRect(100, 100, 100, 80, (255, 0, 0)),
DraggableRect(300, 200, 120, 100, (0, 255, 0)),
DraggableRect(500, 150, 80, 120, (0, 0, 255))
]
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for rect in rects:
rect.handle_event(event)
screen.fill((50, 50, 50))
for rect in rects:
rect.draw(screen)
# 显示提示
font = pygame.font.SysFont(None, 24)
hint = font.render("拖拽矩形移动", True, (255, 255, 255))
screen.blit(hint, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
5.5 游戏手柄支持
Pygame支持多种游戏手柄和摇杆设备。
python
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# 初始化摇杆模块
pygame.joystick.init()
# 获取连接的手柄数量
joystick_count = pygame.joystick.get_count()
print(f"连接的手柄数量: {joystick_count}")
# 初始化手柄
joysticks = []
for i in range(joystick_count):
joystick = pygame.joystick.Joystick(i)
joystick.init()
joysticks.append(joystick)
print(f"手柄 {i}: {joystick.get_name()}")
print(f" 轴数量: {joystick.get_numaxes()}")
print(f" 按钮数量: {joystick.get_numbuttons()}")
print(f" 帽子数量: {joystick.get_numhats()}")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 手柄按钮事件
elif event.type == pygame.JOYBUTTONDOWN:
print(f"手柄{event.joy} 按钮{event.button} 按下")
elif event.type == pygame.JOYBUTTONUP:
print(f"手柄{event.joy} 按钮{event.button} 释放")
# 手柄轴事件
elif event.type == pygame.JOYAXISMOTION:
if abs(event.value) > 0.1: # 忽略小值
print(f"手柄{event.joy} 轴{event.axis} 值{event.value:.2f}")
# 手柄帽子事件(方向键)
elif event.type == pygame.JOYHATMOTION:
print(f"手柄{event.joy} 帽子{event.hat} 值{event.value}")
screen.fill((50, 50, 50))
# 显示手柄状态
font = pygame.font.SysFont(None, 24)
y = 10
for i, joystick in enumerate(joysticks):
text = font.render(f"手柄 {i}: {joystick.get_name()}", True, (255, 255, 255))
screen.blit(text, (10, y))
y += 30
# 显示轴值
for axis in range(joystick.get_numaxes()):
value = joystick.get_axis(axis)
text = font.render(f" 轴{axis}: {value:.2f}", True, (200, 200, 200))
screen.blit(text, (10, y))
y += 25
# 显示按钮状态
button_states = []
for button in range(joystick.get_numbuttons()):
if joystick.get_button(button):
button_states.append(str(button))
if button_states:
text = font.render(f" 按下的按钮: {', '.join(button_states)}", True, (255, 255, 0))
screen.blit(text, (10, y))
y += 25
y += 10
pygame.display.flip()
clock.tick(60)
# 清理
for joystick in joysticks:
joystick.quit()
pygame.joystick.quit()
pygame.quit()
sys.exit()
5.6 使用GPT-5.4生成输入处理代码
本节展示如何使用GPT-5.4来生成复杂的输入处理代码。
5.6.1 生成控制方案代码
提示词示例:
请用Pygame实现一个完整的玩家控制系统,要求:
1. 支持键盘WASD和方向键双方案控制
2. 支持鼠标瞄准和射击
3. 实现平滑的加速和减速
4. 添加冲刺功能(双击方向键或按Shift)
5. 代码结构清晰,包含详细中文注释
5.6.2 生成输入管理器
提示词示例:
请设计一个Pygame的输入管理器类,要求:
1. 统一管理键盘、鼠标和手柄输入
2. 支持按键绑定和自定义配置
3. 实现输入缓冲和组合键检测
4. 提供事件和状态两种查询接口
5. 代码包含详细中文注释
5.7 综合示例:完整控制系统
本节提供一个完整的游戏控制系统示例。
python
import pygame
import sys
import math
import random
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shooter Game")
clock = pygame.time.Clock()
class Player:
def __init__(self):
self.x = SCREEN_WIDTH // 2
self.y = SCREEN_HEIGHT // 2
self.speed = 0
self.max_speed = 8
self.acceleration = 0.5
self.friction = 0.9
self.angle = 0
self.radius = 20
# Dash
self.dashing = False
self.dash_speed = 15
self.dash_duration = 200
self.dash_cooldown = 1000
self.dash_start = 0
self.last_dash = 0
def update(self, dt):
keys = pygame.key.get_pressed()
mouse_x, mouse_y = pygame.mouse.get_pos()
# Calculate angle toward mouse
dx = mouse_x - self.x
dy = mouse_y - self.y
self.angle = math.atan2(-dy, dx)
# Movement input
move_x = 0
move_y = 0
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
move_x -= 1
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
move_x += 1
if keys[pygame.K_w] or keys[pygame.K_UP]:
move_y -= 1
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
move_y += 1
# Normalize movement vector
if move_x != 0 or move_y != 0:
length = math.sqrt(move_x ** 2 + move_y ** 2)
move_x /= length
move_y /= length
# Dash detection
now = pygame.time.get_ticks()
if keys[pygame.K_LSHIFT] and now - self.last_dash > self.dash_cooldown:
self.dashing = True
self.dash_start = now
self.last_dash = now
# Update dash state
if self.dashing:
if now - self.dash_start > self.dash_duration:
self.dashing = False
# Apply movement
current_max = self.dash_speed if self.dashing else self.max_speed
if move_x != 0 or move_y != 0:
self.speed = min(self.speed + self.acceleration, current_max)
else:
self.speed *= self.friction
self.x += move_x * self.speed
self.y += move_y * self.speed
# Boundary clamp
self.x = max(self.radius, min(SCREEN_WIDTH - self.radius, self.x))
self.y = max(self.radius, min(SCREEN_HEIGHT - self.radius, self.y))
def draw(self, surface):
# Draw player as triangle pointing toward mouse
points = []
for i in range(3):
angle = self.angle + i * 2 * math.pi / 3
px = self.x + self.radius * math.cos(angle)
py = self.y - self.radius * math.sin(angle)
points.append((px, py))
color = (255, 255, 0) if self.dashing else (0, 255, 0)
pygame.draw.polygon(surface, color, points)
# Draw dash cooldown arc indicator
if pygame.time.get_ticks() - self.last_dash < self.dash_cooldown:
ratio = (pygame.time.get_ticks() - self.last_dash) / self.dash_cooldown
pygame.draw.arc(surface, (100, 100, 255),
(self.x - 30, self.y - 30, 60, 60),
0, ratio * 2 * math.pi, 3)
class Bullet:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.angle = angle
self.speed = 15
self.radius = 5
self.lifetime = 2000
self.created = pygame.time.get_ticks()
def update(self):
self.x += math.cos(self.angle) * self.speed
self.y -= math.sin(self.angle) * self.speed
def is_alive(self):
return pygame.time.get_ticks() - self.created < self.lifetime
def draw(self, surface):
pygame.draw.circle(surface, (255, 255, 0), (int(self.x), int(self.y)), self.radius)
class Enemy:
def __init__(self):
# Spawn randomly on one of the four screen edges
side = random.randint(0, 3)
if side == 0: # Top edge
self.x = random.randint(0, SCREEN_WIDTH)
self.y = -20
elif side == 1: # Bottom edge
self.x = random.randint(0, SCREEN_WIDTH)
self.y = SCREEN_HEIGHT + 20
elif side == 2: # Left edge
self.x = -20
self.y = random.randint(0, SCREEN_HEIGHT)
else: # Right edge
self.x = SCREEN_WIDTH + 20
self.y = random.randint(0, SCREEN_HEIGHT)
self.speed = random.uniform(1.5, 3.5)
self.radius = 15
def update(self, player):
# Chase the player
dx = player.x - self.x
dy = player.y - self.y
dist = math.sqrt(dx ** 2 + dy ** 2)
if dist > 0:
self.x += (dx / dist) * self.speed
self.y += (dy / dist) * self.speed
def collides_with_bullet(self, bullet):
# Circle-circle collision check
dx = self.x - bullet.x
dy = self.y - bullet.y
return math.sqrt(dx ** 2 + dy ** 2) < self.radius + bullet.radius
def draw(self, surface):
pygame.draw.circle(surface, (220, 50, 50), (int(self.x), int(self.y)), self.radius)
pygame.draw.circle(surface, (255, 120, 120), (int(self.x), int(self.y)), self.radius, 2)
# Draw X eyes
cx, cy = int(self.x), int(self.y)
pygame.draw.line(surface, (30, 30, 30), (cx - 7, cy - 5), (cx - 3, cy - 1), 2)
pygame.draw.line(surface, (30, 30, 30), (cx - 3, cy - 5), (cx - 7, cy - 1), 2)
pygame.draw.line(surface, (30, 30, 30), (cx + 3, cy - 5), (cx + 7, cy - 1), 2)
pygame.draw.line(surface, (30, 30, 30), (cx + 7, cy - 5), (cx + 3, cy - 1), 2)
# ── Game objects ──────────────────────────────────────────────
player = Player()
bullets = []
enemies = []
# Shooting control
last_shot = 0
shoot_delay = 150
# Enemy spawn control
last_spawn = 0
spawn_interval = 2000 # one new enemy every 2 seconds
score = 0
font = pygame.font.Font(None, 24)
font_score = pygame.font.Font(None, 36)
# ── Game loop ─────────────────────────────────────────────────
running = True
while running:
dt = clock.tick(60)
now = pygame.time.get_ticks()
# ── Events ────────────────────────────────────────────────
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Single left-click shot
if now - last_shot > shoot_delay:
last_shot = now
bullets.append(Bullet(player.x, player.y, player.angle))
# Continuous fire while holding left mouse button
if pygame.mouse.get_pressed()[0]:
if now - last_shot > shoot_delay:
last_shot = now
bullets.append(Bullet(player.x, player.y, player.angle))
# ── Spawn enemies ─────────────────────────────────────────
if now - last_spawn > spawn_interval:
last_spawn = now
enemies.append(Enemy())
# ── Update ────────────────────────────────────────────────
player.update(dt)
# Update bullets; remove expired ones
for bullet in bullets[:]:
bullet.update()
if not bullet.is_alive():
bullets.remove(bullet)
# Update enemies; one-shot kill on bullet contact
for enemy in enemies[:]:
enemy.update(player)
for bullet in bullets[:]:
if enemy.collides_with_bullet(bullet):
enemies.remove(enemy)
if bullet in bullets:
bullets.remove(bullet)
score += 1
break
# ── Draw ──────────────────────────────────────────────────
screen.fill((30, 30, 30))
# Draw grid (subtle background)
for gx in range(0, SCREEN_WIDTH, 50):
pygame.draw.line(screen, (40, 40, 40), (gx, 0), (gx, SCREEN_HEIGHT))
for gy in range(0, SCREEN_HEIGHT, 50):
pygame.draw.line(screen, (40, 40, 40), (0, gy), (SCREEN_WIDTH, gy))
for enemy in enemies:
enemy.draw(screen)
for bullet in bullets:
bullet.draw(screen)
player.draw(screen)
# Crosshair
mouse_x, mouse_y = pygame.mouse.get_pos()
pygame.draw.circle(screen, (255, 0, 0), (mouse_x, mouse_y), 10, 2)
pygame.draw.line(screen, (255, 0, 0), (mouse_x - 15, mouse_y), (mouse_x + 15, mouse_y), 1)
pygame.draw.line(screen, (255, 0, 0), (mouse_x, mouse_y - 15), (mouse_x, mouse_y + 15), 1)
# ── HUD ───────────────────────────────────────────────────
info = [
"WASD / Arrow Keys : Move",
"Mouse : Aim",
"Left Click : Shoot",
"Left Shift : Dash",
]
for i, line in enumerate(info):
screen.blit(font.render(line, True, (200, 200, 200)), (10, 10 + i * 22))
# Score (top-right)
screen.blit(font_score.render(f"Score: {score}", True, (255, 215, 0)),
(SCREEN_WIDTH - 150, 10))
# Enemy count (below score)
screen.blit(font.render(f"Enemies: {len(enemies)}", True, (220, 80, 80)),
(SCREEN_WIDTH - 150, 48))
pygame.display.flip()
pygame.quit()
sys.exit()

5.8 本章总结
本章全面介绍了Pygame的输入处理功能。我们学习了键盘输入的事件驱动和状态查询两种模式,掌握了鼠标的位置跟踪、点击检测和滚轮处理方法,了解了游戏手柄的支持方式。通过拖拽示例和综合控制系统,我们展示了如何将各种输入处理技术组合起来创建流畅的游戏控制体验。良好的输入处理是游戏可玩性的关键,掌握这些技术对于开发高质量的游戏至关重要。
本章知识点回顾
| 知识点 | 主要内容 |
|---|---|
| 键盘事件 | KEYDOWN、KEYUP事件处理 |
| 键盘状态 | get_pressed()查询按键状态 |
| 鼠标事件 | MOUSEMOTION、MOUSEBUTTONDOWN/UP |
| 鼠标状态 | get_pos()、get_pressed()、get_rel() |
| 手柄支持 | Joystick类的使用 |
课后练习
- 实现一个虚拟摇杆系统,用鼠标模拟游戏手柄。
- 创建一个可配置的按键绑定系统,允许玩家自定义控制方案。
- 实现双击检测和长按检测功能。
- 使用GPT-5.4生成一个手势识别系统代码。
- 创建一个多点触控模拟系统(使用多个鼠标按钮模拟)。
下章预告
在下一章中,我们将深入学习Pygame的碰撞检测系统,包括矩形碰撞、圆形碰撞、像素精确碰撞等多种检测方法。