摘要
在当今电子信息高速发展时代下,网络游戏和单机游戏已经遍布了我们的生活,随着游戏的发展,有许多游戏早已被淘汰掉,只剩下少部分的经典还依旧有人玩,植物大战僵尸就是其中之一。
植物大战僵尸是一款伴随着无数人成长的策略类游戏,可怕的僵尸因不明原因而向屋子内进军,作为屋子主人的我们,需要通过对植物的合理安排来守卫我们宝贵的家园。
此次设计是中Window10企业版下使用Python在编译软件Vscode进行开发的。
1.引言
随着社会压力的变大,人们通过玩游戏来缓解生活的压力,如今的游戏有太多种选择,与过去的建模,游戏性都有了肉眼可见的进步,但也在此情况下,由于审美的疲劳以及游戏性的难度增加,这导致了部分人的压力增大而使得他们选择过去操作简单舒适的经典来休闲娱乐自己。
《植物大战僵尸》集成了即时战略、塔防御战和卡片收集等要素,玩家控制植物抵御僵尸的进攻,保护这片植物园。游戏中可以选用的植物有40多种,而每个场景最多只能选用10种植物,这就需要玩家根据自己的游戏策略来作出取舍。因为它成功地借鉴了一些战略游戏的要素——采集资源并利用资源建造其它单位,有些玩家甚至拿星际的战略往这款游戏中套用,以阐述这款游戏需要在何时发展“经济”,何时发展“兵力”。[1]
这款游戏要求玩家既有是大脑的智慧,又有小脑的反应。在有了正确的战略思想之后,还要靠战术将战略实现出来。战术范围包括很广,植物的搭配、战斗时的阵型、植物与僵尸相遇时是战是防这都属于战术的范畴。正确的战术是玩家在战斗中胜利关键。选择正确的战术,需要先分析情况,再做出决定。那么提高战术水平也是要提高分析情况的能力。[2]

2.系统结构
系统的一开始会先建立一个块列表(用来存放记录可种植的合法的块范围),怪物列表(用来存放怪物对象),阳光列表(用来存放阳光对象),还有保卫者列表(用来存放豌豆射手以及向日葵),攻击列表(用来存放保卫者发出的攻击)
关于如何加载动图:获取素材的帧图,通过pygame提供的双缓冲技术每次刷新界面显示不同的图片。

关于如何完成购买表1的植物:首先检测下鼠标单击的位置是否在卡槽中植物卡的范围,其次确定金钱是否足够,如果以上操作均合法,那么将进入种植状态,种植的植物将会跟谁你的鼠标的移动而移动,之后开始选择位置,这将会根据鼠标右击一下确定安放位置,检查种植植物的地点是否规范,如所选位置是否已有植物已经种植或者所选的范围不为规定的范围,如果操作均合法就种植该在地区,并执行自身职责;如果属于违法操作,那么将取消种植形态。

表1 植物类型
名称 图标 类型 属性 产物描述 产物图片
豌豆射手 进攻型 血量:130
价格:100阳光值
攻击频率:3600ms 自身可发射出一种可攻击僵尸的产物用来进攻敌人
向日葵 生产型 血量:150
价格:50阳光值
生产频率:4200ms 自身可生成出阳光值,可用于购买植物

关于如何获取阳光值:如果当前状态不为种植形态的话,通过鼠标单击的位置来确定单击的鼠标位置范围内是否存在有阳光,有的话增加阳光值。
表2保卫者产物
名称 图标 属性 作用 来源
阳光 价格:25阳光值
生产来源:向日葵生产获得以及每隔3900ms随机位置生成 点击后可增加自身阳光值大小 向日葵
豌豆攻击 速度:每帧位移1单位
攻击力:30 通过攻击僵尸来保卫屋子 豌豆射手
关于豌豆射手如何发射攻击和向日葵产生阳光:用时间事件来记录时间,并通过调用类方法同时调用增加时间的方法,当达到设定的值的大小时,就会分别给攻击列表,和阳光列表增加对象。
关于攻击列表的消除:为了防止内存溢出,当攻击单位超出屏幕指定的范围后,自动消除攻击对象,除此之外,当攻击单位命中怪物,也会消除这个攻击对象
关于如何进入让保卫者拦截僵尸:当保卫者的左边与僵尸的坐标靠的十分近的时候,那么怪物就会转入攻击状态,并将速度调为0,禁止不动。
关于如何攻击僵尸:检测攻击列表中对象的位置是否与僵尸的坐标有一定的重合,如果重合的话,扣除怪物的血量,如果怪物死亡的话,那么将怪物质为死亡状态
如何让保卫者受伤:遍历保卫者列表中对象的位置和怪物列表中的对象,如果两者重合的话,那么植物就会收到怪物攻击力大小的伤害,当植物的生命小于等于0的时候,植物就会从保卫者列表中清除.
表3 怪物属性图
怪物名称 怪物图片 怪物属性 怪物状态(以下只展示部分状态图)
僵尸 HP:100
速度:走路状态:每帧位移1单位;死亡和攻击状态不位移
攻击力:30
走路状态
攻击状态
死亡状态

