作业:pvz游戏
代码如下:
点击查看代码
import pygame
import random
import sys
pygame.init()
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 600
FPS = 60
clock = pygame.time.Clock()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('学号2024310143129')
# 颜色定义
WHITE = (255, 255, 255)
GREEN = (0, 180, 0) # 草地背景
DARK_GREEN = (0, 120, 0) # 网格线
YELLOW = (255, 255, 0) # 阳光
RED = (255, 50, 50) # 僵尸
BLUE = (50, 50, 255) # 豌豆射手
PURPLE = (180, 50, 180) # 向日葵
ORANGE = (255, 165, 0) # 子弹
GRAY = (150, 150, 150) # 冷却遮罩
BLACK = (0, 0, 0) # 文字
# 字体
font = pygame.font.SysFont(None, 40)
small_font = pygame.font.SysFont(None, 28)
# 游戏状态
GAME_ACTIVE = 0
GAME_OVER = 1
GAME_WIN = 2
class Plant(pygame.sprite.Sprite):
"""植物基类(向日葵、豌豆射手)"""
def __init__(self, type, x, y):
super().__init__()
self.type = type # 'sunflower' 或 'peashooter'
self.health = 100 # 植物血量
self.selected = False # 是否被选中
# 根据类型设置颜色 & 初始化属性
if type == 'sunflower':
self.color = PURPLE
self.sun_production = 25 # 每次产阳光
self.produce_interval = 10000 # 10秒产一次(毫秒)
self.last_produce = pygame.time.get_ticks()
elif type == 'peashooter':
self.color = BLUE
self.attack_interval = 1500 # 1.5秒射一次子弹
self.last_attack = pygame.time.get_ticks()
self.image = pygame.Surface((80, 80))
self.image.fill(self.color)
self.rect = self.image.get_rect(topleft=(x, y))
def update(self):
# 选中状态下显示边框
if self.selected:
pygame.draw.rect(self.image, YELLOW, (0, 0, 80, 80), 3)
else:
self.image.fill(self.color)
class Zombie(pygame.sprite.Sprite):
"""僵尸类(从右侧向左移动,吃植物)"""
def __init__(self, lane):
super().__init__()
self.image = pygame.Surface((100, 120))
self.image.fill(RED)
self.rect = self.image.get_rect(bottomleft=(SCREEN_WIDTH, 120 + lane * 100))
self.speed = 1 # 移动速度
self.health = 100 # 僵尸血量
self.attacking = False # 是否正在攻击
self.attack_damage = 10 # 每次攻击伤害
self.attack_interval = 1000 # 攻击间隔(毫秒)
self.last_attack = pygame.time.get_ticks()
def update(self):
now = pygame.time.get_ticks()
if self.attacking:
# 攻击状态下不移动,定时造成伤害
if now - self.last_attack > self.attack_interval:
self.last_attack = now
return self.attack_damage # 返回伤害值
else:
# 移动状态
self.rect.x -= self.speed
return 0 # 未攻击,返回0伤害
class Bullet(pygame.sprite.Sprite):
"""子弹类(从豌豆射手向右发射)"""
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((20, 10))
self.image.fill(ORANGE)
self.rect = self.image.get_rect(center=(x, y))
self.speed = 6 # 移动速度
self.damage = 20 # 伤害值
def update(self):
self.rect.x += self.speed
if self.rect.left > SCREEN_WIDTH:
self.kill()
class Sun(pygame.sprite.Sprite):
"""阳光类(下落动画,可收集)"""
def __init__(self, x, y, is_falling=True):
super().__init__()
self.image = pygame.Surface((40, 40))
self.image.fill(YELLOW)
self.rect = self.image.get_rect(center=(x, y))
self.value = 25 # 阳光值
self.is_falling = is_falling # 是否从空中落下
self.create_time = pygame.time.get_ticks() # 记录创建时间
if is_falling:
self.y_velocity = 0 # 垂直速度
self.gravity = 0.2 # 重力加速度
self.target_y = random.randint(200, SCREEN_HEIGHT - 100) # 随机落点
else:
self.target_y = y # 从植物生成的阳光不下降
def update(self):
if self.is_falling and self.rect.y < self.target_y:
self.y_velocity += self.gravity
self.rect.y += int(self.y_velocity)
else:
self.is_falling = False # 到达落点后停止下降
# 未收集的阳光15秒后消失
if pygame.time.get_ticks() - self.create_time > 15000:
self.kill()
def main():
# 游戏状态初始化
game_state = GAME_ACTIVE
score = 0
zombies_killed = 0
waves = 1
wave_timer = 0
wave_duration = 60000 # 每波60秒
# 精灵组
plants_group = pygame.sprite.Group()
zombies_group = pygame.sprite.Group()
bullets_group = pygame.sprite.Group()
suns_group = pygame.sprite.Group()
# 游戏变量
sun_points = 150 # 初始阳光
plant_cd = {} # 植物冷却:key=(x,y), value=时间戳
selected_plant = None # 当前选中的植物类型
# 生成初始阳光
for _ in range(5):
sun = Sun(random.randint(100, SCREEN_WIDTH-100), random.randint(50, 150))
suns_group.add(sun)
# 游戏主循环
running = True
while running:
now = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # 左键点击
x, y = event.pos
# 游戏结束状态下点击重新开始
if game_state != GAME_ACTIVE:
game_state = GAME_ACTIVE
sun_points = 150
score = 0
zombies_killed = 0
waves = 1
plants_group.empty()
zombies_group.empty()
bullets_group.empty()
suns_group.empty()
for _ in range(5):
sun = Sun(random.randint(100, SCREEN_WIDTH-100), random.randint(50, 150))
suns_group.add(sun)
continue
# 1. 检查是否点击阳光
temp_sun = Sun(x, y, is_falling=False) # 创建临时阳光用于碰撞检测
sun_hit = pygame.sprite.spritecollideany(temp_sun, suns_group)
if sun_hit:
sun_points += sun_hit.value
sun_hit.kill()
continue
# 2. 检查是否选择植物类型
if 50 <= x < 250 and 50 <= y < 150: # 向日葵区域
if sun_points >= 50:
selected_plant = 'sunflower'
elif 300 <= x < 500 and 50 <= y < 150: # 豌豆射手区域
if sun_points >= 100:
selected_plant = 'peashooter'
# 3. 种植植物
elif 150 <= y <= 550 and selected_plant: # 有效种植区域
grid_x = (x // 80) * 80 # 网格对齐
grid_y = ((y - 150) // 100) * 100 + 150 # 5行种植区域
pos = (grid_x, grid_y)
# 检查位置是否已被占用
if not any(p.rect.collidepoint(grid_x+40, grid_y+40) for p in plants_group):
# 检查冷却
if pos in plant_cd:
if now - plant_cd[pos] < 5000: # 5秒冷却
continue
# 种植植物
cost = 50 if selected_plant == 'sunflower' else 100
if sun_points >= cost:
sun_points -= cost
plant = Plant(selected_plant, grid_x, grid_y)
plants_group.add(plant)
plant_cd[pos] = now
selected_plant = None # 种植后取消选择
if game_state == GAME_ACTIVE:
# 1. 生成僵尸(每波僵尸数量递增)
if now - wave_timer > wave_duration:
waves += 1
wave_timer = now
if random.random() < 0.01 * waves: # 随波数增加生成概率
lane = random.randint(0, 4) # 随机选择车道
zombie = Zombie(lane)
zombies_group.add(zombie)
# 2. 植物行为:产阳光(向日葵)、发射子弹(豌豆射手)
for plant in plants_group:
if plant.type == 'sunflower':
if now - plant.last_produce > plant.produce_interval:
# 生成阳光,从植物顶部飘落
sun = Sun(plant.rect.centerx, plant.rect.top - 20, False)
suns_group.add(sun)
plant.last_produce = now
elif plant.type == 'peashooter':
if now - plant.last_attack > plant.attack_interval:
# 检查前方是否有僵尸
zombie_in_lane = False
for zombie in zombies_group:
if (plant.rect.centery - 50 <= zombie.rect.centery <= plant.rect.centery + 50 and
zombie.rect.left > plant.rect.right):
zombie_in_lane = True
break
if zombie_in_lane:
# 生成子弹,从植物右侧发射
bullet = Bullet(plant.rect.right, plant.rect.centery)
bullets_group.add(bullet)
plant.last_attack = now
# 3. 阳光自动消失(已在Sun类的update中处理)
# 4. 碰撞检测:子弹打僵尸(核心修复!)
bullet_hits = pygame.sprite.groupcollide(bullets_group, zombies_group, True, False)
for bullet, zombie_list in bullet_hits.items(): # 遍历子弹对应的僵尸列表
for zombie in zombie_list:
zombie.health -= bullet.damage # 使用子弹的伤害值(可扩展为不同子弹伤害)
if zombie.health <= 0:
zombies_group.remove(zombie)
score += 100
zombies_killed += 1
# 僵尸吃植物
for zombie in zombies_group:
plant_hits = pygame.sprite.spritecollide(zombie, plants_group, False)
if plant_hits:
zombie.attacking = True
# 选择最左边的植物攻击
target_plant = min(plant_hits, key=lambda p: p.rect.x)
damage = zombie.update() # 更新僵尸状态并获取伤害
if damage > 0:
target_plant.health -= damage
if target_plant.health <= 0:
plants_group.remove(target_plant)
zombie.attacking = False
else:
zombie.attacking = False
zombie.update() # 正常移动
# 5. 检查游戏结束条件
for zombie in zombies_group:
if zombie.rect.left < 50:
game_state = GAME_OVER
# 6. 检查胜利条件(击杀50个僵尸)
if zombies_killed >= 50:
game_state = GAME_WIN
screen.fill(GREEN) # 绿色背景
for x in range(0, SCREEN_WIDTH, 80):
pygame.draw.line(screen, DARK_GREEN, (x, 150), (x, SCREEN_HEIGHT - 50), 1)
for y in range(150, SCREEN_HEIGHT, 100):
pygame.draw.line(screen, DARK_GREEN, (0, y), (SCREEN_WIDTH, y), 1)
pygame.draw.rect(screen, (100, 100, 100), (50, 50, 200, 100), 0, 10) # 向日葵区域
pygame.draw.rect(screen, (100, 100, 100), (300, 50, 200, 100), 0, 10) # 豌豆射手区域
sunflower_icon = pygame.Surface((80, 80))
sunflower_icon.fill(PURPLE)
screen.blit(sunflower_icon, (100, 70))
peashooter_icon = pygame.Surface((80, 80))
peashooter_icon.fill(BLUE)
screen.blit(peashooter_icon, (350, 70))
sunflower_price = small_font.render("50", True, WHITE)
screen.blit(sunflower_price, (100, 160))
peashooter_price = small_font.render("100", True, WHITE)
screen.blit(peashooter_price, (350, 160))
if selected_plant == 'sunflower':
pygame.draw.rect(screen, YELLOW, (50, 50, 200, 100), 3, 10)
elif selected_plant == 'peashooter':
pygame.draw.rect(screen, YELLOW, (300, 50, 200, 100), 3, 10)
sun_text = font.render(f'Sun: {sun_points}', True, WHITE)
screen.blit(sun_text, (20, 20))
score_text = font.render(f'Score: {score}', True, WHITE)
screen.blit(score_text, (SCREEN_WIDTH - 150, 20))
wave_text = font.render(f'Wave: {waves}', True, WHITE)
screen.blit(wave_text, (SCREEN_WIDTH - 150, 60))
zombies_text = font.render(f'Zombies: {zombies_killed}/50', True, WHITE)
screen.blit(zombies_text, (SCREEN_WIDTH - 150, 100))
for pos, time in plant_cd.items():
if now - time < 5000:
x, y = pos
progress = 1 - (now - time) / 5000
overlay = pygame.Surface((80, 80), pygame.SRCALPHA)
overlay.fill((0, 0, 0, int(150 * progress))) # 半透明黑色遮罩
screen.blit(overlay, (x, y))
suns_group.update()
suns_group.draw(screen)
plants_group.update()
plants_group.draw(screen)
zombies_group.update()
zombies_group.draw(screen)
bullets_group.update()
bullets_group.draw(screen)
if game_state == GAME_OVER:
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
screen.blit(overlay, (0, 0))
game_over_text = font.render("游戏结束!", True, WHITE)
screen.blit(game_over_text, (SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT//2 - 50))
final_score_text = font.render(f"最终分数: {score}", True, WHITE)
screen.blit(final_score_text, (SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT//2 + 10))
restart_text = font.render("xuehao:3129", True, WHITE)
screen.blit(restart_text, (SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//2 + 70))
elif game_state == GAME_WIN:
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
screen.blit(overlay, (0, 0))
win_text = font.render("恭喜你胜利了!", True, WHITE)
screen.blit(win_text, (SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//2 - 50))
final_score_text = font.render(f"最中的分: {score}", True, WHITE)
screen.blit(final_score_text, (SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT//2 + 10))
restart_text = font.render("xuehao:3129", True, WHITE)
screen.blit(restart_text, (SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//2 + 70))
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
if __name__ == '__main__':
main()

浙公网安备 33010602011771号