Python入门学习笔记第十二章——游戏制作之武装飞船~~~_飞船的位图-程序员宅基地

技术标签: 入门学习笔记  python  

该整个项目了

在前面十一章的学习中,我们已经对Python这门语言有了一个比较基本的认识和了解,那么接下来,我们会通过一些项目的学习,来加深和拓展我们对Python的认知。那么我们首先就是尝试做一个游戏——武装飞船。
因为Python做游戏我也是第一次,所以要是一步步学着做完了,估计回来再写也写不清楚,就一步步来,一步步写吧。

准备工作

首先,我们需要下载pygame,这里下载的方法有很多,我们可以之间在终端输入pip install pygame就可以实现下载,也可以在官网上下载安装,这里其实还是很容易的,判断安装好的方法就是利用终端打开Python之后输入import pygame,导入pygame模块,显示了pygame的版本就Ok了。

开始游戏项目

这里我们首先创建了一个pygame的窗口,执行下面的代码,会显示一块黑色的屏幕。

#模块sys中有退出游戏的工具
import sys
#模块pygame具备开发游戏所需的功能
import pygame

class AlienInvasion:
	'''管理游戏资源和行为的类'''
	def __init__(self):
		#初始化背景设置,让pygame能够正常的工作
		pygame.init()
		#创建显示窗口,将显示窗口赋给属性screen,这样整个类中所有方法都能够使用他。
		#传递给screen的对象是一个surface,是屏幕的一部分,用于显示游戏元素
		self.screen=pygame.display.set_mode((1200,800))
		pygame.display.set_caption("Alien Invasion")

	def run_game(self):
		while True:
			#监视键盘和鼠标事件,pygame.event,get()这个函数返回一个列表,列表内是上次被调用后发生的所有事件
			for event in pygame.event.get():
				if event.type==pygame.QUIT:
					sys.exit()

			#让程序绘制的屏幕可见
			pygame.display.flip()

if __name__=='__main__':
	ai=AlienInvasion()
	ai.run_game()

设置一个背景颜色

如果说上面的代码默认生成一个黑色屏幕有些单调的话,我们可以通过下面两行代码,进行背景颜色的调整,第一行放到__init__里面,第二行加到run_game里面即可

self.bg_color=(230,230,230)
self.screen.fill(self.bg_color)

这里需要提及的是,在很多软件里面,颜色都是通过RGB或者RGBA来显示的,RGB分别表示红绿蓝三种颜色,A则是透明度的意思,在OpenGL,Android studio,以及我们目前的pygame里面都是用这种方式表示颜色的(因为我只在这三个地方接触到了这个),例子中设置的背景颜色是浅灰色。

创建一个设置类

之前,我们将程序的一些属性设置和运行放到了一起,这样一来,如果我们要修改设置就不是很方便,现在,我们另起一个文件,专门存放设置类,这样之后修改就会容易一些。

class Settings:
	"""用来存储我们这个游戏里面所有设置的类"""
	def __init__(self):
		#初始化游戏的设置
		#屏幕设置
		self.screen_width=1200
		self.screen_height=800
		self.bg_color=(230,230,230)
from settings import Settings

class AlienInvasion:
	'''管理游戏资源和行为的类'''
	def __init__(self):
		#初始化背景设置,让pygame能够正常的工作
		pygame.init()
		# #创建显示窗口,将显示窗口赋给属性screen,这样整个类中所有方法都能够使用他。
		# #传递给screen的对象是一个surface,是屏幕的一部分,用于显示游戏元素
		# self.screen=pygame.display.set_mode((600,400))
		# self.bg_color=(230,230,230)
		self.settings=Settings()
		self.screen=pygame.display.set_mode((self.settings.screen_width,self.settings.screen_height))
		pygame.display.set_caption("Alien Invasion")

添加图像

任何一个游戏,都不可能只有代码,图片也是游戏中一个重要的组成部分。在pygame里面,我们最好使用的是位图,也就是bmp文件,因为这个是pygame默认加载的。这里我们添加一个飞船的位图。
如果需要类似资源的话,可以在Pixabay等网站搜索免费图形。
在这里插入图片描述

创建ship类

既然有了飞船,我们就要创建一个文件来管理他。

import pygame

class Ship:
	"""管理飞船的类"""
	def __init__(self,ai_game):
		#初始化飞船,设置飞船的初始位置
		self.screen=ai_game.screen
		self.screen_rect=ai_game.screen.get_rect()

		#加载飞船图像并且获取其外接矩形
		self.image=pygame.image.load('images/ship.bmp')
		self.rect=self.image.get_rect()

		#对于每一艘新的飞船,我们都把他放到屏幕底部中央
		self.rect.midbottom=self.screen_rect.midbottom

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

