python 全解坦克大战 辅助类 附完整代码雏形

得意犹堪夸世俗,诏黄新湿字如鸦。这篇文章主要讲述python 全解坦克大战 辅助类 附完整代码雏形相关的知识,希望能为你提供帮助。
要素材私聊我
突然来的兴趣这个坦克大战是基于 pygame 的,由于没有完整的学过 pygame,之前一直以为 pygame 对于长按键不支持监听,就在几天前我竟然发现了可以,然后就打开了我的世界大门。
由于这个辅助类我随便写了几个小时,还有很多问题,咱们先慢慢来,先做个坦克大战好了。
这是演示效果:

python 全解坦克大战 辅助类 附完整代码雏形

文章图片

目前这辅助类的功能有使用这个辅助类只需要配置信息,自己创建对应对象,主角只需要创建后就可以自动可以移动,敌人也可以自己随机“AI”进行移动,并且子弹自动触碰敌人和墙壁会互相“销毁”完成射击效果。
我们先来看如何使用这辅助类。
使用示例 坦克大战首先创建一个地图:
map_srpirte=[ [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,w,w,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,w,w,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-],]

用个list就是可以了,w就是障碍物,简单吧?
接下来设置地图配置:
mapConf= space:-, w:"./tank/scene/brick.png", height:12, weight:20

space表示空间,w表示障碍物的精灵图,宽高为精灵图大小。
接下来写下宽高内容:
screenW=mapConf[weight]*34 screenH=mapConf[height]*48

接下来创建组,设置屏幕:
pygame.init() screen = pygame.display.set_mode((screenW,screenH)) group_wallt = pygame.sprite.Group() pygame.key.set_repeat(10) framerate = pygame.time.Clock() group_hero = pygame.sprite.Group() enemy_hero = pygame.sprite.Group()

接下来使用我们的自己写的辅助类 ESprite:
sprite_hero = ESprite(screen,group_hero)

接着设置图片与设置组:
sprite_hero.load("./tank/playerTank/tank_T1_2.png",48, 48, 4, 2) up="./tank/bullet/bullet_up.png" down="./tank/bullet/bullet_down.png" left="./tank/bullet/bullet_left.png" right="./tank/bullet/bullet_right.png" group_hero.add(sprite_hero)

使用我们自己写的敌人类循环创建敌人:
enemy_list=[] for v in range(0,20): enemy = Enemy(screen,enemy_hero) enemy.load("./tank/enemyTank/enemy_1_0.png",48, 48, 4, 2) enemy_hero.add(enemy) enemy_list.append(enemy)

接下来使用自己写的精灵类创建不可触碰体,并且把这个对象添加到主角、敌人不可触碰体设置之中:
posx,posy=0,0 wallet=[] for rows in map_srpirte: for v in rows: posx+=24 if v!=mapConf[space]: sprite_wallt = ESprite(screen,group_wallt) sprite_wallt.load(mapConf[w],24, 24, 1, 1,posx,posy) group_wallt.add(sprite_wallt) sprite_hero.setCollision(sprite_wallt)#添加不可触碰 enemy.setCollision(sprite_wallt)#添加不可触碰 print(str(posx)+,+str(posy)) posy+=24 posx=0

最后开启主循环进行监听、刷新即可:
#主循环 while True: print((screenW,screenH)) framerate.tick(30) ticks = pygame.time.get_ticks()for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() elif event.type==pygame.KEYDOWN: print(key down ......) sprite_hero.control.moveControl(event) pos= up_x:18,up_y:-10,down_x:18,down_y:50, left_x:-6,left_y:16,right_x:50,right_y:18,sprite_hero.shoot(up,down,left,right,12,12,1,1,pos,ticks,enemy_list)screen.fill((0,0,100))group_hero.update(ticks) group_hero.draw(screen) enemy_hero.update(ticks) enemy_hero.draw(screen)group_wallt.update(ticks) group_wallt.draw(screen) for v in enemy_list: v.autoMove((screenW,screenH)) pygame.display.update()

一、写个精灵类 1.1 初始化首先创建一个python 文件名为 Etank.py,并在如下引入依赖:
import pygame,random from pygame.locals import *

pygame 、 random 是所需库,pygame.locals import *主要是用来找到KEY。
接下来创建一个类名为 ESprite 继承于pygame 的Sprite 基类:
class ESprite(pygame.sprite.Sprite):

在 ESprite 中给到一个 init 方法:
def __init__(self,screen,group=None):

其中 screen 是需要进行屏幕刷新的屏幕对象,group 是当前类实例化后所对应的组。
接下来在 init 中调用父类初始化:
pygame.sprite.Sprite.__init__(self)