3.实现代码。
定义一个怪物父类,将它的图片 HP 攻击力 当前速度 走路速度 状态初始化,并给怪物添加三种设置状态的方法,在方法中改变它的状态值,加载图片,攻击速度,除此之外还有获得攻击力,血量,扣除血量的方法,定义初始位置,以及加载动图的方法

class monster:def __init__(self,image):#怪物图片 怪物血量 怪物速度 怪物攻击力大小self.image = []self.HP = 0self.attack=0self.speed = 0#当前帧图self.runspeed=0self.index = 0self.run = []#0代表着run 1代表attack -1代表dieself.staus=0def abateHP(self, attack):self.HP -= attack#获得怪物攻击力def getAttack(self):return self.attack#获得怪物血量def getHP(self):return self.HP#获得当前位置def getXY(self):return [self.x,self.y]#定义初始位置def setSeat(self, x, y):self.x = x+50self.y = y + 10#移动def move(self, screen):if (self.index > len(self.image)-1):self.index=0self.x = self.x - self.speedscreen.blit(pg.transform.scale(self.image[self.index], (106, 109)), (self.x, self.y))self.index += 1if (self.index == len(self.image)) and (self.staus == -1):return - 1else:return 1#转为攻击形态def attackStatus(self,image):#将速度降为0self.speed = 0#重新载入图片self.image = imageself.staus=1#转为跑步形态def runStatus(self, image):#改变速度self.staus=0self.speed = self.runspeedself.image = image#死亡形态def dieStaus(self, image):#改变图片self.image = imageself.speed = 0self.staus = -1def getStaus(self):return self.staus

在这之后,我们定义它的子类通过覆盖父类的属性就可以实现第一个自定义的僵尸了

class littlemonster(monster):def __init__(self,image):#0代表着run 1代表attack -1代表dieself.staus = 0self.image = imageself.HP = 100self.attack = 30self.runspeed = 1self.speed = self.runspeedself.index = 0

按照同样的方法编写一个保卫者的类,编写它的血量,图片,位置,时间(用来触发生产阳光或者豌豆攻击的方法),构造方法获得保卫者当前的横纵坐标,血量,以及对保卫者产生的伤害

class denfendor:def __init__(self):self.HP=0self.image=[]self.rect=self.image[0].get_rect()self.time=0self.index = 0#获得保卫者的位置def getXY(self):return [self.rect.left, self.rect.top]#获得血量def getHP(self):return self.HP#设置受到的伤害def abateHP(self,damage):self.HP -= damage#设置时间def addTime(self):self.time += 300#是否产生产物def isCreate(self,time):self.addTime()if (self.time >= time):self.time = 0return Truereturn False
#加载模型def setModel(self, screen, x, y,num):size = (80, 80)if (num == 1):size=(55,55)if (self.index == len(self.image)):self.index = 0self.rect.left = xself.rect.top = y#重设大小screen.blit(pg.transform.scale(self.image[self.index], size), self.rect)self.index += 1

之后开始编写保卫者的两个子类,豌豆射手以及向日葵,通过覆盖父类的变量来达到使用效果,之后也可以按照这个方法来写更多的子类保卫者
#豌豆射手

