Python学习笔记47:游戏篇之外星人入侵(八)

news2025/1/14 1:08:13

前言

在上篇文章中,我们在游戏窗口中加载三行外星人。文章中也说过我们加载外星人的方式是比较简单的加载方式:一次性加载固定数量的外星人,并且以同样的方式重复加载。这种加载方式简单易懂,比较适合新手,如果想要一些挑战性,可以使用随机出现,行行加载的方式自己研究。

外星人加载出来以后,进一步的需求就是移动外星人了,本篇文章就是实现外星人移动的功能。

外星人的移动

外星人的移动其实也是外星人图片外接矩形x,y坐标值变化的结果,所以外星人的移动函数实现本质依旧是修改外星人的self.rect.x的值。但是这里还是由几个地方需要注意的:

  1. 外星人的移动并不是由游戏用于操作的,就是子弹的移动一样。
  2. 外星人的移动是并不是单一的x方向或者y方向。根据游戏设定,外星人加载后向右移动(你想向左也不是不行),当边上的外星人触碰到对应边界的时候,外星人要向下移动一个单位,同时在x轴上的移动方向要变为与之前相反。
  3. 外星人到达下边界的时候需要进行特殊处理,这个处理和游戏规则有关,比如有几个外星人到达下边界,飞船坠毁。目前我们不做设置。

代码编写

通过对以上的分析,我们可以简单的将外星人的移动分为两部分:

  • X方向的单向移动
  • 向下移动一个单位及修改方向参数,这两个功能在触碰边界是同时发生,所以放到一起实现。

X方向下的单向移动

alien模块中,我们已经定义好了移动的函数,那么我们根据我们需要实现的功能,进行业务代码的编写。

alien模块:

import pygame
from pygame.sprite import Sprite


class Alien(Sprite):
    """外星人类"""
    def __init__(self, setting, screen):
        """初始化外星人并设置其初始位置"""
        super(Alien, self).__init__()
        self.screen = screen
        self.setting = setting
        # 加载图片并外接矩形
        self.image = pygame.transform.scale(
            pygame.image.load('E:/python_project/alien_invasion/assets/image/alien.bmp'), (50, 50))
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 外星人在左上角
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height
        # 存储外星人的位置
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)

    def update(self):
        """移动外星人"""
        self.x += self.setting.alien_speed * self.setting.fleet_direction
        self.rect.x = self.x

   def blitme(self):
       """在指定位置绘制外星人"""
       self.screen.blit(self.image, self.rect)

在alien模块中,我们主要做了两个修改:

  • 第一是我们定义了两个浮点型的x,y坐标,从子弹移动的代码中得出的经验,为了防止移动速度过快,我们可以设定移动速度为不足1的小数。
  • 第二是我们修改了移动外星人的函数。移动的技术主要是看self.x += self.setting.alien_speed * self.setting.fleet_direction这一部分代码。这部分代码中有两个setting模块的值,alien_speed 是移动速度,fleet_direction可以简单理解为移动方向,取值是正负1,与我们使用的运行符号是+=/-=配合使用,如果我们使用+=,那么fleet_direction=1表示正向为向右,反向为向左,实际使用的时候大家自由设定,但是算法的本质是不变的。

这个时候我们在main模块调用外星人的update函数,最后启动游戏看看,这个时候外星人只会向一个方向移动,最终消失。

main模块:

import pygame
from pygame.sprite import Group

import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
from alien_invasion.ship import Ship


def run_game():
    """启动游戏"""

    # 初始化pygame
    pygame.init()
    # 定义一个系统设置对象
    setting = Setting()
    # 新建窗口
    screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
    # 窗口命名
    pygame.display.set_caption(setting.caption)
    # 定义一个飞船对象
    ship = Ship(setting, screen)
    # 创建子弹编组
    bullets = Group()
    # 创建外星人编组
    aliens = Group()
    # 创建编组内的外星人
    gf.create_fleet(setting, screen, aliens)

    while True:
        # 处理监听事件
        gf.check_event(ship, setting, screen, bullets)
        # 移动飞船
        ship.move()
        # 更新子弹位置
        gf.update_bullets(bullets)
        # 更新外星人
        gf.update_aliens(aliens)
        # 刷新屏幕
        gf.update_screen(setting, screen, ship, bullets, aliens)


