20234315 实验四 《Python程序设计》实验报告

课程:《Python程序设计》
班级: 2343
姓名: 陈嘉鑫
学号:20234315
实验教师:王志强
实验日期:2024年5月15日
必修/选修: 公选课

1.实验内容

Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
课代表和各小组负责人收集作业(源代码、视频、综合实践报告)

例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。

例如:利用公开数据集,开展图像分类、恶意软件检测等

例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。

例如:爬取天气数据,实现自动化微信提醒

例如:利用爬虫,实现自动化下载网站视频、文件等。

例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等

注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。

2.实验过程

原因:看到我亲爱的室友染上了植物大战僵尸杂交版2.0,为了帮助他戒掉网瘾,我决定以毒攻毒,自己写一个简易版的植物大战僵尸给他玩。

2.1我们首先导入pygame和random库。为什么会用random呢?当然我们的僵尸肯定是随机放的呀,再把pygame初始化。

2.2在这里我把屏幕尺寸以及颜色设置好,为了好放置我的植物和僵尸2.3在这里我选取了向日葵,僵尸,豌豆射手以及子弹的图片,并对图片的照片进行处理,让每个向日葵,僵尸和豌豆射手的面积正好占一个小格子。

2.4在这里我们需要设置游戏内的各种数值(由于要考虑的东西太多,图下代码中有注释),最后设置了字体。

2.5我们来设置我们的游戏的基本玩法吧

这里定义了一个Plant类,继承自pygame.sprite.Sprite,用于表示植物。它初始化了植物的位置、图像、消耗和生命值。在定义的update中,如果植物的生命值小于等于0,它将被移除。

这里定义一个Sunflower类,用来表示向日葵。它有一个额外的属性,表示生产阳光的间隔。在定义update中,如果计时器到期,它会生产阳光并重置计时器。

定义了一个继承Plant的Peashooter类,表示豌豆射手。它有一个额外的属性,表示射击的间隔。在定义的update中,如果计时器到期,它会发射子弹并重置计时器。

定义了一个继承pygame.sprite.Sprite的Zombie类,表示僵尸。它有速度、生命值和攻击间隔等属性。在定义的update中,僵尸会移动并减少其健康值。如果计时器到期,它会攻击目标并重置计时器。

定义了一个继承pygame.sprite.Sprite的Bullet类,表示子弹它有一个速度属性,并在定义的update中移动自己。如果子弹超出屏幕或击中僵尸,它会被移除,并减少僵尸的生命值。

当游戏还没结束,即running还不是false时,我们设置左键放置豌豆射手,右键放置向日葵,每一帧都重新填充屏幕背景,处理事件。

更新僵尸,植物,子弹的位置,并处理植物与僵尸之间的关系,当僵尸碰到植物的时候,会减少植物的生命值。我们将网格绘制,并将所以物体放置格子中。

显示当前的阳光值,检查是否满足胜利条件,并刷新屏幕内容。使用clock.tick()来设置每秒帧数为60。

判断胜利条件为没有一个僵尸了,而失败条件为僵尸出来格子之外,无论胜利或失败,都要讲running改为False,防止游戏不停止,最后先初始化僵尸的数量为2。

根据规则生成僵尸(每次死亡一个就生成两个),但是生成僵尸的数量不能超过总数。

当游戏结束时,我们会显示结果,并等玩家自动关闭页面。

3.实验结果视频以及代码展示

实验视频:见压缩包

代码展示:

import pygame
import random

pygame.init()

# 屏幕尺寸
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
GRID_SIZE = 50  # 每个小格子的大小
GRID_COLS = SCREEN_WIDTH // GRID_SIZE
GRID_ROWS = SCREEN_HEIGHT // GRID_SIZE

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("最后的作业(左键种植豌豆射手,右键种植向日葵)")

# 颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)

# 载入图片并调整尺寸
plant_img = pygame.image.load('plant.png')
plant_img = pygame.transform.scale(plant_img, (GRID_SIZE, GRID_SIZE))

sunflower_img = pygame.image.load('sunflower.png')
sunflower_img = pygame.transform.scale(sunflower_img, (GRID_SIZE, GRID_SIZE))

zombie_img = pygame.image.load('zombie.png')
zombie_img = pygame.transform.scale(zombie_img, (GRID_SIZE, GRID_SIZE))

bullet_img = pygame.image.load('bullet.png')
bullet_img = pygame.transform.scale(bullet_img, (20, 20))

# 游戏参数
SUN_INCREMENT = 25  # 向日葵产生的阳光值
SUNFLOWER_COST = 50  # 向日葵的阳光值消耗
PEASHOOTER_COST = 100  # 豌豆射手的阳光值消耗
PEASHOOTER_SHOOT_DELAY = 40  # 豌豆射手的攻击间隔
PEASHOOTER_HEALTH = 20  # 豌豆射手的生命值
ZOMBIE_SPEED = 0.03  # 僵尸的移动速度
ZOMBIE_HEALTH = 10  # 僵尸的初始血量
ZOMBIE_ATTACK_DELAY = 80  # 僵尸的攻击延迟
TOTAL_SUN = 2000  # 初始总阳光值
FONT_SIZE = 24  # 字体大小
WIN_CONDITION = 0  # 胜利条件:所有僵尸死亡
ZOMBIE_SPAWN_RATE = 180  # 僵尸生成速率(帧)
ZOMBIE_LIMIT = 20  # 僵尸总数限制