接下来在 init 方法中初始化一些内容:
def __init__(self,screen,group=None):#target是屏幕 pygame.sprite.Sprite.__init__(self) #self.target_surface = target#精灵渲染目标为屏幕 self.screen=screen self.image = None#初始化图片None self.main_image = None#主图片 self.rect = None#需要画图的区域 self.rframe = 0 #图片序列号 行 self.cframe = 0 #图片序列号 列 self.old_frame = -1#老图片序列号 self.frame_width = 1#图片宽 self.frame_height = 1#图片高 self.cols = 1 #列 self.rows = 1 #行 self.last_time = 0 #上次更换时的总帧数,用于判断更换帧 self.X=0 self.Y=0 self.speedX=1 self.speedY=1 self.control=SpriteController(self)#控制初始化 self.group=group self.shootobj=[] self.upImg,self.downImg,self.leftImg,self.rightImg=None,None,None,None self.collisions=[] self.enemy_list=[]

这些初始化后的该类属性之后将会在方法中用到,咱们用到时再做说明。
1.2 添加不可碰撞体在游戏中有很多的不可碰撞体,例如墙壁、障碍物、这些内容对于可活动的游戏觉得是有障碍的,在这里设置一个方法为当前的精灵设置一个不可碰撞体:
#添加不可触碰体 def setCollision(self,collision): self.collisions.append(collision)

1.3加载主图方法在2d游戏中,每一个98%的角色都是需要图片给予对象视觉呈现,此时写一个方法 load 用于加载当前主图内容:
#加载用 def load(self, filename, width, height,rows,cols,posx=100,posy=100): self.main_image = pygame.image.load(filename).convert_alpha()#加载主图 self.frame_width = width#宽高记录 self.frame_height = height self.rect = [posx,posy,width,height]#绘制 self.cols = cols self.rows = rows

该方法的参数说明如下:
  • filename 图片路径
  • width 每个图片宽
  • height 每个图片高
  • rows 行
  • cols 列
  • posx 主图起始绘制位置x
  • posy 主图起始绘制位置y
在以上方法中,通过 filename 加载主图,通过宽高选择主图所绘制的区域,图片示例如下:
python 全解坦克大战 辅助类 附完整代码雏形

文章图片

宽高指的是图片大小的宽高,posx 和 posy 指图片左上角形成的坐标轴的位置,例如图片大小是4848,总长度是宽 482 长是 48*8,那就是8行2列内容,那么 8 就是 rows 2就是参数 cols,posx 和 posy 就是左上角0和0。
1.5刷新方法加载图片后还需要刷新内容,创建一个方法 update,接收两个参数,一个是 current_time 是当前帧数,rate 为刷新时的帧值。
#每次图片动态更新绘制区域 动画播放 def update(self, current_time, rate=60): #当前帧总数如果已经超过了最初的 last_time + 60,那么表示 #已经超过了60帧,那么 frame 图片序列号+1,开始下一张图片if current_time > self.last_time + rate: self.rframe += 1 #图片序列号+1 if self.rframe> self.cols-1:#大于图片最大列就说明进行了一个循环,因为 self.frame 初始值是 0 self.rframe=0 self.last_time = current_time#每次更改图片时就记录更换后的帧if self.rframe != self.old_frame:#新老次序不一 表示更换frame #frame_x绘制矩形的位置x就等于图片数*每个宽度得到x坐标值 frame_x=self.rframe * self.frame_width frame_y=self.cframe * self.frame_height #y值跟随上下左右按键切换图,图片规定同一行一个动作 # 不同按键对应上下左右 # 绘制的区域使用 frame_width frame_height 代替 rect = ( frame_x, frame_y, self.frame_width, self.frame_height ) self.image = self.main_image.subsurface(rect)#选择区域进行图片提取 self.old_frame = self.rframe

以上代码中 if current_time > self.last_time + rate: 表示当前帧是否大于最后一次更换帧数+60,大于则需要刷新,那么则 self.rframe += 1 图片序列号+1,表示更换图片 ,但是不能大于本身图片序列的行和列 if self.rframe> self.cols-1,大于则将 self.rframe=0 。
接着就替换一下 self.last_time = current_time 为最后一次的更换帧数,接下来则替换显示图片的坐标值,也就是 rect 值:
if self.rframe != self.old_frame:#新老次序不一 表示更换frame #frame_x绘制矩形的位置x就等于图片数*每个宽度得到x坐标值 frame_x=self.rframe * self.frame_width frame_y=self.cframe * self.frame_height #y值跟随上下左右按键切换图,图片规定同一行一个动作 # 不同按键对应上下左右 # 绘制的区域使用 frame_width frame_height 代替 rect = ( frame_x, frame_y, self.frame_width, self.frame_height ) self.image = self.main_image.subsurface(rect)#选择区域进行图片提取 self.old_frame = self.rframe

