前言
昨日推箱子游戲的碰撞檢測中,我忽略了箱子互相挨着的情況。修改的方法很簡答:
if len(pygame.sprite.groupcollide(hero_group,box_group,False,False)) > 0:
self.rect.y -= 50
if len(pygame.sprite.groupcollide(hero_group,wall_group,False,False)) + len(pygame.sprite.groupcollide(hero_group,box_group,False,False))> 0:
self.rect.y += 50
self.rect.y += 50
加上檢測是否有箱子即可。
在我看來,黑馬源代碼有一個我容易忽略的點,其編寫敵機、英雄、背景和子彈類多半使用的是私有成員函數。
def __create_sprites(self):
之前的我在直接複製改代碼的時候忽略了這個問題,因爲無法訪問成員函數這個問題瞎忙活了半晌,最後找到前面的下劃線時哭笑不得。
英雄和子彈
英雄擁有的額外屬性:水平移動,定時發射子彈。於update函數中添加位移限制,利用get.pressed函數,省去反覆摁鍵的繁贅。
def __event_handler(self):
key_press = pygame.key.get_pressed()
if key_press[pygame.K_RIGHT]:
self.hero.speed = 3
elif key_press[pygame.K_LEFT]:
self.hero.speed = -3
else:
self.hero.speed = 0
class Hero(GameSprite):
def __init__(self):
#調用父類方法.設置圖片
super().__init__("./image/me1.png",0)
#設置英雄的初始位置
self.rect.centerx = SCREEN_RECT.centerx
self.rect.bottom = SCREEN_RECT.bottom - 120
#創建子彈精靈組
self.bullets = pygame.sprite.Group()
def update(self, *args):
#水平方向移動 不調用父類方法 自己適配子類方法
self.rect.x += self.speed
#判斷是否移出屏幕
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.right > SCREEN_RECT.right:
self.rect.right = SCREEN_RECT.right
def fire(self):
print("發射子彈")
#一次發射三個子彈
for i in (0,1,2):
#創建子彈精靈
bullet = Bullet()
#設置精靈的位置
bullet.rect.bottom = self.rect.y - i*20
bullet.rect.centerx = self.rect.centerx
#添加到子彈精靈組
self.bullets.add(bullet)
值得一提的是定時效果:英雄於規定時間產生三發子彈,但是pygame.USEREVENT 於敵機隨機產生已被使用。先關注函數的返回值
screen = pygame.display.set_mode(SCREEN_RECT.size)
pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
while 1:
for event in pygame.event.get():
if event.type == CREATE_ENEMY_EVENT:
print(CREATE_ENEMY_EVENT)
以下爲輸出板塊
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
24
24
24
24
24
24
24
原來返回值是個int,那麼直接定義新事件:HERO_FIRE_EVENT = pygame.USEREVENT + 1,困難迎刃而解。
class Bullet(GameSprite):
def __init__(self):
super().__init__("./image/bullet1.png",-2)
def update(self, *args):
super().update()
if self.rect.bottom < 0 :
#調用kill方法移出精靈組
self.kill()
def __del__(self):
print("子彈被銷燬")
定時事件被觸發–英雄調用發射子彈函數,循環三次–子彈裝入子彈精靈組–子彈飛出屏幕將會調用私有函數del。
碰撞檢測
飛機大戰有英雄飛機與敵機、敵機與子彈兩種類型碰撞。
def __check_collide(self):
#子彈摧毀敵機
pygame.sprite.groupcollide(self.hero.bullets,self.enemy_group,True,True)
#敵機撞毀英雄
enemies = pygame.sprite.spritecollide(self.hero,self.enemy_group,True)
if len(enemies) > 0 :
self.hero.kill()
PlaneGame.__game_over()
groupcollide(精靈組1,精靈組2,精靈組1碰撞後是否被銷燬,精靈組2碰撞後是否被銷燬,(檢測碰撞方式,默認rect))
子彈與敵機碰撞雙雙消失(若添加積分制度,加個判斷即可)。當敵機與英雄相撞,遊戲結束。
關於飛機炸燬的動畫效果。
我個人是這麼想的:爆炸事件+標誌位----碰撞檢測----飛機炸燬調用del----del函數傳遞出rect並更改標誌位----爆炸精靈組開始繪製圖形。
但如果能夠讓飛機延緩銷燬,就是碰撞設置爲False,然後調用碰撞敵機的函數,更改image似乎也可以。
源代碼
import pygame
import random
#屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,480,720)
#刷新的幀率
FRAME_PER_SEC = 60
#創建敵機的定時器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT #用戶事件的常量
#英雄發射子彈事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1
class GameSprite(pygame.sprite.Sprite): #(繼承父類) 其中sprite是模塊 Sprite是類名稱
'''飛機大戰遊戲精靈'''
#構造函數/初始化
def __init__(self,image_name,speed = 1):
#調用父類初始化方法
super().__init__()
#定義對象的屬性
self.image = pygame.image.load(image_name)
self.rect = self.image.get_rect() #圖像的屬性
self.speed = speed
#print("init已被調用")
def update(self, *args): #*args 一定保留
#屏幕上垂直移動
self.rect.y += self.speed
#繼承了父類 GameSprite 擁有 讀圖 縱向移動
class Background(GameSprite):
"""遊戲背景精靈"""
def __init__(self,is_alt=False):
#調用父類方法
super().__init__("./image/background.png") #往父類初始化傳入參數
#判斷交替圖像
if is_alt:
self.rect.y = -self.rect.height
def update(self, *args):
#1.調用父類的方法實現
super().update()
#2.判斷是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
self.rect.y = -self.rect.height
#敵機類
class Enemy(GameSprite):
def __init__(self):
#調用父類 指定圖片
super().__init__("./image/enemy1.png")
#指定隨機速度
self.speed = random.randint(1,3)
#敵機初始位置
#垂直方向
self.rect.bottom = 0
#水平方向
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self, *args):
super().update()
if self.rect.y >= SCREEN_RECT.height:
#將精靈從所有的組移出 並被del調用
self.kill()
#print("飛出了屏幕")
#銷燬時調用敵機掛了
def __del__(self):
if self.rect.bottom < 720:
self.image = pygame.image.load("./image/enemy1_down1")
self.rect = self.image.get_rect() # 圖像的屬性
#print("敵機掛了 %s" %self.rect)
class Hero(GameSprite):
def __init__(self):
#調用父類方法.設置圖片
super().__init__("./image/me1.png",0)
#設置英雄的初始位置
self.rect.centerx = SCREEN_RECT.centerx
self.rect.bottom = SCREEN_RECT.bottom - 120
#創建子彈精靈組
self.bullets = pygame.sprite.Group()
def update(self, *args):
#水平方向移動 不調用父類方法 自己適配子類方法
self.rect.x += self.speed
#判斷是否移出屏幕
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.right > SCREEN_RECT.right:
self.rect.right = SCREEN_RECT.right
def fire(self):
print("發射子彈")
#一次發射三個子彈
for i in (0,1,2):
#創建子彈精靈
bullet = Bullet()
#設置精靈的位置
bullet.rect.bottom = self.rect.y - i*20
bullet.rect.centerx = self.rect.centerx
#添加到子彈精靈組
self.bullets.add(bullet)
class Bullet(GameSprite):
def __init__(self):
super().__init__("./image/bullet1.png",-2)
def update(self, *args):
super().update()
if self.rect.bottom < 0 :
#調用kill方法移出精靈組
self.kill()
def __del__(self):
print("子彈被銷燬")
class PlaneGame(object):
"""飛機大戰主遊戲"""
def __init__(self):
print("遊戲初始化")
#1.創建遊戲的窗口
self.screen = pygame.display.set_mode(SCREEN_RECT.size) #常量 > 700
#2.創建遊戲的時鐘
self.clock = pygame.time.Clock()
#3.調用私有方法,精靈和精靈組的創建
self.__create_sprites()
#4.設定定時器實踐 - 創建敵機 1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000) #時間一到,觸發名叫CREAT_ENEMY_EVENT的事件
pygame.time.set_timer(HERO_FIRE_EVENT, 500) # 時間一到,觸發名叫CREAT_ENEMY_EVENT的事件
#私有方法 雙下劃線 __
#創建精靈
def __create_sprites(self):
#背景精靈組
bg1 = Background()
bg2 = Background(1)
self.back_group = pygame.sprite.Group(bg1,bg2)
#創建敵機的精靈組
self.enemy_group = pygame.sprite.Group()
self.hero = Hero()
self.hero_group = pygame.sprite.Group(self.hero)
#事件監聽
def __event_handler(self):
for event in pygame.event.get():
#退出事件
if event.type == pygame.QUIT:
PlaneGame.__game_over() #靜態方法通過類名調用
elif event.type == CREATE_ENEMY_EVENT:
#print("敵機出場...")
#創建敵機精靈
enemy = Enemy()
#添加到精靈組
self.enemy_group.add(enemy)
elif event.type == HERO_FIRE_EVENT:
self.hero.fire()
#第一種方式:需要不停摁下
#elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
#print("向右移動")
#第二種方式:不需要不停摁
key_press = pygame.key.get_pressed()
if key_press[pygame.K_RIGHT]:
self.hero.speed = 3
elif key_press[pygame.K_LEFT]:
self.hero.speed = -3
else:
self.hero.speed = 0
def __check_collide(self):
#子彈摧毀敵機
pygame.sprite.groupcollide(self.hero.bullets,self.enemy_group,True,True)
#敵機撞毀英雄
enemies = pygame.sprite.spritecollide(self.hero,self.enemy_group,True)
if len(enemies) > 0 :
self.hero.kill()
PlaneGame.__game_over()
def __updata_sprites(self):
self.back_group.update()
self.back_group.draw(self.screen)
self.enemy_group.update()
self.enemy_group.draw(self.screen)
self.hero_group.update()
self.hero_group.draw(self.screen)
self.hero.bullets.update()
self.hero.bullets.draw(self.screen)
@staticmethod
def __game_over(): #靜態方法 就不接受self的東西
print("遊戲結束")
pygame.QUIT()
exit()
def start_game(self):
print("遊戲開始...")
while True:
#1.刷新頻率
self.clock.tick(FRAME_PER_SEC)
#2.事件監聽
self.__event_handler()
#3.碰撞檢測
self.__check_collide()
#4.更新/繪製精靈
self.__updata_sprites()
#5.更新檢測
pygame.display.update()
print(SCREEN_RECT.size)
if __name__ == '__main__':
#創建遊戲對象
game = PlaneGame()
#啓動遊戲
game.start_game()