当前位置:   article > 正文

Pygame实战!——自制PVZ(植物大战僵尸)_pygame植物大战僵尸

pygame植物大战僵尸

QQ技术分享交流互助群

 我把整个文件(这个实践的)放“喵做梦技术讨论群”里了,需者加群自取。

QQ群号:566341887   (大家一起互帮互助,共同进步,交流分享,奔赴未来!)

书接上回, 废话不多说,直接开干!

第一步、创建文件夹

第一步就迅速点,直接过。

同一级中放代码,存放图片的文件夹P和存放数据的文件夹data。

好,非常快,继续! 

第二步、搭建基本框架

也就是最最最基础,简单的,什么导入模块啊,弹出窗口啊之类了。

这里也是非常的简单,速过!

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. screen=pygame.display.set_mode(scsize)
  6. pygame.display.set_caption("乱来的PVZ")
  7. while True:
  8. for event in pygame.event.get():
  9. if event.type==pygame.QUIT:
  10. pygame.quit()
  11. pygame.display.flip()

顺手把帧率和字体渲染一类简单的给准备好。

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. #基础
  6. screen=pygame.display.set_mode(scsize)
  7. pygame.display.set_caption("乱来的PVZ")
  8. zf=pygame.time.Clock()
  9. ts=pygame.font.SysFont("simhei",20)
  10. tc=ts.render("",True,(0,0,0))
  11. while True:
  12. for event in pygame.event.get():
  13. if event.type==pygame.QUIT:
  14. pygame.quit()
  15. zf.tick(30)
  16. pygame.display.flip()

非常的简单喵,下一步。

第三步、状态机雏形

目前这几步都挺简单的,状态机的话,浅浅分一下:

这么来分,普通模式、娱乐模式、无尽模式、商店、各种各样……太多就先不一一举例了。

无论是什么模式,其实只要会一种了,其他的也就简单了,所以我们先从普通模式开始。

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. #全局变量
  6. menu=0
  7. putongmark=1
  8. #基础
  9. screen=pygame.display.set_mode(scsize)
  10. pygame.display.set_caption("乱来的PVZ")
  11. zf=pygame.time.Clock()
  12. ts=pygame.font.SysFont("simhei",20)
  13. tc=ts.render("",True,(0,0,0))
  14. while True:
  15. for event in pygame.event.get():
  16. if event.type==pygame.QUIT:
  17. pygame.quit()
  18. if menu==0:#进入界面
  19. pass
  20. elif menu==1:#菜单
  21. pass
  22. zf.tick(30)
  23. pygame.display.flip()

第四步、基础化

1.目标

这一步的目标非常明确,是为了让游戏能运行(但是不能打僵尸目前)。

2.准备

我们要根据窗口的大小,绘制相应的图片。

并且要通过读取鼠标的坐标,以及判断是否按下来确定是否改变menu的值。

这种图片如果要还原的话,大家最好还是去网上搜原图,截下来,然后扣掉边边角角拿来用。

本喵的选择是:自己手搓,这样好玩。 

那么开始!

 我自己画了一张,非常抽象,无所谓,自己开心就行。

(通过不同状态下的login按键,提示玩家是否触屏到了按键。) 

 接下来,让我们输入一些简单的代码。(不懂的看以前写的。)

3.缝合

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. #全局变量
  6. menu=0
  7. putongmark=1
  8. #基础
  9. screen=pygame.display.set_mode(scsize)
  10. pygame.display.set_caption("乱来的PVZ")
  11. zf=pygame.time.Clock()
  12. ts=pygame.font.SysFont("simhei",20)
  13. tc=ts.render("",True,(0,0,0))
  14. #login
  15. loginback=pygame.image.load("P/login/loginback.png")
  16. login=pygame.image.load("P/login/loginno.png")
  17. loginmark=0
  18. #menu
  19. menuback=pygame.image.load("P/menu/back.png")
  20. while True:
  21. for event in pygame.event.get():
  22. if event.type==pygame.QUIT:
  23. pygame.quit()
  24. mox, moy = pygame.mouse.get_pos() #实时读取鼠标x,y
  25. if menu==0:#进入界面
  26. screen.blit(loginback,(0,0))
  27. screen.blit(login,(400,400))
  28. if loginmark==0:
  29. login=pygame.image.load("P/login/loginno.png")
  30. else : login=pygame.image.load("P/login/loginyes.png")
  31. loginmark=0
  32. if mox>=400 and mox<=600:
  33. if moy>=400 and moy<=450:
  34. loginmark=1
  35. if loginmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  36. menu=1
  37. elif menu==1:#菜单
  38. screen.blit(menuback,(0,0))
  39. zf.tick(30)
  40. pygame.display.flip()

非常的简单,里面这个对loginmark的判断,就是提示玩家鼠标是否碰到该按键。

我们就先从最简单的开始。

(还要做一个进入到冒险模式的按键)

第五步、冒险模式