在这部分代码里面,我们见到了很多的rect,这也是pygame高效的原因之一,因为这能让我们像处理矩形一样来处理很多的游戏元素。矩形处理让我们进行判断,调整位置更加容易了。
在完成了ship类的创建之后,我们进行飞船的绘制,在两个方法中分别假如代码:

self.ship=Ship(self)
self.ship.blitme()

最后的结果如下
在这里插入图片描述

代码的重构

随着我们一步步写下来,似乎run_game的功能越来越多,也越来越长了,而这样是不利于我们后续进行扩展的,所以我们对这部分代码进行一下重构。
这里我们把之前一个函数的功能分成了两个函数,run_game只负责调用这两个函数。

	def run_game(self):
		while True:
			#用来相应按键和鼠标事件
			self._check_events()
			#用来重新绘制图像,并切换到新的屏幕
			self._update_screen()

	def _check_events(self):
		#监视键盘和鼠标事件,pygame.event,get()这个函数返回一个列表,列表内是上次被调用后发生的所有事件
		for event in pygame.event.get():
			#响应特定事件
			if event.type==pygame.QUIT:
				sys.exit()

	def _update_screen(self):
		#每次循环都需要重绘屏幕
		self.screen.fill(self.settings.bg_color)
		#绘制飞船
		self.ship.blitme()
		#让程序绘制的屏幕可见
		pygame.display.flip()	

驾驶飞船

接下来,我们终于可以和用户对接了,我们将实现在用户按左右键时飞船的移动。

	def _check_events(self):
		#监视键盘和鼠标事件,pygame.event,get()这个函数返回一个列表,列表内是上次被调用后发生的所有事件
		for event in pygame.event.get():
			#响应特定事件
			if event.type==pygame.QUIT:
				sys.exit()
			#判断动了键盘
			elif event.type==pygame.KEYDOWN:
				#判断按了向右键
				if event.key==pygame.K_RIGHT:
					#向右移动飞船
					self.ship.rect.x+=10

这里,我们在监视键盘鼠标的函数里面添加了新的判断条件,如果按了向右键,飞船横坐标+10像素,实现了飞船的移动,但是我们只能是点一下,动一下,接下来我们要实现持续移动。
为了实现持续移动我们进行了如下更改:
在判断键盘执行的时候,从之前直接动飞船,改成调整飞船状态,飞船自己来动位置。

def _check_events(self):
		#监视键盘和鼠标事件,pygame.event,get()这个函数返回一个列表,列表内是上次被调用后发生的所有事件
		for event in pygame.event.get():
			#响应特定事件
			if event.type==pygame.QUIT:
				sys.exit()
			#判断按了键盘
			elif event.type==pygame.KEYDOWN:
				#判断按了向右键
				if event.key==pygame.K_RIGHT:
					#向右移动飞船
					self.ship.moving_right=True
				#判断按了向左键
				if event.key==pygame.K_LEFT:
					#向左移动飞船
					self.ship.moving_left=True				
			#判断松了按键
			elif event.type==pygame.KEYUP:
				#判断是向右键
				if event.key==pygame.K_RIGHT:
					#向右移动飞船
					self.ship.moving_right=False
				#判断是向左键
				if event.key==pygame.K_LEFT:
					#向左移动飞船
					self.ship.moving_left=False	

在飞船的类中,进行了如下调整

	def __init__(self,ai_game):
		#初始化飞船,设置飞船的初始位置
		self.screen=ai_game.screen
		self.screen_rect=ai_game.screen.get_rect()

		#加载飞船图像并且获取其外接矩形
		self.image=pygame.image.load('images/ship.bmp')
		self.rect=self.image.get_rect()

		#对于每一艘新的飞船,我们都把他放到屏幕底部中央
		self.rect.midbottom=self.screen_rect.midbottom

		#移动标志
		self.moving_right=False
		self.moving_left=False

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

	def update(self):
		if self.moving_right:
			self.rect.x+=1
		if self.moving_left:
			self.rect.x-=1

再次调整——速度

我们将飞船的速度也放进了Setting文件中,同时把速度从1改为1.5,将速度设置成为小数,可以更细致的控制飞船的速度。
ship文件中的修改部分:

		#对于每一艘新的飞船,我们都把他放到屏幕底部中央
		self.rect.midbottom=self.screen_rect.midbottom

		#在飞船的属性x中存储小数值,这样会更加精确
		self.x=float(self.rect.x)