class peas(denfendor):def __init__(self):#受到的伤害self.dagame=0#设置血量self.HP = 130#加载图片self.time = 0self.index=0self.image = [pg.image.load(r'images/peasModel/22072568-{:d}.png'.format(i)).convert_alpha() for i in range(1,9)]self.rect = self.image[0].get_rect()self.rect.left, self.rect.top = 300, 300#设置向日葵
class sunflower(denfendor):def __init__(self):super()#受到的伤害self.dagame = 0self.HP=150self.time = 0self.index = 0self.image = [pg.image.load(r'images/sunflowerModel/summer-{:d}.png'.format(i)).convert_alpha() for i in range(1, 55)]self.rect = self.image[0].get_rect()self.rect.left, self.rect.top = 300, 300#开始编写阳光类 实现对图片的加载,以及设置阳光的位置,还有判断是否被点击到```cpp
class sunshine():def __init__(self):self.image = pg.image.load(r'images/sunshine.png')self.image = pg.transform.scale(self.image, (70, 70))self.clickAfterWait = 5self.flag = Falseself.seat=(0,0)def setModel(self, screen, x, y):self.seat = (x, y)screen.blit(self.image, self.seat)def isClick(self, x, y):flag=Falseif (self.seat[0] <= x <= self.seat[0] + 70):if (self.seat[1] <= y <= self.seat[1] + 70):flag = Trueself.flag=flagreturn flag

编写一个Block类,这个类是用来确定植物是否有合法的放置位置,通过初始化块的信息来定义每个块的具体范围,定义一个方法判断是否该块有被种植以及鼠标点击的坐标是否中块内

class Block:def __init__(self, num):#块的位置col = int(num % 9)row = int(num / 9)self.leftTopX = 359 + col * 55self.leftTopY = 137 + row * 66self.rightDownX = self.leftTopX + 55self.rightDownY = self.leftTopY + 66#是否被种植了self.isHave = False#设置获得isHavedef setIsHave(self):self.isHave = not self.isHavedef getIsHave(self):return self.isHavedef getXY(self):return [self.leftTopX, self.leftTopY, self.rightDownX, self.rightDownY]#检查是否在这个方块内def checkIsInBlock(self, x, y):if (self.leftTopX <= x <= self.rightDownX) and (self.leftTopY <= y <= self.rightDownY):return Truereturn False

然后开始编写攻击方式块的父类AttackWay,初始化载入图片,以及攻击大小,定义设置攻击的初始位置,以及实现攻击的移动,还有获得当前攻击对象的位置,除此之外还有攻击力大小

class AttackWay:def __init__(self):#载入图像self.image = pg.image.load('').convert_alpha()self.attack=0#定义初始位置def setSeat(self, x, y):self.x = x+50self.y = y+10#攻击移动def move(self, screen):self.x+=1self.seat = (self.x, self.y)screen.blit(self.image, self.seat)#获得攻击的位置def getXY(self):return [self.x,self.y]#获得攻击力大小def getAttack(self):return self.attack
定义豌豆攻击的子类继承攻击父类
#豌豆攻击方式
class wdAttack(AttackWay):def __init__(self):self.attack=30self.image = pg.image.load(r'images\attack\wandou.png').convert_alpha()self.image = pg.transform.scale(self.image, (30,30))

接着卡槽内卡牌的放置模型
初始化图片,图片大小,卡牌价格,设置卡牌位置,获得图片的宽高以及位置,还有设置卡牌的金额,将它价格转为图片的形式加载在游戏界面中

class card:def __init__(self):self.image=pg.image.load('')self.rect = pg.image.load('')self.rect.left = 0self.rect.top = 0self.price = 100#获得价格def getPrice(self):return self.price#设置卡牌位置def setModel(self, screen, num=0):size = (80, 80)self.rect.left = 380 + num * 95self.rect.top = 10#设置向日葵大小if num == 1:size = (70, 70)self.rect.left=390+num*95self.image = pg.transform.scale(self.image, size)screen.blit(self.image, self.rect)self.setPrice(screen, num)#获得图片宽高,起始位置def getImageWidth(self):return self.rect.widthdef getImageHeigth(self):return self.rect.heightdef getImageX(self):return self.rect.leftdef getImageY(self):return self.rect.top#设置金钱def setPrice(self, screen,num):rect = self.rectrect.top = 70rect.left=410+92*numscreen.blit(pg.font.SysFont('arial', 20).render(str(self.price), True, (255, 255, 255)), rect)
令豌豆射手和向日葵继承卡牌父类
#豌豆射手模型
class peasCard(card):def __init__(self):super()self.image = pg.image.load('images/peasModel/22072568-3.png')self.rect = self.image.get_rect()self.price = 100self.rect.left = 390self.rect.top = 0#向日葵模型
class sunflowerCard(card):def __init__(self):super()self.image = pg.image.load('images/sunflowerModel/summer-1.png')self.rect = self.image.get_rect()self.price = 50self.rect.left = 390self.rect.top = 0
接着上面的类实现后就可以开始编写主方法了,首先初始化块的信息# 初始化块信息blocks = createBlock()
定义一个方法来调用block类里面的构造方法创建45个块的对象
def createBlock():blocks = []for i in range(45):temp = data.block.Block(i)blocks.append(temp)return blocks
接着初始化信息 是否暂停、怪物出现的频率 计时器,攻击列表,阳光列表,怪物列表等信息,下有注释isPause = False#怪物出现频率monster_delay = 3000#计时器delay = 0# 攻击方式allAttack = []# 初始化阳光sunshineList = []
# 怪物列表
monsterList = []# 初始化金钱sunshineNotes = 1000#保卫者列表defendors = []# 购买该植物需要扣除的金钱record = 0