把这个也缝合进去之后,我们就可以真正开始了。 

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. #全局变量
  6. menu=0
  7. putongmark=1
  8. #基础
  9. screen=pygame.display.set_mode(scsize)
  10. pygame.display.set_caption("乱来的PVZ")
  11. zf=pygame.time.Clock()
  12. ts=pygame.font.SysFont("simhei",20)
  13. tc=ts.render("",True,(0,0,0))
  14. #login
  15. loginback=pygame.image.load("P/login/loginback.png")
  16. login=pygame.image.load("P/login/loginno.png")
  17. loginmark=0
  18. #menu
  19. menuback=pygame.image.load("P/menu/back.png")
  20. mx=pygame.image.load("P/menu/maoxianno.png")
  21. mxmark=0
  22. while True:
  23. for event in pygame.event.get():
  24. if event.type==pygame.QUIT:
  25. pygame.quit()
  26. mox, moy = pygame.mouse.get_pos() #实时读取鼠标x,y
  27. if menu==0:#进入界面
  28. screen.blit(loginback,(0,0))
  29. screen.blit(login,(400,400))
  30. if loginmark==0:
  31. login=pygame.image.load("P/login/loginno.png")
  32. else : login=pygame.image.load("P/login/loginyes.png")
  33. loginmark=0
  34. if mox>=400 and mox<=600:
  35. if moy>=400 and moy<=450:
  36. loginmark=1
  37. if loginmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  38. menu=1
  39. elif menu==1:#菜单
  40. screen.blit(menuback,(0,0))
  41. screen.blit(mx,(700,200))
  42. if mxmark==0:
  43. mx=pygame.image.load("P/menu/maoxianno.png")
  44. else : mx=pygame.image.load("P/menu/maoxianyes.png")
  45. mxmark=0
  46. if mox>=700 and mox<=880:
  47. if moy>=200 and moy<=250:
  48. mxmark=1
  49. if mxmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  50. menu=2
  51. elif menu==2:#冒险模式
  52. pass
  53. zf.tick(30)
  54. pygame.display.flip()

1.要素准备

在植物大战僵尸中,关卡里面有几个重要的元素:草坪背景、阳光数量显示、卡牌、僵尸

那么就先随便画点图片(大不了后期做优化)。

但是,不急着画,先理论,后运用。毕竟这个图片大小,是有点影响后面的。

2.理论基础

(1)草坪

关卡中,我们要先知道,这个窗口的大小为1000×600,我们接下来要对其进行合理地分划。

我数出来,关卡中草坪的区块是9×5个,那么我们可以把每个区块的大小画成100×100。

 从上到下,从左到右依次为:

阳光卡片槽铲子菜单
小推车草坪僵尸产地
关卡名称僵尸进度

简单完善一下这个背景。

有点简陋,不过也还好。

(2)植物放置

当我们选取卡片槽中的一张卡片时,要把它放进各个区块中,就要对鼠标在哪个区块进行判断。

分别用qkx和qky 来表示鼠标所处草坪区块的位置,根据我的这个图片的话,这个草坪的最左上角差不多是(30,90),接下来通过一些列的对鼠标mox和moy的判断,来确定qkx和qky。

点击过卡片后,一株虚拟的植物应该随着鼠标而移动,简而言之就是screen.blit(choice,(mox,moy)),再通过if判断鼠标是否按下,来确定是否种下植物。

而且要注意的是,如果鼠标的mox和moy不在这个草坪的范围内的话,点击鼠标时,就要重新将植物返回到卡片槽中。

(3)僵尸

这个的话,randint是必不可少的,但是不止这点。

randint可以用来确定僵尸出现在那一排,以及出现什么类型的僵尸。后面这个出现什么类型的僵尸,可以通过当前游戏关卡的进度来确定。

出现之后,通过建立精灵组和精灵,控制每一个僵尸的血量,以及速度。

(4)阳光

对于阳光的数量,我们可以通过pygame.font.SysFont("simhei",20)来实现显示数据。

其次,对于白天和屋顶的关卡,阳光会随机自动从天上掉下来,因此可以使用randint来实现。(还要考虑到阳光下落的位置,下落时长,都要考虑)

(5)进度条

这个更加简单了,通过图片缩放即可。但是有个注意点,这个进度条是从右到左,于是我们就可以在图片缩放的条件下,把这个进度条往左边挪一点,这么来看的话,就像是进度条往左移动了。

(6)铲子和菜单

铲子:和植物放置一个原理,只要把放置改为清除即可。

菜单:那就再跳出一块,并且把关卡中僵尸移动速度,植物的子弹发射之类全部改为0,这样就像是被静止了。再在菜单中按选项。

3.代码

不急哈,慢慢来。

先来张卡片,豌豆射手!(图片均为自己画的,不喜勿喷,谢谢!)

如此,也就将就一下吧。 

(1)精灵类

这个我们先设豌豆射手的生命值为100,经过本喵的检测,豌豆射手大概每1.5s发射一颗豌豆。

为了更加方便,我们将频率改成25帧/s。

    zf.tick(25)

每秒运行25次,即运行一遍程序耗时0.04秒,每运行一次,将豌豆射手的cd减1,可设豌豆射手的初始cd约为38。

  1. #shiwu
  2. #wandousheshou
  3. class wandousheshou(pygame.sprite.Sprite):
  4. def __init__(self):
  5. super().__init__()
  6. self.image=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  7. hp=100
  8. cd=38