1.6 #创建发射对象子弹上下左右的主图不一样,朝向不一,如图所示:
python 全解坦克大战 辅助类 附完整代码雏形

文章图片

此时编写一个方法 shoot:
def shoot(self,upImg,downImg,leftImg,rightImg,width,height,rows,cols,pos,ticks=None,enemy_list=[]): self.enemy_list=enemy_list if self.control.isShoot==True: #创建发射物 shootobj=ESprite(self.screen) self.group.add(shootobj) posx,posy=0,0 sprite_img= if self.control.shoot_direction==self.control.direction_UP: posx=self.rect[0]+pos[up_x] posy=self.rect[1]+pos[up_y]sprite_img=upImg elif self.control.shoot_direction==self.control.direction_DOWN: posx=self.rect[0]+pos[down_x] posy=self.rect[1]+pos[down_y]sprite_img=downImg elif self.control.shoot_direction==self.control.direction_LEFT: posx=self.rect[0]+pos[left_x] posy=self.rect[1]+pos[left_y]sprite_img=leftImg elif self.control.shoot_direction==self.control.direction_RIGHT: posx=self.rect[0]+pos[right_x] posy=self.rect[1]+pos[right_y] sprite_img=rightImgshootobj.load(sprite_img, width, height, rows, cols,posx=posx,posy=posy) self.shootobj.append(obj:shootobj,shoot_direction:self.control.shoot_direction) print(SHOOT!!!!!!!!!!...) #print(self.shootobj.rect) print(len(self.shootobj)) #加组后一定要刷新,不然会读不到 surface.image 也就是没有在页面之上 self.group.update(ticks) self.group.draw(self.screen)#数组中已经创建的继续移动 for s in self.shootobj: if s[obj]!=None: SPEEDX=self.control.shoot_SPEEDX SPEEDY=self.control.shoot_SPEEDY if s[shoot_direction]==self.control.direction_UP: print(direction_UP...) SPEEDX=0 SPEEDY=-SPEEDY elif s[shoot_direction]==self.control.direction_DOWN: print(direction_DOWN...) SPEEDX=0 SPEEDY=+SPEEDY elif s[shoot_direction]==self.control.direction_LEFT: print(direction_LEFT...) SPEEDX=-SPEEDX SPEEDY=0 elif s[shoot_direction]==self.control.direction_RIGHT: print(direction_RIGHT...) SPEEDX=SPEEDX SPEEDY=0 s[obj].rect=s[obj].rect[0]+SPEEDX,s[obj].rect[1]+SPEEDY,s[obj].frame_width,s[obj].frame_heightif self.collisions!=[]: for index, v in enumerate(self.collisions): if s[obj]!=None: if pygame.sprite.collide_mask(s[obj],v): s[obj].rect=-1000,-1000,s[obj].frame_width,s[obj].frame_height s[obj].kill() self.collisions[index].rect=-1000,-1000,self.collisions[index].rect[2],self.collisions[index].rect[3] self.collisions[index].kill() #s[obj]=False #s[obj]=None if self.enemy_list!=[]: for index, v in enumerate(self.enemy_list): if s[obj]!=None: if pygame.sprite.collide_mask(s[obj],v): s[obj].rect=-1000,-1000,s[obj].frame_width,s[obj].frame_height s[obj].kill() self.enemy_list[index].rect=-1000,-1000,self.enemy_list[index].rect[2],self.enemy_list[index].rect[3] self.enemy_list[index].kill()

以上方法中参数 upImg,downImg,leftImg,rightImg 为上下左右子弹的图片,width,height,rows,cols,pos,ticks=None,enemy_list=[] 一次是宽高,行列和ticks 刷新帧,enemy_list 敌人。
因为之后还需要检测敌人碰撞后销毁。
在该函数中第一行打码是 self.enemy_list=enemy_list 表示设置当前的敌人列表。
接着 if self.control.isShoot==True: 这个判断表示是否按发射键(之后会在控制中进行讲解);
按下发射键后就开始创建发射对象,同样这个对象是ESprite 对象:
#创建发射物 shootobj=ESprite(self.screen) self.group.add(shootobj) posx,posy=0,0 sprite_img=