在全局变量中先布置下环境,设置界面大小,设置时间事件,初始化卡牌信息,怪物走路,攻击,死亡的帧图(这里本来想放在Monster类中定义的,但由于每新建一个僵尸对象时由于加载的图片太多导致会卡一次,所以放在全局中直接先声明一次)

import pygame as pg
import sys
import model.wdModel
import model.defendorCard
import model.monster
import model.attackWay
import data.block
import random
import timeimport pygame as pg
pg.init()# 设置窗口大小
window_size = (1320, 513)
screen = pg.display.set_mode(window_size)
# 设置卡牌
peasCard = model.defendorCard.peasCard()
sunflowerCard = model.defendorCard.sunflowerCard()# 阳光
SUNDELAY = 300
# 设置窗口标题
pg.display.set_caption('no konw')# 设置背景图片
bgPath = r'images/bg.jpg'
bg = pg.image.load(bgPath).convert()# 设置卡槽图片
cardPath = r'images/card.png'
card = pg.image.load(cardPath).convert()
pauseimage = pg.image.load('images/pause.png')
#由于要加载的怪物形态太多了,所以在这里先初始化图片
run = [pg.image.load(r'images/littlemonst/run/427090eef01f3a29384879ac8e25bc315d607cf0-{:d}.png'.format(i)).convert_alpha() for i in range(1, 125)]
attack = [pg.image.load(r'images/littlemonst/attack/427090eef01f3a29384879ac8e25bc315d607cf0-{:d}.png'.format(i)).convert_alpha() for i in range(1, 30)]
die = [pg.image.load(r'images/littlemonst/goToDie/cafe8d66d016092468b62432da0735fae6cd3408-{:d}.png'.format(i)).convert_alpha() for i in range(1, 38)]HP = 5

300ms触发一次事件

SUNSHINEEVENT = pg.USEREVENT
pg.time.set_timer(SUNSHINEEVENT, SUNDELAY)# 刷新界面
clock = pg.time.Clock()
接着进入While True循环中,在开头判断当前是否是已经停止的状态,如果不为停止那么当前血量是否小于等于0,如果小于等于0的话,将当前状态置为停止,并播放录音game overif not isPause:if (HP <= 0):pg.mixer.init()pg.mixer.music.load(r"images/gameover.mp3")pg.mixer.music.play()isPause=not isPause如果为暂停状态下的话,加载暂停图片else:pg.display.update()screen.blit(pg.transform.scale(pauseimage,(300,300)), (500, 100))接着开始对背景和卡牌,以及剩余的血量大小加载进入界面中
然后通过pygame提供的mouse中的类属性获得当前按下的鼠标情况以及记录鼠标的x,y信息HPIMAGE=pg.font.SysFont('arial', 35).render("The rest of life:"+ str(HP), True, (0, 0, 0))# 设置金钱图片fontImg = pg.font.SysFont('arial', 20).render(str(sunshineNotes), True, (0, 0, 0))press = pg.mouse.get_pressed()pg.display.update()screen.blit(bg, (0, 0))screen.blit(card, (300, 0))screen.blit(fontImg, (330, 70))screen.blit(HPIMAGE,(0,0))x, y = pg.mouse.get_pos()然后判断监听下事件判断是否要退出,以及是否有按下空格键,如果按下空格键,将当前isPause的信息设置为相反的值for event in pg.event.get(): # EXITif event.type == pg.QUIT:sys.exit()if event.type == pg.KEYDOWN:if (event.key == 32):isPause = not isPause

