Python游戏编程(十五)飞机大战

我就是我 2023-07-08 06:59 167阅读 0赞

我们将用面向对象的思维写一个飞机大战的小游戏 。

分为几个类:

  • 全局设置类:包括一些基本的参数以及音乐、图片文件的路劲。
  • 子弹基础类
  • 玩家子弹
  • 敌人子弹
  • 飞机基类
  • 玩家飞机基类
  • 敌人飞机类

目录

(一)class Settings():

(二)class Bullet:

(三)玩家、敌人子弹类

(四)class Plane:

(五)玩家飞机

(六)敌人飞机

(七)设置方法

(八)主函数


watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTc1NTk2Ng_size_16_color_FFFFFF_t_70

(一)class Settings():

  1. import sys, time, random
  2. import pygame
  3. from pygame.locals import *
  4. import threading
  5. SCREENWIDTH = 512
  6. SCREENHEIGHT = 768
  7. Gray = (128, 128, 128)
  8. # 全局的设置类
  9. class Settings():
  10. def __init__(self):
  11. # 定义游戏窗口大小,为背景图片的一半
  12. self.screen_size = (self.screen_width, self.screen_height) = (SCREENWIDTH, SCREENHEIGHT)
  13. self.bg_color = Gray
  14. self.bg_image = './images/bg2.jpg'
  15. # 不能播放 mp3,所以转成 wav
  16. self.bg_music = './music/01.wav'
  17. self.gameover_image = './images/gameover.jpg'
  18. self.title = '飞机大战'
  19. # 英雄机参数
  20. self.move_step = 5 # 键盘控制的速度
  21. self.hero_style = './images/me.png'
  22. # 敌机参数
  23. self.enemy_speed = 4 # 敌机飞行速度
  24. self.enemy_style_list = ['./images/e0.png', './images/e1.png', './images/e2.png']
  25. # 子弹参数
  26. self.bullet_style = './images/pd.png'
  27. self.bullet_hero_v = 10 # 英雄机子弹速度
  28. self.bullet_enemy_v = 8 # 敌机子弹速度
  29. # 实例化设置对象
  30. settings = Settings()

设置一个全局的类,其中包括一些基本的参数。

最后对于一个类,都需要经过初始化和实例化后才可以发挥作用,类中使用__init__自动初始化,然后创建了一个settings对象,进行类的实例化。

(二)class Bullet:

  1. # 子弹基类
  2. class Bullet:
  3. def __init__(self, screen, x, y):
  4. self.x = x
  5. self.y = y
  6. self.screen = screen
  7. self.image = pygame.image.load(settings.bullet_style)
  8. def __del__(self):
  9. pass
  10. def bulletRect(self):
  11. bulletrect = self.image.get_rect()
  12. bulletrect.topleft = (self.x, self.y)
  13. return bulletrect
  14. def display(self):
  15. self.screen.blit(self.image, (self.x, self.y))
  16. def move(self):
  17. return True

设置子弹的基础,玩家的子弹和敌机的子弹都要从这个子弹基类继承。

当一个Python程序结束之后,Python解释器会自动调用__del__()方法来释放内存;当然我们也可以根据需要自己调用__del__()方法来删除对象,释放占用的内存。因为我们想要提高计算机的效率,暂时用不到的事情可以先不让计算机做。

pygame.image.load()方法用来加载图片。

用blit()方法在一个Surface对象上面绘制一个Surface对象。

最后建立move()方法,调用这个方法返回布尔值,通过布尔值来确定书否移除这个子弹。

(三)玩家、敌人子弹类

玩家子弹类和英雄子弹类都继承子弹类,实现的功能差不多。

  1. # 英雄子弹类
  2. class HeroBullet(Bullet):
  3. def __init__(self, screen, x, y):
  4. super().__init__(screen, x, y)
  5. def move(self):
  6. self.y -= settings.bullet_hero_v
  7. # 判断子弹是否出界
  8. if self.y <= -20:
  9. return True
  10. # 敌机子弹类
  11. class EnemyBullet(Bullet):
  12. def __init__(self, screen, x, y):
  13. super().__init__(screen, x, y)
  14. def move(self):
  15. self.y += settings.bullet_enemy_v
  16. # 判断子弹是否出界
  17. if self.y >= settings.screen_height:
  18. return True