# 字体设置
font = pygame.font.Font(None, FONT_SIZE)

# 定义类
class Plant(pygame.sprite.Sprite):
    def __init__(self, grid_x, grid_y, image, cost, health):
        super().__init__()
        self.image = image
        self.rect = self.image.get_rect()
        self.grid_x = grid_x
        self.grid_y = grid_y
        self.rect.x = grid_x * GRID_SIZE
        self.rect.y = grid_y * GRID_SIZE
        self.cost = cost
        self.health = health

    def update(self):
        if self.health <= 0:
            self.kill()

class Sunflower(Plant):
    def __init__(self, grid_x, grid_y):
        super().__init__(grid_x, grid_y, sunflower_img, SUNFLOWER_COST, 10)
        self.sun_production_delay = 180  # 增加向日葵生产阳光的间隔
        self.sun_timer = 0

    def update(self):
        super().update()
        if self.sun_timer > 0:
            self.sun_timer -= 1
        else:
            self.produce_sun()
            self.sun_timer = self.sun_production_delay

    def produce_sun(self):
        global total_sun
        total_sun += SUN_INCREMENT

class Peashooter(Plant):
    def __init__(self, grid_x, grid_y):
        super().__init__(grid_x, grid_y, plant_img, PEASHOOTER_COST, PEASHOOTER_HEALTH)
        self.shoot_delay = PEASHOOTER_SHOOT_DELAY
        self.shoot_timer = 0

    def update(self):
        super().update()
        if self.shoot_timer > 0:
            self.shoot_timer -= 1
        else:
            self.shoot()
            self.shoot_timer = self.shoot_delay

    def shoot(self):
        bullet = Bullet(self.rect.x + self.rect.width, self.rect.y + self.rect.height // 2)
        bullets.add(bullet)

class Zombie(pygame.sprite.Sprite):
    def __init__(self, grid_x, grid_y):
        super().__init__()
        self.image = zombie_img
        self.rect = self.image.get_rect()
        self.grid_x = grid_x
        self.grid_y = grid_y
        self.rect.x = grid_x * GRID_SIZE
        self.rect.y = grid_y * GRID_SIZE
        self.speed = ZOMBIE_SPEED
        self.health = ZOMBIE_HEALTH
        self.attack_delay = ZOMBIE_ATTACK_DELAY
        self.attack_timer = self.attack_delay

    def update(self):
        self.rect.x -= self.speed * GRID_SIZE  # 僵尸每次移动0.1个小格子
        if self.health <= 0:
            self.kill()
        if self.attack_timer > 0:
            self.attack_timer -= 1

    def attack(self, target):
        if self.attack_timer == 0:
            target.health -= 5
            self.attack_timer = self.attack_delay

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = bullet_img
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.speed = 0.5 * GRID_SIZE  # 子弹每次移动半个小格子

    def update(self):
        self.rect.x += self.speed
        if self.rect.x > SCREEN_WIDTH:
            self.kill()

        hit_zombies = pygame.sprite.spritecollide(self, zombies, False)
        for zombie in hit_zombies:
            zombie.health -= 2
            self.kill()

# 创建精灵组
plants = pygame.sprite.Group()
zombies = pygame.sprite.Group()
bullets = pygame.sprite.Group()

# 游戏参数初始化
total_sun = TOTAL_SUN
total_zombies_spawned = 0
win_message = None

# 记录已种植植物的位置
occupied_positions = set()

# 游戏主循环
running = True
clock = pygame.time.Clock()
zombie_spawn_timer = ZOMBIE_SPAWN_RATE

def spawn_zombies(count):
    global total_zombies_spawned
    if total_zombies_spawned + count <= ZOMBIE_LIMIT:
        for _ in range(count):
            grid_y = random.randint(0, GRID_ROWS - 1)
            zombie = Zombie(GRID_COLS, grid_y)
            zombies.add(zombie)
        total_zombies_spawned += count

def check_win_condition():
    global win_message, running
    if any(zombie.rect.x < 0 for zombie in zombies):
        win_message = "100分,少年恭喜你毕业了!"
        running = False  # 结束游戏循环
    elif len(zombies) == 0 and total_zombies_spawned == ZOMBIE_LIMIT:
        win_message = "0分,继续给我学Python!"
        running = False  # 结束游戏循环

# 初始生成两个僵尸
spawn_zombies(2)

while running:
    screen.fill(WHITE)

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            x, y = event.pos
            grid_x = x // GRID_SIZE
            grid_y = y // GRID_SIZE
            if event.button == 1:  # 左键
                if (grid_x, grid_y) not in occupied_positions:
                    if total_sun >= PEASHOOTER_COST:
                        peashooter = Peashooter(grid_x, grid_y)
                        plants.add(peashooter)
                        total_sun -= PEASHOOTER_COST
                        occupied_positions.add((grid_x, grid_y))
            elif event.button == 3:  # 右键
                if (grid_x, grid_y) not in occupied_positions:
                    if total_sun >= SUNFLOWER_COST:
                        sunflower = Sunflower(grid_x, grid_y)
                        plants.add(sunflower)
                        total_sun -= SUNFLOWER_COST
                        occupied_positions.add((grid_x, grid_y))

    # 更新所有精灵
    plants.update()
    zombies.update()
    bullets.update()

    # 碰撞检测和攻击
    for zombie in zombies:
        for plant in plants:
            if pygame.sprite.collide_rect(zombie, plant):
                zombie.attack(plant)
                if plant.health <= 0:
                    plant.kill()
                    occupied_positions.remove((plant.grid_x, plant.grid_y))

    # 绘制网格
    for row in range(GRID_ROWS):
        pygame.draw.line(screen, BLACK, (0, row * GRID_SIZE), (SCREEN_WIDTH, row * GRID_SIZE))
    for col in range(GRID_COLS):
        pygame.draw.line(screen, BLACK, (col * GRID_SIZE, 0), (col * GRID_SIZE, SCREEN_HEIGHT))

    # 绘制精灵
    plants.draw(screen)
    zombies.draw(screen)
    bullets.draw(screen)

    # 显示阳光值
    sun_text = font.render(f"San值: {total_sun}", True, BLACK)
    screen.blit(sun_text, (10, 10))

    # 检查胜利条件
    check_win_condition()

    # 刷新屏幕
    pygame.display.flip()

    # 控制帧率
    clock.tick(60)

    # 每过一段时间生成新的僵尸
    zombie_spawn_timer -= 1
    if zombie_spawn_timer == 0:
        spawn_zombies(1)
        zombie_spawn_timer = ZOMBIE_SPAWN_RATE

# 游戏结束后显示结果
while True:
    screen.fill(WHITE)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
    if win_message:
        text = font.render(win_message, True, BLACK)
        text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
        screen.blit(text, text_rect)
    pygame.display.flip()

4.实验遇到的问题以及解决方案

1.问题:当时将照片导入的时候,由于尺寸原因,占用了很多的格子?

解决方案:pygame.transform.scale是Pygame中的一个函数,用于对传入的Surface对象进行缩放操作。可以通过指定宽度和高度来进行缩放,也可以指定一个新的Surface对象的大小作为目标尺寸进行缩放。运用这个函数可以将不同尺寸的png转换成一个格子大小。

2.问题:当时虽然僵尸都已经被我们的豌豆射手消灭了,不知道为什么游戏不能退出?

解决方案:这时我想起了老师给我们写的石头剪刀布的游戏,当问你是否想进行游戏的时候,你如果是Y那将会是True,如果是N将是False,直接退出程序,那我最后判断输出条件的时候是不是可以把我的running改为False呢,一试试果然成功了。

3.问题:我的豌豆射手为什么可以在同一个地方布置两次呢?

解决方案:我想起了数据结构中的DFS算法,每次访问的时候都带有一个visited数组,这样再次就不会访问了,根据这个思路,我将代码调整,结果成功。

5.实验感悟以及全课总结

实验感悟:

这次实验四与以往不同,以往都是强哥带着我们手把手教我做,这次强哥直接给我们上强度了,让我们自己写一个大作业,我原本想写一个爬虫,但是好像根本访问不进去。于是我便将目光锁定在了游戏之中,又因为受到我室友的熏陶。我想试着把植物大战僵尸简易版做出来,我借助了大模型生成了一个最初版本的植物大战僵尸,但是效果很不令我满意。我这时候下定决心自己不能完全交给大模型,我对于我游戏的设置要求进行了反复的增添,运行了一遍又一遍,在我不断地将我的优秀规则变得丰富时,我的游戏能够更加完善,我获得了乐趣,理解了这个实验真正意义所在。

全科总结:

虽然是学长推荐我学的这门课程,但到了后面我真正地知道了什么是“人生苦短,我用Python”,python真的能帮助我们解决许多难题,给我们带来许多遍历,强哥讲课幽默的性格深深让我对这门课产生了浓厚的兴趣,我确实在强哥的课上学到了东西,发现了Python的美。

希望老师能够在下面的教学中能够提问到更多的同学(同学们可能并不是怕老师点到),让更多的同学去欣赏Python带来的美,而不是单单为了期末分数。

言尽于此,无需多言。感谢志强老师能够教授我的Python公选课,此行受益匪浅,希望老师的选修课能够帮助更多的同学们。

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值