如果不为暂停的话,那么为鼠标定义一个监听事件
如果鼠标点击的卡牌上并且当前还未处于购买状态的话,首先判断是否单机到阳光,这里通过调用方法判断当前鼠标的x,y是否点击到其中一个阳光,如果点击到的话,增加阳光值,并将当前对象改为None
之后再判断点击位置是否为哪个的购卡位置,根据不同位置判断购买的种类并把它添加到保卫者列表中去,记录购卡所需的金额,并且标志当前处于购卡状态,购买的植物将会随着鼠标的移动而移动,如果金额不够的话,无法进行购买

                # 购买卡牌if event.type == pg.MOUSEBUTTONDOWN:# 如果还未选中if not isSelect:# 获取阳光值sunshineNotes = clickSunshine(sunshineList, sunshineNotes, x, y)# 购卡temp = buyCard(defendors, x, y, sunshineNotes)defendors, isSelect, record = temp[0], temp[1], temp[2]def clickSunshine(sunshineList, sunshineNotes, x, y):for i in range(len(sunshineList)):if sunshineList[i][0].isClick(x, y) and sunshineList[i]:sunshineList[i] = NonesunshineNotes += 25return sunshineNotes

购卡

def buyCard(defendors, x, y, sunshineNote):record = 0isSelect = False# 豌豆射手if 387 <= x <= 465 and 0 <= y <= 92 and sunshineNote-peasCard.getPrice() >= 0:defendor = model.wdModel.peas()defendors.append([defendor, x, y, 0, None])record = peasCard.getPrice()isSelect = True# 向日葵if 477 <= x <= 555 and 0 <= y <= 97 and sunshineNote-sunflowerCard.getPrice() >= 0:defendor = model.wdModel.sunflower()defendors.append([defendor, x, y, 1, None])record = sunflowerCard.getPrice()isSelect = Truereturn [defendors, isSelect, record]

如果当前处于购卡状态下的话,判断鼠标是否单击右键
首先判断当前鼠标放置的位置是否在块的范围内,并且该块是否已经被种植了
如果可以种植的话,更改当前的购买状态,并将当前的植物的位置固定在改块之中,且调用该块的方法重新设定该块已经有保卫者了,这里加了一个方法,如果当前保卫者为豌豆射手的话,种植下去的一瞬间就会发射一枚攻击对象。
如果不可以种植的话,那么就在当前的保卫者列表中清除这个保卫者。

   # 设置放置位置if press[2] and isSelect:# 检查该围着附近是否有花temp = check(blocks, x, y)# 如果该区域没有花的话就放置保卫者if not temp[0] and temp[1] != -1:isSelect = FalsesunshineNotes -= recordblocks[temp[1]].setIsHave()defendors[-1].append(temp[1])defendors[-1][1] = blocks[temp[1]].leftTopX-10defendors[-1][2] = blocks[temp[1]].leftTopY - 5if(str(type(defendors[-1][0])) == "<class 'model.wdModel.peas'>"):attack = model.attackWay.wdAttack()attack.setSeat(defendors[-1][1], defendors[-1][2])allAttack.append(attack)# attack = model.attackWay.wdAttack(defendors[-1][0])else:# 如果选中区域内有保卫者或者在选区外面的话 注销保卫者isSelect = Falsedefendors.pop()

这是用来检验当前鼠标指定的位置是否中块里面

def check(blocks, x, y):index = -1flag = Truefor i in range(len(blocks)):# 它在哪个方块里面if blocks[i].checkIsInBlock(x, y):index = ibreak# 该方块是否存在保卫者if index != -1 and not blocks[index].getIsHave():flag = not flagreturn [flag, index]