暂且得到这么一个类。为了判断鼠标选择植物后,种下植物的位置,我们需要先制作卡片在卡槽中。

(2)卡片制作

这个卡片,不单单是读取图片,直接绘制在屏幕上。首先要知道整个流程,先是在正式开始前,选择好卡片,后面卡片再根据玩家选择顺序出现在卡槽中。

所以说,我们需要一个列表,用来存储玩家选择卡片的顺序,并且赋予不同卡片不同的编号。

(当前提供9个卡槽)

choice=[0]*10 #1-豌豆射手'
运行

这样一来,许多乱七八糟的问题就解决了,直接用编号来判断所选择的植物,先用编号代替,后期再一次性渲染即可。

那么对于我这个草坪而言,卡槽的最左端的x为104,差不多100,且卡片大小为50×70,则第一张卡片应该位于(100,0),用center的话,应该(125,35)。

先做一个简单的class,如下。

  1. choice=[0]*10 #1-豌豆射手
  2. choicemark=1
  3. choicelocation=1
  4. #kp
  5. class kp(pygame.sprite.Sprite):
  6. def __init__(self):
  7. super().__init__()
  8. if choice[choicemark]==1:
  9. self.image=pygame.image.laod("P/kp/wandousheshou/kpshiwu.png")
  10. self.rect=self.image.get_rect(center=(75+choicelocation*50,35))
  11. kp_group=pygame.sprite.Group()

(3)判断种植地区

chyn=0 #0未选取卡片;1已选取卡片'
运行

先来一个全局变量,确认是否选取了植物。

  1. elif menu==2:#冒险模式
  2. screen.blit(gqcp,(0,0))
  3. for i in range(0,10):
  4. kp_group.add(kp())
  5. choicemark=i+1
  6. kp_group.draw(screen)
  7. if event.type == pygame.MOUSEBUTTONDOWN:
  8. if chyn==0:
  9. chyn=1
  10. pdx=(mox-100)//50+1
  11. if pdx==1: choicekp=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  12. else: chyn=0
  13. if chyn==1:
  14. screen.blit(choicekp,(mox,moy))

在冒险模式中,这个for是用来渲染的,使卡槽中出现卡片。

其次,用if判断鼠标是否按下,再根据是否已经选择卡片的不同情况运行不同程序。

这个位置是怎么判断的呢?

喵:减去初始位置的x值100,采用取整的方法,整除50是因为每张卡片的宽为50。

+1是因为,整除不是四舍五入,如果鼠标位置为130时,没有+1的话,运行出来结果是0。

下面的那个if是在鼠标选择卡片后,让植物被鼠标拖动。 

接下来是把植物种在草坪上,不但要判断mox,还有moy。上文已知每个草坪的大小为100×100,且草坪左上角为(30,90),那么同理可以判断种植位置。

wandousheshou_group=pygame.sprite.Group()

存放豌豆射手之类植物的组。

  1. elif chyn==1 and mox>=30 and moy>=90:
  2. pdx=(mox-30)//100
  3. pdy=(moy-90)//100
  4. wandousheshou_group.add(wandousheshou())
  5. chyn=0

(pdx和pdy都要设置一个全局变量)

相应的,我们要对wandousheshou这个类class进行一些调整和补充。

  1. class wandousheshou(pygame.sprite.Sprite):
  2. def __init__(self):
  3. super().__init__()
  4. self.image=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  5. self.rect=self.image.get_rect(center=(30+30+pdx*100,90+35+pdy*100))
  6. hp=100
  7. cd=38

这样一来,我们就能很好地实现了这个选择卡片,以及放置相应植物。

4.测试

写到这里,差不多有模有样的已经有了,那让我们来小小测试一下。