注意这两部分的先后关系,颠倒的话飞船会出现在最左下角。

def update(self):
		if self.moving_right:
			self.x+=self.settings.ship_speed
		if self.moving_left:
			self.x-=self.settings.ship_speed

		#根据self.x更新self.rect.x
		self.rect.x=self.x

因为self.rect只能存整数,我们又设置了一个self.x用来存小数,之后再把值传回去。
在处理好了飞船的移动之后,我们要解决一个问题——飞船一直移动,会飞出屏幕外,所以我们还需要进行修改,让飞船一直在我们这个屏幕里面。

限制飞船活动范围

修改部分代码,在修改飞船位置之前先实现一个飞船和屏幕位置的判断。

	def update(self):
		if self.moving_right and self.rect.right<self.screen_rect.right:
			self.x+=self.settings.ship_speed
		if self.moving_left and self.rect.left>0:
			self.x-=self.settings.ship_speed

		#根据self.x更新self.rect.x
		self.rect.x=self.x

再次重构

随着我们这个飞船功能的开发,我们需要再次进行一波重构,这次的重构对象是_check_event()方法。将按键和松键分开。

    def _check_events(self):
		#监视键盘和鼠标事件,pygame.event,get()这个函数返回一个列表,列表内是上次被调用后发生的所有事件
		for event in pygame.event.get():
			#响应特定事件
			if event.type==pygame.QUIT:
				sys.exit()
			#判断按了键盘
			elif event.type==pygame.KEYDOWN:
			    self._check_keydown_events(event)			
			#判断松了按键
			elif event.type==pygame.KEYUP:
				self._check_keyup_events(event)

	def _check_keydown_events(self,event):
		#判断按了向右键
		if event.key==pygame.K_RIGHT:
			#向右移动飞船
			self.ship.moving_right=True
		#判断按了向左键
		if event.key==pygame.K_LEFT:
			#向左移动飞船
			self.ship.moving_left=True	

	def _check_keyup_events(self,event):
		#判断是向右键
		if event.key==pygame.K_RIGHT:
			#向右移动飞船
			self.ship.moving_right=False
		#判断是向左键
		if event.key==pygame.K_LEFT:
			#向左移动飞船
			self.ship.moving_left=False		

退出方法

目前我们都是通过窗口提供的X键来退出的,但是对于很多喜欢全屏游戏的用户来说,他们是用不到这个功能的,所以我们还是需要提供一个快捷键来让用户退出。

	def _check_keydown_events(self,event):
		#判断按了向右键
		if event.key==pygame.K_RIGHT:
			#向右移动飞船
			self.ship.moving_right=True
		#判断按了向左键
		if event.key==pygame.K_LEFT:
			#向左移动飞船
			self.ship.moving_left=True	
		#提供一个退出游戏的快捷键
		if event.key==pygame.K_q:
			sys.exit()

全屏游戏

我们上面退出快捷键也是为了这个目标而准备的。为了实现这一点,我们需要做出如下修改:

	def __init__(self):
		#初始化背景设置,让pygame能够正常的工作
		pygame.init()
		# #创建显示窗口,将显示窗口赋给属性screen,这样整个类中所有方法都能够使用他。
		# #传递给screen的对象是一个surface,是屏幕的一部分,用于显示游戏元素
		# self.screen=pygame.display.set_mode((600,400))
		# self.bg_color=(230,230,230)
		self.settings=Settings()
		# self.screen=pygame.display.set_mode((self.settings.screen_width,self.settings.screen_height))
		#让pygame生成一个覆盖整个显示器的屏幕
		self.screen=pygame.display.set_mode((0,0),pygame.FULLSCREEN)
		#使用屏幕的rect属性来更新对象settings
		self.settings.screen_width=self.screen.get_rect().width
		self.settings.screen_height=self.screen.get_rect().height
		pygame.display.set_caption("Alien Invasion")
		self.ship=Ship(self)

不过一定要确定之前设置好了退出快捷键,不然可能会出不来。

射击

