# 敵機(jī)出場
##目標(biāo)
*使用**定時器**添加敵機(jī)
*設(shè)計`Enemy`類
## 01. 使用定時器添加敵機(jī)
運(yùn)行**備課代碼**武鲁,**觀察**敵機(jī)的**出現(xiàn)規(guī)律**:
1.游戲啟動后睁本,**每隔 1 秒**會**出現(xiàn)一架敵機(jī)**
2.每架敵機(jī)**向屏幕下方飛行**盆色,飛行**速度各不相同**
3.每架敵機(jī)出現(xiàn)的**水平位置**也不盡相同
4.當(dāng)敵機(jī)**從屏幕下方飛出**查刻,不會再飛回到屏幕中
### 1.1 定時器
* 在 `pygame` 中可以使用 `pygame.time.set_timer()` 來添加 **定時器**
*所謂**定時器**铐达,就是**每隔一段時間**,去**執(zhí)行一些動作**
```python
set_timer(eventid, milliseconds) -> None
```
*`set_timer`可以創(chuàng)建一個**事件**
*可以在**游戲循環(huán)**的**事件監(jiān)聽**方法中捕獲到該事件
*第 1 個參數(shù)**事件代號**需要基于常量`pygame.USEREVENT`來指定
? ? *`USEREVENT`是一個整數(shù)野芒,再增加的事件可以使用`USEREVENT + 1`指定,依次類推...
*第 2 個參數(shù)是**事件觸發(fā)**間隔的**毫秒值**
**定時器事件的監(jiān)聽**
*通過`pygame.event.get()`可以獲取當(dāng)前時刻所有的**事件列表**
***遍歷列表**并且判斷`event.type`是否等于`eventid`狞悲,如果相等,表示**定時器事件**發(fā)生
### 1.2 定義并監(jiān)聽創(chuàng)建敵機(jī)的定時器事件
`pygame`的**定時器**使用套路非常固定:
1.定義**定時器常量**——`eventid`
2.在**初始化方法**中妇斤,調(diào)用`set_timer`方法**設(shè)置定時器事件**
3.在**游戲循環(huán)**中摇锋,**監(jiān)聽定時器事件**
#### 1) 定義事件
* 在 `plane_sprites.py` 的頂部定義 **事件常量**
```python
# 敵機(jī)的定時器事件常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
```
*在`PlaneGame`的**初始化方法**中**創(chuàng)建用戶事件**
```python
# 4. 設(shè)置定時器事件 - 每秒創(chuàng)建一架敵機(jī)
pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
```
#### 2) 監(jiān)聽定時器事件
* 在 `__event_handler` 方法中增加以下代碼:
```python
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("敵機(jī)出場...")
```
## 02. 設(shè)計`Enemy`類
1.游戲啟動后丹拯,**每隔 1 秒**會**出現(xiàn)一架敵機(jī)**
2.每架敵機(jī)**向屏幕下方飛行**,飛行**速度各不相同**
3.每架敵機(jī)出現(xiàn)的**水平位置**也不盡相同
4.當(dāng)敵機(jī)**從屏幕下方飛出**荸恕,不會再飛回到屏幕中