上面的代码比较零散,本喵就在下面展示一下本喵当前写的。

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. #全局变量
  6. menu=0
  7. putongmark=1
  8. pdx,pdy=0,0
  9. #基础
  10. screen=pygame.display.set_mode(scsize)
  11. pygame.display.set_caption("乱来的PVZ")
  12. zf=pygame.time.Clock()
  13. ts=pygame.font.SysFont("simhei",20)
  14. tc=ts.render("",True,(0,0,0))
  15. #login
  16. loginback=pygame.image.load("P/login/loginback.png")
  17. login=pygame.image.load("P/login/loginno.png")
  18. loginmark=0
  19. #menu
  20. menuback=pygame.image.load("P/menu/back.png")
  21. mx=pygame.image.load("P/menu/maoxianno.png")
  22. mxmark=0
  23. #base
  24. gqcp=pygame.image.load("P/base/cp.png")
  25. choice=[1]*11 #1-豌豆射手
  26. choicemark=1
  27. choicelocation=1
  28. chyn=0 #0未选取卡片;1已选取卡片
  29. choicekp=pygame.image.load("P/kp/wandousheshou/kpshiwu.png")
  30. #kp
  31. class kp(pygame.sprite.Sprite):
  32. def __init__(self):
  33. super().__init__()
  34. if choice[choicemark]==1:
  35. self.image=pygame.image.load("P/kp/wandousheshou/kpshiwu.png")
  36. self.rect=self.image.get_rect(center=(75+choicelocation*50,35))
  37. #shiwu
  38. #wandousheshou
  39. class wandousheshou(pygame.sprite.Sprite):
  40. def __init__(self):
  41. super().__init__()
  42. self.image=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  43. self.rect=self.image.get_rect(center=(30+30+pdx*100,90+35+pdy*100))
  44. hp=100
  45. cd=38
  46. kp_group=pygame.sprite.Group()
  47. wandousheshou_group=pygame.sprite.Group()
  48. while True:
  49. for event in pygame.event.get():
  50. if event.type==pygame.QUIT:
  51. pygame.quit()
  52. mox, moy = pygame.mouse.get_pos() #实时读取鼠标x,y
  53. if menu==0:#进入界面
  54. screen.blit(loginback,(0,0))
  55. screen.blit(login,(400,400))
  56. if loginmark==0:
  57. login=pygame.image.load("P/login/loginno.png")
  58. else : login=pygame.image.load("P/login/loginyes.png")
  59. loginmark=0
  60. if mox>=400 and mox<=600:
  61. if moy>=400 and moy<=450:
  62. loginmark=1
  63. if loginmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  64. menu=1
  65. elif menu==1:#菜单
  66. screen.blit(menuback,(0,0))
  67. screen.blit(mx,(700,200))
  68. if mxmark==0:
  69. mx=pygame.image.load("P/menu/maoxianno.png")
  70. else : mx=pygame.image.load("P/menu/maoxianyes.png")
  71. mxmark=0
  72. if mox>=700 and mox<=880:
  73. if moy>=200 and moy<=250:
  74. mxmark=1
  75. if mxmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  76. menu=2
  77. elif menu==2:#冒险模式
  78. screen.blit(gqcp,(0,0))
  79. for i in range(0,10):
  80. kp_group.add(kp())
  81. choicemark=i+1
  82. kp_group.draw(screen)
  83. wandousheshou_group.draw(screen)
  84. if event.type == pygame.MOUSEBUTTONDOWN:
  85. if chyn==0:
  86. chyn=1
  87. pdx=(mox-100)//50+1
  88. if pdx==1: choicekp=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  89. else: chyn=0
  90. elif chyn==1 and mox>=30 and moy>=90:
  91. pdx=(mox-30)//100
  92. pdy=(moy-90)//100
  93. wandousheshou_group.add(wandousheshou())
  94. chyn=0
  95. if chyn==1:
  96. screen.blit(choicekp,(mox,moy))
  97. zf.tick(25)
  98. pygame.display.flip()

再展示一下视频,这个效果。

自制PVZ的小测试

5.僵尸

僵尸也是必不可少的一部分,先来一只(风韵犹存的)普通僵尸。

 非常的好看,接下来就交给代码了。

先创建一个精灵类。

  1. #jiangshi
  2. #putong
  3. class putong(pygame.sprite.Sprite):
  4. def __init__()
  5. super().__init__()
  6. self.image=pygame.image.load("P/jiangshi/putong.png")
  7. hp=100

不急好吧,假设血量为100。我试了一下。

数出来普通豌豆射手打普通僵尸,打了5颗后,僵尸掉手臂,10颗后僵尸死亡。7秒左右,普通僵尸就会走完一块草坪。

据此进行编写代码。

(1)思路

先随机生成数字1~5,来确定僵尸生成在哪一路。再多加几张图,让僵尸正常走路,不是死板平移。控制好僵尸的移动速度,以及血量。

(2)初步

我们需要定时来生成新的僵尸,并且随机生成在任意一条路。

  1. time=0
  2. shch=0
  3. #jiangshi
  4. #putong
  5. class putong(pygame.sprite.Sprite):
  6. def __init__(self):
  7. super().__init__()
  8. self.image=pygame.image.load("P/jiangshi/putong.png")
  9. self.rect=self.image.get_rect(center=(950,shch*100+30))
  10. hp=100
  11. putongjiangshi_group=pygame.sprite.Group()

然后再在冒险模式中,对精灵与精灵组进行操作。

  1. elif menu==2:#冒险模式
  2. time+=1
  3. if time>=100:
  4. time=0
  5. shch=randint(1,5)
  6. putongjiangshi_group.add(putong())
  7. putongjiangshi_group.draw(screen)

非常的简单,无需多言解释吧。

(3)优化僵尸移动

给僵尸多画几张图~

在精灵类里面,加一个update,让它实时更新走路姿势和位置。

  1. #jiangshi
  2. #putong
  3. class putong(pygame.sprite.Sprite):
  4. def __init__(self):
  5. super().__init__()
  6. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  7. self.rect=self.image.get_rect(center=(950,shch*100+30))
  8. self.zishi=1
  9. self.hp=100
  10. def update(self):
  11. if self.zishi==1:
  12. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  13. elif self.zishi==3:
  14. self.image=pygame.image.load("P/jiangshi/putong/putongd1.png")
  15. elif self.zishi==5:
  16. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  17. elif self.zishi==7:
  18. self.image=pygame.image.load("P/jiangshi/putong/putongd2.png")
  19. elif self.zishi>=9 : self.zishi=0
  20. self.zishi+=1
  21. self.rect.x-=0.6

