作业: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()
运行截图 ![](https://img2024.cnblogs.com/blog/3608895/202506/3608895-20250621201525484-689826515.png)
posted @ 2025-06-21 20:17  bolun123  阅读(27)  评论(0)    收藏  举报