少女Q的量化交易轉型之路 #week 2 之1(Special Hurricane week + 1 python project)
週一上午還按部就班的上著課, 毫無預兆, 下午學校就給通知放假了。
去年來美帝第一個月,佛州就遭遇颶風‘emma‘?,由南向北,把底下的島國古巴,多明尼加掃了一通,從邁阿密登美,沿著佛州一路朝上。去年的標語是:‘‘史上最強颶風!!古巴被夷為平地!!邁阿密人民都慌張哭了!!‘‘ 這從中國中原地區的本q哪聽說過這麼厲害的自然災害,去年連夜跑路逃到新奧爾良。但其實並搞沒什麼大事情。
今年…我的心裡毫無波動甚至有點竊喜…不跑了(主要還是懶2333)。現在的這個颶風Michael從墨西哥灣來,範圍小,破壞力大,從panama city登陸,東北方向移動,時速達到130mile/h,達到了4級颶風的標準。按道理講破壞力是很大的,4級可以掀翻木質房屋和牆體,折斷大樹,並且造成大面積停電。好在不是我們村登陸,不然災後重建要有段時間了。
我現在是週三下午6:54, 颶風正在過境。狀況是high road整片區域停電(熱點碼字),窗外電線杆被吹斷一根,沒有別的肉眼可見災害。但我的LTE非常慢,懷疑att基站應該遭受了點破壞。想傳點照片,網速太慢,下一個blog傳。
Project: Alien invasion(入門到實踐)
專案簡介:
玩家控制著一艘最初出現在螢幕底部中央的飛船,玩家可以使用箭頭鍵左右移動飛船,還可以使用空格鍵進行射擊。遊戲開始時,一群外星人從螢幕中向下移動。玩家的任務是射殺外星人。消滅乾淨一波後,將會出現一波新的外星人,移動速度更快。外星人撞到玩家或者螢幕底部,玩家就損失一艘飛船。損失三艘後,遊戲結束。
規劃專案:
開發比較大的專案時,每個階段應該回顧一下之前內容並且搞清楚接下來要通過編碼來完成什麼功能。
- 需要一個主檔案alien_invasion.py建立一整個遊戲都要用到的物件,並且包含了整個遊戲的主迴圈。更像是一個大框架,下一步要做什麼,都用一句話呼叫。
- 為了簡化主檔案的複雜程度,需要一個settings.py這個類只包含__init__()。裡面都是一些設定,比如遊戲外觀,飛船的樣子,飛船速度等,如果要調整引數,直接在settings.py裡面改引數。
- 既然引數設定一類的,我們打包好了。對於功能一類的如果每個功能(按右鍵往右邊,按左鍵往左)都要在主檔案裡,那主檔案會太擁擠複雜,一眼看不出來。所以新建一個game_functions,py來儲存這寫功能。
- 飛船,子彈,是兩個part,都要在setting裡面寫會容易混淆麻煩。不如分成兩個包:ship.py, bullet.py
- 先弄出來一個外星人,引數設定,長寬高什麼的。並將之顯示在螢幕上。alien.py
- 再用一個內外迴圈,弄出一堆外星人。
- 對外星人進行移動操作,先往右,碰到screen邊緣就往下,再往左,依次反覆並在一定的框裡。
- 最後一步,是當bullets的rec碰到alien時候,兩個都會消失,用pygame.sprite.groupcollide()可以實現
遊戲實現 alien_invasion.py 主要檔案,輪廓 (每一條程式碼的含義解釋,都註釋在邊上了)
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
"""
Created on Mon Oct 8 12:46:37 2018
@author: Yueqi Huang
"""
#--------------------------建立pygame視窗以及響應使用者輸入-------------------
from settings import Settings
from ship import Ship
from alien import Alien
import pygame
#pygame 包含了開發遊戲所需要的功能
import game_functions as gf
#調入PYGAME
from pygame.sprite import Group
def run_game():
# 自己定義的run函式
pygame.init()
# 初始化背景設定,讓pygame可以正常工作。
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
# 建立一個1200x800的視窗,相當於呼叫了。
pygame.display.set_caption("Alien Invasion")
# 物件screen是一個surface。在pygame中,surface是螢幕的一部分。
# 用於顯示遊戲元素。在這個遊戲裡,每個元素都是一個surface。display.set.mode()返回的surface表示整個遊戲視窗,每經過以此迴圈,自動重繪這個surface。
ship = Ship(ai_settings, screen)
# 建立一個用於儲存子彈的編組
bullets = Group()
# 建立一個外星人
# alien = Alien(ai_settings, screen)
aliens = Group()
gf.create_fleet(ai_settings, screen, ship, aliens) # 新增了一個用於儲存SHIP物件的形參
# 開始遊戲的主迴圈
while True:
gf.check_events(ai_settings, screen, ship, bullets)
# 監視鍵盤和滑鼠
ship.update()
# 更新小船狀態(怎麼走)
gf.update_bullets(aliens, bullets)
# 之前關於子彈的性質,動作都放到game function裡了。
gf.update_aliens(ai_settings, aliens)
# 關於外星人的移動都放好了
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
# 螢幕持續更新
run_game()
#每次給遊戲新增新功能時,通常也引入一些新的設定。下面是一個名SETTINGS的模組,其中包含一個名為
#setting的類,用於將所有設定儲存在一個地方,以免在程式碼中到處新增設定。
class Settings():
"""儲存《外星人入侵》的所有設定的類"""
def __init__(self):
"""初始化遊戲的設定"""
# 螢幕的設定
self.screen_width = 1000
self.screen_height = 600
self.bg_color = (230, 230, 230)
# 速率設定
self.ship_speed_factor = 5
# 子彈設定
self.bullet_speed_factor = 10
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 10 # 設定每個螢幕最多有三顆子彈
#外星人設定
self.alien_speed_factor = 5
self.fleet_drop_speed = 10
#fleet_direction為1表示向右移,為-1表示向左移
self.fleet_direction = 1
game_functions.py 內容很多,全部是一步步的大操作化簡成的小操作,本來都可以放在main函式裡面的。
# 為了讓run_game()簡化一下並隔離事件管理迴圈。把監視鍵盤和滑鼠移出來了。
import sys
import pygame
from Bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""響應按鍵"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
# 按住是KEYDOWN,按起來是KEYUP,用這傳遞按鍵狀態給update進行移動。
elif event.key == pygame.K_UP:
ship.moving_up = True
elif event.key == pygame.K_DOWN:
ship.moving_down = True
# 判斷空格鍵用的
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
def fire_bullet(ai_settings, screen, ship, bullets): # 只包含玩家按空格鍵時發射子彈的程式碼
"""如果還沒達到限制,就發射一顆子彈"""
# 新建一個子彈,並將其加到編組bullets中。
if len(bullets) < ai_settings.bullets_allowed: # 如果子彈<3顆,建立一個新子彈。如果大於三顆,按空格,什麼都不會發生。
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""響應鬆開"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
elif event.key == pygame.K_UP:
ship.moving_up = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
def check_events(ai_settings, screen, ship, bullets):
# 監視鍵盤和滑鼠事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 檢測到按下的鍵是右鍵,則向右移動。按左鍵則往左走動,不按鍵不走。
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings,screen,ship, aliens, bullets):
"""更新螢幕上的影象,並切換到新螢幕"""
# 每次迴圈時都重繪螢幕
screen.fill(ai_settings.bg_color)
# 設定背景顏色 紅(255,0,0) 綠(0,255,0) 藍(0,0,255)
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme() # 繪製飛船的
# alien.blitme()
aliens.draw(screen)
# 用這個draw可以繪製一個編組的外星人了
pygame.display.flip()
# 讓最近繪製的螢幕可見
def update_bullets(aliens, bullets):
"""更新子彈位置,並且刪除已消失的子彈"""
bullets.update()
# 更新子彈狀態
# 刪除消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
# print(len(bullets)) # 可以顯示當前還有多少顆子彈,核實已經消失的子彈確實刪除了。
# 檢查是否有子彈擊中了外星人
# 如果是擊中了,就刪除相應的子彈和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
#--------------------外星人PART,原本都在create_fleet分解成了計算外星人數量和建立佇列兩個PART--------------
def get_number_aliens_x(ai_settings, alien_width):
"""計算每行可容納多少個外星人"""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x/(2 * alien_width)) #確保能用int計算出外星人的數量
return (number_aliens_x)
def get_number_rows(ai_settings, ship_height, alien_height):
"""計算螢幕容納多少行外星人"""
available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""建立一個外星人並放在其當前行"""
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def create_fleet(ai_settings, screen, ship, aliens):
"""建立外星人群"""
#建立一個外星人,並計算一行可以容納多少個外星人
# 外星人間距為外星人寬度
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
#建立第一行外星人
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
#建立一個外星人並將其加入當前行
create_alien(ai_settings, screen, aliens, alien_number, row_number)
def check_fleet_edges(ai_settings, aliens):
"""有外星人到達邊緣時應採取相應措施"""
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""將外星人下移,並且改變方向"""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def update_aliens(ai_settings, aliens):
"""更新外星人群中所有外星人的位置"""
"""檢查是否有外星人位於螢幕邊緣,並更新外星人位置"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
ship.py 用來放飛船的一系列動作和引數
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""初始化飛船並設定其初始位置"""
self.screen = screen
self.ai_settings = ai_settings
# 載入飛船影象並獲取外接矩形
self.image = pygame.image.load('images/ship.bmp') # 載入影象,並返回了一個表示飛船的surface
self.rect = self.image.get_rect() # 可以獲得surface的屬性rect。可以像處理矩形一樣處理它
self.screen_rect = screen.get_rect()
# 將每艘新飛船放在螢幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.top = self.screen_rect.top
self.rect.bottom = self.screen_rect.bottom
# self.center = float(self.rect.centerx)
# self.center1 = float(self.rect.top)
# 在飛船的屬性center中儲存小數點
# 移動標誌,初始化
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
def update(self):
"""根據移動標誌調整飛船的位置 左右移動"""
# 更新飛船的center值
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.centerx += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.rect.centerx -= self.ai_settings.ship_speed_factor
if self.moving_up:
self.rect.top -= self.ai_settings.ship_speed_factor
if self.moving_down:
self.rect.top += self.ai_settings.ship_speed_factor
def blitme(self):
"""在指定位置繪製飛船"""
self.screen.blit(self.image, self.rect)
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一個對飛船發射子彈進行管理的類"""
def __init__(self, ai_settings, screen, ship):
"""在飛船所處的位置建立一個子彈物件"""
super(Bullet, self).__init__()
self.screen = screen
# 在(0,0)處建立一個表示子彈的矩形,再設定正確的位置,相當於新建了一個矩陣。
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
self.rect.centerx = ship.rect.centerx
# 將子彈的centerx設定為飛船的rect.centerx。子彈應從飛船頂部飛出。
self.rect.top = ship.rect.top
# 儲存用小數表示的子彈位置
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
# 將子彈的顏色和速度設定分別儲存到SELF.COLOR和SELF.SPEED
def update(self):
"""向上移動子彈"""
# 更新表示子彈位置的小數值
self.y -= self.speed_factor
# 更新表示子彈的rect位置
self.rect.y = self.y
def draw_bullet(self):
"""在螢幕上繪製子彈"""
pygame.draw.rect(self.screen, self.color, self.rect)
alien.py 主要作用是放關於alien的引數和操作
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""表示單個外星人的類"""
def __init__(self, ai_settings, screen):
"""初始化外星人並設定其起始位置"""
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
# 載入外星人影象,並設定rect屬性
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
# 每個外星人最初都在螢幕左上角附近
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# 儲存外星人的準確位置
self.x = float(self.rect.x)
def blitme(self):
"""在指定位置繪製外星人"""
self.screen.blit(self.image, self.rect)
def check_edges(self): # 判斷有沒有撞到邊上
"""如果外星人位於螢幕邊緣,就返回TRUE"""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
def update(self):
"""向左或者右移外星人"""
self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x = self.x
Code part結束
遊戲最終介面: 全篇最強程式碼: run_game(),可以說整個遊戲系統,都被簡化成了這一句。 我覺得整個code大概思路是這樣,先給這個遊戲分為幾個part,可以由功能分(eg. 飛行part, 射擊part, edge detection part等),也可以是分成不同的物件再組合起來(就像這個code)。再考慮事件進行的步驟,飛船先動,在射子彈,外星人出現,外星人消失等。全部放在main函式裡面,一步步加上去。當主檔案的操作非常多了之後,可以考慮建立一個新的檔案,儘量減少程式碼,把複雜的邏輯和操作放在別的檔案裡,會顯得非常的乾淨,也很有條理。
總之,自己是碼不出來的,跟著碼一遍對整個專案的思路有個大概的瞭解,怎樣從巨集觀到微觀。包括檔案之間的呼叫也是很有層次的。debug也加快了一些。反正多積累下經驗,才能敢刷leetcode去。