最底下这个self.rect.x-=0.6是算出来的,为了保证约7秒走一格草坪。 

 6.豌豆发射

我们要知道,不是所有的路线都有僵尸,不是所有的植物发射的都是豌豆。

来一个二维数组,存储草坪上的植物。为了方便,既然草坪是9×5,那么来个10×6的二维数组好了。

zhiwu=[[0]*10]*6'
运行

再来一个精灵类,用来控制豌豆的发射。

我刚刚又去测了一下,豌豆飞过5格草坪的时间约是1秒,再结合运行一次程序是0.04秒,每格草坪为100×100,可以得出每运行一次程序,豌豆在屏幕中x上移动4个像素。

  1. #wandou
  2. class wandou(pygame.sprite.Sprtie):
  3. def __init__(self):
  4. super().__init__()
  5. self.image=pyagme.image.load("P/kp/wandousheshou/wandou.png")
  6. def update(self):
  7. self.rect.x+=4

非常简单,除此之外我们还需要一个列表来读取每条路线僵尸的个数。

jiance=[0]*6'
运行

在每次生成僵尸的时候顺便把这个jiance[i]+=1,相对应的还要对僵尸之类做一些调整,先不管,后期再优化。

豌豆射手发射豌豆的条件是只要有僵尸即可,与数量无关。

1、那么对jiance这个列表进行遍历,且要遍历检测这一列草坪上的植物。如果没有僵尸就不遍历草坪上植物,这样还能避免没必要的遍历。

  1. for i in range(1,6):
  2. if jiance[i]>0 :
  3. for j in range(1,10):
  4. if zhiwu[i][j]==1 and cd[i][j]<=0:
  5. wandoux=j
  6. wandouy=i
  7. wandou_group.add(wandou())
  8. cd[i][j]=38

2、种植植物的时候也要顺带加个改变二维数组里面的编号。

3、检测到有豌豆射手的时候,那就要读取它的位置,这个用全局变量也可以,或是调用的时候直接读取也可以。

  1. elif chyn==1 and mox>=30 and moy>=90:
  2. pdx=(mox-30)//100
  3. pdy=(moy-90)//100
  4. zhiwu[pdy+1][pdx+1]=1
  5. wandousheshou_group.add(wandousheshou())
  6. chyn=0

4、再来一个cd的二维数组,来控制发射间隔。

  1. cd=[([0]*10) for i in range(6)]
  2. elif menu==2:#冒险模式
  3. for i in range(1,6):
  4. for j in range(1,10):
  5. cd[i][j]-=1

5、删去原豌豆射手精灵类里面的cd。

7.完善优化

okok啊!下面就是简简单单的了,加个碰撞,从而修改生命值,先来僵尸的和豌豆的。

(1)豌豆与僵尸碰撞

在冒险模式中加入以下碰撞检测。

  1. col = pygame.sprite.groupcollide(wandou_group, putongjiangshi_group, True, False)
  2. for sprite, list in col.items():
  3. for s in list:
  4. s.hp -= 10

确认碰撞之后,就是修改僵尸的ph值了。

  1. #jiangshi
  2. #putong
  3. class putong(pygame.sprite.Sprite):
  4. def __init__(self):
  5. super().__init__()
  6. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  7. self.rect=self.image.get_rect(center=(950,shch*100+30))
  8. self.zishi=1
  9. self.location=shch
  10. self.hp=100
  11. def update(self):
  12. if self.zishi==1:
  13. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  14. elif self.zishi==3:
  15. self.image=pygame.image.load("P/jiangshi/putong/putongd1.png")
  16. elif self.zishi==5:
  17. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  18. elif self.zishi==7:
  19. self.image=pygame.image.load("P/jiangshi/putong/putongd2.png")
  20. elif self.zishi>=9 : self.zishi=0
  21. self.zishi+=1
  22. self.rect.x-=0.6
  23. global wf
  24. global jiance
  25. if self.rect.x<0:
  26. wf=0
  27. if self.hp<=0:
  28. jiance[self.location]-=1
  29. self.kill()

哦对了,那个wf是用于失败检测的,也就是游戏失败。

然后最后那个if是没血的时候,把该精灵从精灵组移除,还要改变当前路线上对僵尸数量的检测。

(2)优化豌豆射手攻击

目前写代码写下来呢就是有个问题,僵尸哪怕在植物身后,在僵尸身后的豌豆射手也会发生豌豆,这个就不得不把这个检测升级成二维了。

jiance=[([0]*10) for i in range(6)]'
运行

生成僵尸的时候也要改动。

  1. if time>=100:
  2. time=0
  3. shch=randint(1,5)
  4. jiance[shch][9]+=1
  5. putongjiangshi_group.add(putong())