接下来是给监听事件添加一个时间监听事件,每次触发监听事件一次,则会记录事件+300ms
每1,2秒时计算一次保卫者收到的伤害,到60秒之后怪物的生成事件加快,每monster_delay秒后就会自动新建一个怪物对象并添加到怪物列表中去,每隔3.9秒就会在地图上随机生成一个阳光。
除此之外豌豆射手和向日葵会分别在上一次产生产物之后的3.6秒以及3.9秒后再次产生相对应的产物

                if event.type == SUNSHINEEVENT:delay += 300if (sys.maxsize-8000<delay < sys.maxsize-2000):delay = 0#abateHP(defendors, monsterList, defendorsLen, blocks)if(delay%1200):defendorsLen = selectSeat(defendors, isSelect, screen, x, y)lista = abateHP(defendors, monsterList, defendorsLen, blocks)defendors = lista[0]monsterList = lista[1]if (delay == 60000):monster_delay = 2100#每monster_delay ms生成一个怪物if (delay % monster_delay == 0):littleMonster = model.monster.littlemonster(run)littleMonster.setSeat(900, random.randint(1, 5)*70)monsterList.append(littleMonster)# 每隔3900ms生成一个阳光if(delay%3900==0):sunshineList = createSunshine(sunshineList)for i in range(len(defendors)):#根据植物种类添加不同事情的触发时间if (str(type(defendors[i][0])) == "<class 'model.wdModel.peas'>"):#每隔3600ms发送一次攻击if(defendors[i][0].isCreate(3600)):attack = model.attackWay.wdAttack()attack.setSeat(defendors[i][1], defendors[i][2])allAttack.append(attack)elif (str(type(defendors[i][0])) == "<class 'model.wdModel.sunflower'>"):#每隔4200ms产生阳光值if(defendors[i][0].isCreate(4200)):sunshineList=createSunshine(sunshineList,defendors[i][1],defendors[i][2])
通过判断两者是否在图上位置属于重合位置,让植物收到怪物攻击力大小的伤害,如果植物的血量小于等于0,那么将该植物从保卫者列表中删除,并且将当前块设置为暂无种植状态
def abateHP(defendors,monsterList,defendorsLen,blocks):for i in range(defendorsLen):defendorXY = defendors[i][0].getXY()if (defendors[i] != None):for j in range(len(monsterList)):if(monsterList[j]!=None):monsterXY = monsterList[j].getXY()if ((monsterXY[0] - 30 <= defendorXY[0] <= monsterXY[0] + 30) and ((35+monsterXY[1]) < defendorXY[1] <= monsterXY[1]+60)) and (defendors[i][0]!=None):defendors[i][0].abateHP(monsterList[j].getAttack())if (defendors[i][0].getHP() <= 0):#守卫者死后 将那块地还原成可种植的状态if (defendors[i][-1] != None):blocks[defendors[i][-1]].setIsHave()monsterList[j].runStatus(run)defendors[i] = Nonemonsters=[monster for monster in monsterList if monster!=None]temp = [defendor for defendor in defendors if defendor != None]return [temp,monsters]这里为产生阳光的方法,新建一个对象添加到阳光列表中去
def createSunshine(sunshineList,x=-1,y=-1):sunshine = model.wdModel.sunshine()sunshineSeatX = random.randint(50, 900)sunshineSeatY = random.randint(40, 70)#如果位置有被设定到的话if (x != -1):sunshineSeatX = xsunshineSeatY = ysunshineList.append([sunshine, sunshineSeatX, sunshineSeatY])return sunshineList

接着开始清理下阳光列表中被点击的阳光或者过界的阳光
编写方法判断怪物是否越界 如果怪物越界的话,将这个怪物对象变为None 并将血量自减
#怪物是否越界

def checkOutOfBorder(monsterList):global HPfor i in range(len(monsterList)):XY = monsterList[i].getXY()if (XY[0] < 290):monsterList[i] = NoneHP-=1return [monster for monster in monsterList if monster!=None]
将攻击列表全都加载出来显示
def showAttack(allAttack, screen):for i in range(len(allAttack)):if (allAttack[i] != None):allAttack[i].move(screen)

清除下已经越过界面范围内攻击对象

#清空已过边界的攻击者def clearAttack(allAttack):for i in range(len(allAttack)):if(allAttack[i]!=None):XY = allAttack[i].getXY()if (XY[0] > 1000 or XY[1] > 500):allAttack[i] = NoneaA = []for i in range(len(allAttack)):if (allAttack[i] != None):aA.append(allAttack[i])return aA

根据判断当前是否处于购买状态下,判断最后一个保卫者是否跟随鼠标一起移动

def selectSeat(defendors, isSelect, screen, x, y):# 如果在购卡if isSelect:defendors[-1][0].setModel(screen, x - 35, y - 35, defendors[-1][3])return len(defendors) - 1return len(defendors)

加载显示已经购买安置好的守卫者

检测下僵尸是否被保卫者拦截到了,如果被拦截到的话,将僵尸的状态改为attck的状态
#是否被拦截到了

