20213401王尊阳 实验四 Python综合实践
学号20213401 《python程序设计》实验四报告
课程:《Python程序设计》
班级:2134
姓名:王尊阳
学号:20213401
实验教师:王志强
实验日期:2022年5月19日
必修/选修: 公选课
1.实验内容
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
2.实验要求
在华为ECS服务器(OpenOuler系统)和物理机(Windows/Linux系统)上使用VIM、PDB、IDLE、Pycharm等工具编程实现。
- 实验过程及结果
3.1 代码
from cmath import tan
from re import S, T
from telnetlib import GA
from turtle import width
from pygame.sprite import Sprite
import pygame
import random
import time
Screen_Width=900
Screen_Height=600
ck_color=pygame.Color(0,0,0)#黑色窗口颜色
TEXT_COLOR=pygame.Color(0,0,255)
#定义一个基类
class BaseItem(Sprite):
def __init__(self,color,width,height):
pygame.sprite.Sprite.__init__(self)
class Game():
window=None
MyTank=None
#存储敌方坦克列表
EnemyTankList=[]
#定义敌方坦克数量
EnemyTankCount=5
#存储我方子弹列表
myBulletList=[]
#存储敌方子弹的列表
enemyBulletList=[]
#存储爆炸效果的列表
explodeList=[]
#存储墙壁的列表
wallList=[]
def __init__(self):
pass
#游戏开始
def startGame(self):
pygame.display.init() #初始化窗口
Game.window=pygame.display.set_mode([Screen_Width,Screen_Height])#设置窗口大小和显示
#初始化我方坦克
self.createMytank()
#初始化敌方坦克,并将敌方坦克添加到列表中
self.createEnemyTank()
#初始化墙壁
self.createWall()
pygame.display.set_caption('坦克大战小游戏')#设置窗口标题
while True:
#坦克速度降低
time.sleep(0.02)
Game.window.fill(ck_color)#窗口填充色
self.getEvent()#事件获取
Game.window.blit(self.getTextSurface('敌方坦克剩余数量为%d'%len(Game.EnemyTankList)),(10,10))#blit用图像绘制到另一个上面
#判断我方坦克是否是存活
if Game.MyTank and Game.MyTank.live:
Game.MyTank.displayTank()
else:
#删除我方坦克
del Game.MyTank
Game.MyTank=None
#循环遍历敌方坦克列表,展示敌方坦克
self.blitEnemyTank()
#循环遍历显示我方坦克子弹
self.blitMyBullet()
#遍历敌方子弹列表,展示敌方子弹
self.blitEnemyBullet()
#循环遍历爆炸列表,展示爆炸效果
self.blitExplode()
#循环遍历墙壁列表,展示墙壁
self.blitWall()
#进行判断,如果按下键盘才能移动
if Game.MyTank and Game.MyTank.live:
if not Game.MyTank.stop:
Game.MyTank.move()
#检测我方坦克是否与墙壁发生碰撞
Game.MyTank.hitWall()
#检测我方坦克是否与敌方坦克发生碰撞
Game.MyTank.myTank_hit_enemyTank()
pygame.display.update()#这里是为了运行窗口长存,不要一闪而过
#循环遍历墙壁列表,展示墙壁
def blitWall(self):
for wall in Game.wallList:
#判断墙壁是否存活
if wall.live:
#调用墙壁的显示方法
wall.displayWall()
else:
#从墙壁列表移除
Game.wallList.remove(wall)
#初始化墙壁
def createWall(self):
for i in range(6):
#初始化墙壁
wall=Wall(i*160,250)
#将墙壁添加到列表中
Game.wallList.append(wall)
#创建我方坦克的方法
def createMytank(self):
Game.MyTank = MyTank(450,300)
#创建Music对象
music=Music('5170.wav')
#播放音乐
music.play()
def createEnemyTank(self):
top=100
#循环生成敌方坦克
for i in range(Game.EnemyTankCount):
left=random.randint(0,800)
speed=random.randint(1,4)
enemy=EnemyTank(left,top,speed)
Game.EnemyTankList.append(enemy)
#循环展示爆炸效果
def blitExplode(self):
for explode in Game.explodeList:
#判断是否活着
if explode.live:
#展示
explode.displayExplode()
else:
#在爆炸列表里移除
Game.explodeList.remove(explode)
#循环遍历敌方坦克列表,展示敌方坦克
def blitEnemyTank(self):
for enemyTank in Game.EnemyTankList:
#判断当前敌方坦克是否活着
if enemyTank.live:
enemyTank.displayTank()
enemyTank.randMove()
#调用检测是否与墙壁碰撞
enemyTank.hitWall()
#检测敌方坦克是否与我方坦克发生碰撞
if Game.MyTank and Game.MyTank.live:
enemyTank.enemyTank_hit_myTank()
#发射子弹
enemyBullet=enemyTank.shot()
#判断敌方子弹是不是None,如果不为None则添加到敌方子弹列表
if enemyBullet:
#将敌方子弹存储到敌方子弹列表中
Game.enemyBulletList.append(enemyBullet)
else:#不活着,从敌方坦克列表中移除
Game.EnemyTankList.remove(enemyTank)
#循环便利我方子弹存储列表
def blitMyBullet(self):
for myBullet in Game.myBulletList:
#判断当前子弹是否活着,是则移动
if myBullet.live:
myBullet.displayBullet()
#调用子弹的方法
myBullet.move()
#调用检测我方子弹是否与敌方坦克碰撞
myBullet.myBullet_hit_enemyTank()
#检测我方子弹是否与墙壁碰撞
myBullet.hitWall()
#若子弹死亡,则在列表中删除
else:
Game.myBulletList.remove(myBullet)
#遍历敌方子弹列表,展示敌方子弹
def blitEnemyBullet(self):
for enemyBullet in Game.enemyBulletList:
if enemyBullet.live:#判断敌方子弹是否存活
enemyBullet.displayBullet()
enemyBullet.move()
#调用敌方子弹与我方坦克碰撞的方法
enemyBullet.enemyBullet_hit_myTank()
#检测敌方子弹是否与墙壁碰撞
enemyBullet.hitWall()
else:
Game.enemyBulletList.remove(enemyBullet)
#游戏结束
def gameover(self):
print('游戏结束,谢谢使用')
exit()
def getTextSurface(self,text):
pygame.font.init()#初始化文字模块
#print(pygame.font.get_fonts())#查看所有的字体名称
font=pygame.font.SysFont('kaiti',18)#获取字体对象
textSurface=font.render(text,True,TEXT_COLOR)
return textSurface
#事件获取,使窗口不再无响应
def getEvent(self):
#获取所有事件
eventList=pygame.event.get()
for event in eventList:
#判断按键类型
if event.type == pygame.QUIT:#窗口关闭
self.gameover()
if event.type == pygame.KEYDOWN:#判断键盘按键使坦克移动
#当坦克不存在或者死亡
if not Game.MyTank:
#判断按下的是ESC,让坦克重生
if event.key == pygame.K_ESCAPE:
#让我方坦克重生及调用创建坦克的方法
self.createMytank()
if Game.MyTank and Game.MyTank.live:
if event.key == pygame.K_LEFT:
#切换方向
Game.MyTank.direction='L'
Game.MyTank.stop=False
#Game.MyTank.move()
print('按左键使坦克向左移动')
elif event.key == pygame.K_RIGHT:
Game.MyTank.direction='R'
Game.MyTank.stop=False
#Game.MyTank.move()
print('按右键使坦克向右移动')
elif event.key == pygame.K_UP:
Game.MyTank.direction='U'
Game.MyTank.stop=False
#Game.MyTank.move()
print('按上键使坦克向上移动')
elif event.key == pygame.K_DOWN:
Game.MyTank.direction='D'
Game.MyTank.stop=False
#Game.MyTank.move()
print('按下键使坦克向下移动')
elif event.key == pygame.K_SPACE:
print("biu")
#如果当前我房子单列表大小<3的时候才可以创建
if len(Game.myBulletList)<3:
#创建我方子弹
myBullet=Bullet(Game.MyTank)
Game.myBulletList.append(myBullet)
if event.type == pygame.KEYUP:
#判断松开的键位是上下左右,不要是空格
if event.key==pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_LEFT or event.key ==pygame.K_RIGHT:
if Game.MyTank and Game.MyTank.live:
Game.MyTank.stop=True
class Tank(BaseItem):
def __init__(self,left,top):
#保存加载的图片
self.images={
'U':pygame.image.load('up(1).jpg'),
'D':pygame.image.load('down(1).jpg'),
'L':pygame.image.load('left(1).jpg'),
'R':pygame.image.load('right(1).jpg'),
}
#方向
self.direction='U'
#根据方向获得图片
self.image=self.images[self.direction]
self.rect=self.image.get_rect()#获得图片区域
#设置区域的left top
self.rect.left=left
self.rect.top=top
#速度设置
self.speed=5
#坦克移动开关
self.stop=True
#是否活着
self.live=True
#新增属性原来坐标
self.oldLeft=self.rect.left
self.oldTop=self.rect.top
#移动
def move(self):
#移动后记录原始的坐标
self.oldLeft=self.rect.left
self.oldTop=self.rect.top
#判断坦克方向移动
if self.direction == 'L':
if self.rect.left>0:
self.rect.left -=self.speed
elif self.direction == 'U':
if self.rect.top>0:
self.rect.top -=self.speed
elif self.direction == 'D':
if self.rect.top+self.rect.height<Screen_Height:
self.rect.top +=self.speed
elif self.direction == 'R':
if self.rect.left+self.rect.height<Screen_Width:
self.rect.left +=self.speed
#射击
def shot(self):
return Bullet(self)
#
def stay(self):
self.rect.left=self.oldLeft
self.rect.top=self.oldTop
#检测坦克是否与墙壁发生碰撞
def hitWall(self):
for wall in Game.wallList:
if pygame.sprite.collide_rect(self,wall):
#将坐标设置为移动之前的坐标
self.stay()
#坦克显示
def displayTank(self):
self.image=self.images[self.direction]#获取对象
Game.window.blit(self.image,self.rect)
#我方坦克类
class MyTank(Tank):
def __init__(self,left,top):
super(MyTank,self).__init__(left,top)
#检测我方坦克与敌方坦克发生碰撞
def myTank_hit_enemyTank(self):
#循环遍历敌方坦克列表
for enemyTank in Game.EnemyTankList:
if pygame.sprite.collide_rect(self,enemyTank):
self.stay()
#敌方坦克
class EnemyTank(Tank):
def __init__(self,left,top,speed):
#调用父类初始化方法
super(EnemyTank,self).__init__(left,top)
#加载图片集
self.images={
'U':pygame.image.load('up(2).png'),
'D':pygame.image.load('down(2).png'),
'L':pygame.image.load('left(2).png'),
'R':pygame.image.load('right(2).png')
}
#随机方向
self.direction=self.randDirection()
#根据方向获取图片
self.image=self.images[self.direction]
#区域
self.rect=self.image.get_rect()
#赋值left和top
self.rect.left=left
self.rect.top=top
#速度
self.speed=speed
#移动开关键
self.flag=True
#步数变量,当步数小于等于0时敌方坦克变化方向
self.step=100
#敌方坦克与我方坦克是否发生碰撞
def enemyTank_hit_myTank(self):
if pygame.sprite.collide_rect(self,Game.MyTank):
self.stay()
#随机生成敌方坦克的方向
def randDirection(self):
num=random.randint(1,4)
if num ==1:
return 'U'
if num ==2:
return 'D'
if num ==3:
return 'L'
if num ==4:
return 'R'
def randMove(self):
if self.step<=0:
#修改方向
self.direction=self.randDirection()
#让步数复位
self.step=100
else:
self.move()
#让步数递减
self.step-=1
#重写shot()
def shot(self):
#随机生成100以内的一些数
num=random.randint(1,100)
if num<5:
return Bullet(self)
#子弹类
class Bullet(BaseItem):
def __init__(self,tank):
#加载图片
self.image=pygame.image.load('bullet(1).png')
#坦克的方向决定子弹的方向
self.direction=tank.direction
#获取区域
self.rect=self.image.get_rect()
#子弹位置与坦克方向有关
if self.direction == 'U':
self.rect.left=tank.rect.left+tank.rect.width/2-self.rect.width/2
self.rect.top=tank.rect.top-self.rect.height
elif self.direction == 'D':
self.rect.left=tank.rect.left+tank.rect.width/2-self.rect.width/2
self.rect.top=tank.rect.top+tank.rect.height
elif self.direction == 'L':
self.rect.left=tank.rect.left-self.rect.width/2-self.rect.width/2
self.rect.top=tank.rect.top+tank.rect.width/2-self.rect.width/2
elif self.direction == 'R':
self.rect.left=tank.rect.left+tank.rect.width
self.rect.top=tank.rect.top+tank.rect.width/2-self.rect.width/2
#子弹速度
self.speed=6
#子弹的状态是否碰到墙壁,若碰到,修改状态
self.live=True
#移动
def move(self):
if self.direction =='U':
if self.rect.top>0:
self.rect.top-=self.speed
else:
#修改子弹状态
self.live=False
elif self.direction =='R':
if self.rect.left+self.rect.width<Screen_Width:
self.rect.left+=self.speed
else:
#修改子弹状态
self.live=False
elif self.direction =='D':
if self.rect.top+self.rect.height<Screen_Height:
self.rect.top+=self.speed
else:
#修改子弹状态
self.live=False
elif self.direction =='L':
if self.rect.left>0:
self.rect.left-=self.speed
else:
#修改子弹状态
self.live=False
#子弹是否碰撞墙壁
def hitWall(self):
#循环遍历墙壁列表
for wall in Game.wallList:
if pygame.sprite.collide_rect(self,wall):
#修改子弹生存状态,让子弹消失
self.live=False
#墙壁的生命减小
wall.hp-=1
if wall.hp<=0:
#修改枪毙的生存状态
wall.live=False
#展示子弹
def displayBullet(self):
#将图片加载到窗口
Game.window.blit(self.image,self.rect)
#我方子弹与敌方坦克的碰撞
def myBullet_hit_enemyTank(self):
#循环遍历敌方坦克列表,判断是否发生碰撞
for enemyTank in Game.EnemyTankList:
if pygame.sprite.collide_rect(enemyTank,self):
#修改敌方坦克和我方子弹的状态
enemyTank.live=False
self.live=False
#创建爆炸对象
explode=Explode(enemyTank)
#将爆炸对象添加到爆炸列表中
Game.explodeList.append(explode)
#敌方子弹与我方坦克的碰撞
def enemyBullet_hit_myTank(self):
if Game.MyTank and Game.MyTank.live:
if pygame.sprite.collide_rect(Game.MyTank,self):
#产生爆炸对象
explode=Explode(Game.MyTank)
#将爆炸对象添加到爆炸列表中
Game.explodeList.append(explode)
#修改敌方子弹与我方坦克的状态
self.live=False
Game.MyTank.live=False
class Wall():
def __init__(self,left,top):
#加载墙壁图片
self.image=pygame.image.load('wall.png')
#获取墙壁的区域
self.rect=self.image.get_rect()
#设置位置left,top
self.rect.left=left
self.rect.top=top
#是否活着
self.live=True
#设置生命值
self.hp=3
#墙壁显示
def displayWall(self):
Game.window.blit(self.image,self.rect)
class Explode():
def __init__(self,tank):
#爆炸的位置由当前子弹打中的坦克位置决定
self.rect=tank.rect
self.images=[
pygame.image.load('boom(0).jpg'),
pygame.image.load('boom(1).jpg'),
pygame.image.load('boom(2).jpg'),
pygame.image.load('boom(3).jpg'),
pygame.image.load('boom(4).jpg'),
]
self.step=0
self.image=self.images[self.step]
#是否活着
self.live=True
#爆炸显示
def displayExplode(self):
if self.step<len(self.images):
#根据索引获取爆炸对象
self.image=self.images[self.step]
self.step+=1
#添加到主窗口
Game.window.blit(self.image,self.rect)
else:
#修改活着的状态
self.live=False
self.step=0
class Music():
def __init__(self,filename):
self.filename=filename
#初始化音乐混合器
pygame.mixer.init()
#加载音乐
pygame.mixer.music.load(self.filename)
#play
def play(self):
pygame.mixer.music.play()
if __name__=='__main__':
#要多次循环!否则窗口一闪而过
Game().startGame()
3.2运行结果
- 实验过程中遇到的问题和解决过程
问题1:如何实现我方子弹与敌方坦克精灵类
问题1解决方案:pygame里的sprite,并使用继承方法
问题2:如何判断子弹是否与墙壁碰撞
问题2解决方案:搜索pygame相关代码。找到 pygame.sprite.collide_rect(),成功解决了相关问题
问题3:这几百行代码没有思路,写的时候感觉不清晰
问题3解决方案:打游戏、程序等大型代码时,要学会使用注释,学会先用注释梳理思路,逐步填充,不呀一口吞一个胖子,此游戏代码看似很长,其实许多地方都有重复可利用的地方,自己使用注释梳理自己清晰的过程,有助于使得自己的代码顺利完成。
问题4:背景音乐无法正常播放
问题4解决方案:寻找了pygame官网,发现mixer混合器没有初始化,初始化后,将music start改为music play即可正常播放了。 - 其他(感悟、思考等)
在一开始听到老师最后一次实验内容时,心里十分无助且不明白为什么最后一次实验这么难,我觉得对大一新生来说,能真正独立做出完整大程序的同学,应该很少很少,但是毕竟是作业需要,我也只能被迫试试。就在我慢慢做这个实验,慢慢查询资料,搜索视频,询问同学……就在这个过程中,我慢慢的可能理解了老师布置这个作业的含义。
当我在看着视频或者资料编写代码时,我学会了许多以前没接触到的东西。这种代码跟以往那种小功能的代码从思路到实践都让我有很大的收获:
1.以前在写代码时,不喜欢写什么流程图,可能会去构思,但是绝对不会去手写或者在电脑上打出来。但是呢,这次实验让我真正明白了这个的重要性,这次代码我打了两周多,因为任务量大,连续的空闲时间短,所以需要分隔时间多次打代码,这就显示出了注释和流程图的重要性。
2.代码用到了大量pygame的函数,所以查询是很重要的。学会查询相关信息,能让我变得更加自主,更加独立。
3.这次代码虽然我不是完全独立自主的完成的,但是让我对python用了亲身的实际体会,让我认识到了python的能力,使我对python有了更大的兴趣。我觉得,这次大实验让我积累了经验,使我对以后的程序代码的编写打下了基础,我觉得十分有意义。
python选修课让我收获很大,让我认识到了python语言的魅力。从集合、字典到后来的爬虫、封装、继承……我觉得,python给我打开了一个更为广阔的语言的大门,虽然选修课结束了,但是学习永远不会结束,以后我也会继续学习、巩固,不会让选修课学到的知识白白流失,加密、游戏的设计都让我倍感兴趣,觉得很牛哈哈,这些我都没有从c语言中感受过,感谢老师的教导,让我受益匪浅!
参考资料
pygame官网
哔哩哔哩
菜鸟教程