Python在类中可任意通过__init__方法实现对实例中代码实现自动初始化,但是Python本身不会自动执行初始化操作,但是我们可能要用带继承类的其他方法,所以要调用super().__init__()方法来来实现继承类的初始化。

(四)class Plane:

  1. # 飞机基类
  2. class Plane:
  3. def __init__(self, screen, style, geo):
  4. self.screen = screen
  5. self.image = pygame.image.load(style)
  6. self.bullet_list = []
  7. self.x = geo[0]
  8. self.y = geo[1]
  9. self.is_dead = False
  10. self.finished = False
  11. self.bomb_seq = ['4','4','3','3','2','2','1','1']
  12. def __del__(self):
  13. pass
  14. def planeRect(self):
  15. planerect = self.image.get_rect()
  16. planerect.topleft = (self.x, self.y)
  17. return planerect
  18. def display(self):
  19. for b in self.bullet_list:
  20. b.display()
  21. # 回收子弹
  22. if b.move():
  23. self.bullet_list.remove(b)
  24. # 爆炸效果
  25. if self.is_dead:
  26. death_x = self.x
  27. death_y = self.y
  28. death_w = self.image.get_width()
  29. death_h = self.image.get_height()
  30. try:
  31. bomb_image = './images/bomb'+self.bomb_seq.pop()+'.png'
  32. self.image = pygame.image.load(bomb_image)
  33. except:
  34. self.image = pygame.image.load('./images/bomb4.png')
  35. self.finished = True
  36. finally:
  37. x = death_x + (death_w - self.image.get_width())/2
  38. y = death_y + (death_h - self.image.get_height())/2
  39. self.screen.blit(self.image, (x, y))
  40. else:
  41. # 重新绘制飞机
  42. self.screen.blit(self.image, (self.x, self.y))
  43. def fire(self):
  44. self.bullet_list.append(Bullet(self.screen, self.x, self.y))
  45. print(len(self.bullet_list))
  46. def over(self):
  47. #print("Oops: plane over ...")
  48. #del self
  49. return self.finished

screen为屏幕的Surface对象,表示一个矩形图像。

pop()方法:用来删除列表中最后一个元素。

  1. >>> bomb_seq = ['4','4','3','3','2','2','1','1']
  2. >>> bomb_seq.pop()
  3. '1'
  4. >>> bomb_seq
  5. ['4', '4', '3', '3', '2', '2', '1']

(五)玩家飞机

  1. # 英雄飞机
  2. class HeroPlane(Plane):
  3. def __init__(self, screen):
  4. # 英雄机初始位置
  5. geo = (200, 600)
  6. super().__init__(screen, settings.hero_style, geo)
  7. self.step = settings.move_step
  8. # 英雄机移动范围
  9. self.limit_left = -(self.image.get_width()/2)+10
  10. self.limit_right = settings.screen_width-self.image.get_width()/2-10
  11. self.limit_top = 5
  12. self.limit_bottom = settings.screen_height-self.image.get_height()
  13. def fire(self):
  14. self.bullet_list.append(HeroBullet(self.screen, self.x+53, self.y))
  15. def move_left(self):
  16. if self.x <= self.limit_left:
  17. pass
  18. else:
  19. self.x -= self.step
  20. def move_right(self):
  21. if self.x >= self.limit_right:
  22. pass
  23. else:
  24. self.x += self.step
  25. def move_up(self):
  26. if self.y <= self.limit_top:
  27. pass
  28. else:
  29. self.y -= self.step
  30. def move_down(self):
  31. if self.y >= self.limit_bottom:
  32. pass
  33. else:
  34. self.y += self.step

其中玩家移动的范围下左右都是由screen_size决定的,但是上方我们直接设置一个值就好了,这里我们设置为5:self.limit_top = 5,意思是玩家飞机最高能飞到5个像素的位置。

然后是英雄飞机左右上下的移动,先判断是否超出边界,如果在边界范围内的话,就进行“移动”,也就是更改相应方向的坐标。