这也是我们对宇宙飞船最后的加工了,让他具有射击的功能,这样我们就有了一块可以适用很多射击游戏的拼图了。
首先,我们需要添加子弹设置,所以我们需要再起一个文件:

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
	"""管理飞船所发射的子弹的类"""
	def __init__(self,ai_game):
		'''在飞船当前的位置创造一个子弹对象'''
		#继承情况下的初始化
		super().__init__()
		self.screen=ai_game.screen
		self.settings=ai_game.settings
		self.color=self.settings.bullet_color

		#在(0,0)处创建一个子弹矩形,之后在设置正确的位置
		self.rect=pygame.Rect(0,0,self.settings.bullet_width,self.settings.bullet_height)
		self.rect.midtop=ai_game.ship.rect.midtop

		#存储用小数表示的子弹位置
		self.y=float(self.rect.y)

	def update(self):
		"""向上移动子弹"""
		self.y-=self.settings.bullet_speed
		#更新表示子弹的rect位置
		self.rect.y=self.y

	def draw_bullet(self):
		#在屏幕上绘制子弹
		pygame.draw.rect(self.screen,self.color,self.rect)

我们在编写子弹的类的时候,需要继承pygame的Spirit类,通过使用精灵,我们可以把所有的子弹编组统一进行操作管理。
另外,setting里面也要添加:

		#子弹设置
		self.bullet_speed=1.0
		self.bullet_width=3
		self.bullet_height=15
		self.bullet_color=(60,60,60)

在主文件中需要添加这部分实现将子弹存到编组里面

self.bullets=pygame.spirite.Group()
#用来更新子弹位置 self.bullets.update()

终于开火了

主要修改位置:

	def _fire_bullet(self):
		#创建一颗子弹,并加入到编组中去
		new_bullet=Bullet(self)
		self.bullets.add(new_bullet)

	def _update_screen(self):
		#每次循环都需要重绘屏幕
		self.screen.fill(self.settings.bg_color)
		#绘制飞船
		self.ship.blitme()
		#遍历编组,绘制子弹
		for bullet in self.bullets.sprites():
			bullet.draw_bullet()
		#让程序绘制的屏幕可见
		pygame.display.flip()	
处理消失的子弹

现在我们已经可以实现子弹的发射了,但是我们知道,子弹出了屏幕之后依旧存在,只是看不到了而已,但是依旧会对程序造成负担,所以,我们需要对于已经出了屏幕的子弹进行删除。

	def run_game(self):
		while True:
			#用来相应按键和鼠标事件
			self._check_events()
			#用来判断这一时刻飞船的位置
			self.ship.update()
			#用来更新子弹位置
			self.bullets.update()
			#删除消失的子弹
			#for循环过程中间本身不可以修改列表或编组的元素,所以我们使用的是副本(copy)
			for bullet in self.bullets.copy():
				if bullet.rect.bottom<=0:
					self.bullets.remove(bullet)
			print(len(self.bullets))
			#用来重新绘制图像,并切换到新的屏幕
			self._update_screen()

于是,到现在,我们终于是完成了这样一个宇宙飞船,他可以移动,可以开火,是一个不错的游戏拼图了。之后的过程中,我们会继续扩展,最后做成一个小游戏。
(我的天,这个也太多了,比我前面两个还多。。。)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_51529433/article/details/119837559

智能推荐

监听网络变化--含7.0以上适配_android.net.conn.connectivity_change-程序员宅基地

文章浏览阅读3.7k次,点赞3次,收藏7次。我们知道最早监听网络变化,是通过广播,静态或动态注册广播,处理"android.net.conn.CONNECTIVITY_CHANGE"这个action就可以了intent就可以了。我们发现"android.net.conn.CONNECTIVITY_CHANGE"这个action已经加了注解@Deprecated,不推荐使用了。根据注释说明,7.0及以上静态注册广播(manifest中)..._android.net.conn.connectivity_change

计算机学习目标_bytetrack+yolov5 c++-程序员宅基地

文章浏览阅读291次。开个坑_bytetrack+yolov5 c++

fatal error: filesystem: 没有那个文件或目录_fatal error: filesystem: no such file or directory-程序员宅基地

文章浏览阅读4.8k次,点赞12次,收藏39次。fatal error: filesystem: 没有那个文件或目录_fatal error: filesystem: no such file or directory

2020起重机械指挥作业考试题库及起重机械指挥模拟考试系统_换算英制直径5分钢丝绳为公制多少毫米?()。-程序员宅基地

