Pac-Man(吃豆人) 游戏
目录
前言
1. Pygame游戏开发基础
1.1 Pygame简介
1.2 游戏开发基本概念
1.3 Pygame核心模块介绍
2. 游戏设计与规划
2.1 游戏规则设计
2.2 游戏对象规划
2.3 技术方案选择
3. 创建游戏窗口与初始化
3.1 初始化Pygame环境
3.2 设置游戏窗口
3.3 定义颜色和游戏参数
3.4 初始化游戏时钟
4. 吃豆人角色设计与实现
4.1 吃豆人类设计
4.2 实现吃豆人移动
4.3 绘制吃豆人
5. 幽灵角色设计与AI实现
5.1 幽灵类设计
5.2 幽灵AI行为实现
5.3 绘制幽灵
6. 迷宫生成与渲染
6.1 迷宫表示方法
6.2 迷宫渲染
7. 游戏物品:豆子与能量豆
7.1 豆子生成
7.2 豆子渲染
7.3 豆子与吃豆人的交互
8. 碰撞检测系统
8.1 角色与墙壁的碰撞检测
8.2 吃豆人与幽灵的碰撞检测
8.3 吃豆人与豆子的碰撞检测
9. 游戏状态管理
9.1 游戏状态定义
9.2 游戏状态切换
9.3 幽灵状态管理
10. 游戏UI与视觉效果
10.1 分数和生命值显示
10.2 游戏结束画面
10.3 角色动画
11. 游戏主循环与事件处理
11.1 游戏主循环结构
11.2 事件处理
11.3 键盘输入处理
12. 代码优化与性能改进
12.1 碰撞检测优化
12.2 渲染优化
12.3 内存管理
13. 完整代码
前言
吃豆人(Pac-Man)是一款经典的街机游戏,自1980年问世以来一直深受玩家喜爱。在这篇博客中,我将详细介绍如何使用Python的Pygame库从零开始构建一个完整的吃豆人游戏。我们将逐步实现游戏的核心功能,包括角色控制、碰撞检测、AI行为等,并且深入剖析每个组件的工作原理。
无论你是游戏开发新手还是想要提升编程技能的中级开发者,这个项目都将帮助你理解游戏开发的基本原理和技巧。让我们开始这段有趣的编程之旅吧!
1. Pygame游戏开发基础
1.1 Pygame简介
Pygame是一个为Python设计的游戏开发库,它基于SDL(Simple DirectMedia Layer)构建,提供了图形、声音、输入设备等功能的简单接口,非常适合初学者和中级开发者使用。
Pygame的主要特点包括:
- 跨平台:可在Windows、macOS、Linux等多种系统上运行
- 易于学习:API设计简洁直观
- 功能全面:提供图形渲染、声音播放、输入处理等游戏开发必备功能
- 活跃的社区:有大量的教程和资源可供学习
1.2 游戏开发基本概念
在开始编写游戏代码前,让我们先了解一些游戏开发的基本概念:
1. 游戏循环(Game Loop)
游戏循环是几乎所有电子游戏的核心,它通常包含以下三个主要步骤:
- 处理输入(Processing Input):检测并响应用户的键盘、鼠标等输入
- 更新游戏状态(Updating Game State):根据输入和游戏规则更新游戏对象的状态
- 渲染(Rendering):将当前游戏状态绘制到屏幕上
2. 精灵(Sprite)
在游戏开发中,精灵是指可以在屏幕上移动的图形对象。在我们的吃豆人游戏中,吃豆人和幽灵都是精灵。
3. 碰撞检测(Collision Detection)
碰撞检测用于判断游戏对象之间是否发生接触或重叠,这在游戏中非常重要。例如,我们需要检测吃豆人是否碰到了豆子或幽灵。
4. 帧率控制(Frame Rate Control)
帧率是指游戏每秒更新和渲染的次数。控制帧率对于确保游戏在不同硬件上有一致的表现非常重要。
1.3 Pygame核心模块介绍
Pygame提供了多个模块来处理游戏开发的不同方面:
pygame.display
:创建和管理游戏窗口pygame.event
:处理用户输入和其他事件pygame.draw
:提供基本图形绘制功能pygame.image
:加载和处理图像pygame.mixer
:处理音频播放pygame.font
:渲染文本pygame.time
:控制时间和帧率pygame.Rect
:处理矩形区域(对碰撞检测很有用)
在我们的吃豆人游戏中,将主要使用这些模块来实现各种功能。
2. 游戏设计与规划
2.1 游戏规则设计
首先,让我们确定我们的吃豆人游戏的基本规则:
- 玩家控制吃豆人在迷宫中移动,目标是吃掉所有的豆子
- 迷宫中有四个会追逐吃豆人的幽灵
- 如果幽灵碰到吃豆人,吃豆人会失去一条生命
- 玩家初始有3条生命,全部失去后游戏结束
- 吃掉小豆子可以获得10分
- 吃掉大豆子(能量豆)可以获得50分,并且能暂时让幽灵变成可食用状态
- 在幽灵处于可食用状态时,吃豆人可以吃掉幽灵获得200分
- 吃掉所有豆子后,玩家获胜
2.2 游戏对象规划
我们的游戏需要以下几种主要对象:
-
吃豆人(Pac-Man) :
- 属性:位置、方向、速度、生命值、分数
- 行为:移动、改变方向、吃豆子、与幽灵交互
-
幽灵(Ghost) :
- 属性:位置、颜色、方向、速度、状态(普通/可食用)
- 行为:移动、追逐吃豆人、在被吃后重生
-
迷宫(Maze) :
- 属性:网格布局(墙壁和通道)
- 用途:限制角色移动范围,提供游戏环境
-
豆子(Dots) :
- 小豆子:被吃后加10分
- 大豆子(能量豆):被吃后加50分并激活幽灵的可食用状态
-
游戏管理器:
- 控制游戏状态(运行中、暂停、游戏结束)
- 管理得分系统
- 处理游戏逻辑(如关卡切换、胜利条件检查)
2.3 技术方案选择
对于我们的吃豆人游戏,我们将采用以下技术方案:
- 游戏引擎:Pygame(提供图形渲染、输入处理等基础功能)
- 图形表示:使用简单的几何图形(圆形、矩形等)绘制游戏元素
- 碰撞检测:基于距离计算的简单碰撞检测方法
- AI算法:简化的追逐算法,幽灵会有一定概率朝吃豆人的方向移动
- 地图表示:使用二维数组表示迷宫布局,1表示墙壁,0表示通道
这种方案适合初学者理解,同时也能实现一个功能完整的吃豆人游戏。
3. 创建游戏窗口与初始化
3.1 初始化Pygame环境
首先,我们需要导入必要的模块并初始化Pygame环境:
python
import pygame
import random
import math# 初始化 Pygame
pygame.init()
pygame.init()
函数初始化所有Pygame模块,这是使用Pygame的第一步。
3.2 设置游戏窗口
接下来,我们创建游戏窗口并设置标题:
python
# 设置游戏窗口
WIDTH, HEIGHT = 800, 600
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pac-Man 游戏")
这里我们创建了一个800x600像素的游戏窗口,并将其标题设置为"Pac-Man 游戏"。pygame.display.set_mode()
函数返回一个Surface对象,我们将使用这个对象来绘制游戏元素。
3.3 定义颜色和游戏参数
为了使代码更清晰,我们定义了一些常用颜色和游戏参数:
python
# 颜色定义
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PINK = (255, 192, 203)
ORANGE = (255, 165, 0)
CYAN = (0, 255, 255)# 游戏参数
CELL_SIZE = 30
GRID_WIDTH = WIDTH // CELL_SIZE
GRID_HEIGHT = HEIGHT // CELL_SIZE
颜色在Pygame中用RGB元组表示,例如(255, 255, 0)表示黄色。
CELL_SIZE
定义了游戏网格中每个单元格的大小(30像素),GRID_WIDTH
和GRID_HEIGHT
计算了游戏窗口可以容纳的网格数量。这种网格系统将帮助我们更容易地放置游戏对象并处理碰撞检测。
3.4 初始化游戏时钟
Pygame提供了一个Clock对象来控制游戏的帧率:
clock = pygame.time.Clock()
在游戏循环中,我们将使用clock.tick(60)
来确保游戏以约60帧每秒的速度运行。这对于保持游戏运行速度一致非常重要,无论游戏运行在什么样的硬件上。
4. 吃豆人角色设计与实现
4.1 吃豆人类设计
我们创建一个PacMan
类来封装吃豆人的属性和行为:
python
class PacMan:def __init__(self):self.x = GRID_WIDTH // 2self.y = GRID_HEIGHT // 2self.direction = "right"self.speed = 0.1self.mouth_open = Trueself.mouth_counter = 0self.score = 0self.lives = 3
这个初始化方法设置了吃豆人的起始位置(在网格中心),方向(向右),速度,嘴巴状态(用于动画),分数和生命值。
4.2 实现吃豆人移动
让我们添加一个方法来处理吃豆人的移动:
python
def move(self, direction, grid):new_x, new_y = self.x, self.yif direction == "right":new_x += self.speedelif direction == "left":new_x -= self.speedelif direction == "up":new_y -= self.speedelif direction == "down":new_y += self.speed# 检查是否能移动(不与墙碰撞)cell_x, cell_y = int(new_x), int(new_y)if 0 <= cell_x < GRID_WIDTH and 0 <= cell_y < GRID_HEIGHT and grid[cell_y][cell_x] != 1:self.x, self.y = new_x, new_yself.direction = direction# 更新嘴巴动画self.mouth_counter += 1if self.mouth_counter >= 10:self.mouth_counter = 0self.mouth_open = not self.mouth_open
这个方法接受一个方向参数和迷宫网格,然后尝试向该方向移动吃豆人。它首先计算吃豆人的新位置,然后检查这个位置是否有效(在网格范围内且不是墙壁)。如果有效,就更新吃豆人的位置和方向。
此外,这个方法还更新了嘴巴的动画状态,每10帧切换一次嘴巴的开合状态,这将创建吃豆人标志性的"吃"的动画效果。
4.3 绘制吃豆人
接下来,我们需要一个方法来绘制吃豆人:
python
def draw(self, win):x = int(self.x * CELL_SIZE + CELL_SIZE // 2)y = int(self.y * CELL_SIZE + CELL_SIZE // 2)radius = CELL_SIZE // 2# 绘制 Pac-Manif self.mouth_open:# 嘴巴张开if self.direction == "right":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y), (x + radius, y - radius // 2), (x + radius, y + radius // 2)])elif self.direction == "left":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y), (x - radius, y - radius // 2), (x - radius, y + radius // 2)])elif self.direction == "up":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y), (x - radius // 2, y - radius), (x + radius // 2, y - radius)])elif self.direction == "down":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y), (x - radius // 2, y + radius), (x + radius // 2, y + radius)])else:# 嘴巴闭合pygame.draw.circle(win, YELLOW, (x, y), radius)
这个方法使用Pygame的绘图函数来绘制吃豆人。当嘴巴打开时,我们绘制一个黄色圆形和一个黑色三角形(表示嘴巴),三角形的位置根据吃豆人的方向而改变。当嘴巴闭合时,我们只绘制一个完整的黄色圆形。
5. 幽灵角色设计与AI实现
5.1 幽灵类设计
现在让我们创建一个Ghost
类来处理幽灵的行为:
python
class Ghost:def __init__(self, x, y, color):self.x = xself.y = yself.color = colorself.direction = random.choice(["right", "left", "up", "down"])self.speed = 0.05self.frightened = False
每个幽灵都有一个位置、颜色、方向、速度和状态(是否处于"惊吓"状态)。我们将创建四个不同颜色的幽灵,每个幽灵都有自己的起始位置。
5.2 幽灵AI行为实现
幽灵的关键行为是在迷宫中移动并尝试追逐吃豆人。我们实现了一个简单的AI系统:
python
def move(self, grid, pacman):directions = ["right", "left", "up", "down"]# 简单的 AI - 有 80% 概率朝向 Pac-Man,20% 概率随机移动if random.random() < 0.8 and not self.frightened:# 寻找 Pac-Man 的方向if pacman.x > self.x and "right" in directions:self.direction = "right"elif pacman.x < self.x and "left" in directions:self.direction = "left"elif pacman.y > self.y and "down" in directions:self.direction = "down"elif pacman.y < self.y and "up" in directions:self.direction = "up"else:# 随机选择方向self.direction = random.choice(directions)# 移动幽灵new_x, new_y = self.x, self.yif self.direction == "right":new_x += self.speedelif self.direction == "left":new_x -= self.speedelif self.direction == "up":new_y -= self.speedelif self.direction == "down":new_y += self.speed# 检查是否能移动(不与墙碰撞)cell_x, cell_y = int(new_x), int(new_y)if 0 <= cell_x < GRID_WIDTH and 0 <= cell_y < GRID_HEIGHT and grid[cell_y][cell_x] != 1:self.x, self.y = new_x, new_yelse:# 如果碰到墙,选择新方向self.direction = random.choice(directions)
这个方法实现了一个简单但有效的AI:
- 正常状态下,幽灵有80%的概率朝着吃豆人的方向移动,20%的概率随机移动
- 当幽灵处于"惊吓"状态时,它们只会随机移动
- 如果幽灵碰到墙壁,它会选择一个新的随机方向
这种AI行为创造了一种挑战性但不是不可战胜的游戏体验。
5.3 绘制幽灵
幽灵的外观是游戏中的重要视觉元素,我们需要一个方法来绘制它们:
python
def draw(self, win):x = int(self.x * CELL_SIZE + CELL_SIZE // 2)y = int(self.y * CELL_SIZE + CELL_SIZE // 2)radius = CELL_SIZE // 2# 绘制幽灵主体color = BLUE if self.frightened else self.color# 绘制幽灵的半圆顶部pygame.draw.circle(win, color, (x, y - radius // 3), radius)# 绘制幽灵的矩形底部pygame.draw.rect(win, color, (x - radius, y - radius // 3, radius * 2, radius))# 绘制幽灵底部的波浪形状wave_height = radius // 3pygame.draw.polygon(win, color, [(x - radius, y + radius * 2 // 3), # 左上角(x - radius * 2 // 3, y + radius * 2 // 3 - wave_height), # 第一个波谷(x - radius // 3, y + radius * 2 // 3), # 第一个波峰(x, y + radius * 2 // 3 - wave_height), # 第二个波谷(x + radius // 3, y + radius * 2 // 3), # 第二个波峰(x + radius * 2 // 3, y + radius * 2 // 3 - wave_height), # 第三个波谷(x + radius, y + radius * 2 // 3), # 右上角(x + radius, y + radius * 2 // 3 - radius), # 右下角(x - radius, y + radius * 2 // 3 - radius), # 左下角])# 绘制眼睛 (白色部分)eye_radius = radius // 3left_eye_x = x - radius // 2right_eye_x = x + radius // 2eye_y = y - radius // 3pygame.draw.circle(win, WHITE, (left_eye_x, eye_y), eye_radius)pygame.draw.circle(win, WHITE, (right_eye_x, eye_y), eye_radius)# 绘制眼球 (瞳孔)pupil_radius = eye_radius // 2# 根据方向移动眼球pupil_offset_x, pupil_offset_y = 0, 0if self.direction == "left":pupil_offset_x = -pupil_radius // 2elif self.direction == "right":pupil_offset_x = pupil_radius // 2elif self.direction == "up":pupil_offset_y = -pupil_radius // 2elif self.direction == "down":pupil_offset_y = pupil_radius // 2pygame.draw.circle(win, BLACK, (left_eye_x + pupil_offset_x, eye_y + pupil_offset_y), pupil_radius)pygame.draw.circle(win, BLACK, (right_eye_x + pupil_offset_x, eye_y + pupil_offset_y), pupil_radius)
这个方法使用多个形状来创建经典的幽灵外观:
- 半圆形顶部
- 矩形主体
- 波浪形底部
- 两个眼睛,眼球会根据幽灵的移动方向而变化位置
当幽灵处于"惊吓"状态时,它们会变成蓝色,让玩家知道现在可以吃掉它们。
6. 迷宫生成与渲染
6.1 迷宫表示方法
在我们的游戏中,迷宫被表示为一个二维数组,其中:
- 0表示空白区域(可以移动)
- 1表示墙壁(不可移动)
我们实现了一个函数来创建一个随机迷宫:
python
def create_maze():grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]# 添加边界for x in range(GRID_WIDTH):grid[0][x] = 1grid[GRID_HEIGHT - 1][x] = 1for y in range(GRID_HEIGHT):grid[y][0] = 1grid[y][GRID_WIDTH - 1] = 1# 添加随机墙壁for _ in range(GRID_WIDTH * GRID_HEIGHT // 10):x = random.randint(1, GRID_WIDTH - 2)y = random.randint(1, GRID_HEIGHT - 2)grid[y][x] = 1# 确保 Pac-Man 的起始位置是空的grid[GRID_HEIGHT // 2][GRID_WIDTH // 2] = 0return grid
这个函数首先创建一个全是0的网格,然后:
- 在网格的边缘添加墙壁,形成一个封闭的区域
- 在网格内部随机添加一些墙壁,数量约为网格总单元数的10%
- 确保吃豆人的起始位置(网格中心)是空的
这种方法生成的迷宫每次游戏都不同,增加了游戏的可重玩性。
6.2 迷宫渲染
我们需要一个方法来绘制迷宫:
python
# 绘制迷宫
for y in range(GRID_HEIGHT):for x in range(GRID_WIDTH):if grid[y][x] == 1:pygame.draw.rect(win, BLUE, [x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE])
这段代码遍历整个网格,当遇到值为1的单元格时,在相应位置绘制一个蓝色矩形表示墙壁。
7. 游戏物品:豆子与能量豆
7.1 豆子生成
在我们的游戏中,有两种豆子:普通豆子和能量豆(大豆子)。我们实现了一个函数来在迷宫中生成这些豆子:
python
def create_dots(grid):dots = []big_dots = []for y in range(GRID_HEIGHT):for x in range(GRID_WIDTH):if grid[y][x] == 0 and (x != GRID_WIDTH // 2 or y != GRID_HEIGHT // 2): # 避免在Pac-Man起始位置放置豆子# 15% 概率创建大豆子,85% 概率创建小豆子if random.random() < 0.15:big_dots.append((x, y))else:dots.append((x, y))return dots, big_dots
这个函数遍历网格中的所有空白单元格(值为0),然后:
- 避免在吃豆人的起始位置放置豆子
- 有15%的概率在该位置放置一个能量豆
- 有85%的概率放置一个普通豆子
函数返回两个列表,分别包含普通豆子和能量豆的位置。
7.2 豆子渲染
接下来,我们需要在游戏中绘制这些豆子:
python
# 绘制小豆子
for x, y in dots:pygame.draw.circle(win, WHITE, (x * CELL_SIZE + CELL_SIZE // 2, y * CELL_SIZE + CELL_SIZE // 2), CELL_SIZE // 10)# 绘制大豆子
for x, y in big_dots:pygame.draw.circle(win, WHITE, (x * CELL_SIZE + CELL_SIZE // 2, y * CELL_SIZE + CELL_SIZE // 2), CELL_SIZE // 5)
普通豆子被绘制为小白色圆点,而能量豆被绘制为较大的白色圆点。
7.3 豆子与吃豆人的交互
当吃豆人经过豆子的位置时,我们需要检测这种碰撞并作出相应反应:
python
# 检查吃豆子
pacman_cell_x, pacman_cell_y = int(pacman.x), int(pacman.y)# 小豆子
for i, (x, y) in enumerate(dots[:]):if x == pacman_cell_x and y == pacman_cell_y:dots.remove((x, y))pacman.score += 10# 大豆子
for i, (x, y) in enumerate(big_dots[:]):if x == pacman_cell_x and y == pacman_cell_y:big_dots.remove((x, y))pacman.score += 50# 幽灵进入惊恐状态frightened_timer = 300 # 约5秒for ghost in ghosts:ghost.frightened = True
这段代码检查吃豆人当前所在的网格单元是否有豆子:
- 如果有普通豆子,移除该豆子并增加10分
- 如果有能量豆,移除该豆子,增加50分,并让所有幽灵进入"惊吓"状态
8. 碰撞检测系统
8.1 角色与墙壁的碰撞检测
在我们的游戏中,角色不能穿过墙壁。我们在move
方法中实现了这种碰撞检测:
# 检查是否能移动(不与墙碰撞)
cell_x, cell_y = int(new_x), int(new_y)
if 0 <= cell_x < GRID_WIDTH and 0 <= cell_y < GRID_HEIGHT and grid[cell_y][cell_x] != 1:self.x, self.y = new_x, new_yself.direction = direction
else:# 如果是幽灵撞墙,就选择新方向if isinstance(self, Ghost):self.direction = random.choice(["right", "left", "up", "down"])
这段代码先检查预期的新位置是否在网格范围内,然后检查该位置是否是墙壁(值为1)。如果不是墙壁,角色可以移动到新位置;如果是墙壁,则不允许移动。特别地,如果是幽灵撞到墙壁,它会选择一个新的随机方向。
8.2 吃豆人与幽灵的碰撞检测
吃豆人与幽灵之间的碰撞检测是游戏的核心机制之一。我们使用基于距离的碰撞检测方法:
python
# 检查与幽灵碰撞
for ghost in ghosts:distance = math.sqrt((pacman.x - ghost.x) ** 2 + (pacman.y - ghost.y) ** 2)if distance < 0.7: # 碰撞阈值if ghost.frightened:# Pac-Man 吃掉幽灵ghost.x, ghost.y = random.randint(1, GRID_WIDTH - 2), random.randint(1, GRID_HEIGHT - 2)ghost.frightened = Falsepacman.score += 200else:# 幽灵吃掉 Pac-Manpacman.lives -= 1pacman.x, pacman.y = GRID_WIDTH // 2, GRID_HEIGHT // 2if pacman.lives <= 0:game_over = True
这段代码计算吃豆人和每个幽灵之间的欧几里得距离。如果距离小于阈值(0.7个网格单位),则认为发生了碰撞。碰撞的结果取决于幽灵的状态:
- 如果幽灵处于"惊吓"状态,吃豆人会吃掉幽灵,获得200分,幽灵会重生在一个随机位置
- 如果幽灵处于正常状态,吃豆人会失去一条生命,并重置到起始位置。如果吃豆人的生命值降到0,游戏结束
8.3 吃豆人与豆子的碰撞检测
我们已经在第7.3节中介绍了吃豆人与豆子的碰撞检测。为了完整性,这里再次展示相关代码:
python
# 检查吃豆子
pacman_cell_x, pacman_cell_y = int(pacman.x), int(pacman.y)# 小豆子
for i, (x, y) in enumerate(dots[:]):if x == pacman_cell_x and y == pacman_cell_y:dots.remove((x, y))pacman.score += 10# 大豆子
for i, (x, y) in enumerate(big_dots[:]):if x == pacman_cell_x and y == pacman_cell_y:big_dots.remove((x, y))pacman.score += 50# 幽灵进入惊恐状态frightened_timer = 300 # 约5秒for ghost in ghosts:ghost.frightened = True
这种碰撞检测基于网格位置:如果吃豆人当前所在的网格单元与豆子的位置相同,则认为吃豆人吃到了豆子。
9. 游戏状态管理
9.1 游戏状态定义
我们的游戏有几种不同的状态:
- 游戏运行中
- 游戏暂停
- 游戏结束(玩家胜利或失败)
我们使用布尔变量game_over
来跟踪游戏是否结束:
python
running = True # 游戏程序是否继续运行
game_over = False # 当前游戏是否结束
9.2 游戏状态切换
游戏状态可以通过几种方式切换:
- 游戏结束:当玩家失去所有生命或吃掉所有豆子时
python
# 检查游戏胜利
if len(dots) == 0 and len(big_dots) == 0:game_over = True# 或者当玩家失去所有生命时
if pacman.lives <= 0:game_over = True
- 重新开始游戏:当游戏结束后按下回车键
python
if event.type == pygame.KEYDOWN:if event.key == pygame.K_RETURN and game_over:# 重新开始游戏pacman, grid, dots, big_dots, ghosts = init_game()game_over = False
- 退出游戏:当玩家关闭游戏窗口时
python
if event.type == pygame.QUIT:running = False
9.3 幽灵状态管理
幽灵有两种状态:正常状态和"惊吓"状态。我们使用frightened
属性和一个计时器来管理这种状态:
python
# 处理幽灵惊恐状态
if frightened_timer > 0:frightened_timer -= 1if frightened_timer == 0:for ghost in ghosts:ghost.frightened = False
当吃豆人吃到能量豆时,所有幽灵进入"惊吓"状态,并设置一个计时器(300帧,约5秒)。每帧都会减少计时器的值,当计时器归零时,所有幽灵回到正常状态。
10. 游戏UI与视觉效果
10.1 分数和生命值显示
我们在游戏界面顶部显示玩家的分数和剩余生命值:
python
# 绘制分数和生命值
font = pygame.font.SysFont(None, 36)
score_text = font.render(f"分数: {pacman.score}", True, WHITE)
lives_text = font.render(f"生命: {pacman.lives}", True, WHITE)
win.blit(score_text, (10, 10))
win.blit(lives_text, (WIDTH - 110, 10))
这段代码创建两个文本Surface对象,分别显示分数和生命值,然后将它们绘制在游戏窗口的顶部。
10.2 游戏结束画面
当游戏结束时,我们显示一个游戏结束画面,告诉玩家游戏结果并提供重新开始的提示:
python
# 游戏结束显示
if game_over:font = pygame.font.SysFont(None, 72)if pacman.lives <= 0:game_over_text = font.render("游戏结束!", True, RED)else:game_over_text = font.render("恭喜你赢了!", True, YELLOW)restart_text = font.render("按 Enter 重新开始", True, WHITE)win.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 50))win.blit(restart_text, (WIDTH // 2 - restart_text.get_width() // 2, HEIGHT // 2 + 50))
根据游戏结束的原因(玩家胜利或失败),显示不同的信息。游戏胜利时显示黄色的"恭喜你赢了!",失败时显示红色的"游戏结束!"。同时,提示玩家按Enter键重新开始游戏。
10.3 角色动画
为了增加游戏的视觉吸引力,我们为吃豆人和幽灵添加了简单的动画效果:
- 吃豆人的嘴巴动画:吃豆人的嘴巴会周期性地开合,创造出经典的"吃"的效果
python
# 更新嘴巴动画
self.mouth_counter += 1
if self.mouth_counter >= 10:self.mouth_counter = 0self.mouth_open = not self.mouth_open
- 幽灵的眼球动画:幽灵的眼球会根据移动方向改变位置
python
# 根据方向移动眼球
pupil_offset_x, pupil_offset_y = 0, 0
if self.direction == "left":pupil_offset_x = -pupil_radius // 2
elif self.direction == "right":pupil_offset_x = pupil_radius // 2
elif self.direction == "up":pupil_offset_y = -pupil_radius // 2
elif self.direction == "down":pupil_offset_y = pupil_radius // 2
这些动画效果虽小,但大大增加了游戏的视觉体验和角色的生动感。
11. 游戏主循环与事件处理
11.1 游戏主循环结构
游戏主循环是游戏程序的核心,负责处理输入、更新游戏状态和渲染画面。我们的主循环结构如下:
python
def main():clock = pygame.time.Clock()pacman, grid, dots, big_dots, ghosts = init_game()frightened_timer = 0running = Truegame_over = Falsewhile running:clock.tick(60) # 限制帧率为60FPS# 事件处理for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseif event.type == pygame.KEYDOWN:if event.key == pygame.K_RETURN and game_over:# 重新开始游戏pacman, grid, dots, big_dots, ghosts = init_game()game_over = Falseif not game_over:# 处理输入keys = pygame.key.get_pressed()# 处理吃豆人移动if keys[pygame.K_RIGHT]:pacman.move("right", grid)elif keys[pygame.K_LEFT]:pacman.move("left", grid)elif keys[pygame.K_UP]:pacman.move("up", grid)elif keys[pygame.K_DOWN]:pacman.move("down", grid)# 更新游戏状态# 幽灵移动for ghost in ghosts:ghost.move(grid, pacman)# 检查吃豆子# ...# 检查碰撞# ...# 检查游戏胜利# ...# 渲染draw_game(win, pacman, grid, dots, big_dots, ghosts)# 游戏结束显示# ...pygame.quit()
这个结构遵循了典型的游戏循环模式:
- 限制帧率
- 处理事件
- 根据输入更新游戏状态
- 渲染当前游戏状态
- 处理特殊情况(如游戏结束)
- 如此循环直到游戏退出
11.2 事件处理
Pygame使用事件队列来处理用户输入和其他事件。我们的事件处理代码如下:
python
for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseif event.type == pygame.KEYDOWN:if event.key == pygame.K_RETURN and game_over:# 重新开始游戏pacman, grid, dots, big_dots, ghosts = init_game()game_over = False
这段代码处理两种事件:
pygame.QUIT
事件(当玩家关闭游戏窗口时触发)pygame.KEYDOWN
事件,特别是检查游戏结束时按下回车键重新开始游戏
11.3 键盘输入处理
除了事件队列外,我们还使用pygame.key.get_pressed()
函数来检测当前按下的键,这适用于需要持续检测的输入,如方向键控制:
python
keys = pygame.key.get_pressed()# 处理吃豆人移动
if keys[pygame.K_RIGHT]:pacman.move("right", grid)
elif keys[pygame.K_LEFT]:pacman.move("left", grid)
elif keys[pygame.K_UP]:pacman.move("up", grid)
elif keys[pygame.K_DOWN]:pacman.move("down", grid)
这段代码检查方向键(上、下、左、右)是否被按下,并据此移动吃豆人。使用elif
确保每帧只处理一个方向,优先级从右到左再到上再到下。
12. 代码优化与性能改进
12.1 碰撞检测优化
在我们的游戏中,碰撞检测是一个频繁执行的操作。为了提高性能,我们可以采取以下优化措施:
- 减少不必要的计算:只在距离较近时才进行精确的碰撞检测
python
# 优化前
distance = math.sqrt((pacman.x - ghost.x) ** 2 + (pacman.y - ghost.y) ** 2)
if distance < 0.7:# 处理碰撞# 优化后
dx = pacman.x - ghost.x
dy = pacman.y - ghost.y
# 避免开方运算,直接比较平方
if dx*dx + dy*dy < 0.7*0.7:# 处理碰撞
- 使用网格位置进行初步筛选:对于豆子的碰撞检测,我们只检查吃豆人当前所在的网格单元,而不是所有豆子
python
# 优化前
for dot in dots:if collision(pacman, dot):# 处理碰撞# 优化后
pacman_cell_x, pacman_cell_y = int(pacman.x), int(pacman.y)
for x, y in dots[:]:if x == pacman_cell_x and y == pacman_cell_y:# 处理碰撞
12.2 渲染优化
渲染是游戏中另一个性能关键点。我们可以通过以下方式优化渲染过程:
- 只渲染可见区域:如果游戏地图非常大,只渲染当前屏幕可见的部分
python
# 计算可见区域
view_left = max(0, int(pacman.x) - VIEW_RANGE)
view_right = min(GRID_WIDTH, int(pacman.x) + VIEW_RANGE + 1)
view_top = max(0, int(pacman.y) - VIEW_RANGE)
view_bottom = min(GRID_HEIGHT, int(pacman.y) + VIEW_RANGE + 1)# 只渲染可见区域
for y in range(view_top, view_bottom):for x in range(view_left, view_right):# 渲染网格[y][x]
- 减少渲染调用:合并相同类型的渲染操作,减少API调用次数
python
# 优化前
for dot in dots:draw_dot(dot)# 优化后
# 创建一个Surface来预渲染所有豆子
dots_surface = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
for dot in dots:draw_dot(dots_surface, dot)
# 一次性将所有豆子绘制到屏幕上
win.blit(dots_surface, (0, 0))
12.3 内存管理
良好的内存管理对于游戏性能也很重要:
- 避免内存泄漏:确保不再需要的对象被正确清理
python
# 游戏退出时清理资源
pygame.quit()
- 重用对象:避免频繁创建和销毁临时对象
python
# 不好的做法:每次创建新字体对象
font = pygame.font.SysFont(None, 36)
score_text = font.render(f"分数: {pacman.score}", True, WHITE)# 更好的做法:只创建一次字体对象,重复使用
# 在游戏初始化时
self.font = pygame.font.SysFont(None, 36)# 在渲染时
score_text = self.font.render(f"分数: {pacman.score}", True, WHITE)
- 使用适当的数据结构:选择适合操作类型的数据结构
python
# 对于经常需要检查成员关系的集合,使用set而不是list
dots = set((x, y) for x in range(GRID_WIDTH) for y in range(GRID_HEIGHT) if grid[y][x] == 0)# 检查成员关系
if (pacman_cell_x, pacman_cell_y) in dots:dots.remove((pacman_cell_x, pacman_cell_y))pacman.score += 10
13. 完整代码
以下是我们吃豆人游戏的完整代码:
import pygame
import random
import math# 初始化 Pygame
pygame.init()# 设置游戏窗口
WIDTH, HEIGHT = 800, 600
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pac-Man 游戏")# 颜色定义
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
PINK = (255, 192, 203)
ORANGE = (255, 165, 0)
CYAN = (0, 255, 255)# 游戏参数
CELL_SIZE = 30
GRID_WIDTH = WIDTH // CELL_SIZE
GRID_HEIGHT = HEIGHT // CELL_SIZE# Pac-Man 参数
class PacMan:def __init__(self):self.x = GRID_WIDTH // 2self.y = GRID_HEIGHT // 2self.direction = "right"self.speed = 0.1self.mouth_open = Trueself.mouth_counter = 0self.score = 0self.lives = 3def move(self, direction, grid):new_x, new_y = self.x, self.yif direction == "right":new_x += self.speedelif direction == "left":new_x -= self.speedelif direction == "up":new_y -= self.speedelif direction == "down":new_y += self.speed# 检查是否能移动(不与墙碰撞)cell_x, cell_y = int(new_x), int(new_y)if 0 <= cell_x < GRID_WIDTH and 0 <= cell_y < GRID_HEIGHT and grid[cell_y][cell_x] != 1:self.x, self.y = new_x, new_yself.direction = direction# 更新嘴巴动画self.mouth_counter += 1if self.mouth_counter >= 10:self.mouth_counter = 0self.mouth_open = not self.mouth_opendef draw(self, win):x = int(self.x * CELL_SIZE + CELL_SIZE // 2)y = int(self.y * CELL_SIZE + CELL_SIZE // 2)radius = CELL_SIZE // 2# 绘制 Pac-Manif self.mouth_open:# 嘴巴张开if self.direction == "right":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y),(x + radius, y - radius // 2),(x + radius, y + radius // 2)])elif self.direction == "left":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y),(x - radius, y - radius // 2),(x - radius, y + radius // 2)])elif self.direction == "up":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y),(x - radius // 2, y - radius),(x + radius // 2, y - radius)])elif self.direction == "down":pygame.draw.circle(win, YELLOW, (x, y), radius)pygame.draw.polygon(win, BLACK, [(x, y),(x - radius // 2, y + radius),(x + radius // 2, y + radius)])else:# 嘴巴闭合pygame.draw.circle(win, YELLOW, (x, y), radius)# 幽灵参数
class Ghost:def __init__(self, x, y, color):self.x = xself.y = yself.color = colorself.direction = random.choice(["right", "left", "up", "down"])self.speed = 0.05self.frightened = Falsedef move(self, grid, pacman):directions = ["right", "left", "up", "down"]# 简单的 AI - 有 80% 概率朝向 Pac-Man,20% 概率随机移动if random.random() < 0.8 and not self.frightened:# 寻找 Pac-Man 的方向if pacman.x > self.x and "right" in directions:self.direction = "right"elif pacman.x < self.x and "left" in directions:self.direction = "left"elif pacman.y > self.y and "down" in directions:self.direction = "down"elif pacman.y < self.y and "up" in directions:self.direction = "up"else:# 随机选择方向self.direction = random.choice(directions)# 移动幽灵new_x, new_y = self.x, self.yif self.direction == "right":new_x += self.speedelif self.direction == "left":new_x -= self.speedelif self.direction == "up":new_y -= self.speedelif self.direction == "down":new_y += self.speed# 检查是否能移动(不与墙碰撞)cell_x, cell_y = int(new_x), int(new_y)if 0 <= cell_x < GRID_WIDTH and 0 <= cell_y < GRID_HEIGHT and grid[cell_y][cell_x] != 1:self.x, self.y = new_x, new_yelse:# 如果碰到墙,选择新方向self.direction = random.choice(directions)def draw(self, win):x = int(self.x * CELL_SIZE + CELL_SIZE // 2)y = int(self.y * CELL_SIZE + CELL_SIZE // 2)radius = CELL_SIZE // 2# 绘制幽灵主体color = BLUE if self.frightened else self.color# 绘制幽灵的半圆顶部pygame.draw.circle(win, color, (x, y - radius // 3), radius)# 绘制幽灵的矩形底部pygame.draw.rect(win, color, (x - radius, y - radius // 3, radius * 2, radius))# 绘制幽灵底部的波浪形状wave_height = radius // 3pygame.draw.polygon(win, color, [(x - radius, y + radius * 2 // 3), # 左上角(x - radius * 2 // 3, y + radius * 2 // 3 - wave_height), # 第一个波谷(x - radius // 3, y + radius * 2 // 3), # 第一个波峰(x, y + radius * 2 // 3 - wave_height), # 第二个波谷(x + radius // 3, y + radius * 2 // 3), # 第二个波峰(x + radius * 2 // 3, y + radius * 2 // 3 - wave_height), # 第三个波谷(x + radius, y + radius * 2 // 3), # 右上角(x + radius, y + radius * 2 // 3 - radius), # 右下角(x - radius, y + radius * 2 // 3 - radius), # 左下角])# 绘制眼睛 (白色部分)eye_radius = radius // 3left_eye_x = x - radius // 2right_eye_x = x + radius // 2eye_y = y - radius // 3pygame.draw.circle(win, WHITE, (left_eye_x, eye_y), eye_radius)pygame.draw.circle(win, WHITE, (right_eye_x, eye_y), eye_radius)# 绘制眼球 (瞳孔)pupil_radius = eye_radius // 2# 根据方向移动眼球pupil_offset_x, pupil_offset_y = 0, 0if self.direction == "left":pupil_offset_x = -pupil_radius // 2elif self.direction == "right":pupil_offset_x = pupil_radius // 2elif self.direction == "up":pupil_offset_y = -pupil_radius // 2elif self.direction == "down":pupil_offset_y = pupil_radius // 2pygame.draw.circle(win, BLACK, (left_eye_x + pupil_offset_x, eye_y + pupil_offset_y), pupil_radius)pygame.draw.circle(win, BLACK, (right_eye_x + pupil_offset_x, eye_y + pupil_offset_y), pupil_radius)# 创建迷宫
def create_maze():grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]# 添加边界for x in range(GRID_WIDTH):grid[0][x] = 1grid[GRID_HEIGHT - 1][x] = 1for y in range(GRID_HEIGHT):grid[y][0] = 1grid[y][GRID_WIDTH - 1] = 1# 添加随机墙壁for _ in range(GRID_WIDTH * GRID_HEIGHT // 10):x = random.randint(1, GRID_WIDTH - 2)y = random.randint(1, GRID_HEIGHT - 2)grid[y][x] = 1# 确保 Pac-Man 的起始位置是空的grid[GRID_HEIGHT // 2][GRID_WIDTH // 2] = 0return grid# 创建豆子
def create_dots(grid):dots = []big_dots = []for y in range(GRID_HEIGHT):for x in range(GRID_WIDTH):if grid[y][x] == 0 and (x != GRID_WIDTH // 2 or y != GRID_HEIGHT // 2): # 避免在Pac-Man起始位置放置豆子# 15% 概率创建大豆子,85% 概率创建小豆子if random.random() < 0.15:big_dots.append((x, y))else:dots.append((x, y))return dots, big_dots# 游戏初始化
def init_game():pacman = PacMan()grid = create_maze()dots, big_dots = create_dots(grid)ghosts = [Ghost(1, 1, RED),Ghost(GRID_WIDTH - 2, 1, PINK),Ghost(1, GRID_HEIGHT - 2, ORANGE),Ghost(GRID_WIDTH - 2, GRID_HEIGHT - 2, CYAN)]return pacman, grid, dots, big_dots, ghosts# 绘制游戏
def draw_game(win, pacman, grid, dots, big_dots, ghosts):win.fill(BLACK)# 绘制迷宫for y in range(GRID_HEIGHT):for x in range(GRID_WIDTH):if grid[y][x] == 1:pygame.draw.rect(win, BLUE, [x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE])# 绘制小豆子for x, y in dots:pygame.draw.circle(win, WHITE, (x * CELL_SIZE + CELL_SIZE // 2, y * CELL_SIZE + CELL_SIZE // 2),CELL_SIZE // 10)# 绘制大豆子for x, y in big_dots:pygame.draw.circle(win, WHITE, (x * CELL_SIZE + CELL_SIZE // 2, y * CELL_SIZE + CELL_SIZE // 2), CELL_SIZE // 5)# 绘制 Pac-Manpacman.draw(win)# 绘制幽灵for ghost in ghosts:ghost.draw(win)# 绘制分数和生命值font = pygame.font.SysFont(None, 36)score_text = font.render(f"分数: {pacman.score}", True, WHITE)lives_text = font.render(f"生命: {pacman.lives}", True, WHITE)win.blit(score_text, (10, 10))win.blit(lives_text, (WIDTH - 110, 10))pygame.display.update()# 主游戏循环
def main():clock = pygame.time.Clock()pacman, grid, dots, big_dots, ghosts = init_game()frightened_timer = 0running = Truegame_over = Falsewhile running:clock.tick(60)for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseif event.type == pygame.KEYDOWN:if event.key == pygame.K_RETURN and game_over:# 重新开始游戏pacman, grid, dots, big_dots, ghosts = init_game()game_over = Falseif not game_over:keys = pygame.key.get_pressed()# 处理 Pac-Man 移动if keys[pygame.K_RIGHT]:pacman.move("right", grid)elif keys[pygame.K_LEFT]:pacman.move("left", grid)elif keys[pygame.K_UP]:pacman.move("up", grid)elif keys[pygame.K_DOWN]:pacman.move("down", grid)# 处理幽灵移动for ghost in ghosts:ghost.move(grid, pacman)# 检查吃豆子pacman_cell_x, pacman_cell_y = int(pacman.x), int(pacman.y)# 小豆子for i, (x, y) in enumerate(dots[:]):if x == pacman_cell_x and y == pacman_cell_y:dots.remove((x, y))pacman.score += 10# 大豆子for i, (x, y) in enumerate(big_dots[:]):if x == pacman_cell_x and y == pacman_cell_y:big_dots.remove((x, y))pacman.score += 50# 幽灵进入惊恐状态frightened_timer = 300 # 约5秒for ghost in ghosts:ghost.frightened = True# 处理幽灵惊恐状态if frightened_timer > 0:frightened_timer -= 1if frightened_timer == 0:for ghost in ghosts:ghost.frightened = False# 检查与幽灵碰撞for ghost in ghosts:distance = math.sqrt((pacman.x - ghost.x) ** 2 + (pacman.y - ghost.y) ** 2)if distance < 0.7: # 碰撞阈值if ghost.frightened:# Pac-Man 吃掉幽灵ghost.x, ghost.y = random.randint(1, GRID_WIDTH - 2), random.randint(1, GRID_HEIGHT - 2)ghost.frightened = Falsepacman.score += 200else:# 幽灵吃掉 Pac-Manpacman.lives -= 1pacman.x, pacman.y = GRID_WIDTH // 2, GRID_HEIGHT // 2if pacman.lives <= 0:game_over = True# 检查游戏胜利if len(dots) == 0 and len(big_dots) == 0:game_over = True# 绘制游戏draw_game(win, pacman, grid, dots, big_dots, ghosts)# 游戏结束显示if game_over:font = pygame.font.SysFont(None, 72)if pacman.lives <= 0:game_over_text = font.render("游戏结束!", True, RED)else:game_over_text = font.render("恭喜你赢了!", True, YELLOW)restart_text = font.render("按 Enter 重新开始", True, WHITE)win.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 50))win.blit(restart_text, (WIDTH // 2 - restart_text.get_width() // 2, HEIGHT // 2 + 50))pygame.display.update()pygame.quit()if __name__ == "__main__":main()
相关文章:
Pac-Man(吃豆人) 游戏
目录 前言 1. Pygame游戏开发基础 1.1 Pygame简介 1.2 游戏开发基本概念 1.3 Pygame核心模块介绍 2. 游戏设计与规划 2.1 游戏规则设计 2.2 游戏对象规划 2.3 技术方案选择 3. 创建游戏窗口与初始化 3.1 初始化Pygame环境 3.2 设置游戏窗口 3.3 定义颜色和游戏参数…...
Unity Dots从入门到精通 Mono和Dots通讯
文章目录 前言安装 DOTS 包Mono To DotsDots To Mono 前言 DOTS(面向数据的技术堆栈)是一套由 Unity 提供支持的技术,用于提供高性能游戏开发解决方案,特别适合需要处理大量数据的游戏,例如大型开放世界游戏。 本文讲…...
WLAN(无线局域网)安全
WLAN安全涉及到保护无线局域网免受各种威胁和攻击,以确保数据的保密性、完整性和可用性。以下是关于WLAN安全的多方面介绍: 一、主要安全威胁 窃听:攻击者利用特殊设备监听无线信号,获取传输中的数据,如用户的账号密…...
故障诊断——neo4j入门
文章目录 neo4jQuickStartDemo neo4j QuickStart 详情可见博客:https://www.cnblogs.com/nhdlb/p/18703804,使用docker拉取最近的一个版本进行创建 docker run -it -d -p 7474:7474 -p 7687:7687 \ -v /disk5/neo4j_docker/data:/data \ -v /disk5/ne…...
【商城实战(25)】解锁UniApp移动端适配秘籍,打造完美商城体验
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
Qt 数据库操作(Sqlite)
数据库简介 关于数据库的基础知识这里就不做介绍了,相关博客可以查看: SQL基础知识 数据库学霸笔记 上面博客都写的比较详细,本文主要介绍如何使用Qt进行数据库相关操作,数据库分为关系型数据库和非关系型数据,关系…...
LINUX 指令大全
Linux服务器上有许多常用的命令,可以帮助你管理文件、目录、进程、网络和系统配置等。以下是一些常用的Linux命令: 文件和目录管理 ls:列出当前目录中的文件和子目录 bash lspwd:显示当前工作目录的路径 bash pwdcd:切…...
【Synchronized】不同的使用场景和案例
【Synchronized】不同的使用场景和案例 【一】锁的作用范围与锁对象【1】实例方法(对象锁)【2】静态方法(类锁)【3】代码块(显式指定锁对象)【4】类锁(通过Class对象显式锁定) 【二】…...
华为欧拉操作系统安装Docker服务
华为欧拉 20.03 操作系统安装 Docker 服务 一、安装前准备 系统环境检查 确认当前运行的操作系统为华为欧拉 24.03。可通过在终端执行以下命令查看: cat /etc/os - release欧拉系统可以使用以下命令: cat /etc/openEuler-release确保系统已连接互联…...
告别复杂日志解析 用bin2sql轻松实现MySQL数据闪回
mysqlbinlog⼯具使用 use test; CREATE TABLE t1 (id INT(11) NOT NULL AUTO_INCREMENT,name VARCHAR(20) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;INSERT INTO t1(id, name) SELECT 101, tome101; INSERT INTO t1(id, name) SELECT 102, tome1…...
uniapp简单table表
<template><view class"container"><scroll-view scroll-x"true" scroll-y"true" class"table-scroll"><view class"table-header"><view class"table-cell fixed-column">序号<…...
prompt大师高效提示词解析
Prompt大师李继刚高效提示词示例解析 一、「汉语新解」提示词 核心结构 采用Lisp语言框架嵌套中文语义,通过(defun 新汉语老师 ()...)定义角色风格(融合奥斯卡王尔德、鲁迅的批判性语言),用(隐喻 (一针见血...))构建解释逻辑链。…...
uni-app如何发布项目为app_2025
参考大佬的:uni-app项目打包成apk(本地打包篇)_uniapp打包apk-CSDN博客 1、导入前配置 在 HBuilder X 中打开uni-app项目中的 mainifest.json 文件,appid没有的话可以点“重新获取” 2、打包 然后关注控制台,导出成功…...
MySQL与Canal、RabbitMQ集成指南
MySQL 部分 1. 查看是否开启 binlog MySQL 8 默认开启 binlog。可以通过以下命令查看是否开启: SHOW VARIABLES LIKE log_bin;如果返回结果为 ON,则表示 binlog 已开启。 Variable_nameValuelog_binON 2. 若未开启 binlog,则需手动配置 …...
新品发布|启英泰伦联合启明云端推出离在线语音大模型方案
当前,生成式大模型正以颠覆性姿态重塑人机交互的边界,并逐渐向终端场景渗透。然而,云端大模型在落地终端场景时面临两大挑战: 在真实噪声场景下容易听不清、误识别,影响交互准确性;云端处理冗余数据及大规…...
网编高级 day01
网编高级 day01 0. 大纲1. Modbus协议1.1. Modbus起源1.2. 分类1.3. Modbus TCP特点 2. Modbus TCP协议格式2.1. 报文头2.2. 寄存器2.3. 功能码 0. 大纲 协议: modbus协议:modbus TCP、modbus RTUhtml 网页:http协议Webserver 工具&#x…...
2001-2023年上市公司数字化转型年报词频统计(年报词频统计和MDA词频统计两种方式)(吴非、赵宸宇、甄红线300+关键词三种方法)
2001-2023年上市公司数字化转型年报词频统计(年报词频统计和MD&A词频统计两种方式)(吴非、赵宸宇、甄红线300关键词三种方法) 1、时间:2001-2023年 2、来源:上市公司年报 3、参考文献: …...
数据分析与AI丨AI Fabric:数据和人工智能架构的未来
AI Fabric 架构是模块化、可扩展且面向未来的,是现代商业环境中企业实现卓越的关键。 在当今商业环境中,数据分析和人工智能领域发展可谓日新月异。几乎每天都有新兴技术诞生,新的应用场景不断涌现,前沿探索持续拓展。可遗憾的是&…...
MQ消息发送不在MySQL事务中,该如何保证一致性?
在 MQ 消息发送与 MySQL 事务分离的场景下,可通过以下方案保障数据一致性: 一、核心原则 确保 业务操作成功 与 消息发送成功 的最终一致性,避免因网络抖动、服务宕机等异常导致以下问题: 场景1:业务操作成功但消息未…...
[rust] rust学习
rust学习 1. 项目组织结构 工程 # 创建一个工程 cargo new my-project工作空间 在 Rust 中,工作空间(Workspace) 是一个包含多个 Rust 项目的共享环境,用于管理多个 crate(库或可执行文件)。它允许多个…...
艾尔登复刻Ep1——客户端制作、场景切换、网络控制
需要添加的插件内容 Netcode for GameObjects:是一个为 Unity 游戏开发提供高级网络功能的 SDK。它的主要作用是允许开发者在其 GameObject 和 MonoBehaviour 工作流中集成网络功能,并且可以与多种底层传输层协议兼容。 具体内容请看:https:…...
正则表达式(复习)
文章目录 一、[]: 一个字符集合二、{}: 重复次数三、特殊符号四、(): 分组五、python代码示例六、注意 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个…...
密码学笔记
密码学 一、密码学基础概念 1. CIA三要素 机密性 (Confidentiality):信息不被未授权者访问。 例子:用钥匙锁住日记本,只有你有钥匙。 完整性 (Integrity):信息在传输/存储中不被篡改。 例子:快递包裹封条,…...
C#结构体(Struct)详解
在 C# 中,结构体(struct) 是一种值类型数据类型,适用于封装小型数据组。与类(class)不同,结构体在栈(Stack)上分配内存,且赋值时会发生值复制。以下是结构…...
Tomato靶机通关攻略
1.安装并开启靶机 2.用Kali查询靶机IP Kali 的IP 靶机的IP 3.访问靶机 4.用御剑扫描端口 5.获取敏感目录 分别访问三个目录 6.查看目录 发现info.php并进入 查看源码,发现文件包含漏洞 利用漏洞查看日志文件 http://192.168.40.139/antibot_image/antibots/info…...
Go Ebiten小游戏开发:俄罗斯方块
在这篇文章中,我们将一起开发一个简单的俄罗斯方块游戏,使用Go语言和Ebiten游戏库。Ebiten是一个轻量级的游戏库,适合快速开发2D游戏。我们将逐步构建游戏的基本功能,包括游戏逻辑、图形绘制和用户输入处理。 项目结构 我们的项…...
Github 2025-03-12 C开源项目日报Top5
根据Github Trendings的统计,今日(2025-03-12统计)共有5个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目5C++项目1Lean的LEDE源码:为国产龙芯LOONGSON SoC loongarch64/飞腾Phytium腾锐2000系列架构添加支持 创建周期:2338 天开发语言:C协议类…...
【机器学习-基础知识】统计和贝叶斯推断
1. 概率论基本概念回顾 1. 概率分布 定义: 概率分布(Probability Distribution)指的是随机变量所有可能取值及其对应概率的集合。它描述了一个随机变量可能取的所有值以及每个值被取到的概率。 对于离散型随机变量,使用概率质量函数来描述。对于连续型随机变量,使用概率…...
Unity3D 着色器优化(Shader Optimization)
前言 Unity3D 着色器(Shader)优化是提升渲染性能的关键环节,尤其是在移动设备或复杂场景中。以下是系统的优化策略和实践建议: 对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经…...
基于SpringBoot的“体育购物商城”的设计与实现(源码+数据库+文档+PPT)
基于SpringBoot的“体育购物商城”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体模块设计 前台用户登录界面 系统首页界面…...
数据库约束
数据库约束 1. NULL约束2. UNIQUE:唯一约束3. DEFAULT:默认值约束4. PRIMARY KEY:主键约束5. FOREIGN KEY:外键约束6. CHECK约束 数据库约束是关系型数据库的一个重要功能,主要作用是保证数据的正确性,也就…...
【经典算法】Leetcode-零钱兑换问题
一、题目 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。你可以认为每种硬币的数量是无限的…...
Go 语言使用Protobuf 进行序列化详解
文章目录 Go 语言使用Protobuf 进行序列化详解1. Protobuf是什么?2. 安装Protobuf 及 Go 依赖3. 编写.proto 文件4. 实现序列化和反序列化 Go 语言使用Protobuf 进行序列化详解 1. Protobuf是什么? 以下是 Protobuf 官方中文文档的概述: Protobuf(Protocol Buffers) 是一种…...
Windows控制台函数:标准输入输出流交互函数GetStdHandle()
目录 什么是 GetStdHandle? 它长什么样? 怎么用它? 它跟 std::cout 有什么不一样? GetStdHandle 是一个 Windows API 函数,用于获取标准输入、标准输出或标准错误设备的句柄。它定义在 Windows 的核心头文件 <…...
自然语言处理初学者指南
文章目录 一、说明二、自然语言处理发展史2.1 最早的自然语言处理简介2.2 历史2.3 NLP 的早期工作 三、NLP的现代方法3.1 单词编码3.2 循环神经网络3.3 强化学习3.4 深度学习 四、更进一步的方法 一、说明 对于初学者来说,自然语言处理的发展历史非常有必要了解&am…...
Kubernetes教程(七)了解集群、标签、Pod和Deployment
了解集群、标签、Pod和Deployment 一、K8s资源对象二、K8s集群1. Master2. Node 三、Namespace(命名空间)四、Label(标签)五、Pod1. 共享网络命名空间2. 共享数据 六、工作负载1. 设置副本数2. 应用升级 结语 Kubernetes的知识真的…...
【BUG分析】微服务无法读取Nacos中的共享配置
项目场景 基于Spring Cloud微服务的商城系统。 使用Nacos进行统一配置管理,在bootstrap.xml中读取配置参数。 问题描述 购物车微服务可以读取Nacos中的共享mybatis配置,商品管理微服务却读不到,启动报错提示无法配置数据库源: …...
SpringMVC (一)基础
目录 SpringMVC 一 简单使用 1 新建模块选择指定参数 2 创建实现类 3 将项目启动 4 运行结果:在浏览器当中响应执行 二 RequestMapping 三 请求限定 SpringMVC SpringMVC是Spring的web模块,用来开发Web应用,SpringMVC应用最终作为B/…...
【ES6】ES6中的类
基础定义和使用 class Animal {constructor(name, species, age) {this.name namethis.species speciesthis.age age} }let dog new Animal("Spot", "Dog", 4)私有变量 变量名前带#即可。 Getter 和Setter方法 继承 // 父类 class Point{construc…...
兴达易控Profinet 转 ModbusTCP跨网段通信模块
Profinet 转 ModbusTCP/跨网段通信模块 Profinet转ModbusTCP/跨网段通信模块,作为现代工业自动化系统中不可或缺的重要组件,正日益受到广泛关注和应用。 这种模块的核心功能是将Profinet网络协议转换为Modbus TCP协议,实现不同网络之间的无缝…...
linux 的免密切换用户PAM配置
/etc/pam.d/su是Linux系统中与用户切换(su命令)相关的PAM(Pluggable Authentication Modules,可插拔认证模块)配置文件。以下是对它的详细介绍: 简介 作用 PAM是一种用于管理系统认证的机制,…...
使用 Python pandas操作 Excel 文件
使用 Python pandas 操作 Excel 文件 flyfish pandas 是 Python 中一个强大的数据处理和分析库,它提供了丰富的数据结构(如 Series 和 DataFrame)和数据操作方法,能够方便地处理各种数据格式,包括 Excel 文件。 安装…...
UE5.5 Niagara 发射器粒子更新模块
Particle State (粒子状态)模块 Particle State 主要用于控制粒子的生存状态,包括死亡、消失、响应事件等。 Particle State Kill Particles When Lifetime Has Elapsed 当粒子的生命周期结束时,销毁这些粒子。 Lifetime &…...
状态模式的C++实现示例
核心思想 状态模式(State Pattern) 是一种行为设计模式,允许对象在其内部状态改变时改变其行为。它将状态相关的逻辑分散到不同的状态类中,避免了使用大量的条件语句来处理不同状态下的行为。 状态抽象化:将对象的状…...
ThinkPHP8.0+MySQL8.0搭建简单实用电子证书查询系统
客户花了100元买了一个系统,开始不能导入,到处找人帮忙解决。给解决能导入了,不能修改,满足不了用户的需求。用户一狠心,花200块钱,叫我给他定制了一个电子证书查询系统。还免费给部署到服务器。惭愧惭愧……...
STM32全系大阅兵(2)
接前一篇文章:STM32全系大阅兵(1) 本文内容参考: STM32家族系列的区别_stm32各个系列区别-CSDN博客 STM32--STM32 微控制器详解-CSDN博客...
upload-labs通关攻略 【Pass-01~Pass-19】
注意:GitHub中upload-labs有多个版本:19关、21关、23关。版本不同,关卡的顺序也很可能不同。此次例子是21关。 项目地址:https://github.com/c0ny1/upload-labs 一、Pass-01 【js前端检测的绕过】 1. 打开Pass-01 访问:ip/uploa…...
HCIP复习拓扑练习(修改版)
拓扑: 实际: 需求: 需求分析 1.这意味着学校内部网络能够正常解析域名并进行互联网访问。 2. PC1和PC2处于同一个内网192.168.1.0/24,其中PC1有权限访问外部网段3.3.3.0/24,而PC2没有。这涉及ACL(访问控制…...
Zabbix 7.2 + Grafana 中文全自动安装ISO镜像
简介 基于Zabbix 官方的Alma Linux 8 作为基础镜像。 镜像源都改为国内大学镜像站,自动联网安装ZabbixGrafana。 安装中文字体、Zabbix和Grafana也配置默认中文。 Zabbix 也指定中文字体,绘图无乱码。 配置时区为东八区,Zabbix配置We…...
使用 Python 将 PDF 转换为文本:打造一个简单高效的提取工具
在数字化时代,PDF 文件是我们日常生活中常见的文档格式。无论是学术论文、工作报告还是电子书,PDF 的广泛使用让提取其中文字内容成为一个常见需求。手动复制粘贴显然效率低下,而借助 Python,我们可以轻松实现自动化提取。本文将介…...