if __name__ == '__main__':
    run_game()

修改gf模块,增加update_aliens函数,移动外星人。

gf模块如下:

def create_fleet(setting, screen, aliens):
    # 创建外星人
  	--snip--

def update_aliens(aliens):
    aliens.update()

启动main模块:
在这里插入图片描述
可以看到,我们的外星人不用我们发射子弹,一个个排队向左最后全部消失。

这是因为我们少了向下移动和设置反向的代码,接下来我们就对这部分代码进行完善。

下移及变向

因为变向和下移发生在X方向的变化之前,所以我们在aliens.update()函数调用之前,就必须先进行下移和变向操作。

  1. 定义函数
    定义一个函数check_fleet_edges()检查外星人是否超过边界。在这个函数中,我们需要使用到外星人组,已经setting模块中的用于计算方向的fleet_direction属性。

    代码如下:

    gf模块:新增函数check_fleet_edges()update_aliens()函数新增参数setting,调用check_fleet_edges()函数

    --snip--
    
    def update_aliens(aliens, setting):
        check_fleet_edges(aliens, setting)
        aliens.update()
    
    def check_fleet_edges(aliens, setting):
        """检查外星人是否超过边界并进行对应处理"""
        pass
    

    main模块:

    update_aliens()函数增加参数setting

    --snip--
            # 更新外星人
            gf.update_aliens(aliens, setting)
            # 刷新屏幕
            gf.update_screen(setting, screen, ship, bullets, aliens)
    --snip--
    
  2. 业务实现
    对业务进行简单分析:

    • 因为外星人是组,所以我们需要循环外星人组,对每个外星人都判断是否到达边界。
    • 判断边界每次都需要使用外星人自身的一些参数,所以判断的函数应当定义在Alien类中。
    • 处于边界的业务代码需要完成所有外星人下移以及设置方向的功能

    分析后,我们可以简单写下如下所示代码:

    gf模块实现check_fleet_edges()函数的功能:

    --snip--
    def check_fleet_edges(aliens, setting):
    """检查外星人是否超过边界并进行对应处理"""
    	# 循环遍历所有外星人,本次循环的目的是判定X方向是否触碰边界
    	for x_alien in aliens.sprites():
        	# 调用alien的check_edges方法,检查是否在边界
        	if x_alien.check_edges():
            	# 在边界的话,所有外星人向下一个
            	for y_alien in aliens.sprites():
            		# 移动距离,为了方便修改或者填充后续功能,可以使用setting获取并设置
                	y_alien.rect.y += 10
            	# 设置反向:*= -1,这样当再次换向的时候又能变会正向,负负得正
            	setting.fleet_direction *= -1
            	# 只要有一个就可以了,不用再向下循环了
            	break
    

    alien模块实现check_edges()函数的功能:

    	def update(self):
    	--snip--
    	
    	def check_edges(self):
            """如果外星人位于屏幕边缘,就返回True"""
            screen_rect = self.screen.get_rect()
            if self.rect.right >= screen_rect.right:
                return True
            elif self.rect.left <= 0:
                return True
    	
    	def blitme(self):
    	--snip--
    

以上代码编写完毕以后,启动main模块,外星人左右移动的同时向下移动。

在这里插入图片描述

项目代码

这里贴一下目前主要模块的代码:

main模块:

import pygame
from pygame.sprite import Group

import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
from alien_invasion.ship import Ship


def run_game():
    """启动游戏"""

    # 初始化pygame
    pygame.init()
    # 定义一个系统设置对象
    setting = Setting()
    # 新建窗口
    screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
    # 窗口命名
    pygame.display.set_caption(setting.caption)
    # 定义一个飞船对象
    ship = Ship(setting, screen)
    # 创建子弹编组
    bullets = Group()
    # 创建外星人编组
    aliens = Group()
    # 创建编组内的外星人
    gf.create_fleet(setting, screen, aliens)

    while True:
        # 处理监听事件
        gf.check_event(ship, setting, screen, bullets)
        # 移动飞船
        ship.move()
        # 更新子弹位置
        gf.update_bullets(bullets)
        # 更新外星人
        gf.update_aliens(aliens, setting)
        # 刷新屏幕
        gf.update_screen(setting, screen, ship, bullets, aliens)