(六)敌人飞机

  1. # 敌机
  2. class EnemyPlane(Plane):
  3. def __init__(self, screen):
  4. geo = (random.choice(range(408)), -75)
  5. enemy_style = settings.enemy_style_list[random.choice(range(3))]
  6. super().__init__(screen, enemy_style, geo)
  7. self.pipe_x = self.image.get_width()/2-1 # 1 for the width of bullet
  8. self.pipe_y = self.image.get_height()
  9. #self.planeRect.topleft = geo
  10. #是否要删除敌机,返回布尔值
  11. def move(self, hero):
  12. self.y += settings.enemy_speed
  13. if self.y > settings.screen_height:
  14. return True
  15. # 飞机的碰撞检测
  16. #使用位置检测碰撞:
  17. #if self.x > hero.x-50 and self.x < hero.x+50 and self.y > hero.y-40 and self.y < hero.y+40:
  18. #使用Rect对象的colliderect()方法:
  19. if self.planeRect().colliderect(hero.planeRect()):
  20. self.is_dead = True
  21. hero.is_dead = True
  22. # 看看我中弹了没
  23. for bo in hero.bullet_list:
  24. if self.planeRect().colliderect(bo.bulletRect()):
  25. #if bo.x > self.x+12 and bo.x < self.x+92 and bo.y < self.y+60 and bo.y > self.y:
  26. hero.bullet_list.remove(bo)
  27. # 爆炸
  28. self.is_dead = True
  29. # 看看英雄机中弹了没
  30. for bo in self.bullet_list:
  31. if hero.planeRect().colliderect(bo.bulletRect()):
  32. #if bo.x > hero.x+25 and bo.x < hero.x+75 and bo.y > hero.y+5 and bo.y < hero.y+50:
  33. self.bullet_list.remove(bo)
  34. hero.is_dead = True
  35. def fire(self):
  36. self.bullet_list.append(EnemyBullet(self.screen, self.x+self.pipe_x, self.y+self.pipe_y))

(七)设置方法

这里写一个事件检测和绘制文字的函数,把功能放到这个函数里面,选哟这两个功能的时候就调用相应的函数就可以了。

  1. def check_event(hero, usedbullet):
  2. #Key event capture and key_control
  3. which = pygame.key.get_pressed()
  4. if which[K_SPACE]:
  5. hero.fire()
  6. usedbullet += 1
  7. print("Space...")
  8. if which[K_LEFT] or which[K_a]:
  9. hero.move_left()
  10. print("Left...")
  11. elif which[K_RIGHT] or which[K_d]:
  12. hero.move_right()
  13. print("Right...")
  14. elif which[K_UP] or which[K_w]:
  15. hero.move_up()
  16. print("Up...")
  17. elif which[K_DOWN] or which[K_s]:
  18. hero.move_down()
  19. print("Down...")
  20. for event in pygame.event.get():
  21. if event.type == MOUSEMOTION:
  22. hero.x = event.pos[0]
  23. hero.y = event.pos[1]
  24. if event.type == QUIT:
  25. print("exit...")
  26. sys.exit()
  27. return usedbullet
  28. #draw score
  29. def drawText(text, font, screen, x, y):
  30. TEXTCOLOR = (0, 0, 0)
  31. textobj = font.render(text, 1, TEXTCOLOR)
  32. textrect = textobj.get_rect()
  33. textrect.topleft = (x, y)
  34. screen.blit(textobj, textrect)

事件检测这里使用的是pygame.key.get_pressed()这个方法,在前面的游戏中有使用pygame.event.get()这个方法,二者之间有一些不同,在这个游戏里面,用这两种事件调用的方法游戏的操作会有显著的区别。

大家可以在上面代码中修改一下,在游戏操作里面体验一下有神么不同。

看看官网中的介绍:

get()方法中:

This will get all the messages and remove them from the queue. If a type or sequence of types is given only those messages will be removed from the queue.

get_pressed()方法中:

Returns a sequence of boolean values representing the state of every key on the keyboard. Use the key constant values to index the array. A True value means the that button is pressed.

  1. """
  2. event = pygame.event.get()
  3. if event.type == KEYDOWN:
  4. if event.key == K_SPACE:
  5. hero.fire()
  6. print("Space...")
  7. if event.key == K_LEFT or K_a:
  8. hero.move_left()
  9. if event.key == K_RIGHT or K_d:
  10. hero.move_right()
  11. if event.key == K_UP or K_w:
  12. hero.move_up()
  13. if event.key == K_DOWN or K_s:
  14. hero.move_down()
  15. """