僵尸这个精灵里面每次移动也要对应的检测一下是否发生了变化。

  1. class putong(pygame.sprite.Sprite):
  2. def __init__(self):
  3. super().__init__()
  4. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  5. self.rect=self.image.get_rect(center=(950,shch*100+30))
  6. self.zishi=1
  7. self.location=shch
  8. self.hp=100
  9. self.pastlo=9
  10. def update(self):
  11. if self.zishi==1:
  12. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  13. elif self.zishi==3:
  14. self.image=pygame.image.load("P/jiangshi/putong/putongd1.png")
  15. elif self.zishi==5:
  16. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  17. elif self.zishi==7:
  18. self.image=pygame.image.load("P/jiangshi/putong/putongd2.png")
  19. elif self.zishi>=9 : self.zishi=0
  20. self.zishi+=1
  21. self.rect.x-=0.6
  22. global wf
  23. global jiance
  24. lo=(self.rect.x-30)//100+1
  25. if self.pastlo-lo>0:
  26. jiance[self.location][self.pastlo]-=1
  27. jiance[self.location][lo]+=1
  28. self.pastlo=lo
  29. if self.rect.x<0:
  30. wf=0
  31. if self.hp<=0:
  32. jiance[self.location][lo]-=1
  33. self.kill()

那么改了哪些地方呢:

1、__init__里面多加了一个pastlo,用来保存上一次位置。

2、update中对当前位置和上次位置的比较,从而即使改变jiance里面数量,保存准确性。

(3)加入阳光系统

当前为止,为了方便测试,阳光一直没加,那么接下来就让我们加入阳光系统。

又要画图哩,来张阳光的图片。

既然一般来说这个阳光的初始值是50,那全局变量一开始赋值50先。

sunsum=50'
运行

还要把这个数字明确实时地显示在游戏界面中。

  1. tc=ts.render(str(sunsum),True,(0,0,0))
  2. screen.blit(tc,(35,60))

 (这个tc的绘制要绘制在背景图之后,这样不会被背景图覆盖。)

接下来要考虑两个问题,一个是阳光在白天会从天上落下,还有就是植物会产生阳光,综合一下,可以弄一个精灵类。

全局变量先来一对。sunv表示掉阳光速度。

sunx,suny,sunv=0,0,70'
运行

先在while中,根据速率生成阳光,并随机位置。

sun_group=pygame.sprite.Group()
  1. if sunv<=0:
  2. sunx=randint(50,850)
  3. suny=randint(5,50)
  4. sun_group.add(sun())
  5. sunv=randint(100,135)
  6. sunv-=1

注意缩进喵!

一开始呢,我们就来一个简单的精灵。

  1. #sun
  2. class sun(pygame.sprite.Sprite):
  3. def __init__(self):
  4. super().__init__()
  5. self.image=pygame.image.load("P/base/sun.png")
  6. self.rect=self.image.get_rect(center=(sunx+50,suny+50))
  7. def update(self):
  8. self.rect.y+=3
  9. global chyn
  10. if mox>=self.rect.x and mox<=self.rect.x+100 and moy>=self.rect.y and moy<=self.rect.y+100 and event.type == pygame.MOUSEBUTTONDOWN:
  11. global sunsum
  12. sunsum+=25
  13. self.kill()
  14. if moy>=650:
  15. self.kill()

在这个游戏中呢,点阳光拾取另外算。

  1. if event.type == pygame.MOUSEBUTTONDOWN:
  2. sun_group.update()

 还有种植植物的时候减去相应的阳光数量。植物前再加一个判断,要满足阳光数量。

  1. if event.type == pygame.MOUSEBUTTONDOWN:
  2. if chyn==0 and sunsum>=100:
  3. chyn=1
  4. pdx=(mox-100)//50+1
  5. if pdx==1: choicekp=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  6. else: chyn=0
  7. elif chyn==1 and mox>=30 and moy>=90:
  8. pdx=(mox-30)//100
  9. pdy=(moy-90)//100
  10. zhiwu[pdy+1][pdx+1]=1
  11. wandousheshou_group.add(wandousheshou())
  12. sunsum-=100
  13. chyn=0

如果要弄那个,阳光数量不足,字体变红闪动的话,只要改变tc颜色即可,就不展示了。

(4)植物受伤

这个僵尸再来两张图片表示体现僵尸在吃,跟豌豆和僵尸碰撞差不多,简简单单。

好吧,其实一点也不简单,琢磨了个把小时……

方便点的,我们只需要在植物的update中加点东西即可。

  1. def update(self):
  2. self.hp-=jiance[self.cuny+1][self.cunx+1]*1
  3. if self.hp<=0:
  4. zhiwu[self.cuny+1][self.cunx+1]=0
  5. self.kill()
'
运行

每次update的时候都这么运行一下,有僵尸就扣,根据数量来看,因为这个是运行一遍就扣一次,所以说扣少一点。

同样的,当僵尸靠近的时候,僵尸要原地不动且做出吃的动作。

  1. def update(self):
  2. if zhiwu[self.location][self.pastlo] > 0:
  3. self.zishi+=1
  4. if self.zishi<5:
  5. self.image=pygame.image.load("P/jiangshi/putong/putongchi1.png")
  6. if self.zishi>=5:
  7. self.image=pygame.image.load("P/jiangshi/putong/putongchi2.png")
  8. if self.zishi>=10 :self.zishi=0
  9. else :
  10. if self.zishi==1:
  11. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  12. elif self.zishi==3:
  13. self.image=pygame.image.load("P/jiangshi/putong/putongd1.png")
  14. elif self.zishi==5:
  15. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  16. elif self.zishi==7:
  17. self.image=pygame.image.load("P/jiangshi/putong/putongd2.png")
  18. elif self.zishi>=9 : self.zishi=0
  19. self.zishi+=1
  20. self.rect.x-=0.6