if __name__ == '__main__':
    run_game()

gf模块:注意我们的gf模块并没有完全的进行功能抽象,比如加载外星人和移动外星人的部分,主要是为了大家参考学习的时候方便一点,不用看着函数名跳来跳去。

import sys
import pygame

from alien_invasion.alien import Alien
from alien_invasion.bullet import Bullet


def check_event(ship, setting, screen, bullets):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ship, setting, screen, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def fire(bullets, screen, setting, ship):
    # 创建一颗子弹,并将其加入到编组bullets中
    new_bullet = Bullet(setting, screen, ship)
    bullets.add(new_bullet)


def check_keydown_events(event, ship, setting, screen, bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_UP:
        ship.moving_top = True
    elif event.key == pygame.K_DOWN:
        ship.moving_bottom = True
    elif event.key == pygame.K_SPACE:
        fire(bullets, screen, setting, ship)
    elif event.key == pygame.K_q:
        sys.exit()


def check_keyup_events(event, ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False
    elif event.key == pygame.K_UP:
        ship.moving_top = False
    elif event.key == pygame.K_DOWN:
        ship.moving_bottom = False


def update_screen(setting, screen, ship, bullets, aliens):
    """更新屏幕"""
    # 填充背景色
    screen.fill(setting.bg_color)
    # 加载飞船
    ship.blitme()
    # 绘制子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    # 绘制外星人
    aliens.draw(screen)
    # 让最近绘制的屏幕可见
    pygame.display.flip()


def update_bullets(bullets):
    """更新子弹"""
    bullets.update()
    # 删除已经消失的子弹
    for bullet in bullets.copy():
        # 当子弹矩形底部坐标小于0,说明子弹已经出了上边界
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)


def create_fleet(setting, screen, aliens):
    # 创建外星人
    alien = Alien(setting, screen)
    # 获取外星人的宽度
    alien_width = alien.rect.width
    # 计算可用宽度(左右各保留一个外星人宽度的记录)
    available_space_x = setting.screen_width - 2 * alien_width
    # 看看可以创建多少个外星人一行
    number_aliens_x = int(available_space_x / (2 * alien_width))
    # 创建三行外星人,你也可以创建多行
    for row_number in range(3):
        for alien_number in range(number_aliens_x):
            # 在当前行创建一个外星人
            alien = Alien(setting, screen)
            alien_width = alien.rect.width
            # 计算当前外星人x坐标,因为每个外星人中间要空出一个外星人的位置
            alien.x = alien_width + 2 * alien_width * alien_number
            alien.rect.x = alien.x
            # 计算当前外星人y坐标,同样的每行外星人中间隔一行
            alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
            aliens.add(alien)


def update_aliens(aliens, setting):
    check_fleet_edges(aliens, setting)
    aliens.update()


def check_fleet_edges(aliens, setting):
    """检查外星人是否超过边界并进行对应处理"""
    # 循环遍历所有外星人,本次循环的目的是判定X方向是否触碰边界
    for x_alien in aliens.sprites():
        # 调用alien的check_edges方法,检查是否在边界
        if x_alien.check_edges():
            # 在边界的话,所有外星人向下一个
            for y_alien in aliens.sprites():
                y_alien.rect.y += 10
            # 设置反向:*= -1,这样当再次换向的时候又能变会正向,负负得正
            setting.fleet_direction *= -1
            # 只要有一个就可以了,不用再向下循环了
            break

alien模块:

import pygame
from pygame.sprite import Sprite


class Alien(Sprite):
    """外星人类"""
    def __init__(self, setting, screen):
        """初始化外星人并设置其初始位置"""
        super(Alien, self).__init__()
        self.screen = screen
        self.setting = setting
        # 加载图片并外接矩形
        self.image = pygame.transform.scale(
            pygame.image.load('E:/python_project/alien_invasion/assets/image/alien.bmp'), (50, 50))
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 存储外星人的位置
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)

    def update(self):
        """移动外星人"""
        self.x += self.setting.alien_speed * self.setting.fleet_direction
        self.rect.x = self.x

    def check_edges(self):
        """如果外星人位于屏幕边缘,就返回True"""
        screen_rect = self.screen.get_rect()
        if self.rect.right >= screen_rect.right:
            return True
        elif self.rect.left <= 0:
            return True

    def blitme(self):
        """在指定位置绘制外星人"""
        self.screen.blit(self.image, self.rect)

