前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【pygame系列 第六课 弹球游戏重构 面向对象-下 】

【pygame系列 第六课 弹球游戏重构 面向对象-下 】

作者头像
叶子陪你玩
发布2020-03-12 15:05:49
1K0
发布2020-03-12 15:05:49
举报
文章被收录于专栏:叶子陪你玩编程

本课任务在上一课基础上,实现下面几个任务。

第五步:窗体底部绘制一个挡板

第六步:用鼠标控制挡板左右移动

第七步:小球碰到挡板反弹,碰到底部结束

第八步:绘制游戏得分

第八步:绘制游戏结束界面文字

5.绘制挡板

代码:

代码语言:javascript
复制
import pygame
import sys
import random

# 背景白色
bg = (255,255,255)
# 屏幕宽度和高度
size =width,height=400,300
# 球的颜色 红色
ball_color = (255,0,0)
# 球的大小 半径
ball_size = 20
# 球的初始位置 设置在窗口中心位置
pos_x,pos_y = width//2-ball_size,height//2-ball_size
# 设置球的 初始速度
speed_x=random.randint(1,5)
speed_y=random.randint(1,5)

# 设置挡板的 颜色 蓝色
board_color = (0,0,255)
# 设置挡板的 宽和高
board_width,board_height=50,4
# 设置挡板的初始位置
board_x,board_y = (width-board_width)//2,height-board_height
class Ball:
  def __init__(self,ball_color,pos,ball_size,ball_speed):
    self.color=ball_color
    self.pos=pos
    self.size=ball_size
    self.speed=ball_speed

class Board:
  def __init__(self,board_color,board_pos):
    self.color=board_color
    self.pos=board_pos

class Game:
  def __init__(self):
    self.ball=Ball(ball_color,[pos_x,pos_y],ball_size,[speed_x,speed_y])
    self.board = Board(board_color,[board_x,board_y,board_width,board_height])

  def process_event(self):
      for event in pygame.event.get():
          if event.type == pygame.QUIT:
              sys.exit()

  def run_logic(self):
    if self.ball.pos[0] <self.ball.size or self.ball.pos[0] > width-self.ball.size:
      self.ball.speed[0] = -self.ball.speed[0]
    if self.ball.pos[1] <self.ball.size or self.ball.pos[1] > height-self.ball.size:
      self.ball.speed[1] = -self.ball.speed[1]
    # 更改位置
    self.ball.pos[0] += self.ball.speed[0]
    self.ball.pos[1] += self.ball.speed[1]

  def display_frame(self,screen):
    screen.fill(bg)    pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)
    pygame.draw.rect(screen,self.board.color,self.board.pos)
    pygame.display.flip()

def main():
  pygame.init()
  screen = pygame.display.set_mode(size)
  pygame.display.set_caption("弹球游戏")
  game=Game()

  while True:
    game.process_event()
    game.run_logic()
    game.display_frame(screen)
    pygame.time.wait(100)
main()

效果图:

解释:

绘制挡板和绘制小球是一个思路,挡板具有颜色,大小,绘制的位置。在前面设置了挡板的属性。

代码语言:javascript
复制
# 设置挡板的 颜色 蓝色
board_color = (0,0,255)
# 设置挡板的 宽和高
board_width,board_height=50,4
# 设置挡板的初始位置
board_x,board_y = (width-board_width)//2,height-board_height
代码语言:javascript
复制
挡板也是属于一个类,创建一个Board类,给它设置一个颜色board_color和一个位置board_pos属性,位置属性里面其实就包含了挡板的大小和绘制位置。

6.用鼠标控制挡板左右移动

代码:

代码语言:javascript
复制
import pygame
import sys
import random

# 背景白色
bg = (255,255,255)
# 屏幕宽度和高度
size =width,height=400,300
# 球的颜色 红色
ball_color = (255,0,0)
# 球的大小 半径
ball_size = 20
# 球的初始位置 设置在窗口中心位置
pos_x,pos_y = width//2-ball_size,height//2-ball_size
# 设置球的 初始速度
speed_x=random.randint(1,5)
speed_y=random.randint(1,5)