def isStop(defendors, monsterList,defendorsLen):for i in range(defendorsLen):defendorXY = defendors[i][0].getXY()if(defendors[i]!=None):for j in range(len(monsterList)):monsterXY = monsterList[j].getXY()   if ((monsterXY[0] - 30 <= defendorXY[0]<=monsterXY[0] + 30 ) and (35+monsterXY[1])<defendorXY[1]<=monsterXY[1]+60):monsterList[j].attackStatus(attack)显示怪物当前所进行的动作
#显示怪物
def showMonster(monsterList,screen):for i in range(len(monsterList)):if (monsterList[i].move(screen) == -1) :monsterList[i] = Nonereturn [monster for monster in monsterList if monster != None]

显示阳光的掉落,如果阳光过了边界就清除当前阳光对象

def showSunshine(screen, sunshineList):for i in range(len(sunshineList)):if sunshineList[i] != None:if (sunshineList[i][2] > 540):sunshineList[i] = Noneelse:sunshineList[i][0].setModel(screen, sunshineList[i][1], sunshineList[i][2])sunshineList[i][2] += 1return [sunshine for sunshine in sunshineList if sunshine != None]

检查是否攻击对象攻击到僵尸,如果攻击到的话,攻击对象删除,并对怪物的血量进行扣除,扣除大小由攻击对象的攻击所决定,如果在扣除血量之后怪物对象的血量低于等于0,则将该怪物置为死亡状态

#是否攻击
def isAttack(allAttack, monsterList):#遍历怪物列表for i in range(len(monsterList)):#获得当前怪物的XY值monsterXY = monsterList[i].getXY()for j in range(len(allAttack)):if (allAttack[j] != None):#获得当前攻击的XY值attackXY = allAttack[j].getXY()if (monsterXY[1] + 45 <= attackXY[1] <= monsterXY[1] + 70):num = 20#这里出现了一个问题 如果出现了怪物停下的话 左边的举例应该改变num=20if (monsterList[i].getStaus() == 1):num=70if (monsterXY[0] - num <= attackXY[0] <= monsterXY[0] + num):monsterList[i].abateHP(allAttack[j].getAttack())allAttack[j] = None#检查血量,当血量低于等于0时,改变怪物状态if (monsterList[i].getHP() <= 0):monsterList[i].dieStaus(die)breakreturn [monster for monster in monsterList if monster != None]
然后在循环中对上面的方法进行调用# 清理下阳光列表sunshineList = [sunshine for sunshine in sunshineList if sunshine != None]# 检查是否越界monsterList=checkOutOfBorder(monsterList)# 发射攻击showAttack(allAttack, screen)#清除已经过界的攻击allAttack=clearAttack(allAttack)# 显示守卫者defendorsLen = selectSeat(defendors, isSelect, screen, x, y)showDefendor(defendors, defendorsLen)#是否守卫者拦截到了怪物isStop(defendors, monsterList, defendorsLen)#显示怪物monsterList=showMonster(monsterList, screen)# 设置卡牌位置setCard([peasCard, sunflowerCard], screen)# 显示阳光的掉落sunshin = showSunshine(screen, sunshineList)monsterList=isAttack(allAttack, monsterList)# FPSclock.tick(60)

4.实验





5.总结和展望。
这个程序由于找到的素材很少,所以只用了两个保卫者,和一种形态的僵尸,如果只想要加载静态的话,其实素材还是很容易找的,麻烦的找动图,其实主要是受限于我的美工有限所导致的,希望中以后的实践中能多加强下自己的美工能力。
参考文献:
[1]百度百科-植物大战僵尸
[2]百度百科-植物大战僵尸

