嗨大家好我是安淇,這一次足足花了264行程式,簡直省了好幾堂課不用寫Medium⋯⋯,痾不!我是說成就搞非常的高,遊戲弄好的一瞬間直接展開翅膀(雖然在過程中忘了存擋,全部重做),但是這個專案是參考一位youtuber的程式教學,可以複製程式,但老娘才不會用這種複製貼上,硬要打程式,所以今天就來跟大家一起探索這程式吧!
國外youtube教學:連結
遊戲架構
介紹
簡單來講就是有敵人會社一些雷射,而你就是要用雷射射死他們,大概就是這樣。
角色
你:
按空白鍵可射擊
WASD上下左就移動
敵人:
會把你用雷射弄死
直線前進
不一定的時間內射鐳射
雷射:
會受到遊玩者傷害
直直線走
當射的人死了之後,雷射也會消失。
程式
首先,前面綠色的程式就是簡單講一下程式名、作者、更新的日期,就是像考卷一樣,誰的名字、班級座號,這樣才可以讓別人知道這個是什麼程式,這是誰寫的,這是什麼時候更新的。
接下來下一段,我們要在函式庫裡面讓python知道接下來要打的額外函示,import(匯入)本程式要的函示,然後在第二段的最後一行就是在存的地點我們可以想說是盒子裡面的盒子裡面的盒子,就是pygame裡面有一個font裡面有一個init就是執行的程式。
最後一段就是整個遊戲畫面的長寬比例,長750寬750,那第二行的「 WIN」不是勝利的那種,而是「WINDOW」視窗,這裡就是在初始化出一個特定大小的視窗 750px,最後一行的話就是視窗的名稱,這裡可能大家不知道是哪裡,我就給你們看一下吧。
就是這樣,最後在命個名就好。
接下來就是在寫說雷射敵人的程式,先來講一下第一段,因為字太小了所以我就簡單拿一句來講解:
RED_SPACE_SHIP = pygame.image.load(os.path.join(“assets”, “pixel_ship_red_small.png”))
這裡是在解釋說敵方的外觀,這一次有點不一樣的是說他直接選取在pygame.image.load裡面的圖檔“pixel_ship_red_small.png”,反正就是選一個資料裡面的圖來當角色,而一樣就下來兩行就只是變成其他敵人的外觀,第一段較結束了。
接下來第二段只有一行,但也跟剛剛說的一樣,就只是要我們控制的主角的外觀而已,也一樣就是把圖片在遊戲中顯示。
第三段,這裡的話是講說雷射,也就是我們攻擊的武器,但也跟剛剛一樣,把雷射的圖片匯進去而已。
最後一行也就是背景,跟剛剛的都一樣最後我們會看到這一句:
這是不是有點眼熟?在第一行有看過?
在第一段那邊有剛剛在寫說背景的函示,因為已經被設為函式了,所以不用在特別寫說是多寬多高,只要寫上「WIDTH, HEIGH」就好了。
這裡就開始寫運行的程式,剛剛只是設定外觀而已,那第一個問題來了「class 」是什麼「def 」是什麼?
class的話可以先把他解釋為種族,這個種族是一種狗,也就是寫成「class Dog」也就是說這一個種族裡面會運行著狗狗會做的事,例如亂叫,那一樣回到第一行「class Laser」顧名思義,也就是在運行著關於雷射的移動,或者是與飛船的互動。
def的話就是函示,例如說可能我今天要讓他射子彈,我就建立一個函式,命名為「bullet」,裡面寫著關於射子彈的一些運行 、動作,然後如果我們飛船需要用到射子彈這個程式時,我們直接可以打「bullet」,不用額外再打一個程式上去。
class Laser
那首先剛剛有講到def是一種建立韓式的一種指令,那現在我有在底下劃一道黃線,那這一條黃線就是我們俗成的函示,那我們一個一個慢慢解讀吧。
__init__
init,我們可以解讀成initial,初始化的意思,這個類別一出現,必然會有的一些運作,痾⋯⋯應該這樣講,當我們生成一個角色,那我們看看下面那一塊,生成時,一定要有屬於自己的X, Y(移動),要有自己的照片(外觀),然後再下面的話就是把這一張外關圖,貼上這角色,給他一種面具的概念。
draw
繪製覆蓋整個畫面,舉例來講,我們原本的背景就是簡單的白色,那我們就把畫好的視窗,用剛剛的函式,就可以把它撲上去。
move
這裡的話先讓大家了解一下「vel」是什麼,vel的話,我們可以簡稱速度的意思,掉下來的距離,那我們再回來,下面的程式就是在寫說,掉下來的距離,但因為是掉下來,所以只有y也就是上下的意思,那剛剛講的vel就應該知道是墜落的速度、高度、距離。
off_screen
可以說當某個角色、物品,離開遊戲畫面的程式,那下面的話可以說是這個程式一直偵測是否有超過遊戲畫面,更嚴格來講的話就是指定的安全匡。
collision
當在設計遊戲時,如果沒有這個,就真的會世界末日了,那就是「碰撞」,但這函式也唯一做到的是呼叫collide碰撞韓式,並回報結果,這跟off_screen是同樣的道理。
class Ship
__init__
剛剛已經有講過了,如果有什麼不會的請回去看,但是,這裡有一個很重要的一個東西,那就是 「cool_down_counter」,這裡可以說當敵人發出了子彈,一定要有冷卻的時間,可以說過了幾秒,發射一次子彈,大概就是這樣。
draw
剛剛也有講過,但是,這裡有一個很重要的東西那就是「for laser in laser」那這裡是在講什麼呢,例如,當我射了幾個雷射,但是我卻被敵人射死,那我就會消失,但是我剛剛射的這些子彈也一樣要消失,包括敵人也是,簡單來講就是雷射跟你的命是串再一起的。
move_laser
這裡就是只雷射的程式,我們可以先專注在藍色的字上,剛剛這些藍色的字都有講到,上下移動(墜落)、當離開畫面要被移除、當跟別對碰撞時要扣命,其實只要大家剛剛有讀懂上面所講的,這裡其實就很好理解。
cooldown
我們發射雷射的時候,必須要有緩衝的時間,可以想像成說是裝子彈的那種概念,那我們現在理解「cooldown」是什麼之後,我們回去看一下程式,我的預設是說,每當發射一次雷射,緩衝30毫秒,那我們可以來看第三行那邊,當三十豪秒到了,把這「緩衝時間倒數」歸零,代表說可以再發射一次雷射,否則,就要繼續開始慢慢的倒數到三十毫秒。
shoot
這個是寫說當我們發射雷射的限制,也就是剛剛講的cooldown,這裡就是說,如果「緩衝時間倒數」歸零,代表可以方射,那也就是說,要把雷射的圖檔、位置準備好後,把這新的雷射放進雷射的清單,再把「緩衝時間倒數」設為1。
get_width、get_height
這個的話,就跟「碰撞」有關係,很簡單,就是一直回傳自身的寬度和高度。
class Player
__init__
在這裡的話就其實跟上面的Class Laser的是同樣的道理,一定要有屬於自己的X, Y(移動),要有自己的照片(外觀),然後再下面的話就是把這一張外關圖,貼上這角色,給他一種面具的概念,也就是初始化。
move_Laser
把自己的畫面跟血條更新
def healthbar(self, window):
pygame.draw.rect(window, (255,0,0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width(), 10))
pygame.draw.rect(window, (0,255,0), (self.x, self.y + self.ship_img.get_height() + 10, self.ship_img.get_width() * (self.health/self.max_health), 10))
血條也會有自己的定位,照理來講當飛機一到哪,血條就要跟到哪,所以第二航道第三行就是在寫定位血條的位置,要比飛機低10個像素,那麼當被攻擊時自己的生命值也要減少,也就是讓紅色畫出來,讓綠色看起來越來越小,但是綠色一直都在,只是被紅色蓋掉了
class Enemy
COLOR_MAP = {
"red": (RED_SPACE_SHIP, RED_LASER),
"green": (GREEN_SPACE_SHIP, GREEN_LASER),
"blue": (BLUE_SPACE_SHIP, BLUE_LASER)
}
先設定說紅色會對應到圖片的位子,應該這麼說,紅色的話,就要去找紅色的飛船、飛彈,以此類推。
def __init__(self, x, y, color, health=100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOR_MAP[color]
self.mask = pygame.mask.from_surface(self.ship_img)
在敵人的飛船,會隨機射出不一樣的飛機,那麼在外觀上就回去COLOR_MAP會去找說紅色的飛機要對應哪一個外觀,然後再把這圖檔貼在自己身上。
def move(self, vel):
self.y += vel
並且當一切設置好之後,每次往下移動(vel:速度)
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x-20, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
敵對的雷射也會有自己的冷卻時間,當冷卻時間結束時,呼叫雷射的方程式,需要給敵對自己的座標、顏色,也不關生成了多少個雷射,都要記錄下來,變成一個清單,目的是為了當本體被射下來時,雷射以會跟著一起消失,最後冷卻時間設定為1。
def collide(obj1, obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
當物件之間的距離太少的話,代表兩物件碰撞了,一直重複去確認。
def main():
run = True
FPS = 60
level = 0
lives = 5
main_font = pygame.font.SysFont("comicsans", 50)
lost_font = pygame.font.SysFont("comicsans", 60)enemies = []
wave_length = 5
enemy_vel = 1player_vel = 5
laser_vel = 5player = Player(300, 630)clock = pygame.time.Clock()lost = False
lost_count = 0def redraw_window():
WIN.blit(BG, (0,0))
# draw text
lives_label = main_font.render(f"Lives: {lives}", 1, (255,255,255))
level_label = main_font.render(f"Level: {level}", 1, (255,255,255))WIN.blit(lives_label, (10, 10))
WIN.blit(level_label, (WIDTH - level_label.get_width() - 10, 10))for enemy in enemies:
enemy.draw(WIN)player.draw(WIN)if lost:
lost_label = lost_font.render("You Lost!!", 1, (255,255,255))
WIN.blit(lost_label, (WIDTH/2 - lost_label.get_width()/2, 350))pygame.display.update()while run:
clock.tick(FPS)
redraw_window()if lives <= 0 or player.health <= 0:
lost = True
lost_count += 1if lost:
if lost_count > FPS * 3:
run = False
else:
continueif len(enemies) == 0:
level += 1
wave_length += 5
for i in range(wave_length):
enemy = Enemy(random.randrange(50, WIDTH-100), random.randrange(-1500, -100), random.choice(["red", "blue", "green"]))
enemies.append(enemy)for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()keys = pygame.key.get_pressed()
if keys[pygame.K_a] and player.x - player_vel > 0: # left
player.x -= player_vel
if keys[pygame.K_d] and player.x + player_vel + player.get_width() < WIDTH: # right
player.x += player_vel
if keys[pygame.K_w] and player.y - player_vel > 0: # up
player.y -= player_vel
if keys[pygame.K_s] and player.y + player_vel + player.get_height() + 15 < HEIGHT: # down
player.y += player_vel
if keys[pygame.K_SPACE]:
player.shoot()for enemy in enemies[:]:
enemy.move(enemy_vel)
enemy.move_lasers(laser_vel, player)if random.randrange(0, 2*60) == 1:
enemy.shoot()if collide(enemy, player):
player.health -= 10
enemies.remove(enemy)
elif enemy.y + enemy.get_height() > HEIGHT:
lives -= 1
enemies.remove(enemy)player.move_lasers(-laser_vel, enemies)
開始遊戲的程式,設定字體大小、敵人的數量、用什麼字體⋯⋯,當遊戲還沒結束時,就必須執行有寫的函示,例如說:按下WASD移動、敵人發射的冷卻時間、鍵盤的射擊、自己的血量⋯⋯。整篇下來,就是這一個遊戲主要的程式。
def main_menu():
title_font = pygame.font.SysFont("comicsans", 70)
run = True
while run:
WIN.blit(BG, (0,0))
title_label = title_font.render("Press the mouse to begin...", 1, (255,255,255))
WIN.blit(title_label, (WIDTH/2 - title_label.get_width()/2, 350))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()main_menu()
這裡是開始遊戲的*畫面*,字體大小、定位、要寫說「按下滑鼠並開始」⋯⋯之類的全部設定好之後,等待玩家按下滑鼠,遊戲開始,並且開始遊戲主程式。