接下来判断此时朝向,(根据之后会有一个控制方法,检测朝向)不同朝向的方位创建不同朝向的子弹:
if self.control.shoot_direction==self.control.direction_UP: posx=self.rect[0]+pos[up_x] posy=self.rect[1]+pos[up_y]sprite_img=upImg elif self.control.shoot_direction==self.control.direction_DOWN: posx=self.rect[0]+pos[down_x] posy=self.rect[1]+pos[down_y]sprite_img=downImg elif self.control.shoot_direction==self.control.direction_LEFT: posx=self.rect[0]+pos[left_x] posy=self.rect[1]+pos[left_y]sprite_img=leftImg elif self.control.shoot_direction==self.control.direction_RIGHT: posx=self.rect[0]+pos[right_x] posy=self.rect[1]+pos[right_y] sprite_img=rightImg

接着加载图片,并且记录方向,如果不记录方向,之后使用同一个方法时将会与自身按键操作重合,会意外的控制子弹前进路线:
shootobj.load(sprite_img, width, height, rows, cols,posx=posx,posy=posy) self.shootobj.append(obj:shootobj,shoot_direction:self.control.shoot_direction)

接下来开始检测碰撞,检测碰撞前需要在屏幕刷新当前的sprite,因为检测膨胀是需要判断该精灵是否在屏幕之上:
self.group.update(ticks) self.group.draw(self.screen)

加下来创建一个循环:
#数组中已经创建的继续移动 for s in self.shootobj: if s[obj]!=None:

这个循环遍历子弹是否与某些物体发生碰撞,并且进行位置移动。首先编写位置移动的内容:
SPEEDX=self.control.shoot_SPEEDX SPEEDY=self.control.shoot_SPEEDY if s[shoot_direction]==self.control.direction_UP: print(direction_UP...) SPEEDX=0 SPEEDY=-SPEEDY elif s[shoot_direction]==self.control.direction_DOWN: print(direction_DOWN...) SPEEDX=0 SPEEDY=+SPEEDY elif s[shoot_direction]==self.control.direction_LEFT: print(direction_LEFT...) SPEEDX=-SPEEDX SPEEDY=0 elif s[shoot_direction]==self.control.direction_RIGHT: print(direction_RIGHT...) SPEEDX=SPEEDX SPEEDY=0 s[obj].rect=s[obj].rect[0]+SPEEDX,s[obj].rect[1]+SPEEDY,s[obj].frame_width,s[obj].frame_height

判断刚刚所记录的方向,给予不同位置移动的坐标。
接着判断是否发生碰撞:
if self.collisions!=[]: for index, v in enumerate(self.collisions): if s[obj]!=None: if pygame.sprite.collide_mask(s[obj],v): s[obj].rect=-1000,-1000,s[obj].frame_width,s[obj].frame_height s[obj].kill() self.collisions[index].rect=-1000,-1000,self.collisions[index].rect[2],self.collisions[index].rect[3] self.collisions[index].kill()

只要设置了 collisions 阻碍物,那么就遍历阻碍物是否与子弹发生碰撞,如果发生膨胀首先将该物体移动到屏幕之外调用 kill() 方法对其进行销毁。这样就实现了子弹设计到物体,物体和子弹都同时消失。
最后判断子弹和敌人是否发生碰撞,遍历敌人:
if self.enemy_list!=[]: for index, v in enumerate(self.enemy_list): if s[obj]!=None: if pygame.sprite.collide_mask(s[obj],v): s[obj].rect=-1000,-1000,s[obj].frame_width,s[obj].frame_height s[obj].kill() self.enemy_list[index].rect=-1000,-1000,self.enemy_list[index].rect[2],self.enemy_list[index].rect[3] self.enemy_list[index].kill()

实现方法与墙体类似。
最后在遍历之外加一个设计关闭即可,因为按一次就发射一个子弹:
self.control.isShoot=False#创建完一个后又关闭

二、写个精灵控制类首先创建一个精灵控制类和初始化方法:
#精灵控制类 class SpriteController(): def __init__(self,sprite): self.sprite=sprite self.direction=None self.shoot_direction=1 self.direction_UP=1 self.direction_DOWN=2 self.direction_LEFT=3 self.direction_RIGHT=4 self.shoot_SPEEDX=10 self.shoot_SPEEDY=10 self.isShoot=False