# 设置挡板的 颜色 蓝色
board_color = (0,0,255)
# 设置挡板的 宽和高
board_width,board_height=50,4
# 设置挡板的初始位置
board_x,board_y = (width-board_width)//2,height-board_height
class Ball:
  def __init__(self,ball_color,pos,ball_size,ball_speed):
    self.color=ball_color
    self.pos=pos
    self.size=ball_size
    self.speed=ball_speed

  def move(self):
    if self.pos[0] < self.size or self.pos[0] > width-self.size:
      self.speed[0] = -self.speed[0]
    if self.pos[1] < self.size or self.pos[1] > height-self.size:
      self.speed[1] = -self.speed[1]
    # 更改位置
    self.pos[0] += self.speed[0]
    self.pos[1] += self.speed[1]

class Board:
  def __init__(self,board_color,board_pos):
    self.color=board_color
    self.pos=board_pos
  def move(self):
    mouse_pos=pygame.mouse.get_pos()
    self.pos[0]=mouse_pos[0]

class Game:
  def __init__(self):
    self.ball=Ball(ball_color,[pos_x,pos_y],ball_size,[speed_x,speed_y])
    self.board = Board(board_color,[board_x,board_y,board_width,board_height])

  def process_event(self):
      for event in pygame.event.get():
          if event.type == pygame.QUIT:
              sys.exit()

  def run_logic(self):
    # 小球移动
    self.ball.move()
    # 挡板跟随鼠标移动
    self.board.move()

  def display_frame(self,screen):
    screen.fill(bg)
    pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)
    pygame.draw.rect(screen,self.board.color,self.board.pos)
    pygame.display.flip()

def main():
  pygame.init()
  screen = pygame.display.set_mode(size)
  pygame.display.set_caption("弹球游戏")
  game=Game()

  while True:
    game.process_event()
    game.run_logic()
    game.display_frame(screen)
    pygame.time.wait(100)
main()

效果:

解释:

挡板可以跟随鼠标移动,给挡板类添加一个move()的方法,在这里面获取鼠标的位置,由于挡板竖直方向不变,所以只需要更改x坐标位置就可以了。

代码语言:javascript
复制
  def move(self):    
  mouse_pos=pygame.mouse.get_pos()    
  self.pos[0]=mouse_pos[0]

同理把之前小球移动的代码从Game中的run_logic里移动Ball中的新建的move方法,移过来后需要将原来的所有.ball给删除掉了(在自己的类中调用,就不用ball对象了)

代码语言:javascript
复制
  def move(self):
    if self.pos[0] < self.size or self.pos[0] > width-self.size:
      self.speed[0] = -self.speed[0]
    if self.pos[1] < self.size or self.pos[1] > height-self.size:
      self.speed[1] = -self.speed[1]
    # 更改位置
    self.pos[0] += self.speed[0]
    self.pos[1] += self.speed[1]

最后在Game类中的run_logic只需要直接调用小球和挡板的move方法就可以了。

代码语言:javascript
复制
  def run_logic(self):
    # 小球移动
    self.ball.move()
    # 挡板跟随鼠标移动
    self.board.move()

7.小球碰到挡板反弹,碰到底部结束

代码:

代码语言:javascript
复制
import pygame
import sys
import random

# 背景白色
bg = (255,255,255)  
# 屏幕宽度和高度
size =width,height=400,300
# 球的颜色 红色
ball_color = (255,0,0)
# 球的大小 半径
ball_size = 20
# 球的初始位置 设置在窗口中心位置
pos_x,pos_y = width//2-ball_size,height//2-ball_size
# 设置球的 初始速度
speed_x=random.randint(5,15)
speed_y=random.randint(5,15)

# 设置挡板的 颜色 蓝色
board_color = (0,0,255)
# 设置挡板的 宽和高
board_width,board_height=50,4
# 设置挡板的初始位置
board_x,board_y = (width-board_width)//2,height-board_height

class Ball:
  def __init__(self,ball_color,pos,ball_size,ball_speed):
    self.color=ball_color
    self.pos=pos
    self.size=ball_size
    self.speed=ball_speed

  def move(self):
    if self.pos[0] < self.size or self.pos[0] > width-self.size:
      self.speed[0] = -self.speed[0]
    if self.pos[1] < self.size:
      self.speed[1] = -self.speed[1]
    # 更改位置
    self.pos[0] += self.speed[0]
    self.pos[1] += self.speed[1]  

class Board:
  def __init__(self,board_color,board_pos):
    self.color=board_color
    self.pos=board_pos

  def move(self):
    mouse_pos=pygame.mouse.get_pos()
    self.pos[0]=mouse_pos[0]