***初始化方法**
? ? *指定**敵機(jī)圖片**
? ? ***隨機(jī)**敵機(jī)的**初始位置**和**初始速度**
*重寫**update()**方法
? ? *判斷**是否飛出屏幕**乖酬,如果是,從**精靈組**刪除
### 2.1 敵機(jī)類的準(zhǔn)備
* 在 `plane_sprites` 新建 `Enemy` 繼承自 `GameSprite`
*重寫**初始化方法**融求,直接指定**圖片名稱**
*暫時**不實(shí)現(xiàn)****隨機(jī)速度**和**隨機(jī)位置**的指定
*重寫`update`方法咬像,判斷是否飛出屏幕
```python
class Enemy(GameSprite):
? ? """敵機(jī)精靈"""
? ? def __init__(self):
? ? ? ? # 1. 調(diào)用父類方法,創(chuàng)建敵機(jī)精靈生宛,并且指定敵機(jī)的圖像
? ? ? ? super().__init__("./images/enemy1.png")
? ? ? ? # 2. 設(shè)置敵機(jī)的隨機(jī)初始速度
? ? ? ? # 3. 設(shè)置敵機(jī)的隨機(jī)初始位置
? ? def update(self):
? ? ? ? # 1. 調(diào)用父類方法县昂,讓敵機(jī)在垂直方向運(yùn)動
? ? ? ? super().update()
? ? ? ? # 2. 判斷是否飛出屏幕,如果是陷舅,需要將敵機(jī)從精靈組刪除
? ? ? ? if self.rect.y >= SCREEN_RECT.height:
? ? ? ? ? ? print("敵機(jī)飛出屏幕...")? ?
```
### 2.2 創(chuàng)建敵機(jī)
**演練步驟**
1. 在 `__create_sprites`倒彰,添加 **敵機(jī)精靈組**
? ? *敵機(jī)是**定時被創(chuàng)建的**,因此在初始化方法中莱睁,不需要創(chuàng)建敵機(jī)
2.在`__event_handler`待讳,創(chuàng)建敵機(jī),并且**添加到精靈組**
? ? *調(diào)用**精靈組**的`add`方法可以**向精靈組添加精靈**
3. 在 `__update_sprites`仰剿,讓 **敵機(jī)精靈組** 調(diào)用 `update` 和 `draw` 方法

**演練代碼**
* 修改 `plane_main` 的 `__create_sprites` 方法
```python
# 敵機(jī)組
self.enemy_group = pygame.sprite.Group()
```
* 修改 `plane_main` 的 `__update_sprites` 方法
```python
self.enemy_group.update()
self.enemy_group.draw(self.screen)
```
*定時出現(xiàn)敵機(jī)
```python
elif event.type == CREATE_ENEMY_EVENT:
? ? self.enemy_group.add(Enemy())
```
### 2.3 隨機(jī)敵機(jī)位置和速度
#### 1) 導(dǎo)入模塊
*在導(dǎo)入模塊時创淡,**建議**按照以下順序?qū)?/p>
```python
1. 官方標(biāo)準(zhǔn)模塊導(dǎo)入
2. 第三方模塊導(dǎo)入
3. 應(yīng)用程序模塊導(dǎo)入
```
* 修改 `plane_sprites.py` 增加 `random` 的導(dǎo)入
```python
import random
```
#### 2) 隨機(jī)位置

使用`pygame.Rect`提供的`bottom`屬性,在指定敵機(jī)初始位置時南吮,會比較方便
*? `bottom = y + height`
*? `y = bottom - height`
#### 3) 代碼實(shí)現(xiàn)
*修改**初始化方法**辩昆,隨機(jī)敵機(jī)出現(xiàn)**速度**和**位置**
```python
def __init__(self):
? ? # 1. 調(diào)用父類方法,創(chuàng)建敵機(jī)精靈旨袒,并且指定敵機(jī)的圖像
? ? super().__init__("./images/enemy1.png")
? ? # 2. 設(shè)置敵機(jī)的隨機(jī)初始速度 1 ~ 3
? ? self.speed = random.randint(1, 3)
? ? # 3. 設(shè)置敵機(jī)的隨機(jī)初始位置
? ? self.rect.bottom = 0
? ? max_x = SCREEN_RECT.width - self.rect.width
? ? self.rect.x = random.randint(0, max_x)
```
### 2.4 移出屏幕銷毀敵機(jī)
*敵機(jī)移出屏幕之后汁针,如果**沒有撞到英雄**,敵機(jī)的歷史使命已經(jīng)終結(jié)
*需要從**敵機(jī)組**刪除砚尽,否則會造成**內(nèi)存浪費(fèi)**
#### 檢測敵機(jī)被銷毀
*`__del__`內(nèi)置方法會在對象被銷毀前調(diào)用施无,在開發(fā)中,可以用于**判斷對象是否被銷毀**
```python
def __del__(self):
? ? print("敵機(jī)掛了 %s" % self.rect)
```
####代碼實(shí)現(xiàn)

*判斷敵機(jī)是否飛出屏幕必孤,如果是猾骡,調(diào)用`kill()`方法從所有組中刪除
```python
def update(self):
? ? super().update()
? ? # 判斷敵機(jī)是否移出屏幕
? ? if self.rect.y >= SCREEN_RECT.height:
? ? ? ? # 將精靈從所有組中刪除
? ? ? ? self.kill()
```