接着编写精灵控制响应的方法:
#移动控制 def moveControl(self,event): stepX=self.sprite.speedX stepY=self.sprite.speedY #if self.isShoot!=True: if event.key == pygame.K_RIGHT: stepY=0 stepX=stepX self.sprite.cframe=3 self.shoot_direction=4 elif event.key == pygame.K_LEFT: stepX=-stepX stepY=0 self.sprite.cframe=2 self.shoot_direction=3 elif event.key == pygame.K_UP: stepY=-stepY stepX=0 self.sprite.cframe=0 self.shoot_direction=1 elif event.key == pygame.K_DOWN: stepY=stepY stepX=0 self.sprite.cframe=1 self.shoot_direction=2 elif event.key == pygame.K_SPACE: self.isShoot=True stepX,stepY=0,0 self.sprite.rect=self.sprite.rect[0]+stepX,self.sprite.rect[1]+stepY,self.sprite.frame_width,self.sprite.frame_height if self.sprite.collisions!=[]: for v in self.sprite.collisions: result = pygame.sprite.collide_mask(self.sprite,v) while result: #bug 墙壁反弹错位 unstepX,unstepY=stepX,stepY if unstepX!=0: if unstepX> 0: unstepX=+10 else: unstepX=-10 if unstepY!=0: if unstepY> 0: unstepY=+10 else: unstepY=-10result = pygame.sprite.collide_mask(self.sprite,v) #for vi in range(0,20): self.sprite.rect=self.sprite.rect[0]-stepX,self.sprite.rect[1]-stepY,self.sprite.frame_width,self.sprite.frame_height print ("Collision occurred")

moveControl 方法接收1个参数event,判断按下键的内容,根据不同按键响应不同的移动参数,其他内容都是基础内容,主要是对于阻碍物的阻碍效果:
if self.sprite.collisions!=[]: for v in self.sprite.collisions: result = pygame.sprite.collide_mask(self.sprite,v) while result: #bug 墙壁反弹错位 unstepX,unstepY=stepX,stepY if unstepX!=0: if unstepX> 0: unstepX=+10 else: unstepX=-10 if unstepY!=0: if unstepY> 0: unstepY=+10 else: unstepY=-10result = pygame.sprite.collide_mask(self.sprite,v) #for vi in range(0,20): self.sprite.rect=self.sprite.rect[0]-stepX,self.sprite.rect[1]-stepY,self.sprite.frame_width,self.sprite.frame_height print ("Collision occurred")

遍历后如果发生碰撞直接给予回退:
self.sprite.rect=self.sprite.rect[0]-stepX,self.sprite.rect[1]-stepY,self.sprite.frame_width,self.sprite.frame_height

三、敌人AI类敌人类属于 ESprite 类,首先创建一个类及初始化方法:
#敌人 class Enemy(ESprite): def __init__(self, screen, group=None): super().__init__(screen, group=group) self.screen=screen self.group=group #self.AIControl=SpriteController() self.movieStep=0 self.stepY,self.stepX=0,10

movieStep 为默认自动运行次数,self.stepY,self.stepX=0,10 为默认行走值。
接下来编写自动运行方法:
def autoMove(self,Size): self.group.update(ticks) self.group.draw(self.screen) if self.movieStep==0: random.seed(random.randint(0,100)) d=random.randint(1,16) if d> 0 and d< 5:#上 self.stepY=-10 self.stepX=0 elif d> 4 and d< 9:#下 self.stepY=10 self.stepX=0 elif d> 8 and d< 13:#左 self.stepX=-10 self.stepY=0 elif d> 12 and d< 17:#右 self.stepX=10 self.stepY=0 self.movieStep=20 if self.movieStep!=0: if (self.rect[0],self.rect[1])> (60,60) and (self.rect[0],self.rect[1]) < (Size[0]-60,Size[1]-60): self.rect=self.rect[0]+self.stepX,self.rect[1]+self.stepY,self.frame_width,self.frame_height self.movieStep-=1if self.collisions!=[]: for v in self.collisions: #self.group.update(ticks) #self.group.draw(self.screen) result = pygame.sprite.collide_mask(self,v) while result: #bug 墙壁反弹错位 unstepX,unstepY=self.stepX,self.stepY if unstepX!=0: if unstepX> 0: unstepX=+10 else: unstepX=-10 if unstepY!=0: if unstepY> 0: unstepY=+10 else: unstepY=-10result = pygame.sprite.collide_mask(self,v) #for vi in range(0,20): self.rect=self.rect[0]-self.stepX,self.rect[1]-self.stepY,self.frame_width,self.frame_height print ("Collision occurred")