利用pygame模块设计一个植物大战僵尸游戏初版设定相关推荐

  1. 今天咱们用Python整一个植物大战僵尸游戏 | 附带源码

    <植物大战僵尸>是一款极富策略性的小游戏,可怕的僵尸即将入侵,唯一的防御方式就是栽种植物.此游戏集成了即时战略.塔防御战和卡片收集等要素.游戏的内容就是:玩家控制植物,抵御僵尸的进攻,保护 ...

  2. 利用C语言设计一个猜数字游戏

    问题解决: 设计一个猜数字游戏,通过简单代码,可以空闲时和舍友一起玩. 目录 1.游戏总代码(随便复制,易学习) 2.游戏设计思路解析 3.关于时间戳的知识:(如何让电脑输出一个1~100的随机数)h ...

  3. Python笔记:利用pygame库实现一个俄罗斯方块小游戏(转载)

    import random,time,pygame,sys from pygame.locals import *#导pygame内定义的一些常量 FPS=25#每秒传输帧数(刷新率),此处一秒内在屏 ...

  4. 【Unity3D开发小游戏】《植物大战僵尸游戏》Unity开发教程

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 文章目录 一.前言 二.源码 三.正文 版本 1.主摄像机设置 2.创造草地 ...

  5. python版植物大战僵尸源码_基于python的植物大战僵尸游戏设计与实现.docx

    湖南理工学院毕业设计(论文) PAGE PAGE 1 学 号 毕业设计(论文) 题目:基于python的植物大战僵尸游戏设计与实现 作 者 届 别 届 院 别 信息与通信工程学院 专 业 信息工程 指 ...

  6. 【Python游戏】Python实现一个植物大战僵尸小游戏,非常简单,可以用于做毕业设计哟 | 附源码

    前言 halo,包子们上午好 今天给打击整一个植物大战僵尸 无广告版本 哈哈 说实话,现在的小游戏很多都是有广告,多少有点难受 今天给大家直接安排 相关文件 关注小编,私信小编领取哟! 当然别忘了一件 ...

  7. Py之pygame:有趣好玩——利用pygame库实现一个移动底座弹球的小游戏

    Py之pygame:有趣好玩--利用pygame库实现一个移动底座弹球的小游戏 目录 输出结果 实现代码 输出结果 实现代码 # -*- coding: utf-8 -*-#Py之pygame:有趣好 ...

  8. pip install pygame_使用 Python 和 Pygame 模块构建一个游戏框架!

    这系列的第一篇通过创建一个简单的骰子游戏来探究 Python.现在是来从零制作你自己的游戏的时间. 在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏 ...

  9. pygame为游戏添加背景_万能的Python和Pygame模块构建一个游戏框架

    通过创建一个简单的骰子游戏来探究 Python.现在是来从零制作你自己的游戏的时间. 在我的这系列的第一篇文章中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏.这次,我将展示 ...

最新文章

  1. css绝对定位如何在不同分辨率下的电脑正常显示定位位置?
  2. ubyntu 链接mysql_ubuntu mysql 的安装、配置、简单使用,navicat 连接
  3. C++(九)——职工信息管理系统
  4. 全球及中国彩妆行业渠道模式调研与发展方向分析报告2022版
  5. Linux vi编辑器的使用
  6. GitHub之深入解析脚本·自定义与修改GitHub来更好地为特定的工作流程工作
  7. 常使用的webserver地址
  8. 在openGauss上做开发?这个大赛拿出30万寻找开源的你
  9. fileoutputstream 文件不存在_总结Java中创建并写文件的5种方式
  10. View 绘制体系知识梳理(7) getMeasuredWidth 和 getWidth 的区别
  11. 51nod 更难的矩阵取数问题 + 滚动数组优化
  12. Opencv图像显示
  13. 如何为开源项目做市场
  14. Excel同时冻结多行多列
  15. excel录制宏运行规划求解不能运行的问题
  16. 【MQ】Kafka如何保证幂等性
  17. Adaptive Spectrum Noise Cancellation (自适应频谱噪声消除 ,ASNC)去除强运动伪影
  18. mysql查询条件为不等于某个值时,null 查询不到?
  19. Python-数据类型转换
  20. ESP8266-Arduino编程实例-TMP102数字温度传感器驱动

热门文章

  1. 大数据技术有哪些应该重点学哪些知识
  2. Dynamics 365 设置Postman environment For WebAPI
  3. LGBM函数及参数详解
  4. C语言----- 4/π=1-1/3+1/5-1/7+...,编程计算π的近似值,直到最后一项的值小于10-4次方为止 //输出π的值,并统计累加的项数
  5. 狐狸共享账号服务器查询,卫星节目共享账号,狐狸(动物号)和苹果(水果号)共享节目一览表...
  6. 前端面试必读,助力秋招!
  7. Borax.Lunardate:中国农历日期
  8. 现在电脑有必要安装360或者腾讯电脑管家吗?
  9. 什么是概念模型?概念模型的作用是什么?
  10. 〈原创〉诗如文学之筋骨,琴乃乐中之天籁