class Game:
  def __init__(self):
    self.ball=Ball(ball_color,[pos_x,pos_y],ball_size,[speed_x,speed_y])
    self.board = Board(board_color,[board_x,board_y,board_width,board_height])
    #设置游戏状态标志标量
    self.gameover=False
  def process_event(self):
      for event in pygame.event.get():
          if event.type == pygame.QUIT:
              sys.exit()
              return True
      return False      

  def run_logic(self):
    # 小球移动
    self.ball.move()
    # 挡板跟随鼠标移动
    self.board.move()
    # 如果球已经进入到board厚度以下就要开始检测是否碰撞
    if self.ball.pos[1]>=self.board.pos[1]-self.ball.size:
      # 如果球与board接触,就反弹
      if self.board.pos[0]<=self.ball.pos[0]<=self.board.pos[0]+self.board.pos[2]:
        self.ball.speed[1]=-self.ball.speed[1]
      else:
        # 没有碰到挡板就将游戏状态设置为结束
        self.gameover=True

  def display_frame(self,screen):
    screen.fill(bg)    pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)
    pygame.draw.rect(screen,self.board.color,self.board.pos)
    pygame.display.flip()

def main():
  pygame.init()
  screen = pygame.display.set_mode(size)
  pygame.display.set_caption("弹球游戏")
  game=Game()
  # 当游戏没有结束就一直循环
  while not game.gameover:
    game.process_event()
    game.run_logic()
    game.display_frame(screen)
    pygame.time.wait(100)
  pygame.quit()

main()

解释:

这里是要检测小球运动的过程中是否碰到了挡板,碰到了就反弹将速度设置为相反;碰到底部就游戏结束。在Game类中的init中加上一个gameover的游戏状态标志变量,在run_logic加上逻辑判断的代码,当小球的y坐标self.ball.pos[1]已经小于等于此时的挡板的y坐标 self.board.pos[1]-self.ball.size时,游戏结束,将self.gameover设置为True,这里之所以要减去self.ball.size是因为小球的坐标是在圆心,而碰撞检测是在底部,所以有一个小球半径差。

代码语言:javascript
复制
class Game:  
ef __init__(self):          
# 原来的代码不变         
 #设置游戏状态标志标量    
 self.gameover=False  
 def run_logic(self):    
 # 小球移动    
 self.ball.move()    
 # 挡板跟随鼠标移动    
 self.board.move()   
 # 如果球已经进入到board厚度以下就要开始检测是否碰撞    
 if self.ball.pos[1]>=self.board.pos[1]-self.ball.size:      
 # 如果球与board接触,就反弹      
 if self.board.pos[0]<=self.ball.pos[0]<=self.board.pos[0]+self.board.pos[2]:        
 self.ball.speed[1]=-self.ball.speed[1]      
 else:        
 # 没有碰到挡板就将游戏状态设置为结束        
 self.gameover=True 

在main函数中,将原来的while True循环改成while not gameover;这样当gameover变成True时,就会退出游戏循环,游戏也就结束了,结束后加上pygame.quit()。

代码语言:javascript
复制
def main():  
pygame.init()  
screen = pygame.display.set_mode(size)  
pygame.display.set_caption("弹球游戏")  
game=Game()  # 当游戏没有结束就一直循环  
while not game.gameover:    
game.process_event()    
game.run_logic()    
game.display_frame(screen)    
pygame.time.wait(100)  
pygame.quit()

8.绘制游戏得分

代码语言:javascript
复制
import pygameimport sysimport random
# 背景白色bg = (255,255,255)  # 屏幕宽度和高度size =width,height=400,300# 球的颜色 红色ball_color = (255,0,0)# 球的大小 半径ball_size = 20# 球的初始位置 设置在窗口中心位置pos_x,pos_y = width//2-ball_size,height//2-ball_size# 设置球的 初始速度speed_x=random.randint(5,15)speed_y=random.randint(5,15)
# 设置挡板的 颜色 蓝色board_color = (0,0,255)# 设置挡板的 宽和高board_width,board_height=50,4# 设置挡板的初始位置board_x,board_y = (width-board_width)//2,height-board_height
class Ball:  def __init__(self,ball_color,pos,ball_size,ball_speed):    self.color=ball_color    self.pos=pos    self.size=ball_size    self.speed=ball_speed
  def move(self):    if self.pos[0] < self.size or self.pos[0] > width-self.size:      self.speed[0] = -self.speed[0]    if self.pos[1] < self.size:      self.speed[1] = -self.speed[1]    # 更改位置    self.pos[0] += self.speed[0]    self.pos[1] += self.speed[1]  
