
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:Python案例分享专栏
✨特色专栏:国学周更-心性养成之路
🥭本文内容:Qt Designer 和 PyQt 开发教程
文章目录
-
- 一、游戏需求与设计
- 二、用到的PyQt知识点(详细阐述)
-
- [1. QWidget绘图机制](#1. QWidget绘图机制)
- [2. 动画与定时器](#2. 动画与定时器)
- [3. 键盘事件处理](#3. 键盘事件处理)
- [4. 碰撞检测](#4. 碰撞检测)
- [5. 游戏循环与状态管理](#5. 游戏循环与状态管理)
- [6. 其他辅助知识点](#6. 其他辅助知识点)
- 三、代码实现
- 四、代码解析
-
- [1. 游戏窗口和玩家](#1. 游戏窗口和玩家)
- [2. 敌人设计](#2. 敌人设计)
- [3. 动画与定时器](#3. 动画与定时器)
- [4. 碰撞检测](#4. 碰撞检测)
- 五、扩展思路
- 六、总结

PyQt不仅适合做传统的桌面应用,也能用来开发简单的2D小游戏。本文将带你一步步实现一个带动画效果、碰撞检测和键盘控制的小游戏,帮助你掌握PyQt在游戏开发中的应用。
一、游戏需求与设计
我们设计一个简单的"躲避方块"游戏:
- 玩家控制一个小方块(用键盘上下左右移动)。
- 屏幕上会不断出现自动移动的敌方方块。
- 玩家需要躲避敌方方块,避免碰撞。
- 游戏带有动画效果,敌方方块自动移动。
- 碰撞检测判断玩家是否被敌方方块碰到,碰撞则游戏结束。
二、用到的PyQt知识点(详细阐述)
1. QWidget绘图机制
-
核心类:
QWidgetPyQt中所有窗口和控件的基类是
QWidget。我们通过继承QWidget创建自定义窗口和游戏界面。 -
绘图事件:
paintEvent
paintEvent是QWidget的一个事件处理函数,当窗口需要重绘时(比如窗口被遮挡后重新显示,或者调用update()时),系统会自动调用这个函数。我们重写
paintEvent,使用QPainter对象在窗口上绘制游戏元素(玩家方块、敌人方块、背景等)。 -
绘图工具:
QPainter
QPainter是PyQt中用于绘制图形的类,支持绘制点、线、矩形、圆形、文本等。通过设置画笔颜色、画刷填充颜色,可以绘制各种形状。
在游戏中,我们用
QPainter.drawRect()绘制矩形表示玩家和敌人。 -
抗锯齿:
setRenderHint(QPainter.Antialiasing)该设置让绘制的图形边缘更平滑,视觉效果更好。
2. 动画与定时器
-
动画原理
游戏动画本质是不断刷新画面,更新游戏元素的位置或状态,给人连续运动的感觉。
-
定时器:
QTimerPyQt提供
QTimer类,可以定时触发信号。在游戏中,我们创建一个定时器,设置时间间隔(如30毫秒),每隔这个时间调用一次游戏循环函数
game_loop。这样就实现了游戏状态的周期性更新和界面重绘。
-
游戏循环
game_loop函数中,我们更新敌人位置、检测碰撞、移除出界敌人,然后调用update()请求重绘。这就是典型的游戏主循环结构。
-
多定时器配合
除了主循环定时器,我们还用另一个定时器
spawn_timer定时生成新的敌人,实现游戏动态难度。
3. 键盘事件处理
-
事件机制
PyQt中,键盘按下会触发
keyPressEvent事件。我们重写
keyPressEvent方法,捕获用户按键。 -
键值判断
通过
event.key()获取按键代码,判断是上下左右箭头键(Qt.Key_Up等)。 -
玩家移动
根据按键,调整玩家矩形的位置(
QRect.translate()),并限制玩家不能移出窗口边界。 -
实时响应
每次按键后调用
update(),触发重绘,玩家位置即时更新。
4. 碰撞检测
-
矩形碰撞检测
游戏中玩家和敌人都用
QRect表示位置和大小。
QRect类提供intersects()方法,判断两个矩形是否有重叠区域。 -
碰撞判断逻辑
在游戏循环中,遍历所有敌人,判断它们的矩形是否与玩家矩形相交。
一旦检测到碰撞,触发游戏结束逻辑。
-
效率考虑
对于简单游戏,遍历检测足够快。复杂游戏可用空间划分等优化。
5. 游戏循环与状态管理
-
游戏状态变量
用
self.is_game_over标记游戏是否结束,控制游戏逻辑是否继续执行。 -
定时器启动与停止
游戏结束时停止定时器,防止游戏继续更新。
-
界面刷新
通过调用
self.update()请求重绘,触发paintEvent,实现动画效果。
6. 其他辅助知识点
-
窗口尺寸与坐标系
PyQt窗口的坐标系以左上角为原点,x轴向右,y轴向下。
位置和大小都用像素表示。
-
消息框提示
用
QMessageBox.information弹出游戏结束提示框,增强用户体验。 -
随机数生成
用Python的
random.randint生成敌人出现的随机高度,增加游戏趣味性。
三、代码实现
python
import sys
import random
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
from PyQt5.QtGui import QPainter, QColor, QBrush
from PyQt5.QtCore import Qt, QTimer, QRect
class Enemy:
def __init__(self, x, y, size, speed):
self.rect = QRect(x, y, size, size)
self.speed = speed
def move(self):
# 敌人向左移动
self.rect.translate(-self.speed, 0)
class PlayerGame(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt小游戏 - 躲避方块")
self.setGeometry(100, 100, 800, 600)
# 玩家属性
self.player_size = 40
self.player_pos = QRect(100, 280, self.player_size, self.player_size)
self.player_speed = 20
# 敌人列表
self.enemies = []
self.enemy_size = 40
self.enemy_speed = 5
# 游戏状态
self.is_game_over = False
# 定时器,控制游戏刷新和敌人生成
self.timer = QTimer()
self.timer.timeout.connect(self.game_loop)
self.timer.start(30) # 30ms刷新一次,大约33帧每秒
self.spawn_timer = QTimer()
self.spawn_timer.timeout.connect(self.spawn_enemy)
self.spawn_timer.start(1500) # 每1.5秒生成一个敌人
def spawn_enemy(self):
# 敌人从右侧随机高度出现
y = random.randint(0, self.height() - self.enemy_size)
x = self.width()
enemy = Enemy(x, y, self.enemy_size, self.enemy_speed)
self.enemies.append(enemy)
def game_loop(self):
if self.is_game_over:
return
# 移动敌人
for enemy in self.enemies:
enemy.move()
# 移除已经移出屏幕的敌人
self.enemies = [e for e in self.enemies if e.rect.right() > 0]
# 碰撞检测
for enemy in self.enemies:
if enemy.rect.intersects(self.player_pos):
self.game_over()
break
# 刷新界面
self.update()
def game_over(self):
self.is_game_over = True
self.timer.stop()
self.spawn_timer.stop()
QMessageBox.information(self, "游戏结束", "你被撞到了!游戏结束。")
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# 画背景
painter.fillRect(self.rect(), QColor(230, 230, 230))
# 画玩家方块(蓝色)
painter.setBrush(QBrush(QColor(0, 100, 255)))
painter.drawRect(self.player_pos)
# 画敌人方块(红色)
painter.setBrush(QBrush(QColor(255, 50, 50)))
for enemy in self.enemies:
painter.drawRect(enemy.rect)
def keyPressEvent(self, event):
if self.is_game_over:
return
key = event.key()
if key == Qt.Key_Up:
if self.player_pos.top() - self.player_speed >= 0:
self.player_pos.translate(0, -self.player_speed)
elif key == Qt.Key_Down:
if self.player_pos.bottom() + self.player_speed <= self.height():
self.player_pos.translate(0, self.player_speed)
elif key == Qt.Key_Left:
if self.player_pos.left() - self.player_speed >= 0:
self.player_pos.translate(-self.player_speed, 0)
elif key == Qt.Key_Right:
if self.player_pos.right() + self.player_speed <= self.width():
self.player_pos.translate(self.player_speed, 0)
self.update()
if __name__ == "__main__":
app = QApplication(sys.argv)
game = PlayerGame()
game.show()
sys.exit(app.exec_())
四、代码解析
1. 游戏窗口和玩家
PlayerGame继承自QWidget,作为游戏主窗口。- 玩家用
QRect表示位置和大小,初始在窗口左侧中间。 - 玩家移动通过键盘事件
keyPressEvent控制,限制移动范围不超出窗口。
2. 敌人设计
- 敌人用
Enemy类表示,包含矩形区域和移动速度。 - 敌人从窗口右侧随机高度生成,向左移动。
- 超出左侧边界的敌人被移除,避免内存泄漏。
3. 动画与定时器
- 使用
QTimer定时调用game_loop方法,实现游戏状态更新和动画。 - 另一个定时器
spawn_timer负责定时生成敌人。 - 通过
update()触发paintEvent重绘界面。
4. 碰撞检测
- 利用
QRect.intersects()判断玩家和敌人是否重叠。 - 一旦检测到碰撞,游戏结束,停止定时器并弹出提示框。
五、扩展思路
- 增加分数和计时:统计玩家存活时间或躲避敌人数。
- 多种敌人类型:不同速度、大小的敌人增加难度。
- 玩家攻击:添加发射子弹功能,消灭敌人。
- 音效和背景音乐 :用PyQt的
QSound模块丰富游戏体验。 - 更复杂的动画:利用Qt的动画框架实现平滑移动和特效。
六、总结
通过这个小游戏,我们学习了:
- PyQt的绘图机制和事件处理。
- 使用定时器实现游戏循环和动画。
- 键盘事件捕获实现玩家控制。
- 矩形碰撞检测的简单实现。
码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识,点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更---心性养成之路》,学习技术的同时,我们也注重了心性的养成。