'
运行

第六步、微调

累死了属于是,这个琢磨了好些天,大家在测试的时候根据实际情况微调即可,其他什么娱乐模式啊什么的就不写了。

喵:其他种类的植物和僵尸以此类推即可!

第七步、结

 我把整个文件放“喵做梦技术讨论群”里了,需者加群自取。

这里不太方便展示,就只放一下代码部分吧。(毕竟这个图片,影响对代码某些数字的理解。)

  1. import pygame
  2. from random import randint
  3. pygame.init()
  4. scsize=(1000,600)
  5. def pzh(pr1,pr2,position1,position2):
  6. return pr1.overlap(pr2,(position2[0]-position1[0],position2[1]-position1[1]))
  7. #全局变量
  8. menu=0
  9. putongmark=1
  10. pdx,pdy=0,0
  11. #基础
  12. screen=pygame.display.set_mode(scsize)
  13. pygame.display.set_caption("乱来的PVZ")
  14. zf=pygame.time.Clock()
  15. ts=pygame.font.SysFont("simhei",20)
  16. tc=ts.render("",True,(0,0,0))
  17. time=0
  18. shch=0
  19. zhiwu=[([0]*10) for i in range(6)]
  20. cd=[([0]*10) for i in range(6)]
  21. jiance=[([0]*10) for i in range(6)]
  22. wandoux,wandouy=0,0
  23. sunx,suny,sunv=0,0,70
  24. #login
  25. loginback=pygame.image.load("P/login/loginback.png")
  26. login=pygame.image.load("P/login/loginno.png")
  27. loginmark=0
  28. #menu
  29. menuback=pygame.image.load("P/menu/back.png")
  30. mx=pygame.image.load("P/menu/maoxianno.png")
  31. mxmark=0
  32. #base
  33. gqcp=pygame.image.load("P/base/cp.png")
  34. choice=[1]*11 #1-豌豆射手
  35. choicemark=1
  36. choicelocation=1
  37. chyn=0 #0未选取卡片;1已选取卡片
  38. choicekp=pygame.image.load("P/kp/wandousheshou/kpshiwu.png")
  39. fail=pygame.image.load("P/base/fail.png")
  40. wf=1
  41. sunsum=50
  42. #kp
  43. class kp(pygame.sprite.Sprite):
  44. def __init__(self):
  45. super().__init__()
  46. if choice[choicemark]==1:
  47. self.image=pygame.image.load("P/kp/wandousheshou/kpshiwu.png")
  48. self.rect=self.image.get_rect(center=(75+choicelocation*50,35))
  49. #shiwu
  50. #wandousheshou
  51. class wandousheshou(pygame.sprite.Sprite):
  52. def __init__(self):
  53. super().__init__()
  54. self.image=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  55. self.rect=self.image.get_rect(center=(30+30+pdx*100,90+35+pdy*100))
  56. self.cunx=pdx
  57. self.cuny=pdy
  58. self.hp=100
  59. def update(self):
  60. self.hp-=jiance[self.cuny+1][self.cunx+1]*1
  61. if self.hp<=0:
  62. zhiwu[self.cuny+1][self.cunx+1]=0
  63. self.kill()
  64. #wandou
  65. class wandou(pygame.sprite.Sprite):
  66. def __init__(self):
  67. super().__init__()
  68. self.image=pygame.image.load("P/kp/wandousheshou/wandou.png")
  69. self.rect=self.image.get_rect(center=(wandoux*100-20,wandouy*100+10))
  70. def update(self):
  71. self.rect.x+=4
  72. #jiangshi
  73. #putong
  74. class putong(pygame.sprite.Sprite):
  75. def __init__(self):
  76. super().__init__()
  77. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  78. self.rect=self.image.get_rect(center=(950,shch*100+30))
  79. self.zishi=1
  80. self.location=shch
  81. self.hp=100
  82. self.pastlo=9
  83. def update(self):
  84. if zhiwu[self.location][self.pastlo] > 0:
  85. self.zishi+=1
  86. if self.zishi<5:
  87. self.image=pygame.image.load("P/jiangshi/putong/putongchi1.png")
  88. if self.zishi>=5:
  89. self.image=pygame.image.load("P/jiangshi/putong/putongchi2.png")
  90. if self.zishi>=10 :self.zishi=0
  91. else :
  92. if self.zishi==1:
  93. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  94. elif self.zishi==3:
  95. self.image=pygame.image.load("P/jiangshi/putong/putongd1.png")
  96. elif self.zishi==5:
  97. self.image=pygame.image.load("P/jiangshi/putong/putong.png")
  98. elif self.zishi==7:
  99. self.image=pygame.image.load("P/jiangshi/putong/putongd2.png")
  100. elif self.zishi>=9 : self.zishi=0
  101. self.zishi+=1
  102. self.rect.x-=0.6
  103. global wf
  104. global jiance
  105. lo=(self.rect.x-30)//100+1
  106. if self.pastlo-lo>0:
  107. jiance[self.location][self.pastlo]-=1
  108. jiance[self.location][lo]+=1
  109. self.pastlo=lo
  110. if self.rect.x<0:
  111. wf=0
  112. if self.hp<=0:
  113. jiance[self.location][lo]-=1
  114. self.kill()
  115. #sun
  116. class sun(pygame.sprite.Sprite):
  117. def __init__(self):
  118. super().__init__()
  119. self.image=pygame.image.load("P/base/sun.png")
  120. self.rect=self.image.get_rect(center=(sunx+50,suny+50))
  121. def update(self):
  122. self.rect.y+=3
  123. global chyn
  124. if mox>=self.rect.x and mox<=self.rect.x+100 and moy>=self.rect.y and moy<=self.rect.y+100 and event.type == pygame.MOUSEBUTTONDOWN:
  125. global sunsum
  126. sunsum+=25
  127. self.kill()
  128. if moy>=650:
  129. self.kill()
  130. kp_group=pygame.sprite.Group()
  131. wandousheshou_group=pygame.sprite.Group()
  132. wandou_group=pygame.sprite.Group()
  133. putongjiangshi_group=pygame.sprite.Group()
  134. sun_group=pygame.sprite.Group()
  135. while True:
  136. for event in pygame.event.get():
  137. if event.type==pygame.QUIT:
  138. pygame.quit()
  139. mox, moy = pygame.mouse.get_pos() #实时读取鼠标x,y
  140. if menu==0:#进入界面
  141. screen.blit(loginback,(0,0))
  142. screen.blit(login,(400,400))
  143. if loginmark==0:
  144. login=pygame.image.load("P/login/loginno.png")
  145. else : login=pygame.image.load("P/login/loginyes.png")
  146. loginmark=0
  147. if mox>=400 and mox<=600:
  148. if moy>=400 and moy<=450:
  149. loginmark=1
  150. if loginmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  151. menu=1
  152. elif menu==1:#菜单
  153. screen.blit(menuback,(0,0))
  154. screen.blit(mx,(700,200))
  155. if mxmark==0:
  156. mx=pygame.image.load("P/menu/maoxianno.png")
  157. else : mx=pygame.image.load("P/menu/maoxianyes.png")
  158. mxmark=0
  159. if mox>=700 and mox<=880:
  160. if moy>=200 and moy<=250:
  161. mxmark=1
  162. if mxmark==1 and event.type == pygame.MOUSEBUTTONDOWN:
  163. menu=2
  164. elif menu==2:#冒险模式
  165. tc=ts.render(str(sunsum),True,(0,0,0))
  166. time+=1
  167. if time>=100:
  168. time=0
  169. shch=randint(1,5)
  170. jiance[shch][9]+=1
  171. putongjiangshi_group.add(putong())
  172. screen.blit(gqcp,(0,0))
  173. for i in range(0,10):
  174. kp_group.add(kp())
  175. choicemark=i+1
  176. kp_group.draw(screen)
  177. wandousheshou_group.draw(screen)
  178. putongjiangshi_group.draw(screen)
  179. wandou_group.draw(screen)
  180. sun_group.draw(screen)
  181. screen.blit(tc,(35,60))
  182. putongjiangshi_group.update()
  183. wandou_group.update()
  184. wandousheshou_group.update()
  185. sun_group.update()
  186. for i in range(1,6):
  187. for j in range(1,10):
  188. if zhiwu[i][j]==1 and cd[i][j]<=0:
  189. youmeiyou=0
  190. for o in range(j,10):
  191. if jiance[i][o]>0:
  192. youmeiyou=1
  193. break
  194. if youmeiyou==1:
  195. wandoux=j
  196. wandouy=i
  197. wandou_group.add(wandou())
  198. cd[i][j]=38
  199. col = pygame.sprite.groupcollide(wandou_group, putongjiangshi_group, True, False)
  200. for sprite, list in col.items():
  201. for s in list:
  202. s.hp -= 10
  203. if event.type == pygame.MOUSEBUTTONDOWN:
  204. if chyn==0 and sunsum>=100:
  205. chyn=1
  206. pdx=(mox-100)//50+1
  207. if pdx==1: choicekp=pygame.image.load("P/kp/wandousheshou/shiwu.png")
  208. else: chyn=0
  209. elif chyn==1 and mox>=30 and moy>=90:
  210. pdx=(mox-30)//100
  211. pdy=(moy-90)//100
  212. zhiwu[pdy+1][pdx+1]=1
  213. wandousheshou_group.add(wandousheshou())
  214. sunsum-=100
  215. chyn=0
  216. if event.type == pygame.MOUSEBUTTONDOWN:
  217. sun_group.update()
  218. if chyn==1:
  219. screen.blit(choicekp,(mox,moy))
  220. if wf==0:
  221. screen.blit(fail,(200,100))
  222. if sunv<=0:
  223. sunx=randint(50,850)
  224. suny=randint(5,50)
  225. sun_group.add(sun())
  226. sunv=randint(100,135)
  227. sunv-=1
  228. for i in range(1,6):
  229. for j in range(1,10):
  230. cd[i][j]-=1
  231. zf.tick(25)
  232. pygame.display.flip()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/907226
推荐阅读
相关标签
  

闽ICP备14008679号