class Board:  def __init__(self,board_color,board_pos):    self.color=board_color    self.pos=board_pos
  def move(self):    mouse_pos=pygame.mouse.get_pos()    self.pos[0]=mouse_pos[0]
class Game:  def __init__(self):    self.ball=Ball(ball_color,[pos_x,pos_y],ball_size,[speed_x,speed_y])    self.board = Board(board_color,[board_x,board_y,board_width,board_height])    #设置游戏状态标志标量    self.gameover=False    self.score = 0  def process_event(self):      for event in pygame.event.get():          if event.type == pygame.QUIT:              sys.exit()              return True      return False      
  def run_logic(self):    # 小球移动    self.ball.move()    # 挡板跟随鼠标移动    self.board.move()    # 如果球已经进入到board厚度以下就要开始检测是否碰撞    if self.ball.pos[1]>=self.board.pos[1]-self.ball.size:      # 如果球与board接触,就反弹      if self.board.pos[0]<=self.ball.pos[0]<=self.board.pos[0]+self.board.pos[2]:        self.ball.speed[1]=-self.ball.speed[1]        self.score+=1      else:        # 没有碰到挡板就将游戏状态设置为结束        self.gameover=True
  def display_frame(self,screen,font):    screen.fill(bg)    pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)    pygame.draw.rect(screen,self.board.color,self.board.pos)    # 显示计分    text = font.render("score:"+str(self.score), True, (255,0,0))    screen.blit(text,(10,10))        pygame.display.flip()
def main():  pygame.init()  screen = pygame.display.set_mode(size)  pygame.display.set_caption("弹球游戏")  game=Game()  # 字体设置  font=pygame.font.SysFont("Arial",24)  # 当游戏没有结束就一直循环  while not game.gameover:    game.process_event()    game.run_logic()    game.display_frame(screen,font)    pygame.time.wait(100)  pygame.quit()
main()

效果:

解释:

现在添加一些小功能已经非常简单了,这一切都归功于一开始面向对象的设计方法。给Game类中添加也给score的属性,在run_logic碰撞检测中添加一行self.socre+=1就可以了。

代码语言:javascript
复制
self.score+=1

在display_flame中添加两行代码,创建文本对象,然后将其绘制到屏幕的左上角,同时给方法中传递一个font的字体对象。

代码语言:javascript
复制
def display_frame(self,screen,font):    screen.fill(bg)    pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)    pygame.draw.rect(screen,self.board.color,self.board.pos)    # 显示计分    text = font.render("score:"+str(self.score), True, (255,0,0))    screen.blit(text,(10,10))        pygame.display.flip()

在main中设置字体。

代码语言:javascript
复制
# 字体设置font=pygame.font.SysFont("Arial",24)

9.游戏结束和重新开始

代码:

代码语言:javascript
复制
import pygameimport sysimport random
# 背景白色bg = (255,255,255)  # 屏幕宽度和高度size =width,height=400,300# 球的颜色 红色ball_color = (255,0,0)# 球的大小 半径ball_size = 20# 球的初始位置 设置在窗口中心位置pos_x,pos_y = width//2-ball_size,height//2-ball_size# 设置球的 初始速度speed_x=random.randint(5,15)speed_y=random.randint(5,15)
# 设置挡板的 颜色 蓝色board_color = (0,0,255)# 设置挡板的 宽和高board_width,board_height=50,4# 设置挡板的初始位置board_x,board_y = (width-board_width)//2,height-board_height
class Ball:  def __init__(self,ball_color,pos,ball_size,ball_speed):    self.color=ball_color    self.pos=pos    self.size=ball_size    self.speed=ball_speed
  def move(self):    if self.pos[0] < self.size or self.pos[0] > width-self.size:      self.speed[0] = -self.speed[0]    if self.pos[1] < self.size:      self.speed[1] = -self.speed[1]    # 更改位置    self.pos[0] += self.speed[0]    self.pos[1] += self.speed[1]  
class Board:  def __init__(self,board_color,board_pos):    self.color=board_color    self.pos=board_pos
  def move(self):    mouse_pos=pygame.mouse.get_pos()    self.pos[0]=mouse_pos[0]