以上代码中以下代码表示该AI随机上下左右:
if self.movieStep==0: random.seed(random.randint(0,100)) d=random.randint(1,16) if d> 0 and d< 5:#上 self.stepY=-10 self.stepX=0 elif d> 4 and d< 9:#下 self.stepY=10 self.stepX=0 elif d> 8 and d< 13:#左 self.stepX=-10 self.stepY=0 elif d> 12 and d< 17:#右 self.stepX=10 self.stepY=0 self.movieStep=20 if self.movieStep!=0: if (self.rect[0],self.rect[1])> (60,60) and (self.rect[0],self.rect[1]) < (Size[0]-60,Size[1]-60): self.rect=self.rect[0]+self.stepX,self.rect[1]+self.stepY,self.frame_width,self.frame_height self.movieStep-=1

不同方向他有不同值对应,self.movieStep运行次数为0则重新置于20,若不等于0则自动运行,只需要设置对应的 rect 即可。
【python 全解坦克大战 辅助类 附完整代码雏形】接着就是判断是否碰到障碍物,实现与控制方法一致:
if self.collisions!=[]: for v in self.collisions: #self.group.update(ticks) #self.group.draw(self.screen) result = pygame.sprite.collide_mask(self,v) while result: #bug 墙壁反弹错位 unstepX,unstepY=self.stepX,self.stepY if unstepX!=0: if unstepX> 0: unstepX=+10 else: unstepX=-10 if unstepY!=0: if unstepY> 0: unstepY=+10 else: unstepY=-10result = pygame.sprite.collide_mask(self,v) #for vi in range(0,20): self.rect=self.rect[0]-self.stepX,self.rect[1]-self.stepY,self.frame_width,self.frame_height print ("Collision occurred")

