模块安装(安装pygame)
-
1.pip 安装
- windows+R-->cmd-->命令行输入 pip install pygame
-
2.pycharm中安装
- file-->settting-->Project Interpreter-->右侧+--install-->搜索框输入pygame-->下方 install package
-
3.下载好安装包之后直接安装
- 在Python官网下载好pygame-1.9.6-cp37-cp37m-win_amd64.whl,打开命令窗口,切换到安装包目录,执行 pip install pygame-1.9.6-cp37-cp37m-win_amd64.whl
-
使用 pip list 查看是否有pygame
面向对象分析
-
游戏原理分析
- 游戏原理:和动画原理相同,快速切换图片,为了避免上一次贴图,每次刷新前,重贴所有图片
-
实现框架的搭建(类的设计)
-
主逻辑类
-
基本坦克类
-
我方坦克类
-
敌方坦克类
-
子弹类
-
墙壁类
-
爆炸类
-
框架搭建
-
参考代码
- 主逻辑类 class MainGame: def start(self): """开始游戏""" pass def game_over(self): """结束游戏""" pass # - 基本坦克类 class BaseTank: pass # - 我方坦克类 class HeroTank: pass # - 敌方坦克类 class EnemyTank: pass # - 子弹类 class Bullet: pass # - 墙壁类 class Wall: pass # - 爆炸类 class Bomb: pass
主逻辑类
-
属性:游戏主窗口
-
方法:开始游戏
-
窗口初始化
-
设置窗口
-
设置标题(坦克大战v_1.0)
-
窗口背景
-
游戏应该在无限循环中
class MainGame: #游戏主窗口 window = None def start(self): """开始游戏""" # 调用窗口初始化 pygame.display.init() # 创建窗口 MainGame.window = pygame.display.set_mode((900,500)) # 设置窗口标题 pygame.display.set_caption("坦克大战v_1.0") while True: #窗口背景颜色 MainGame.window.fill((0,0,0)) #刷新 pygame.display.update() def game_over(self): """结束游戏""" pass
-
主逻辑内进行事件检测
-
获取新事件
- pygame.event.get():
- 鼠标点击窗口事件 pygame.QUIT
- 键盘按下事件 pygame.KEYDOWN
- pygame.event.get():
-
键盘长按事件
- pygame.key.getpressed()
-
参考代码(在主逻辑代码中添加)
def deal_event(self): """事件检测""" # print(pygame.event.get()) for event in pygame.event.get(): # 1. 鼠标点击关闭窗口事件 if event.type == pygame.QUIT: print("点击关闭窗口按钮") sys.exit() elif event.type==pygame.KEYDOWN: # print("按下键盘") if event.key==pygame.K_LEFT: print("左移") elif event.key==pygame.K_RIGHT: print("右移") elif event.type==pygame.MOUSEBUTTONDOWN: print("鼠标点击事件")
我方坦克分析
-
由于我方坦克和敌方坦克有相似属性和方法,所以可以定义基本坦克类,让我方坦克和敌方坦克继承基本坦克类
-
基本坦克类:
- 属性:图片、方向、图片矩形区域、坦克位置、移动速度、是否活着
- 方法:移动、贴图
-
参考代码(定义基本坦克类,让我方坦克类继承)
class BaseTank: def __init__(self,x,y): """基本坦克类的属性""" # 加载图片文件,返回图片对象 #将坦克图片储存在字典中 self.images = { "U":pygame.image.load("tank_img/p1tankU.gif"), "D":pygame.image.load("tank_img/p1tankU.gif"), "L":pygame.image.load("tank_img/p1tankU.gif"), "R":pygame.image.load("tank_img/p1tankU.gif"), } #给初始化坦克一个方向 self.direction = "U" #根据坦克方向获取坦克图片 self.image = self.images[self.direction] #获取图片矩形区域 self.rect = self.image.get_rect() #根据传入的参数,决定坦克的位置 self.rect.x = x #坦克的x坐标 self.rect.y = y #坦克的y坐标 #移动速度 self.speed = 3 #是否活着 self.live = True def display_tank(self): """贴坦克图片的方法""" #先获取坦克图片 self.image = self.images[self.direction] #贴坦克图片 MainGame.window.blit(self.image,self.rect) # - 我方坦克类 class HeroTank(BaseTank): def __init__(self,x,y): super(HeroTank, self).__init__(x,y) self.speed = 2
-
创建我方坦克,并加载图片
- 在主逻辑中,一开始就存在我方坦克,定义P类属性记录
- 先定义创建我方坦克的方法(create_hero_tank),再定义加载坦克图片的方法(load_hero_tank)
- 在开始游戏时,调用创建坦克对象的方法,在循环中加载坦克图片
#主逻辑中记录坦克 P1 = None def create_tank(self): """创建我方坦克""" #判断是否创建了我方坦克 if not MainGame.P1: MainGame.P1 = HeroTank(500,400) #坦克的初始位置 def load_heor_tank(self): """加载我方坦克""" if MainGame.P1 and MainGame.P1.live: #如果坦克活着就调用坦克贴图的方法 MainGame.P1.display_tank() else: #如果坦克死了,就删除坦克对象 del MainGame.P1 MainGame.P1 = None #在开始游戏时调用 self.create_tank() #在开始游戏循环中调用 self.load_heor_tank()
-
实现我方坦克移动的方法
-
在基本坦克类添加坦克移动方法
-
判断坦克的方向属性,是哪个方向就向哪个方向移动(向右x为正,向下y为正)
- 移动方式:图片坐标+坦克速度 (图片坐标-坦克速度)
def move(self): """坦克移动的方法""" if self.direction=="U": #坦克的方向 if self.rect.y>0: #坦克的y坐标在边界内就一直移动 self.rect.y -= self.speed elif self.direction=="D": #坦克的方向 if self.rect.y<SCREEN_HEIGHT-self.rect.height: #坦克的y坐标在边界内就一直移动 self.rect.y += self.speed elif self.direction=="L": #坦克的方向 if self.rect.x>0: #坦克的x坐标在边界内就一直移动 self.rect.x -= self.speed elif self.direction=="R": #坦克的方向 if self.rect.x<SCREEN_WIDTH-self.rect.width: #坦克的x坐标在边界内就一直移动 self.rect.x -= self.speed #在加载我方坦克中调用坦克移动方法 MainGame.P1.move() #此时坦克只能向上移动
-
-
优化坦克移动的方法
- 检测键盘长按事件,获取按键状态
- 检测到键盘改变方向之后,调用父类移动方法
#我方坦克重写move()方法 def move(self): """我方坦克移动方法""" # 键盘长按事件,获取键盘上所有按键状态,按下1,没按0 keys_status = pygame.key.get_pressed() # print(keys_status) if keys_status[pygame.K_UP]: #按键“上”被按下 self.direction = "U" #修改方向属性 super(HeroTank, self).move() #调用父类中移动方法 elif keys_status[pygame.K_DOWN]: self.direction = "D" super(HeroTank, self).move() elif keys_status[pygame.K_LEFT]: self.direction = "L" super(HeroTank, self).move() elif keys_status[pygame.K_RIGHT]: self.direction = "R" super(HeroTank, self).move()
敌方坦克分析
-
敌方坦克和基本坦克类有相同的属性,如位置,同时也有自己的属性,如随机方向和随机速度、图片等,所以让敌方坦克类继承基本坦克类,再复写敌方坦克的属性和方法
# - 敌方坦克类 class EnemyTank: def __init__(self,x,y): super(EnemyTank, self).__init__(x,y) # 将敌方坦克图片储存在字典中 self.images = { "U": pygame.image.load("tank_img/enemy1U.gif"), "D": pygame.image.load("tank_img/enemy1D.gif.gif"), "L": pygame.image.load("tank_img/enemy1L.gif.gif"), "R": pygame.image.load("tank_img/enemy1R.gif.gif"), } #随机方向 self.direction = self.random_direction() #根据方向取出对应的图片 self.image = self.images[self.direction] #随机速度 self.speed = random.randint(1,2) def random_direction(self): directons = ["U","D","L","R"] return random.choice(directons)
-
主逻辑中,新增创建敌方坦克方法create_enemy_tank和加载敌方坦克load_enemy_tank的方法
-
在最开始添加ENEMY_TANK_COUNT常量记录敌方坦克数量,在主逻辑中定义存储敌方坦克对象的列表enemy_tank_list
-
在游戏开始,调用创建敌方坦克方法create_enemy_tank,在游戏循环中,调用加载敌方坦克load_enemy_tank的方法
ENEMY_TANK_COUNT = 5 class MainGame: #主逻辑中定义类属性 #记录敌方坦克 enemy_tank_list = [] def create_enemy_tank(self): """创建敌方坦克""" #创建多辆坦克 for i in range(ENEMY_TANK_COUNT): #随机敌方坦克的坐标 e_tank = EnemyTank(random.randint(0,8)*100,100) #创建敌方坦克后添加到主逻辑的敌方坦克列表中 MainGame.enemy_tank_list.append(e_tank) def load_enemy_tank(self): """加载敌方坦克""" #获取每一辆敌方坦克对象 for e_tank in MainGame.enemy_tank_list: #判断坦克是否活着 if e_tank.live: #如果活着,贴坦克图片 e_tank.display_tank() else: MainGame.enemy_tank_list.remove(e_tank) 在游戏开始,调用创建敌方坦克方法self.create_enemy_tank(), 在游戏循环中,调用加载敌方坦克self.load_enemy_tank() 此时坦克只能朝一个方向移动
敌方坦克随机移动
-
敌方坦克中新增step属性
- 作用:记录移动的步数,超过步数,重新生成方向
-
敌方坦克中:重写父类的移动方法,如果步长大于0,调用父类移动方法,同时步长-1,否则重新生成方向和步长
#记录步数(每个方向走的步数) self.step = 50 #敌方坦克类:重写父类中的移动方法 def move(self): """敌方坦克移动""" if self.step>0: super(EnemyTank, self).move() self.step -= 1 else: #重新生成方向 self.direction = self.random_direction() # self.step = 50 self.step = (5-self.speed)*random.randint(40,60)
我方坦克发射子弹
-
子弹类
-
属性:图片,是否或者,速度,方向,子弹位置
-
方法:贴图,移动
class Bullet: def __init__(self,tank): #图片 self.image = pygame.image.load("tank_img/tankmissile.gif") #是否活着 self.live = True #移动速度 self.speed = 5 #子弹方向和坦克方向一致 self.direction = tank.direction #设置子弹位置 self.rect = self.image.get_rect() self.rect.x = tank.rect.centerx #子弹中心就是坦克中心 self.rect.y = tank.rect.centery def display_bullet(self): """子弹贴图方法""" MainGame.window.blit(self.image,self.rect)
-
-
由于是坦克发射子弹,所以要在父类中新增发射方法
-
在按下空格按键的时候,创建子弹,所有在事件检测中添加代码
#基本坦克类中,新增发射子弹方法 class BaseTank: def shot(self): bullet = Bullet(self) return bullet #事件检测中添加我方坦克发射方法 def deal_event(self): """事件检测""" for event in pygame.event.get(): if event.type == pygame.KEYDOWN: #键盘按下事件 if event.key==pygame.K_SPACE: # print("发射子弹") #创建一个子弹对象,添加到我方子弹列表中 bullet = MainGame.P1.shot() MainGame.bullet_hero_list.append(bullet) #在主逻辑类中,新增存储子弹的列表 bullet_hero_list = [] #记录我方子弹
-
在主逻辑类中,新增加载我方子弹的方法
def load_bullet_hero(self): """加载我方坦克发射的子弹""" #取出每一颗子弹 for b in MainGame.bullet_hero_list: #判断子弹是否活着 if b.live: #贴子弹图片 b.display_bullet() else: MainGame.bullet_hero_list.remove(b) #在开始游戏循环中,调用加载子弹方法 self.load_bullet_hero() #此时子弹还不能移动
-
新增子弹移动方法
def move(self): """子弹移动方法""" #子弹方向朝上,应向上移动 if self.direction == "U": #在边界内,子弹正常移动,超出边界不移动 if self.rect.centery > -self.rect.height//2: self.rect.centery -= self.speed else: self.live = False elif self.direction == "D": # 在边界内,子弹正常移动,超出边界不移动 if self.rect.centery < SCREEN_HEIGHT+self.rect.height//2: self.rect.centery += self.speed else: self.live = False elif self.direction == "L": # 在边界内,子弹正常移动,超出边界不移动 if self.rect.centerx >-self.rect.width: self.rect.centerx -= self.speed else: self.live = False elif self.direction == "R": # 在边界内,子弹正常移动,超出边界不移动 if self.rect.centerx < SCREEN_WIDTH+self.rect.width//2: self.rect.centerx += self.speed else: self.live = False
-
一次最多发射三颗子弹
- 在事件检测中修改
elif event.key==pygame.K_SPACE: # print("发射子弹") #如果我方坦克活着 if MainGame.P1 and MainGame.P1.live: if len(MainGame.bullet_hero_list)<3: #创建一个子弹对象,添加到我方子弹列表中 bullet = MainGame.P1.shot() MainGame.bullet_hero_list.append(bullet) else: print("一次最多可以发射三颗子弹")
敌方坦克发射子弹
-
敌方坦克类中定义发射子弹的计算器count,主逻辑中增加存储敌方的子弹列表
-
敌方坦克类中重写shot方法,count==200时,再发射子弹
#敌方坦克类中定义发射子弹的计算器 self.count = 1 #主逻辑中增加存储敌方的子弹列表 bullet_enemy_list = [] #敌方坦克类中重写shot方法,count==200时,再发射子弹 def shot(self): self.count+=1 if self.count==200: b = super(EnemyTank, self).shot() MainGame.bullet_hero_list.append(b) self.count=1
-
游戏类中,新增加载敌方子弹的方法
def load_bullet_enemy(self): """加载敌方子弹""" #取出每一颗子弹 for b in MainGame.bullet_enemy_list: if b.live: b.display_bullet() else: MainGame.bullet_enemy_list.remove(b) #游戏循环中,调用self.load_bullet_enemy() #在在家敌方坦克中调用shot方法 #敌方坦克发射子弹-->创建子弹对象,添加到主逻辑子弹列表-->游戏循环中遍历子弹列表,贴子弹图片,和子弹移动
子弹消灭坦克
-
碰撞检测,冲突检测
#我方子弹是否打中敌方坦克 #将创建的子弹和每一辆敌方坦克进行碰撞检测 def hit_enemy_tank(self): #将创建的子弹和每一辆敌方坦克进行碰撞检测 for e_tank in MainGame.enemy_tank_list: # 进行一对一的冲突检测 if pygame.sprite.collide_rect(self,e_tank): #如果相撞,则修改子弹和坦克的生存属性 self.live = False e_tank.live = False #在加载我方坦克子弹中,完成碰撞方法的调用 def load_bullet_hero(self): """加载我方坦克发射的子弹""" #取出每一颗子弹 for b in MainGame.bullet_hero_list: #判断子弹是否活着 if b.live: #贴子弹图片 b.display_bullet() #调用子弹移动的方法 b.move() b.hit_enemy_tank() #调用子弹碰撞敌方坦克的方法 #敌方子弹是否打中我方坦克 def hit_hero_tank(self): if pygame.sprite.collide_rect(self,MainGame.P1): # 如果相撞,则修改子弹和我方坦克的生存属性 self.live = False MainGame.P1.live = False #在加载敌方坦克子弹中,完成碰撞方法的调用 def load_bullet_enemy(self): """加载敌方子弹""" #取出每一颗子弹 for b in MainGame.bullet_enemy_list: if b.live: b.display_bullet() b.move() if MainGame.P1 and MainGame.P1.live: b.hit_hero_tank() #调用子弹碰撞我方坦克方法 else: MainGame.bullet_enemy_list.remove(b)
爆炸效果
-
属性:图片,位置,生存状态
-
方法:贴图
#在主逻辑类中,新增爆炸效果列表 bomb_list = [] class Bomb: def __init__(self,tank): self.images = [ pygame.image.load("tank_img/blast0.gif"), pygame.image.load("tank_img/blast1.gif"), pygame.image.load("tank_img/blast2.gif"), pygame.image.load("tank_img/blast3.gif"), pygame.image.load("tank_img/blast4.gif") # pygame.image.load("tank_img/blast5.gif"), # pygame.image.load("tank_img/blast6.gif"), # pygame.image.load("tank_img/blast7.gif") ] #根据索引取爆炸图片 self.image_index = 0 self.image = self.images[self.image_index] self.rect = tank.rect self.live = True def display_bomb(self): """贴爆炸效果图""" if self.image_index<len(self.images): self.image = self.images[self.image_index] MainGame.window.blit(self.image,self.rect) self.image_index+=1 else: self.live = False self.image_index = 0 #子弹类中,打中敌方坦克创建爆炸对象,加入爆炸效果列表 def hit_enemy_tank(self): """。。。。""" #创建爆炸效果对象,添加到爆炸效果列表中 bomb = Bomb(e_tank) MainGame.bomb_list.append(bomb) #子弹类中,打中我方坦克创建爆炸对象,加入爆炸效果列表 def hit_hero_tank(self): """....""" # 创建爆炸效果对象,添加到爆炸效果列表中 bomb = Bomb(MainGame.P1) MainGame.bomb_list.append(bomb)
-
主逻辑类中,新增加载爆炸效果的方法
def load_bomb(self): """加载爆炸效果的方法""" for bomb in MainGame.bomb_list: if bomb.live: bomb.display_bomb() else: MainGame.bomb_list.remove(bomb) #在游戏循环中调用 self.load_bomb()
-
实现障碍物类
-
属性:图片,位置,血量
-
方法:贴图
class Wall: def __init__(self,x,y): self.image = pygame.image.load("tank_img/walls.gif") self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.hp = 5 def display_wall(self): MainGame.window.blit(self.image,self.rect)
-
在主逻辑类中新增障碍物列表
#记录墙壁列表 wall_list = []
-
主逻辑类中,新增创建障碍物create_wall和加载障碍物load_wall方法
def create_wall(self): """创建墙壁方法 墙壁60*60 窗口900""" for i in range(9): wall = Wall(i*100,300) MainGame.wall_list.append(wall) def load_wall(self): """加载墙壁方法""" for wall in MainGame.wall_list: if wall.hp>0: wall.display_wall() else: MainGame.wall_list.remove(wall) #在游戏开始调用:self.create_wall() #在游戏循环中调用:self.load_wall()
-
子弹和墙壁碰撞(子弹类中新增与墙壁的碰撞方法)
#子弹类中新增与墙壁的碰撞方法 def hit_wall(self): for wall in MainGame.wall_list: if pygame.sprite.collide_rect(self, wall): self.live = False #墙壁生命值减少 wall.hp -= 1
-
在加载我方子弹方法中,调用子弹撞墙的方法
def load_bullet_hero(self): """加载我方坦克发射的子弹""" b.hit_wall() #调用撞墙方法
-
在加载敌方子弹方法中,调用子弹撞墙的方法
def load_bullet_enemy(self): """加载敌方子弹""" b.hit_wall() #调用撞墙方法 #此时我方和敌方坦克可以打障碍物 #此时坦克可以穿墙
-
坦克无法穿墙
-
在基本坦克类中,新增记录旧坐标的属性oldx,oldy,在移动之前先记录旧坐标
def move(self): #记录旧坐标 self.oldx = self.rect.x self.oldy = self.rect.y
-
在基本坦克类中,新增坐标还原的方法
-