setting模块:

class Setting:
    """系统设置类"""
    def __init__(self):
        # 窗口宽
        self.screen_width = 1200
        # 窗口高
        self.screen_height = 800
        # 背景颜色
        self.bg_color = (230, 230, 230)
        # 窗口名
        self.caption = "Alien Invasion"
        # 子弹宽
        self.bullet_width = 3
        # 子弹长
        self.bullet_height = 15
        # 子弹颜色
        self.bullet_color = (60, 60, 60)
        # 飞船移动速度
        self.bullet_speed = 0.2
        # 外星人左右移动的步距
        self.alien_speed = 0.1
        # 外星人左右移动的倍数
        self.fleet_direction = 1

结尾

飞船的加载移动就讲完了,后面我们需要学习的是子弹接触外星人,飞船接触外星人部分的代码实现。

加油!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1956765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++----日期类

今天这篇博客是对我们上面的两篇博客的一个小小的检验。我们今天来实现一下我们手机上的一个小功能。对日期的实现。那么如何实现以及使用哪些知识&#xff0c;我们马上来看看吧。 思想概括 其实实现日期类并不是什么难事。如果用c语言的话大家可能会觉得很简单&#xff0c;主…

【VS Code】我用到的一些VS Code插件和命令

【命令】 打开终端&#xff1a; Ctrl ~ 【插件】 材质图标主题插件&#xff1a;Material Icon Theme 让文件图标看起来更美丽

yandex图标点选验证码YOLOV8识别案例

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 某yandex图标点选验证码如下: 使用过yolov8的小伙伴可能都知道,这种直接打个标注,基本上就可以了,至于问题图片由于不能很好的切割做分类,所以干脆也做成目标…

还在用JVM跑你的Java代码吗?太慢了,试试Oracle的GraalVM吧

前言 对于Java开发者们来说&#xff0c;几乎每天都在和JVM打交道&#xff0c;然而JVM即将过时了。那些对新技术保持敏锐洞察力的开发者&#xff0c;可能已经在生产环境中部署GraalVM生成的二进制程序了&#xff0c;小伙伴们&#xff0c;你们已经用起来了吗&#xff1f; Graal…

AOP~面向切面编程介绍

AOP基础 概述 AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;面向特定方法的编程。 动态代理是面向切面编程最主流的实现。 SpringAOP是Spring框架的高级技术&#xff0c;旨在管理bean对象的过程中&#xff0c…

【基础篇】Docker 架构与组件 TWO

嗨&#xff0c;小伙伴们&#xff01;我是小竹笋&#xff0c;一名热爱创作的工程师。上一篇我们聊了聊 Docker 的历史与发展、与虚拟机的对比以及它在行业中的应用。今天&#xff0c;让我们更进一步&#xff0c;深入探讨 Docker 的架构与关键组件。 欢迎订阅公众号&#xff1a;…

Fantastic-admin:Vue 中后台管理系统

Fantastic-admin&#xff1a;Vue 中后台管理系统 在当今的前端开发世界里&#xff0c;fantastic-admin 作为一款功能强大的 Vue 中后台管理系统框架&#xff0c;简直是开发者的福音。本文将介绍 fantastic-admin 的基本信息、特点&#xff0c;以及如何快速上手和使用。 项目简介…

亚信安慧AntDB亮相PostgreSQL中国技术大会,获“数据库最佳应用奖”并分享数据库应用实践

7月12日&#xff0c;第13届PostgreSQL中国技术大会在杭州顺利举办&#xff0c;亚信安慧AntDB数据库荣获“数据库最佳应用奖”。大会上&#xff0c;亚信安慧AntDB数据库同事带来《基于AntDB的CRM系统全域数据库替换实践》和《亚信安慧AntDB数据库运维之路》两场精彩演讲&#xf…

AWS资源购买和使用:亚马逊云显示配额不足怎么解决