完整代码
import pygame,random from pygame.locals import *class MySprite(pygame.sprite.Sprite): def __init__(self,screen,group=None):#target是屏幕 pygame.sprite.Sprite.__init__(self) #self.target_surface = target#精灵渲染目标为屏幕 self.screen=screen self.image = None#初始化图片None self.main_image = None#主图片 self.rect = None#需要画图的区域 self.rframe = 0 #图片序列号 行 self.cframe = 0 #图片序列号 列 self.old_frame = -1#老图片序列号 self.frame_width = 1#图片宽 self.frame_height = 1#图片高 self.cols = 1 #列 self.rows = 1 #行 self.last_time = 0 #上次更换时的总帧数,用于判断更换帧 self.X=0 self.Y=0 self.speedX=1 self.speedY=1 self.control=SpriteController(self)#控制初始化 self.group=group self.shootobj=[] self.upImg,self.downImg,self.leftImg,self.rightImg=None,None,None,None self.collisions=[] self.enemy_list=[]#添加不可触碰体 def setCollision(self,collision): self.collisions.append(collision) #加载用 def load(self, filename, width, height,rows,cols,posx=100,posy=100): self.main_image = pygame.image.load(filename).convert_alpha()#加载主图 self.frame_width = width#宽高记录 self.frame_height = height self.rect = [posx,posy,width,height]#绘制 self.cols = cols self.rows = rows#每次图片动态更新绘制区域 动画播放 def update(self, current_time, rate=60): #当前帧总数如果已经超过了最初的 last_time + 60,那么表示 #已经超过了60帧,那么 frame 图片序列号+1,开始下一张图片if current_time > self.last_time + rate: self.rframe += 1 #图片序列号+1 if self.rframe> self.cols-1:#大于图片最大列就说明进行了一个循环,因为 self.frame 初始值是 0 self.rframe=0 self.last_time = current_time#每次更改图片时就记录更换后的帧if self.rframe != self.old_frame:#新老次序不一 表示更换frame #frame_x绘制矩形的位置x就等于图片数*每个宽度得到x坐标值 frame_x=self.rframe * self.frame_width frame_y=self.cframe * self.frame_height #y值跟随上下左右按键切换图,图片规定同一行一个动作 # 不同按键对应上下左右 # 绘制的区域使用 frame_width frame_height 代替 rect = ( frame_x, frame_y, self.frame_width, self.frame_height ) self.image = self.main_image.subsurface(rect)#选择区域进行图片提取 self.old_frame = self.rframe#设置上下左右发射对象 def setShootSprite(self,up,down,left,right,width,height,rows,cols): self.upImg=up self.downImg=down self.leftImg=left self.rightImg=right self.swidth=width self.sheight=height self.srows=rows self.scols=cols#创建发射对象 def shoot(self,upImg,downImg,leftImg,rightImg,width,height,rows,cols,pos,ticks=None,enemy_list=[]): self.enemy_list=enemy_list if self.control.isShoot==True: #创建发射物 shootobj=MySprite(self.screen) self.group.add(shootobj) posx,posy=0,0 sprite_img= if self.control.shoot_direction==self.control.direction_UP: posx=self.rect[0]+pos[up_x] posy=self.rect[1]+pos[up_y]sprite_img=upImg elif self.control.shoot_direction==self.control.direction_DOWN: posx=self.rect[0]+pos[down_x] posy=self.rect[1]+pos[down_y]sprite_img=downImg elif self.control.shoot_direction==self.control.direction_LEFT: posx=self.rect[0]+pos[left_x] posy=self.rect[1]+pos[left_y]sprite_img=leftImg elif self.control.shoot_direction==self.control.direction_RIGHT: posx=self.rect[0]+pos[right_x] posy=self.rect[1]+pos[right_y] sprite_img=rightImgshootobj.load(sprite_img, width, height, rows, cols,posx=posx,posy=posy) self.shootobj.append(obj:shootobj,shoot_direction:self.control.shoot_direction) print(SHOOT!!!!!!!!!!...) #print(self.shootobj.rect) print(len(self.shootobj)) #加组后一定要刷新,不然会读不到 surface.image 也就是没有在页面之上 self.group.update(ticks) self.group.draw(self.screen)#数组中已经创建的继续移动 for s in self.shootobj: if s[obj]!=None: SPEEDX=self.control.shoot_SPEEDX SPEEDY=self.control.shoot_SPEEDY if s[shoot_direction]==self.control.direction_UP: print(direction_UP...) SPEEDX=0 SPEEDY=-SPEEDY elif s[shoot_direction]==self.control.direction_DOWN: print(direction_DOWN...) SPEEDX=0 SPEEDY=+SPEEDY elif s[shoot_direction]==self.control.direction_LEFT: print(direction_LEFT...) SPEEDX=-SPEEDX SPEEDY=0 elif s[shoot_direction]==self.control.direction_RIGHT: print(direction_RIGHT...) SPEEDX=SPEEDX SPEEDY=0 s[obj].rect=s[obj].rect[0]+SPEEDX,s[obj].rect[1]+SPEEDY,s[obj].frame_width,s[obj].frame_heightif self.collisions!=[]: for index, v in enumerate(self.collisions): if s[obj]!=None: if pygame.sprite.collide_mask(s[obj],v): s[obj].rect=-1000,-1000,s[obj].frame_width,s[obj].frame_height s[obj].kill() self.collisions[index].rect=-1000,-1000,self.collisions[index].rect[2],self.collisions[index].rect[3] self.collisions[index].kill() #s[obj]=False #s[obj]=None if self.enemy_list!=[]: for index, v in enumerate(self.enemy_list): if s[obj]!=None: if pygame.sprite.collide_mask(s[obj],v): s[obj].rect=-1000,-1000,s[obj].frame_width,s[obj].frame_height s[obj].kill() self.enemy_list[index].rect=-1000,-1000,self.enemy_list[index].rect[2],self.enemy_list[index].rect[3] self.enemy_list[index].kill()self.control.isShoot=False#创建完一个后又关闭#精灵控制类 class SpriteController(): def __init__(self,sprite): self.sprite=sprite self.direction=None self.shoot_direction=1 self.direction_UP=1 self.direction_DOWN=2 self.direction_LEFT=3 self.direction_RIGHT=4 self.shoot_SPEEDX=10 self.shoot_SPEEDY=10 self.isShoot=False#移动控制 def moveControl(self,event): stepX=self.sprite.speedX stepY=self.sprite.speedY #if self.isShoot!=True: if event.key == pygame.K_RIGHT: stepY=0 stepX=stepX self.sprite.cframe=3 self.shoot_direction=4 elif event.key == pygame.K_LEFT: stepX=-stepX stepY=0 self.sprite.cframe=2 self.shoot_direction=3 elif event.key == pygame.K_UP: stepY=-stepY stepX=0 self.sprite.cframe=0 self.shoot_direction=1 elif event.key == pygame.K_DOWN: stepY=stepY stepX=0 self.sprite.cframe=1 self.shoot_direction=2 elif event.key == pygame.K_SPACE: self.isShoot=True stepX,stepY=0,0 self.sprite.rect=self.sprite.rect[0]+stepX,self.sprite.rect[1]+stepY,self.sprite.frame_width,self.sprite.frame_height if self.sprite.collisions!=[]: for v in self.sprite.collisions: result = pygame.sprite.collide_mask(self.sprite,v) while result: #bug 墙壁反弹错位 unstepX,unstepY=stepX,stepY if unstepX!=0: if unstepX> 0: unstepX=+10 else: unstepX=-10 if unstepY!=0: if unstepY> 0: unstepY=+10 else: unstepY=-10result = pygame.sprite.collide_mask(self.sprite,v) #for vi in range(0,20): self.sprite.rect=self.sprite.rect[0]-stepX,self.sprite.rect[1]-stepY,self.sprite.frame_width,self.sprite.frame_height print ("Collision occurred")#敌人 class Enemy(MySprite): def __init__(self, screen, group=None): super().__init__(screen, group=group) self.screen=screen self.group=group #self.AIControl=SpriteController() self.movieStep=0 self.stepY,self.stepX=0,10 def autoMove(self,Size): self.group.update(ticks) self.group.draw(self.screen) if self.movieStep==0: random.seed(random.randint(0,100)) d=random.randint(1,16) if d> 0 and d< 5:#上 self.stepY=-10 self.stepX=0 elif d> 4 and d< 9:#下 self.stepY=10 self.stepX=0 elif d> 8 and d< 13:#左 self.stepX=-10 self.stepY=0 elif d> 12 and d< 17:#右 self.stepX=10 self.stepY=0 self.movieStep=20 if self.movieStep!=0: if (self.rect[0],self.rect[1])> (60,60) and (self.rect[0],self.rect[1]) < (Size[0]-60,Size[1]-60): self.rect=self.rect[0]+self.stepX,self.rect[1]+self.stepY,self.frame_width,self.frame_height self.movieStep-=1if self.collisions!=[]: for v in self.collisions: #self.group.update(ticks) #self.group.draw(self.screen) result = pygame.sprite.collide_mask(self,v) while result: #bug 墙壁反弹错位 unstepX,unstepY=self.stepX,self.stepY if unstepX!=0: if unstepX> 0: unstepX=+10 else: unstepX=-10 if unstepY!=0: if unstepY> 0: unstepY=+10 else: unstepY=-10result = pygame.sprite.collide_mask(self,v) #for vi in range(0,20): self.rect=self.rect[0]-self.stepX,self.rect[1]-self.stepY,self.frame_width,self.frame_height print ("Collision occurred")map_srpirte=[ [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,w,w,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,w,w,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,w,w,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-], [-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-],] mapConf= space:-, w:"./tank/scene/brick.png", height:12, weight:20#map_srpirteConf=[w,24,24,1,1] screenW=mapConf[weight]*34 screenH=mapConf[height]*48pygame.init() screen = pygame.display.set_mode((screenW,screenH)) group_wallt = pygame.sprite.Group() pygame.key.set_repeat(10) framerate = pygame.time.Clock()group_hero = pygame.sprite.Group() enemy_hero = pygame.sprite.Group() sprite_hero = MySprite(screen,group_hero)sprite_hero.load("./tank/playerTank/tank_T1_2.png",48, 48, 4, 2) up="./tank/bullet/bullet_up.png" down="./tank/bullet/bullet_down.png" left="./tank/bullet/bullet_left.png" right="./tank/bullet/bullet_right.png" #sprite_hero.setShootSprite() group_hero.add(sprite_hero)enemy_list=[] for v in range(0,20): enemy = Enemy(screen,enemy_hero) enemy.load("./tank/enemyTank/enemy_1_0.png",48, 48, 4, 2) enemy_hero.add(enemy) enemy_list.append(enemy)posx,posy=0,0 wallet=[] for rows in map_srpirte: for v in rows: posx+=24 if v!=mapConf[space]: sprite_wallt = MySprite(screen,group_wallt) sprite_wallt.load(mapConf[w],24, 24, 1, 1,posx,posy) group_wallt.add(sprite_wallt) sprite_hero.setCollision(sprite_wallt) enemy.setCollision(sprite_wallt) print(str(posx)+,+str(posy)) posy+=24 posx=0#sprite_wallt = MySprite(screen,group_wallt) #sprite_wallt.load(mapConf[w],24, 24, 1, 1,posx,posy) #group_wallt.add(sprite_wallt) #主循环 while True: print((screenW,screenH)) framerate.tick(30) ticks = pygame.time.get_ticks()for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() elif event.type==pygame.KEYDOWN: print(key down ......) sprite_hero.control.moveControl(event) pos= up_x:18,up_y:-10,down_x:18,down_y:50, left_x:-6,left_y:16,right_x:50,right_y:18,sprite_hero.shoot(up,down,left,right,12,12,1,1,pos,ticks,enemy_list)screen.fill((0,0,100))group_hero.update(ticks) group_hero.draw(screen) enemy_hero.update(ticks) enemy_hero.draw(screen)group_wallt.update(ticks) group_wallt.draw(screen) for v in enemy_list: v.autoMove((screenW,screenH)) pygame.display.update()


    推荐阅读