(八)主函数

  1. def main():
  2. # 初始化 Pygame
  3. pygame.init()
  4. # 创建一个游戏窗口
  5. screen = pygame.display.set_mode(settings.screen_size, 0, 0)
  6. # 设置窗口标题
  7. pygame.display.set_caption(settings.title)
  8. # 在窗口中加载游戏背景
  9. background = pygame.image.load(settings.bg_image)
  10. #设置积分器
  11. font = pygame.font.SysFont(None, 48)
  12. # 背景音乐
  13. pygame.mixer.init()
  14. pygame.mixer.music.load(settings.bg_music)
  15. #pygame.mixer.music.play()
  16. # 创建英雄机
  17. hero = HeroPlane(screen)
  18. play = True
  19. enemylist = []
  20. bg_y = -(settings.screen_height)
  21. usedbullet = 0
  22. score = 0
  23. while True:
  24. #检查退出
  25. for event in pygame.event.get():
  26. if event.type == QUIT:
  27. pygame.quit()
  28. sys.exit()
  29. if not pygame.mixer.music.get_busy():
  30. print("play music")
  31. pygame.mixer.music.play()
  32. # 从坐标(0, -768)开始绘图,所以看到的是背景图片的下半部
  33. screen.blit(background, (0, bg_y))
  34. bg_y += 2
  35. if bg_y >= 0:
  36. bg_y = -(settings.screen_height)
  37. if play:
  38. drawText('Score:{}'.format(score),font,screen,256,256)
  39. drawText('Used Bullet:{}'.format(usedbullet),font,screen,256,300)
  40. # 绘制英雄机
  41. hero.display()
  42. # 随机绘制敌机
  43. if random.choice(range(50)) == 10:
  44. enemylist.append(EnemyPlane(screen))
  45. # 遍历敌机并绘制移动
  46. for em in enemylist:
  47. em.display()
  48. # 敌机随机发炮弹
  49. if random.choice(range(50)) == 10:
  50. em.fire()
  51. # 判断敌机是否出界
  52. if em.move(hero):
  53. enemylist.remove(em)
  54. # 判断敌机是否炸毁
  55. if em.over():
  56. score += 1
  57. enemylist.remove(em)
  58. # 英雄机炸毁,游戏结束
  59. if hero.over():
  60. play = False
  61. #pygame.display.flip()
  62. pygame.display.update()
  63. else:
  64. gameover = pygame.image.load(settings.gameover_image)
  65. screen.blit(gameover, ((settings.screen_width-gameover.get_width())/2,
  66. (settings.screen_height-gameover.get_height())/2))
  67. pygame.display.update()
  68. #print("Game Over")
  69. #continue
  70. # 检查按键事件
  71. usedbullet = check_event(hero,usedbullet)
  72. time.sleep(0.04)
  73. # 判断是否为主运行程序,是则调用 main()
  74. if __name__ == '__main__':
  75. main()

对于全局变量报错:UnboundLocalError: local variable ‘usedbullet’ referenced before assignment的解释:

在程序中设置的 sum 属于全局变量,而在函数中没有 sum 的定义,根据python访问局部变量和全局变量的规则:当搜索一个变量的时候,python先从局部作用域开始搜索,如果在局部作用域没有找到那个变量,那样python就在全局变量中找这个变量,如果找不到抛出异常(NAMEERROR或者Unbound-LocalError,这取决于python版本。)

如果内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改.那么python会认为它是一个局部变量,又因为函数中没有sum的定义和赋值,所以报错。

其中if __name__ == ‘__main__‘:是只作为脚本执行时,执行main()函数,如果作为包导入其他脚本中时,则不执行。

这个游戏修改了很多天,对于初学者,需要理解面向对象思维的编程特点。当然如果按照之前的面向过程的思路来写的话,代码可能会短一些,但是修改起来就会有很大的问题,这也是面向对象和面型过程二者之间的区别,我会在下一个专栏中介绍这些,期待自己学习的进步吧!

这个飞机大战的游戏还是未完成的状态,我们可以在以上代码中进行一些修改,为这个游戏增加一些功能,让这个游戏变得更好玩,当然,在我们增加功能的过程中,也是对Python编程和pygame模块的更深入的理解。

参考:

  1. http://c.biancheng.net/view/2371.html
  2. https://www.runoob.com/w3cnote/python-unboundlocalerror.html
  3. https://gitee.com/luhuadong/Python_Learning/repository/archive/master.zip

发表评论

表情:
评论列表 (有 0 条评论,167人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java飞机

    一、项目分析 1、设计步骤 在一个Java Swing的小游戏中,首先需要考虑的是组件的绘制,其次是如何让需要运动的组件运动起来,最后是如何检测物体的碰撞等。 (1)