class Game:  def __init__(self):    self.ball=Ball(ball_color,[pos_x,pos_y],ball_size,[speed_x,speed_y])    self.board = Board(board_color,[board_x,board_y,board_width,board_height])    #设置游戏状态标志标量    self.gameover=False    self.score = 0      def process_event(self):    for event in pygame.event.get():      if event.type == pygame.QUIT:        return True      if event.type==pygame.KEYUP:        if event.key==pygame.K_SPACE:          if self.gameover:            self.__init__()    return False             def run_logic(self):    # 小球移动    self.ball.move()    # 挡板跟随鼠标移动    self.board.move()    # 如果球已经进入到board厚度以下就要开始检测是否碰撞    if self.ball.pos[1]>=self.board.pos[1]-self.ball.size:      # 如果球与board接触,就反弹      if self.board.pos[0]<=self.ball.pos[0]<=self.board.pos[0]+self.board.pos[2]:        self.ball.speed[1]=-self.ball.speed[1]        self.score+=1      else:        # 没有碰到挡板就将游戏状态设置为结束        self.gameover=True
  def display_frame(self,screen,font,gameover_font):    screen.fill(bg)    if self.gameover:        # 设置结束文本      gameover_text = gameover_font.render("Game Over", True, (255,0,0))      gamecontinue_text =font.render("Enter space to continue",True,(0,255,0))      # 获取结束surface 大小      surface_x,surface_y=gameover_text.get_size()      continue_x,continue_y=gamecontinue_text.get_size()            # 显示结束文本      screen.blit(gameover_text,((width-surface_x)//2,(height-surface_y)//2))      screen.blit(gamecontinue_text,((width-continue_x)//2,(height-continue_y)//2+40))                    pygame.display.flip()    else:      pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)      pygame.draw.rect(screen,self.board.color,self.board.pos)      # 显示计分      text = font.render("score:"+str(self.score), True, (255,0,0))      screen.blit(text,(10,10))      pygame.display.flip()      
def main():  pygame.init()  screen = pygame.display.set_mode(size)  pygame.display.set_caption("弹球游戏")  game=Game()  # 字体设置  font=pygame.font.SysFont("Arial",24)  # 游戏结束字体设置  gameover_font=pygame.font.SysFont("Arial",48)
  # 设置关闭游戏变量  close=False  # 当游戏没有结束就一直循环  while not close:    close=game.process_event()    game.run_logic()    game.display_frame(screen,font,gameover_font)    pygame.time.wait(100)  pygame.quit()
main()

效果:

这里要实现游戏结束界面,同时又可以按下空格键重启,点击关闭按钮可以退出游戏。上一个任务是直接在main函数中设置了也给gameover变量,用来判断游戏结束,现在要实现按下可以重启,所以就不能退出游戏循环,所以在这里新设置了一个close变量,只要不关闭就不会退出游戏循环,如果关闭了,process_event就会返回True。

代码语言:javascript
复制
# 设置关闭游戏变量close=False# 当游戏没有关闭就一直循环while not close:    close=game.process_event()

那不退出循环,怎样表示游戏结束的状态呢,这里用到一个小技巧,在display_frame中将渲染内容分两部分,游戏结束时gameover=True时只渲染结束的字体和界面,没有结束时就渲染小球,挡板,游戏分数运行界面,从而实现游戏结束的一个效果。

代码语言:javascript
复制
def display_frame(self,screen,font,gameover_font):    screen.fill(bg)    if self.gameover:        # 设置结束文本      gameover_text = gameover_font.render("Game Over", True, (255,0,0))      gamecontinue_text =font.render("Enter space to continue",True,(0,255,0))      # 获取结束surface 大小      surface_x,surface_y=gameover_text.get_size()      continue_x,continue_y=gamecontinue_text.get_size()            # 显示结束文本      screen.blit(gameover_text,((width-surface_x)//2,(height-surface_y)//2))      screen.blit(gamecontinue_text,((width-continue_x)//2,(height-continue_y)//2+40))                    pygame.display.flip()    else:      pygame.draw.circle(screen,self.ball.color,self.ball.pos,self.ball.size)      pygame.draw.rect(screen,self.board.color,self.board.pos)      # 显示计分      text = font.render("score:"+str(self.score), True, (255,0,0))      screen.blit(text,(10,10))      pygame.display.flip()

最后如何实现按下空格键重新开始呢,这个比较简单,只需要在事件检测中检查是否有按下空格键,按下的化,并且游戏也是结束状态,设置重新初始化就可以大功告成了。

代码语言:javascript
复制
def process_event(self):for event in pygame.event.get():  if event.type == pygame.QUIT:    return True  if event.type==pygame.KEYUP:    if event.key==pygame.K_SPACE:      if self.gameover:        self.__init__()return False

到这弹球游戏就结束了,有不理解的可以后面留言或者加我微信探讨。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 叶子陪你玩编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档