用 Python 实现打飞机
全世界只有3.14 %?的人關注了
數據與算法之美
所用技術和軟件
python 2.7
pygame 1.9.3
pyCharm
準備工作
安裝好 pygame 在第一次使用 pygame 的時候,pyCharm 會自動 install pygame。
下載好使用的素材。
技術實現
初始化 pygame
首先要初始化 pygame ,之后設定一些基本的要點,比如窗口大小(盡量避免魔法數字),窗口標題以及背景圖像。pygame 通過加載圖片,最后返回一個 surface 對象,我們不需要關系圖片的格式。但是通過?convert()?這個函數,會使我們的圖片轉換效率提高。
# coding=utf8import pygame
WIDTH = 480
HEIGHT = 800
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('飛機大戰')
background = pygame.image.load('resources/image/background.png').convert()
screen.fill(0)
screen.blit(background, (0, 0))
默認圖片左上角為原點 (0,0)。
顯示窗口
如果我們這樣設定,當我們運行的時候,窗口會一閃而過,并不會出現我們想象的畫面。因為窗口只是運行一下就會關閉,所以我們要寫一個循環,使窗口一直保持出現。當然如果我們簡單的寫一個 while True那么我們的程序就出現了死循環,卡死。
所以還需要寫個退出。
while True:? ?screen.fill(0)
? ?screen.blit(background, (0, 0))
? ?for event in pygame.event.get():
? ? ? ?if event.type == pygame.QUIT:
? ? ? ? ? ?pygame.quit()
? ? ? ? ? ?exit()
顯示飛機
首先我們要初始化我們的主角飛機
仍舊需要加載我們需要的資源,我們的資源文件里已經準備好各種各樣的飛機,但是他們都在一張切圖上。
同時我們的資源文件里還有一個叫做?shoot.pack?的文件,里面記錄了每個圖片所在的位置。
我們通過下面的代碼加載資源圖片,并且獲得我們需要的主角飛機。
plane_img = pygame.image.load('resources/image/shoot.png')player = plane_img.subsurface(pygame.Rect(0, 99, 102, 126))
將 player 顯示在屏幕上,并且刷新屏幕
screen.blit(player, [100, 400])pygame.display.update()
效果如下
讓飛機 “飛” 起來
飛機已經出現在我們的屏幕上了,現在需要讓飛機動起來讓他可以上下左右的移動。
首先要獲取鍵盤事件,獲取鍵盤上什么按鍵被按下。
key_pressed = pygame.key.get_pressed()通過?key_pressed?獲取當前的鍵盤按鍵。并進行判斷,這里寫了四個函數進行對?player?移動。
if key_pressed[pygame.K_w] or key_pressed[pygame.K_UP]:? ?player.moveUp()
if key_pressed[pygame.K_s] or key_pressed[pygame.K_DOWN]:
? ?player.moveDown()
if key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
? ?player.moveLeft()
if key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
? ?player.moveRight()
下一步就是完善這四個方法。
簡單的說就是按下方向鍵的時候(w,a,s,d)飛機向四周移動,但是不能移動離開屏幕。
此時我們就應該把我們的飛機形成一個類,類里面有控制飛機的方法。
這里寫類比較麻煩一點
Player的出現
首先要明確一點,這個類需要什么。
我們之前對?player?有什么操作?定義了他的圖片和他出現的位置。所以我們的構造方法就要初始化這些值。
所有的這些對象,我們在?pygame?里叫做精靈(sprite),這個概念也在其他游戲開發中使用。
? ?def __init__(self, plane_img, player_rect, player_position):
? ? ? ?pygame.sprite.Sprite.__init__(self)
? ? ? ?self.img = plane_img.subsurface(player_rect)
? ? ? ?self.rect = player_rect
? ? ? ?self.rect.topleft = player_position
簡單的說就是獲取飛機的圖片,初始化飛機的矩形區域。rect?該屬性會獲得四個值。分別是左上角?x?,y?坐標,矩形的寬度。topleft?初始化飛機的左上角坐標,也就是飛機出現的位置。如下圖所示。
player的控制
當飛機出現了,我們就應該實現我們在循環里寫的方法。我們首先要判斷它還在不在屏幕內,不能讓飛機飛出屏幕??梢酝ㄟ^?rect.top,rect.bottom,rect.left,rect.right四個方法獲取飛機圖片的上下左右四個邊界值。
這樣我們就能對飛機進行判斷
def moveUp(self):? ?if self.rect.top <= 0:
? ? ? ?self.rect.top = 0
? ?else:
? ? ? ?self.rect.top -= self.move
def moveDown(self):
? ?if self.rect.bottom >= HEIGHT:
? ? ? ?self.rect.bottom = HEIGHT
? ?else:
? ? ? ?self.rect.bottom = self.move
def moveLeft(self):
? ?if self.rect.left <= 0:
? ? ? ?self.rect.left = 0
? ?else:
? ? ? ?self.rect.left -= self.move
def moveRight(self):
? ?if self.rect.right >= WIDTH:
? ? ? ?self.rect.right = WIDTH
? ?else:
? ? ? ?self.rect.right = self.move
這里的?move?是我們對飛機的移動的位移定義的常量。
讓子彈飛
子彈要沿著發射方向射出去??梢栽谄聊簧弦恢币苿?#xff0c;直到移出屏幕。
我們只要有定義一個子彈對象,讓這個對象顯示在屏幕上就可以。
先定義飛機子彈類,基本和定義?player?一樣,獲得圖片,裁剪圖片,設置圖片初始位置,在屏幕上顯示圖片
? ?def __init__(self, bullet_image, bullet_position):
? ? ? ?pygame.sprite.Sprite.__init__(self)
? ? ? ?self.image = bullet_image
? ? ? ?self.rect = self.image.get_rect()
? ? ? ?self.rect.midbottom = bullet_position
# 省略其他代碼
# 加載子彈圖片
bullet_rect = pygame.Rect(69, 78, 9, 21)
bullet_img = plane_img.subsurface(bullet_rect)
# 省略其他代碼
while True:
? ?# 省略其他代碼
? ?screen.blit(bullet.img, bullet.rect)
? ?# 省略其他代碼
運行結果
下一步就是讓飛機的子彈跟隨飛機。
我們需要在 Player 類里面添加方法。
首先我們規定,按下空格發射子彈。
? ?player.shoot()
完善shoot方法。子彈類已經有了,我們每次只要在按下空格的時候創建一個對象就好。
首先要每次傳入一個子彈的圖像,然后還有出現位置,這樣子彈才能跟隨飛機。
定義一個pygame.sprite.Group()?來存放精靈組。這樣我們就能把我們的子彈都放進去。
? bullet = Bullet(bullet_img, self.rect.midtop)
? self.bullets.add(bullet)
每次按下空格的時候傳入一個子彈圖片
if key_pressed[pygame.K_SPACE]:? ?player.shoot(bullet_img)
最后我們只需要在屏幕上進行子彈的繪制即可。
player.bullets.draw(screen)這樣我們的子彈就會跟隨飛機出現。
下一步就是讓子彈在屏幕上移動。
創建移動的方法。
def move(self):? ?self.rect.top -= self.move
因為我們的子彈在 bullets 里,所以我們僅需要一個循環,遍歷每個子彈,之后移動即可。當子彈移出屏幕的時候我們只要在?bullets?中移出就可以。
for bullet in player.bullets:? ?bullet.bulletMove()
? ?if bullet.rect.bottom < 0:
? ? ? ?player.bullets.remove(bullet)
結果
這個和我們的預期還是有差別的,頻率太快了。
關于pygame 的鍵盤重復事件 官方好像并沒有這個設置。那么我們只能在添加一個計數器,通過計算器的數值來判斷子彈是否發射。這里的數值是多次測試后,自己感覺一個比較滿意的頻率??梢宰约赫{整。
# 省略其他代碼# 子彈頻率
SHOOT_PC = 0
在鍵盤事件中我們需要判斷頻率。
if key_pressed[pygame.K_SPACE]:? ?SHOOT_PC = SHOOT_PC ?1
? ?if SHOOT_PC % 400 == 0:
? ? ? ?player.shoot(bullet_img)
player 的飛機就算基本繪制好了
繪制敵機
下一步就是繪制敵機。敵機是從屏幕上方移動到屏幕下方。我們任就需要一個類來設置敵機。設置類任就和我們前面的差不多,加載資源,設置?rect,設置位置。
# 加載敵機圖片enemy_rect = pygame.Rect(267, 347, 57, 51)
enemy_img = plane_img.subsurface(enemy_rect)
enemy_position = [200, 200]
enemy = Enemy(enemy_img, enemy_position)
# 敵機類
class Enemy(pygame.sprite.Sprite):
? ?def __init__(self, enemy_img, enemy_position):
? ? ? ?pygame.sprite.Sprite.__init__(self)
? ? ? ?self.image = enemy_img
? ? ? ?self.rect = self.image.get_rect()
? ? ? ?self.rect.topleft = enemy_position
最后在屏幕顯示出來
1
screen.blit(enemy_img, enemy_rect)
現在我們就應該想想敵機的特點了,其實他和子彈的特點基本一直,只不過方向不一樣而已。還有一點是敵機是隨機生成的。
# 敵機計數器EnEMY_PC = 0
# 省略代碼
enemy_position = [random.randint(0, WIDTH - enemy_rect.width), 0]
? ? ? ?enemy = Enemy(enemy_img, enemy_position)
? ? ? ?enemies.add(enemy)
我們隨機在頂部生成飛機。
這個方式的情況和子彈其實差不多,我們應該給出現敵機確定一個頻率。
if EnEMY_PC % 500 == 0:? ?enemy_position = [random.randint(0, WIDTH - enemy_rect.width), 0]
? ?enemy = Enemy(enemy_img, enemy_position)
? ?enemies.add(enemy)
EnEMY_PC = EnEMY_PC ?1
這樣的話出現情況就變得緩慢。下一步實現敵機的移動。敵機的移動原理和子彈的移動其實也是一樣的。不多解釋
移動方法
def enemyMove(self):? ?self.rect.top = self.move
移動實現
for enemy in enemies:? ? ? enemy.enemyMove()
? ? ? if enemy.rect.top > HEIGHT:
? ? ? ? ? enemies.remove(enemy)
? enemies.draw(screen)
碰撞檢測
飛機和敵機還有子彈都有了,我們現在需要進行完成碰撞檢測。有下面幾種場景。
敵機和玩家碰撞在一起
子彈和敵機碰撞在一起
無論是那種情況的碰撞,其實就是兩張圖片有了交集。
如圖
pygame 給我們提供了碰撞檢測的方法。首先兩個對象必須是?sprite?。通過?pygame.sprite.collide_rect()?進行碰撞檢測。
我們先進行一個測試
if pygame.sprite.collide_rect(enemy, player):? ?print '檢測成功'
結果
檢測成功
此時我們就可以完成,當玩家和敵機發生碰撞,游戲結束,當子彈和敵機碰撞,敵機消失。
同樣的 pygame 給我們提供了一個?pygame.sprite.groupcollide()?用于?Group?之間的碰撞檢測.當發生碰撞的時候這兩個對象都會在?Group?中移出。
用于檢測敵機和子彈
pygame.sprite.groupcollide(enemies, player.bullets, 1, 1)敵機和子彈的關系已經和好的處理。
處理敵機和玩家飛機的關系。
我們需要在?Player?里添加一個屬性判斷當前玩家是否被擊中的?boolean?值.當集中的時候把屬性改為?True.當為?True?的時候游戲結束.也就是我們一開始設置的循環就會結束.所以我們需要更改之前寫的一些代碼,使它更加完善。
在 Player 類里面添加是否擊中屬性。
self.is_hit = False修改循環
RUN = Truewhile RUN:
? ?# 省略代碼
? ? ? ?if pygame.sprite.collide_rect(enemy, player):
? ? ? ?player.is_hit = True
? ? ? ?RUN = False
? ?# 省略代碼
執行結果
當玩家被擊中的時候,在顯示一張 GameOver 圖片提示
gameOver = pygame.image.load('resources/image/gameover.png')while GAMEOVER:
? ?screen.fill(0)
? ?screen.blit(gameOver, (0, 0))
? ?pygame.display.update()
? ?# 退出程序
? ?for event in pygame.event.get():
? ? ? ?if event.type == pygame.QUIT:
? ? ? ? ? ?pygame.quit()
? ? ? ? ? ?exit()
做到這里基本算是實現了飛機大戰.但是還有很多細節處理。
細節處理
精細的碰撞檢測
從圖上看,當敵機看似還沒有和我們接觸時,但是已經 GameOver 了。
實際情況是這樣的,所有的圖片都是矩形,當兩張圖片的矩形邊框線碰撞的時候,就算兩個對象碰撞,所以我們要更加精細的使用碰撞檢測。
我們可以按著圖片中心的某個長度為半徑,在這個半徑內發生碰撞才是碰撞。
pygame 給我們提供了這樣的方法。pygame.sprite.collide_circle_ratio()?可以自己算出一個半徑,作為檢測半徑。并且可以做出一個有效檢測的百分比。
if pygame.sprite.collide_circle_ratio(0.6)(player, enemy):? ?player.is_hit = True
? ?RUN = False
同樣,子彈和敵機也可以修改,讓碰撞檢測更加精細。
修改后面的兩個參數,使得碰撞檢測更加精細。
pygame.sprite.groupcollide(enemies, player.bullets, 0.6, 0.8)
動畫
做了怎么就,感覺它沒有一點動效,感覺死氣沉沉的。無論是飛機飛行,還是飛機被擊中,都沒有一個明確的反饋。
對于2d游戲,動畫其實就是一張一張的圖片不停的變化。就和電影的原理類似。要想讓我們的飛機動起來,我們需要定義一個列表來存放這些圖片,然后寫個循環,讓他一直不停的更換圖片就好。
首先我們更改我們的主角?Player
任就是老套路,加載圖片。把加載的圖片放到list 里。
? ? ? ? ? ? ? pygame.Rect(165, 360, 102, 126),
? ? ? ? ? ? ? pygame.Rect(165, 234, 102, 126),
? ? ? ? ? ? ? pygame.Rect(330, 624, 102, 126),
? ? ? ? ? ? ? pygame.Rect(330, 498, 102, 126),
? ? ? ? ? ? ? pygame.Rect(432, 624, 102, 126)]
player_position = [100, 400]
player = Player(plane_img, player_rect, player_position)
之后在?Player?添加循環的方法。獲取圖片。
class Player(pygame.sprite.Sprite):? ?def __init__(self, plane_img, player_rect, player_position):
? ? ? ?pygame.sprite.Sprite.__init__(self)
? ? ? ?self.image = []
? ? ? ?for i in range(len(player_rect)):
? ? ? ? ? ?self.image.append(plane_img.subsurface(player_rect[i]).convert_alpha())
? ? ? ?self.rect = player_rect[0]
? ? ? ?self.rect.topleft = player_position
? ? ? ?self.img_index = 0
? ? ? ?self.move = 1
? ? ? ?self.bullets = pygame.sprite.Group()
? ? ? ?self.is_hit = False
飛機正常飛行的圖片只有兩張。所以我們要循環變化這兩張圖片。所以每發射一個子彈,圖片變化兩張。
screen.blit(player.image[player.img_index], player.rect)player.img_index = SHOOT_PC / 248
# 省略代碼
if key_pressed[pygame.K_SPACE]:
? ?if SHOOT_PC % 495 == 0:
? ? ? ?player.shoot(bullet_img)
? ?SHOOT_PC = SHOOT_PC ?1
? ?if SHOOT_PC >= 495:
? ? ? ?SHOOT_PC = 0
正常發射子彈的動畫效果已經做完。我們還需要進行被擊中爆炸的動畫效果。
擊中的原理和正常也一樣。只不過先要判斷當前飛機狀態,是否被擊中。
if not player.is_hit:? ?screen.blit(player.image[player.img_index], player.rect)
? ?player.img_index = SHOOT_PC / 248
else:
? ?player.img_index = player_shoot / 248
? ?screen.blit(player.image[player.img_index], player.rect)
? ?player_shoot = 30
? ?if player_shoot > 495:
? ? ? ?RUN = False
# 省略代碼
if pygame.sprite.collide_circle_ratio(0.6)(player, enemy):
? ?player.is_hit = True
248,30,495,1457?這些數字是什么?如何計算出來的。先說?495?這個數字。
495 這個數字很隨便,只是控制子彈的發射間隔。完全可以自定義。但是495這個數字一旦確定,其他三個數字基本確定。
248 為 495 的一半,因為發射一個子彈,圖片要變化兩張。
30 這個數字基本也是自定義的,只要比1大就好,他影響了結束動畫出現的時間。
1488 這個數字是通過 248 確定的,是 248 的 6倍,因為飛機被射擊后會有四張圖片的顯示。
同理,把敵機接觸子彈的動畫寫出來。
加載圖片
enemies_shoot_img = [plane_img.subsurface(pygame.Rect(267, 347, 57, 43)),? ? ? ? ? ? ? ? ? ? plane_img.subsurface(pygame.Rect(873, 697, 57, 43)),
? ? ? ? ? ? ? ? ? ? plane_img.subsurface(pygame.Rect(267, 296, 57, 43)),
? ? ? ? ? ? ? ? ? ? plane_img.subsurface(pygame.Rect(930, 697, 57, 43))]
同樣我們需要創建 Group() 來存放被擊中的敵機。
enemies_shoot = pygame.sprite.Group()之后的處理邏輯基本相似,不多介紹
for enemy in enemies:? ?enemy.enemyMove()
? ?if pygame.sprite.collide_circle_ratio(0.6)(player, enemy):
? ? ? ?enemies_shoot.add(enemy)
? ? ? ?enemies.remove(enemy)
? ? ? ?player.is_hit = True
? ? ? ?break
? ?if enemy.rect.top > HEIGHT:
? ? ? ?enemies.remove(enemy)
for enemy_shoots in enemies_shoot:
? ?if enemy_shoots.shoot_index == 0:
? ? ? ?pass
? ?if enemy_shoots.shoot_index > 70:
? ? ? ?enemies_shoot.remove(enemy_shoots)
? ? ? ?continue
? ?screen.blit(enemy_shoots.shoot_imgs[enemy_shoots.shoot_index / 20], enemy_shoots.rect)
? ?enemy_shoots.shoot_index = 1
這樣的話基本完成了動畫效果。
音樂
有了動畫還的有音樂。
音樂的處理只要在特定的地方播放音樂就好,比如子彈發射的時候,背景音樂,被擊中的時候,游戲結束的時候,等等。他們的處理邏輯都一樣。先加載資源,然后在播放。
背景音樂的播放。
pygame?在處理背景音樂的時候都在?pygame.mixer?方法中。其中播放音樂的play中的參數,第一個為播放幾次,-1 為循環播放,后面的浮點表示 從第幾秒開始播放。
backgroundMusic = pygame.mixer.music.load('resources/sound/game_music.mp3')pygame.mixer.music.play(-1, 0.0)
其他的音樂先加載資源,在需要的地方播放。
發射子彈
def shoot(self, bullet_img):? ? ? shootMusic = pygame.mixer.Sound('resources/sound/bullet.mp3')
? ? ? bullet = Bullet(bullet_img, self.rect.midtop)
? ? ? self.bullets.add(bullet)
? ? ? shootMusic.play()
其他音樂處理一樣,不多解釋。
分數&等級
分數
首先繪制得分情況,在屏幕上顯示多少分。
繪制字體基本和繪制精靈是差不多的。首先要生成字體
兩個參數分別是字體和字號
score_font = pygame.font.Font(None, 36)
有了字體那么需要寫點字。
score_font.render("分數",True,(0,0,0),(255,255,255))第一個參數是寫的文字;第二個參數是個布爾值,以為這是否開啟抗鋸齒,就是說True的話字體會比較平滑,不過相應的速度有一點點影響;第三個參數是字體的顏色;第四個是背景色,如果你想沒有背景色(也就是透明),那么可以不加這第四個參數
字體也有了,文本也有了,下一步就是繪制。通過?get_rect()?獲得矩形,之后繪制和精靈繪制方法一樣
score_text = score_font.render('分數:0', True, (128, 128, 128))
text_rect = score_text.get_rect()
text_rect.topleft = [10, 10]
screen.blit(score_text, text_rect)
分數已經顯示了,就可以積分。我們每擊落一個飛機增加 100 分。
if enemy_shoots.shoot_index > 70:? ? ? ? ? enemies_shoot.remove(enemy_shoots)
? ? ? ? ? score = 100
? ? ? ? ? continue
我們還需要在文本的地方強制轉換為 str 。
score_text = score_font.render(str(score), True, (128, 128, 128))
等級
有了分數,那么再加點等級會使游戲更加有趣味性。
同樣的先繪制等級。
level_font = pygame.font.Font(None, 42)level_text = level_font.render('Level 'str(level), True, (128, 128, 128, 128))
level_rect = level_text.get_rect()
level_rect.midtop = [240, 10]
screen.blit(level_text, level_rect)
下一步就是寫等級函數。
隨著分數的增加,等級增加,飛機變多,等等。
首先寫分數和等級的關系。
隨便瞎寫的函數
? ? ? level = 1
這個是控制敵機數量的,我們可以設定一個變量,使敵機越來越多。
if ENEMY_PC % 500 == 0:每增加一級,就添加敵機數量。等級也不能一直增加,所以當等級是摸個值的時候,就算最高級別了。
if score == 100 \* (level ** 2 \ ?level):? ?level = 1
? ?if level != 10:
? ? ? ?enemy_add -= 20
基本到這里算是寫了一個相對完整的游戲。
超模君準備了幾份?數學思維好物
《超模定制筆記本》《數學原來會說謊》
《數學的故事》《簡單微積分》......
免費?送給大家,參與就有機會獲得
原文地址:
http://youngxhui.top/2017/06/22/python-%E5%AE%9E%E7%8E%B0%E5%BE%AE%E4%BF%A1%E6%89%93%E9%A3%9E%E6%9C%BA/#hcq=Y6ZlhXq
版權歸原作者所有,轉載僅供學習使用,不用于任何商業用途,如有侵權請留言聯系刪除,感謝合作。
精品課程推薦:
選購數學科普正版讀物
嚴選“數學思維好物”
送給孩子的益智禮物? ?| ??辦公室神器
算法工程師成長閱讀? ?| ??居家高科技
理工科男女實用型禮物精選?
----點擊頭像關注----
超級數學建模
數據與算法之美
少年數學家
數銳學堂
驚喜醬(個人號)
玩酷屋COOL
總結
以上是生活随笔為你收集整理的用 Python 实现打飞机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每日一笑 | 终于知道为什么胖了之后气质
- 下一篇: 要学习数据科学知识,这些信息需要知道(数