亚马逊云显示配额不足是许多AWS用户在使用云服务时可能遇到的常见问题。这种情况通常发生在用户试图启动新的EC2实例或扩展现有资源时&#xff0c;系统提示已达到特定资源类型的限制。本文中九河云将跟你们一起探讨这一问题的原因及其解决方案。 首先&#xff0c;了解AWS配额的…

最便宜的iPhone SE 4要来,配置爆炸但砍了灵魂

前几天乐视刚刚发布了「低配青春 AIR 版 iPhone SE 4」&#xff0c;电友们就坐不住了&#xff0c;纷纷问真 iPhone SE 4 究竟什么时候才能来。 阿红也去挖了一下情报&#xff0c;别说&#xff0c;还真在路上了。 众所周知&#xff0c;最近一代&#xff0c;也就是第三代 iPhone…

Liquibase 增加一列主键,自动生成 UUID:最佳实践与深度解析

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 自动生成 UUID UUID 是一种全局唯一的标识符&#xff0c;通常由 32 个十六进制数字组成&#xff0c;分为五组&#xff0c;形式如下&#xff1a; 123e4567-e89b-12d3-a456-426614174000在 Java 中&#xff0c;可以使…

从零开始写 Docker(十九)---增加 cgroup v2 支持

本文为从零开始写 Docker 系列第十九篇&#xff0c;添加对 cgroup v2 的支持。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现有一个大致认识&#xff1a; 核心原理&#xff1a;深入理解 Docker 核心原理&#xff1a…

解锁未来安全:WT2003HP8芯片如何重塑智能电子锁的安全性与智能化体验

WT2003HP8混音芯片&#xff1a;智能电子锁的声音守护者 在当今这个追求智能化、便捷化的时代&#xff0c;智能电子锁作为智能家居的重要组成部分&#xff0c;正逐步改变着我们的生活方式。而WT2003HP8混音芯片&#xff0c;凭借其卓越的性能和广泛的应用潜力&#xff0c;在智能…

AFSim 仿真系统--任务处理器入门指南

任务处理器 任务处理器&#xff08;WSF_TASK_PROCESSOR&#xff09;是一个WSF处理器&#xff0c;它提供了检查轨迹管理器中的数据&#xff08;轨迹&#xff09;并对其采取行动的能力。这些行动包括&#xff1a; *向下属分配任务 *激活或停用传感器或干扰器 *开火武器 *操纵平台…

德国航空航天中心(DLR)利用元数据整理归纳Confluence

对于大型企业&#xff0c;在广阔的Confluence空间内找到信息&#xff0c;如同迷失在迷宫中。多年来&#xff0c;德国航空航天中心&#xff08;DLR&#xff09;依赖电子邮件和传统文件管理系统。在知识经理Andr Pliewischkies的领导下&#xff0c;公司引入了Confluence以减少信息…

Layui修改表格分页为英文

Layui修改表格分页为英文 1.前言2.Laypage属性 1.前言 主要记录初次使用Layui没有好好看官方文档踩坑&#xff0c;修改了源码才发现可以自定义 使用的Layui版本2.9.14 2.Laypage属性 Laypage属性中带的有自定义文本的属性 示例代码 table.render({.......page: {skipText: …

[米联客-安路飞龙DR1-FPSOC] SDK入门篇连载-09 PL AXI-GPIO实验

软件版本&#xff1a;Anlogic -TD5.9.1-DR1_ES1.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用安路(Anlogic)FPGA 实验平台&#xff1a;米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…

buu做题(9)

[MRCTF2020]PYWebsite 有个二维码 扫了一下啊二维码 function enc(code){hash hex_md5(code);return hash;}function validate(){var code document.getElementById("vcode").value;if (code ! ""){if(hex_md5(code) "0cd4da0223c0b280829dc3ea4…

【C语言报错已解决】“Undefined Reference”

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言&#xff1a; 在开发过程中&#xff0c;我们经常会遇到各种编译错误或运行时错误。其中&#xff0c;“Undefined Referenc…

JavaFX布局-AnchorPane

JavaFX布局-AnchorPane 常用属性padding 实现方式Javafxml 将子节点锚定到容器的边界上&#xff0c;指定子节点相对于 AnchorPane 的四个边界&#xff08;上、下、左、右&#xff09;的距离适合宽高固定的一些表单如果允许最大化&#xff0c;拖动大小&#xff0c;需要自己计算子…