底部有完整代碼想試驗的可以去看一下,
然后推一下我一篇文章《外國人最喜歡吃的中國美食是醬油?python資料分析》
想要素材看評論區
突然來的興趣
這個坦克大戰是基于 pygame 的,由于沒有完整的學過 pygame,之前一直以為 pygame 對于長按鍵不支持監聽,就在幾天前我竟然發現了可以,然后就打開了我的世界大門,
由于這個輔助類我隨便寫了幾個小時,還有很多問題,咱們先慢慢來,先做個坦克大戰好了,
這是演示效果:

目前這輔助類的功能有
使用這個輔助類只需要配置資訊,自己創建對應物件,主角只需要創建后就可以自動可以移動,敵人也可以自己隨機“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 加載主圖,通過寬高選擇主圖所繪制的區域,圖片示例如下:

寬高指的是圖片大小的寬高,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 #創建發射物件
子彈上下左右的主圖不一樣,朝向不一,如圖所示:

此時撰寫一個方法 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=rightImg
shootobj.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_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()
#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=-10
result = 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=-10
result = 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-=1
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=-10
result = 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 即可,
接著就是判斷是否碰到障礙物,實作與控制方法一致:
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=-10
result = 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=rightImg
shootobj.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_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()
#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=-10
result = 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-=1
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=-10
result = 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']*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()
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()
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/396340.html
標籤:其他
上一篇:C語言—貪吃蛇雙人對戰