文章浏览阅读1k次。题库来源:安全生产模拟考试一点通公众号小程序2020起重机械指挥作业考试题库及起重机械指挥模拟考试系统,包含起重机械指挥作业考试题库答案解析及起重机械指挥模拟考试系统练习。由安全生产模拟考试一点通公众号结合国家起重机械指挥考试最新大纲及起重机械指挥考试真题出具,有助于起重机械指挥考试试题考前练习。1、【判断题】指挥人员负责对可能出现的事故采取必要的防范措施。(√)2、【判断题】手势信号包括通用手势信号、专用手势信号和其它指挥信号。()(×)3、【判断题】吊装用的短环链,不..._换算英制直径5分钢丝绳为公制多少毫米?()。

大数据应用丨大数据时代的医学公共数据库与数据挖掘技术简介_dryad数据库-程序员宅基地

文章浏览阅读1.7k次,点赞2次,收藏25次。本文我们将介绍几种数据库和数据挖掘技术,帮助临床研究人员更好地理解和应用数据库技术。数据挖掘技术可以从大量数据中寻找潜在有价值的信息,主要分为数据准备、数据挖掘、以及结果表达和分析。数据库技术是研究、管理和应用数据库的一门软件科学。通过研究数据库的结构、存储、设计、管理和应用的基本理论和实现方法,对数据库中的数据进行处理和分析。_dryad数据库

随便推点

SpringBoot整合Elastic-job实现_springboot + elasticjob-程序员宅基地

文章浏览阅读3.1k次,点赞3次,收藏13次。SpringBoot整合Elastic-job实现【基本整合】:原理参考:Elastic-Job原理(1)引用pom依赖:<dependency> <groupId>com.dangdang</groupId> <artifactId>elastic-job-lite-core</artifactId> <..._springboot + elasticjob

Attensleep:一种基于注意力的单通道EEG睡眠分期深度学习方法_an attention-based deep learning approach for slee-程序员宅基地

文章浏览阅读791次。AttenSleep 基于注意力的深度学习架构从单通道EEG信号中进行睡眠阶段分类从基于多分辨率卷积神经网络( MRCNN )和自适应特征重标定( AFR )的特征提取模块入手。MRCNN可以提取低频和高频特征,而AFR可以通过建模特征之间的相互依赖关系来提高提取特征的质量。第二个模块是时间上下文编码器( TCE ),它利用多头注意力机制来捕获提取特征之间的时间依赖关系。特别地,多头注意力利用因果卷积对输入特征中的时间关系进行建模。使用三个公共数据集来评估提出的AttnSleep模型的性能。_an attention-based deep learning approach for sleep stage classification wit

Myeclipse技巧-程序员宅基地

文章浏览阅读71次。在了解MyEclipse使用技巧之前我们来看看MyEclipse是什么呢?简单而言,MyEclipse是Eclipse的插件,也是一款功能强大的J2EE集成开发环境,支持代码编写、配置、测试以及除错。下面让我们看看MyEclipse使用技巧的具体内容。MyEclipse使用技巧第一步: 取消自动validationvalidation有一堆,什么xml、jsp、jsf..._myeclipse是什么

c语言统计数组每个数出现的次数,统计数组中某个元素出现的次数和重复的次数...-程序员宅基地

文章浏览阅读8.9k次。//出现的次数function times(arr){var m=0,times=0;//m是数组中的元素,times用来统计出现的次数// for循环遍历arr数组for(var i=0;iif(arr[i]==m){times++;//数组中有相同值就加1}}return times;console.log(times);//这是打印出的出现的次数}times([0, 1, 2, 0, 1, ..._c语言统计数组中每个数字出现的次数

Jmeter连接InfluxDB2.0.4_influxdborganization jmeter-程序员宅基地

文章浏览阅读2.5k次,点赞5次,收藏14次。Jmeter连接InfluxDB2.0.4问题描述:在用Jmeter+InfluxDB构建监控时,因为docker构建的InfluxDB的版本是2.0.4,按照网上的教程进行后端监听器的填写,但是一直出现错误提示401等问题。网上的教程大多是1.X版本的,怀疑是数据库版本不一致导致的数据无法写入,通过调研,问题已解决。以下为配置方法。一、InfluxDB搭建完成后,查看Organization和Bucket名称,这里是ORZ_test和bucket_nameOrganization在这里我的理解_influxdborganization jmeter

关于第三方支付,看这篇文章就够了!-程序员宅基地

文章浏览阅读1.6k次。目录 目录 1、第三方支付概述 2、第三方支付起源 PayPal 支付宝 3、牌照发放 4、支付牌照 5、第三方支付参与者 6、第三方支付行业监管 监管意图对第三方支付可能产生的影响..._第三方支付本行对本行的费用

推荐文章

热门文章

相关标签