<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
windows系統,python3.6+
安裝模組
pip install pyqt5 pip install pygame
按照關卡,設計不同的塔防地圖(博主只設計了三關,有興趣的同學,學會之後,可以自己畫地圖),設定三種炮臺,每種炮臺發射不同的炮彈,每種炮彈對敵人殺傷力不一樣,每種
炮臺的價格也不一樣。玩家通過錢幣購買炮臺,並設有剪出炮臺的操作(有點類似植物大戰殭屍裡的鏟子)。敵人成功抵達塔樓,遊戲結束。
首先,先整理一下專案的主結構,其實看一下主結構,基本就清晰了
config.py
組態檔中,需要引入os模組,並且設定開啟遊戲的螢幕大小,並將資源中參照到的圖片、音訊插入到合適的位置。
因為我們的迷宮遊戲,需要劃開模組。
'''組態檔''' import os '''螢幕大小''' SCREENSIZE = (800, 600) '''圖片路徑''' IMAGEPATHS = { 'choice': { 'load_game': os.path.join(os.getcwd(), 'resources/images/choice/load_game.png'), 'map1': os.path.join(os.getcwd(), 'resources/images/choice/map1.png'), 'map1_black': os.path.join(os.getcwd(), 'resources/images/choice/map1_black.png'), 'map1_red': os.path.join(os.getcwd(), 'resources/images/choice/map1_red.png'), 'map2': os.path.join(os.getcwd(), 'resources/images/choice/map2.png'), 'map2_black': os.path.join(os.getcwd(), 'resources/images/choice/map2_black.png'), 'map2_red': os.path.join(os.getcwd(), 'resources/images/choice/map2_red.png'), 'map3': os.path.join(os.getcwd(), 'resources/images/choice/map3.png'), 'map3_black': os.path.join(os.getcwd(), 'resources/images/choice/map3_black.png'), 'map3_red': os.path.join(os.getcwd(), 'resources/images/choice/map3_red.png'), }, 'end': { 'gameover': os.path.join(os.getcwd(), 'resources/images/end/gameover.png'), 'continue_red': os.path.join(os.getcwd(), 'resources/images/end/continue_red.png'), 'continue_black': os.path.join(os.getcwd(), 'resources/images/end/continue_black.png'), }, 'game': { 'arrow1': os.path.join(os.getcwd(), 'resources/images/game/arrow1.png'), 'arrow2': os.path.join(os.getcwd(), 'resources/images/game/arrow2.png'), 'arrow3': os.path.join(os.getcwd(), 'resources/images/game/arrow3.png'), 'basic_tower': os.path.join(os.getcwd(), 'resources/images/game/basic_tower.png'), 'boulder': os.path.join(os.getcwd(), 'resources/images/game/boulder.png'), 'bush': os.path.join(os.getcwd(), 'resources/images/game/bush.png'), 'cave': os.path.join(os.getcwd(), 'resources/images/game/cave.png'), 'dirt': os.path.join(os.getcwd(), 'resources/images/game/dirt.png'), 'enemy_blue': os.path.join(os.getcwd(), 'resources/images/game/enemy_blue.png'), 'enemy_pink': os.path.join(os.getcwd(), 'resources/images/game/enemy_pink.png'), 'enemy_red': os.path.join(os.getcwd(), 'resources/images/game/enemy_red.png'), 'enemy_yellow': os.path.join(os.getcwd(), 'resources/images/game/enemy_yellow.png'), 'godark': os.path.join(os.getcwd(), 'resources/images/game/godark.png'), 'golight': os.path.join(os.getcwd(), 'resources/images/game/golight.png'), 'grass': os.path.join(os.getcwd(), 'resources/images/game/grass.png'), 'healthfont': os.path.join(os.getcwd(), 'resources/images/game/healthfont.png'), 'heavy_tower': os.path.join(os.getcwd(), 'resources/images/game/heavy_tower.png'), 'med_tower': os.path.join(os.getcwd(), 'resources/images/game/med_tower.png'), 'nexus': os.path.join(os.getcwd(), 'resources/images/game/nexus.png'), 'othergrass': os.path.join(os.getcwd(), 'resources/images/game/othergrass.png'), 'path': os.path.join(os.getcwd(), 'resources/images/game/path.png'), 'rock': os.path.join(os.getcwd(), 'resources/images/game/rock.png'), 'tiles': os.path.join(os.getcwd(), 'resources/images/game/tiles.png'), 'unitfont': os.path.join(os.getcwd(), 'resources/images/game/unitfont.png'), 'water': os.path.join(os.getcwd(), 'resources/images/game/water.png'), 'x': os.path.join(os.getcwd(), 'resources/images/game/x.png'), }, 'pause': { 'gamepaused': os.path.join(os.getcwd(), 'resources/images/pause/gamepaused.png'), 'resume_black': os.path.join(os.getcwd(), 'resources/images/pause/resume_black.png'), 'resume_red': os.path.join(os.getcwd(), 'resources/images/pause/resume_red.png'), }, 'start': { 'play_black': os.path.join(os.getcwd(), 'resources/images/start/play_black.png'), 'play_red': os.path.join(os.getcwd(), 'resources/images/start/play_red.png'), 'quit_black': os.path.join(os.getcwd(), 'resources/images/start/quit_black.png'), 'quit_red': os.path.join(os.getcwd(), 'resources/images/start/quit_red.png'), 'start_interface': os.path.join(os.getcwd(), 'resources/images/start/start_interface.png'), }, } '''地圖路徑''' MAPPATHS = { '1': os.path.join(os.getcwd(), 'resources/maps/1.map'), '2': os.path.join(os.getcwd(), 'resources/maps/2.map'), '3': os.path.join(os.getcwd(), 'resources/maps/3.map'), } '''字型路徑''' FONTPATHS = { 'Calibri': os.path.join(os.getcwd(), 'resources/fonts/Calibri.ttf'), 'm04': os.path.join(os.getcwd(), 'resources/fonts/m04.ttf'), 'Microsoft Sans Serif': os.path.join(os.getcwd(), 'resources/fonts/Microsoft Sans Serif.ttf'), } '''不同難度的settings''' DIFFICULTYPATHS = { 'easy': os.path.join(os.getcwd(), 'resources/difficulties/easy.json'), 'hard': os.path.join(os.getcwd(), 'resources/difficulties/hard.json'), 'medium': os.path.join(os.getcwd(), 'resources/difficulties/medium.json'), } '''音訊路徑''' AUDIOPATHS = { 'bgm': os.path.join(os.getcwd(), 'resources/audios/bgm.mp3'), }
turrent.py 以炮塔類為例
炮塔首先需要初始化,即init函數,另外需要有射擊屬性、以及重置屬性(即我們可以將已經建好的炮塔刪除,然後重新新增)
import pygame from .arrow import Arrow '''炮塔類''' class Turret(pygame.sprite.Sprite): def __init__(self, turret_type, cfg): assert turret_type in range(3) pygame.sprite.Sprite.__init__(self) self.cfg = cfg self.turret_type = turret_type self.imagepaths = [cfg.IMAGEPATHS['game']['basic_tower'], cfg.IMAGEPATHS['game']['med_tower'], cfg.IMAGEPATHS['game']['heavy_tower']] self.image = pygame.image.load(self.imagepaths[turret_type]) self.rect = self.image.get_rect() # 箭 self.arrow = Arrow(turret_type, cfg) # 當前的位置 self.coord = 0, 0 self.position = 0, 0 self.rect.left, self.rect.top = self.position self.reset() '''射擊''' def shot(self, position, angle=None): arrow = None if not self.is_cooling: arrow = Arrow(self.turret_type, self.cfg) arrow.reset(position, angle) self.is_cooling = True if self.is_cooling: self.cool_time -= 1 if self.cool_time == 0: self.reset() return arrow '''重置''' def reset(self): if self.turret_type == 0: # 價格 self.price = 500 # 射箭的冷卻時間 self.cool_time = 30 # 是否在冷卻期 self.is_cooling = False elif self.turret_type == 1: self.price = 1000 self.cool_time = 50 self.is_cooling = False elif self.turret_type == 2: self.price = 1500 self.cool_time = 100 self.is_cooling = False
在這裡我們把遊戲主介面初始化,並把三個難度的地圖列出來,讓玩家選擇。
import sys import pygame '''遊戲選擇主介面''' class MainInterface(pygame.sprite.Sprite): def __init__(self, cfg): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(cfg.IMAGEPATHS['choice']['load_game']).convert() self.rect = self.image.get_rect() self.rect.topleft = (0, 0) '''更新函數''' def update(self): pass '''地圖1''' class MapButton1(pygame.sprite.Sprite): def __init__(self, cfg, position=(175, 240)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['choice']['map1_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['choice']['map1_red']).convert() self.image_3 = pygame.image.load(cfg.IMAGEPATHS['choice']['map1']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''地圖2''' class MapButton2(pygame.sprite.Sprite): def __init__(self, cfg, position=(400, 240)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['choice']['map2_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['choice']['map2_red']).convert() self.image_3 = pygame.image.load(cfg.IMAGEPATHS['choice']['map2']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''地圖3''' class MapButton3(pygame.sprite.Sprite): def __init__(self, cfg, position=(625, 240)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['choice']['map3_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['choice']['map3_red']).convert() self.image_3 = pygame.image.load(cfg.IMAGEPATHS['choice']['map3']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''資訊顯示框''' class InfoBox(pygame.sprite.Sprite): def __init__(self, position=(400, 475)): pygame.sprite.Sprite.__init__(self) self.ori_image = pygame.Surface((625, 200)) self.ori_image.fill((255, 255, 255)) self.ori_image_front = pygame.Surface((621, 196)) self.ori_image_front.fill((0, 0, 0)) self.ori_image.blit(self.ori_image_front, (2, 2)) self.rect = self.ori_image.get_rect() self.rect.center = position '''更新函數''' def update(self, btns): self.image = self.ori_image mouse_pos = pygame.mouse.get_pos() for btn in btns: if btn.rect.collidepoint(mouse_pos): self.image.blit(btn.image_3, (225, 25)) break '''簡單難度按鈕''' class EasyButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(400, 150)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.Surface((285, 100)) self.image_1_front = pygame.Surface((281, 96)) self.image_1.fill((255, 255, 255)) self.image_1_front.fill((0, 0, 0)) self.image_1.blit(self.image_1_front, (2, 2)) self.image_2 = pygame.Surface((285, 100)) self.image_2_front = pygame.Surface((281, 96)) self.image_2.fill((255, 255, 255)) self.image_2_front.fill((24, 196, 40)) self.image_2.blit(self.image_2_front, (2, 2)) self.text = 'easy' self.font = pygame.font.Font(cfg.FONTPATHS['m04'], 42) self.text_render = self.font.render(self.text, 1, (255, 255, 255)) self.image_1.blit(self.text_render, (60, 29)) self.image_2.blit(self.text_render, (60, 29)) self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''中等難度按鈕''' class MediumButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(400, 300)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.Surface((285, 100)) self.image_1_front = pygame.Surface((281, 96)) self.image_1.fill((255, 255, 255)) self.image_1_front.fill((0, 0, 0)) self.image_1.blit(self.image_1_front, (2, 2)) self.image_2 = pygame.Surface((285, 100)) self.image_2_front = pygame.Surface((281, 96)) self.image_2.fill((255, 255, 255)) self.image_2_front.fill((24, 30, 196)) self.image_2.blit(self.image_2_front, (2, 2)) self.text = 'medium' self.font = pygame.font.Font(cfg.FONTPATHS['m04'], 42) self.text_render = self.font.render(self.text, 1, (255, 255, 255)) self.image_1.blit(self.text_render, (15, 29)) self.image_2.blit(self.text_render, (15, 29)) self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''困難難度按鈕''' class HardButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(400, 450)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.Surface((285, 100)) self.image_1_front = pygame.Surface((281, 96)) self.image_1.fill((255, 255, 255)) self.image_1_front.fill((0, 0, 0)) self.image_1.blit(self.image_1_front, (2, 2)) self.image_2 = pygame.Surface((285, 100)) self.image_2_front = pygame.Surface((281, 96)) self.image_2.fill((255, 255, 255)) self.image_2_front.fill((196, 24, 24)) self.image_2.blit(self.image_2_front, (2, 2)) self.text = 'hard' self.font = pygame.font.Font(cfg.FONTPATHS['m04'], 42) self.text_render = self.font.render(self.text, 1, (255, 255, 255)) self.image_1.blit(self.text_render, (60, 29)) self.image_2.blit(self.text_render, (60, 29)) self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''遊戲地圖和困難選擇介面''' class ChoiceInterface(): def __init__(self, cfg): # part1 self.main_interface = MainInterface(cfg) self.map_btn1 = MapButton1(cfg) self.map_btn2 = MapButton2(cfg) self.map_btn3 = MapButton3(cfg) self.info_box = InfoBox() # part2 self.easy_btn = EasyButton(cfg) self.medium_btn = MediumButton(cfg) self.hard_btn = HardButton(cfg) '''外部呼叫''' def update(self, screen): clock = pygame.time.Clock() # part1 self.map_btns = pygame.sprite.Group(self.map_btn1, self.map_btn2, self.map_btn3) map_choice, difficulty_choice = None, None while True: clock.tick(60) self.main_interface.update() self.map_btns.update() self.info_box.update(self.map_btns) screen.blit(self.main_interface.image, self.main_interface.rect) self.map_btns.draw(screen) screen.blit(self.info_box.image, self.info_box.rect) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: mouse_pos = pygame.mouse.get_pos() idx = 0 for btn in self.map_btns: idx += 1 if btn.rect.collidepoint(mouse_pos): map_choice = idx if map_choice: break # part2 self.difficulty_btns = pygame.sprite.Group(self.easy_btn, self.medium_btn, self.hard_btn) while True: clock.tick(60) screen.fill((0, 0, 0)) self.difficulty_btns.update() self.difficulty_btns.draw(screen) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: mouse_pos = pygame.mouse.get_pos() idx = 0 for btn in self.difficulty_btns: idx += 1 if btn.rect.collidepoint(mouse_pos): difficulty_choice = btn.text if difficulty_choice: break return map_choice, difficulty_choice
包括開始按鈕,退出遊戲等操作
start.py
import sys import pygame '''遊戲開始主介面''' class MainInterface(pygame.sprite.Sprite): def __init__(self, cfg): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(cfg.IMAGEPATHS['start']['start_interface']).convert() self.rect = self.image.get_rect() self.rect.center = cfg.SCREENSIZE[0] / 2, cfg.SCREENSIZE[1] / 2 '''更新函數''' def update(self): pass '''開始遊戲按鈕''' class PlayButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(220, 415)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['start']['play_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['start']['play_red']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''結束遊戲按鈕''' class QuitButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(580, 415)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['start']['quit_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['start']['quit_red']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''遊戲開始介面''' class StartInterface(): def __init__(self, cfg): self.main_interface = MainInterface(cfg) self.play_btn = PlayButton(cfg) self.quit_btn = QuitButton(cfg) self.components = pygame.sprite.LayeredUpdates(self.main_interface, self.play_btn, self.quit_btn) '''外部呼叫''' def update(self, screen): clock = pygame.time.Clock() while True: clock.tick(60) self.components.update() self.components.draw(screen) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: mouse_pos = pygame.mouse.get_pos() if self.play_btn.rect.collidepoint(mouse_pos): return True elif self.quit_btn.rect.collidepoint(mouse_pos): return False
gaming.py
import sys import json import math import random import pygame from ..sprites import Enemy from ..sprites import Turret from .pause import PauseInterface from collections import namedtuple '''遊戲進行中介面''' class GamingInterface(): def __init__(self, cfg): self.cfg = cfg # 遊戲地圖大小 map_w = self.cfg.SCREENSIZE[0] map_h = 500 # 按鈕大小和位置 button_w = 60 button_h = 60 button_y = 520 # 間隙 gap = 20 # 按鈕放在工具列, 工具列兩端各有一個資訊顯示框 toolbar_w = gap * 7 + button_w * 6 info_w = (self.cfg.SCREENSIZE[0] - toolbar_w) // 2 info_h = self.cfg.SCREENSIZE[1] - map_h toolbar_h = self.cfg.SCREENSIZE[1] - map_h # 介面佈置 self.map_rect = pygame.Rect(0, 0, map_w, map_h) self.map_surface = pygame.Surface((map_w, map_h)) self.leftinfo_rect = pygame.Rect(0, map_h, info_w, info_h) self.rightinfo_rect = pygame.Rect(self.cfg.SCREENSIZE[0] - info_w, map_h, info_w, info_h) self.toolbar_rect = pygame.Rect(info_w, map_h, toolbar_w, toolbar_h) # 草 self.grass = pygame.image.load(cfg.IMAGEPATHS['game']['grass']) # 岩石(鋪路用的) self.rock = pygame.image.load(cfg.IMAGEPATHS['game']['rock']) # 汙垢 self.dirt = pygame.image.load(cfg.IMAGEPATHS['game']['dirt']) # 水 self.water = pygame.image.load(cfg.IMAGEPATHS['game']['water']) # 灌木 self.bush = pygame.image.load(cfg.IMAGEPATHS['game']['bush']) # 紐帶 self.nexus = pygame.image.load(cfg.IMAGEPATHS['game']['nexus']) # 洞穴 self.cave = pygame.image.load(cfg.IMAGEPATHS['game']['cave']) # 獲取地圖元素的大小,請保證素材庫裡組成地圖的元素圖大小一致 self.element_size = int(self.grass.get_rect().width) # 一些字型 self.info_font = pygame.font.Font(cfg.FONTPATHS['Calibri'], 14) self.button_font = pygame.font.Font(cfg.FONTPATHS['Calibri'], 20) # 可以放炮塔的地方 self.placeable = {0: self.grass} # 地圖元素字典(數位對應.map檔案中的數位) self.map_elements = { 0: self.grass, 1: self.rock, 2: self.dirt, 3: self.water, 4: self.bush, 5: self.nexus, 6: self.cave } # 用於記錄地圖中的道路 self.path_list = [] # 當前的地圖,將地圖匯入到這裡面 self.current_map = dict() # 當前滑鼠攜帶的圖示(即選中道具) -> [道具名, 道具] self.mouse_carried = [] # 在地圖上建造好了的炮塔 self.built_turret_group = pygame.sprite.Group() # 所有的敵人 self.enemies_group = pygame.sprite.Group() # 所有射出的箭 self.arrows_group = pygame.sprite.Group() # 玩家操作用的按鈕 Button = namedtuple('Button', ['rect', 'text', 'onClick']) self.buttons = [ Button(pygame.Rect((info_w + gap), button_y, button_w, button_h), 'T1', self.takeT1), Button(pygame.Rect((info_w + gap * 2 + button_w), button_y, button_w, button_h), 'T2', self.takeT2), Button(pygame.Rect((info_w + gap * 3 + button_w * 2), button_y, button_w, button_h), 'T3', self.takeT3), Button(pygame.Rect((info_w + gap * 4 + button_w * 3), button_y, button_w, button_h), 'XXX', self.takeXXX), Button(pygame.Rect((info_w + gap * 5 + button_w * 4), button_y, button_w, button_h), 'Pause', self.pauseGame), Button(pygame.Rect((info_w + gap * 6 + button_w * 5), button_y, button_w, button_h), 'Quit', self.quitGame) ] '''開始遊戲''' def start(self, screen, map_path=None, difficulty_path=None): # 讀取遊戲難度對應的引數 with open(difficulty_path, 'r') as f: difficulty_dict = json.load(f) self.money = difficulty_dict.get('money') self.health = difficulty_dict.get('health') self.max_health = difficulty_dict.get('health') difficulty_dict = difficulty_dict.get('enemy') # 每60s生成一波敵人 generate_enemies_event = pygame.constants.USEREVENT + 0 pygame.time.set_timer(generate_enemies_event, 60000) # 生成敵人的flag和當前已生成敵人的總次數 generate_enemies_flag = False num_generate_enemies = 0 # 每0.5秒出一個敵人 generate_enemy_event = pygame.constants.USEREVENT + 1 pygame.time.set_timer(generate_enemy_event, 500) generate_enemy_flag = False # 防止變數未定義 enemy_range = None num_enemy = None # 是否手動操作箭塔射擊 manual_shot = False has_control = False # 遊戲主迴圈 while True: if self.health <= 0: return for event in pygame.event.get(): if event.type == pygame.QUIT: self.quitGame() if event.type == pygame.MOUSEBUTTONUP: # --左鍵選物品 if event.button == 1: # ----滑鼠點選在地圖上 if self.map_rect.collidepoint(event.pos): if self.mouse_carried: if self.mouse_carried[0] == 'turret': self.buildTurret(event.pos) elif self.mouse_carried[0] == 'XXX': self.sellTurret(event.pos) # ----滑鼠點選在工具列 elif self.toolbar_rect.collidepoint(event.pos): for button in self.buttons: if button.rect.collidepoint(event.pos): if button.text == 'T1': button.onClick() elif button.text == 'T2': button.onClick() elif button.text == 'T3': button.onClick() elif button.text == 'XXX': button.onClick() elif button.text == 'Pause': button.onClick(screen) elif button.text == 'Quit': button.onClick() break # --右鍵釋放物品 if event.button == 3: self.mouse_carried = [] # --按中間鍵手動控制炮塔射箭方向一次,否則自由射箭 if event.button == 2: manual_shot = True if event.type == generate_enemies_event: generate_enemies_flag = True if event.type == generate_enemy_event: generate_enemy_flag = True # --生成敵人, 生成的敵人隨當前已生成敵人的總次數的增加而變強變多 if generate_enemies_flag: generate_enemies_flag = False num_generate_enemies += 1 idx = 0 for key, value in difficulty_dict.items(): idx += 1 if idx == len(difficulty_dict.keys()): enemy_range = value['enemy_range'] num_enemy = value['num_enemy'] break if num_generate_enemies <= int(key): enemy_range = value['enemy_range'] num_enemy = value['num_enemy'] break if generate_enemy_flag and num_enemy: generate_enemy_flag = False num_enemy -= 1 enemy = Enemy(random.choice(range(enemy_range)), self.cfg) self.enemies_group.add(enemy) # --射箭 for turret in self.built_turret_group: if not manual_shot: position = turret.position[0] + self.element_size // 2, turret.position[1] arrow = turret.shot(position) else: position = turret.position[0] + self.element_size // 2, turret.position[1] mouse_pos = pygame.mouse.get_pos() angle = math.atan((mouse_pos[1] - position[1]) / (mouse_pos[0] - position[0] + 1e-6)) arrow = turret.shot(position, angle) has_control = True if arrow: self.arrows_group.add(arrow) else: has_control = False if has_control: has_control = False manual_shot = False # --移動箭和碰撞檢測 for arrow in self.arrows_group: arrow.move() points = [(arrow.rect.left, arrow.rect.top), (arrow.rect.left, arrow.rect.bottom), (arrow.rect.right, arrow.rect.top), (arrow.rect.right, arrow.rect.bottom)] if (not self.map_rect.collidepoint(points[0])) and (not self.map_rect.collidepoint(points[1])) and (not self.map_rect.collidepoint(points[2])) and (not self.map_rect.collidepoint(points[3])): self.arrows_group.remove(arrow) del arrow continue for enemy in self.enemies_group: if pygame.sprite.collide_rect(arrow, enemy): enemy.life_value -= arrow.attack_power self.arrows_group.remove(arrow) del arrow break self.draw(screen, map_path) '''將場景畫到遊戲介面上''' def draw(self, screen, map_path): self.drawToolbar(screen) self.loadMap(screen, map_path) self.drawMouseCarried(screen) self.drawBuiltTurret(screen) self.drawEnemies(screen) self.drawArrows(screen) pygame.display.flip() '''畫出所有射出的箭''' def drawArrows(self, screen): for arrow in self.arrows_group: screen.blit(arrow.image, arrow.rect) '''畫敵人''' def drawEnemies(self, screen): for enemy in self.enemies_group: if enemy.life_value <= 0: self.money += enemy.reward self.enemies_group.remove(enemy) del enemy continue res = enemy.move(self.element_size) if res: coord = self.find_next_path(enemy) if coord: enemy.reached_path.append(enemy.coord) enemy.coord = coord enemy.position = self.coord2pos(coord) enemy.rect.left, enemy.rect.top = enemy.position else: self.health -= enemy.damage self.enemies_group.remove(enemy) del enemy continue # 畫血條 green_len = max(0, enemy.life_value / enemy.max_life_value) * self.element_size if green_len > 0: pygame.draw.line(screen, (0, 255, 0), (enemy.position), (enemy.position[0] + green_len, enemy.position[1]), 1) if green_len < self.element_size: pygame.draw.line(screen, (255, 0, 0), (enemy.position[0] + green_len, enemy.position[1]), (enemy.position[0] + self.element_size, enemy.position[1]), 1) screen.blit(enemy.image, enemy.rect) '''畫已經建造好的炮塔''' def drawBuiltTurret(self, screen): for turret in self.built_turret_group: screen.blit(turret.image, turret.rect) '''畫滑鼠攜帶物''' def drawMouseCarried(self, screen): if self.mouse_carried: position = pygame.mouse.get_pos() coord = self.pos2coord(position) position = self.coord2pos(coord) # 在地圖裡再畫 if self.map_rect.collidepoint(position): if self.mouse_carried[0] == 'turret': screen.blit(self.mouse_carried[1].image, position) self.mouse_carried[1].coord = coord self.mouse_carried[1].position = position self.mouse_carried[1].rect.left, self.mouse_carried[1].rect.top = position else: screen.blit(self.mouse_carried[1], position) '''畫工具列''' def drawToolbar(self, screen): # 資訊顯示框 info_color = (120, 20, 50) # --左 pygame.draw.rect(screen, info_color, self.leftinfo_rect) left_title = self.info_font.render('Player info:', True, (255, 255, 255)) money_info = self.info_font.render('Money: ' + str(self.money), True, (255, 255, 255)) health_info = self.info_font.render('Health: ' + str(self.health), True, (255, 255, 255)) screen.blit(left_title, (self.leftinfo_rect.left + 5, self.leftinfo_rect.top + 5)) screen.blit(money_info, (self.leftinfo_rect.left + 5, self.leftinfo_rect.top + 35)) screen.blit(health_info, (self.leftinfo_rect.left + 5, self.leftinfo_rect.top + 55)) # --右 pygame.draw.rect(screen, info_color, self.rightinfo_rect) right_title = self.info_font.render('Selected info:', True, (255, 255, 255)) screen.blit(right_title, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 5)) # 中間部分 pygame.draw.rect(screen, (127, 127, 127), self.toolbar_rect) for button in self.buttons: mouse_pos = pygame.mouse.get_pos() if button.rect.collidepoint(mouse_pos): self.showSelectedInfo(screen, button) button_color = (0, 200, 0) else: button_color = (0, 100, 0) pygame.draw.rect(screen, button_color, button.rect) button_text = self.button_font.render(button.text, True, (255, 255, 255)) button_text_rect = button_text.get_rect() button_text_rect.center = (button.rect.centerx, button.rect.centery) screen.blit(button_text, button_text_rect) '''顯示被滑鼠選中按鈕的作用資訊''' def showSelectedInfo(self, screen, button): if button.text in ['T1', 'T2', 'T3']: turret = Turret({'T1': 0, 'T2': 1, 'T3': 2}[button.text], self.cfg) selected_info1 = self.info_font.render('Cost: ' + str(turret.price), True, (255, 255, 255)) selected_info2 = self.info_font.render('Damage: ' + str(turret.arrow.attack_power), True, (255, 255, 255)) selected_info3 = self.info_font.render('Affordable: ' + str(self.money >= turret.price), True, (255, 255, 255)) screen.blit(selected_info1, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 35)) screen.blit(selected_info2, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 55)) screen.blit(selected_info3, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 75)) elif button.text == 'XXX': selected_info = self.info_font.render('Sell a turret', True, (255, 255, 255)) screen.blit(selected_info, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 35)) elif button.text == 'Pause': selected_info = self.info_font.render('Pause game', True, (255, 255, 255)) screen.blit(selected_info, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 35)) elif button.text == 'Quit': selected_info = self.info_font.render('Quit game', True, (255, 255, 255)) screen.blit(selected_info, (self.rightinfo_rect.left + 5, self.rightinfo_rect.top + 35)) '''出售炮塔(半價)''' def sellTurret(self, position): coord = self.pos2coord(position) for turret in self.built_turret_group: if coord == turret.coord: self.built_turret_group.remove(turret) self.money += int(turret.price * 0.5) del turret break '''建造炮塔''' def buildTurret(self, position): turret = self.mouse_carried[1] coord = self.pos2coord(position) position = self.coord2pos(coord) turret.position = position turret.coord = coord turret.rect.left, turret.rect.top = position if self.money - turret.price >= 0: if self.current_map.get(turret.coord) in self.placeable.keys(): self.money -= turret.price self.built_turret_group.add(turret) if self.mouse_carried[1].turret_type == 0: self.mouse_carried = [] self.takeT1() elif self.mouse_carried[1].turret_type == 1: self.mouse_carried = [] self.takeT2() elif self.mouse_carried[1].turret_type == 2: self.mouse_carried = [] self.takeT3() '''拿炮塔1''' def takeT1(self): T1 = Turret(0, self.cfg) if self.money >= T1.price: self.mouse_carried = ['turret', T1] '''拿炮塔2''' def takeT2(self): T2 = Turret(1, self.cfg) if self.money >= T2.price: self.mouse_carried = ['turret', T2] '''拿炮塔3''' def takeT3(self): T3 = Turret(2, self.cfg) if self.money >= T3.price: self.mouse_carried = ['turret', T3] '''出售炮塔''' def takeXXX(self): XXX = pygame.image.load(self.cfg.IMAGEPATHS['game']['x']) self.mouse_carried = ['XXX', XXX] '''找下一個路徑單元''' def find_next_path(self, enemy): x, y = enemy.coord # 優先順序: 下右左上 neighbours = [(x, y+1), (x+1, y), (x-1, y), (x, y-1)] for neighbour in neighbours: if (neighbour in self.path_list) and (neighbour not in enemy.reached_path): return neighbour return None '''將真實座標轉為地圖座標, 20個單位長度的真實座標=地圖座標''' def pos2coord(self, position): return (position[0] // self.element_size, position[1] // self.element_size) '''將地圖座標轉為真實座標, 20個單位長度的真實座標=地圖座標''' def coord2pos(self, coord): return (coord[0] * self.element_size, coord[1] * self.element_size) '''匯入地圖''' def loadMap(self, screen, map_path): map_file = open(map_path, 'r') idx_j = -1 for line in map_file.readlines(): line = line.strip() if not line: continue idx_j += 1 idx_i = -1 for col in line: try: element_type = int(col) element_img = self.map_elements.get(element_type) element_rect = element_img.get_rect() idx_i += 1 element_rect.left, element_rect.top = self.element_size * idx_i, self.element_size * idx_j self.map_surface.blit(element_img, element_rect) self.current_map[idx_i, idx_j] = element_type # 把道路記下來 if element_type == 1: self.path_list.append((idx_i, idx_j)) except: continue # 放洞穴和大本營 self.map_surface.blit(self.cave, (0, 0)) self.map_surface.blit(self.nexus, (740, 400)) # 大本營的血條 nexus_width = self.nexus.get_rect().width green_len = max(0, self.health / self.max_health) * nexus_width if green_len > 0: pygame.draw.line(self.map_surface, (0, 255, 0), (740, 400), (740 + green_len, 400), 3) if green_len < nexus_width: pygame.draw.line(self.map_surface, (255, 0, 0), (740 + green_len, 400), (740 + nexus_width, 400), 3) screen.blit(self.map_surface, (0, 0)) map_file.close() '''暫停遊戲''' def pauseGame(self, screen): pause_interface = PauseInterface(self.cfg) pause_interface.update(screen) '''退出遊戲''' def quitGame(self): pygame.quit() sys.exit(0)
import sys import pygame '''遊戲暫停主介面''' class MainInterface(pygame.sprite.Sprite): def __init__(self, cfg): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(cfg.IMAGEPATHS['pause']['gamepaused']).convert() self.rect = self.image.get_rect() self.rect.center = cfg.SCREENSIZE[0] / 2, cfg.SCREENSIZE[1] / 2 '''更新函數''' def update(self): pass '''恢復遊戲按鈕''' class ResumeButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(391, 380)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['pause']['resume_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['pause']['resume_red']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''遊戲暫停介面''' class PauseInterface(): def __init__(self, cfg): self.main_interface = MainInterface(cfg) self.resume_btn = ResumeButton(cfg) self.components = pygame.sprite.LayeredUpdates(self.main_interface, self.resume_btn) '''外部呼叫''' def update(self, screen): clock = pygame.time.Clock() background = pygame.Surface(screen.get_size()) count = 0 flag = True while True: count += 1 clock.tick(60) self.components.clear(screen, background) self.components.update() if count % 10 == 0: count = 0 flag = not flag if flag: self.components.draw(screen) else: screen.blit(self.main_interface.image, self.main_interface.rect) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: mouse_pos = pygame.mouse.get_pos() if self.resume_btn.rect.collidepoint(mouse_pos): return True
import sys import pygame '''遊戲結束主介面''' class MainInterface(pygame.sprite.Sprite): def __init__(self, cfg): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(cfg.IMAGEPATHS['end']['gameover']).convert() self.rect = self.image.get_rect() self.rect.center = cfg.SCREENSIZE[0] / 2, cfg.SCREENSIZE[1] / 2 '''更新函數''' def update(self): pass '''繼續遊戲按鈕''' class ContinueButton(pygame.sprite.Sprite): def __init__(self, cfg, position=(400, 409)): pygame.sprite.Sprite.__init__(self) self.image_1 = pygame.image.load(cfg.IMAGEPATHS['end']['continue_black']).convert() self.image_2 = pygame.image.load(cfg.IMAGEPATHS['end']['continue_red']).convert() self.image = self.image_1 self.rect = self.image.get_rect() self.rect.center = position '''更新函數: 不斷地更新檢測滑鼠是否在按鈕上''' def update(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): self.image = self.image_2 else: self.image = self.image_1 '''遊戲結束類''' class EndInterface(): def __init__(self, cfg): self.main_interface = MainInterface(cfg) self.continue_btn = ContinueButton(cfg) self.components = pygame.sprite.LayeredUpdates(self.main_interface, self.continue_btn) '''外部呼叫''' def update(self, screen): clock = pygame.time.Clock() background = pygame.Surface(screen.get_size()) count = 0 flag = True while True: count += 1 clock.tick(60) self.components.clear(screen, background) self.components.update() if count % 10 == 0: count = 0 flag = not flag if flag: self.components.draw(screen) else: screen.blit(self.main_interface.image, self.main_interface.rect) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: mouse_pos = pygame.mouse.get_pos() if self.continue_btn.rect.collidepoint(mouse_pos): return True
啟動遊戲主程式
tower.py
import cfg import pygame from modules import * '''主函數''' def main(): pygame.init() pygame.mixer.init() pygame.mixer.music.load(cfg.AUDIOPATHS['bgm']) pygame.mixer.music.play(-1, 0.0) pygame.mixer.music.set_volume(0.25) screen = pygame.display.set_mode(cfg.SCREENSIZE) pygame.display.set_caption("塔防遊戲 —— hacklex") # 呼叫遊戲開始介面 start_interface = StartInterface(cfg) is_play = start_interface.update(screen) if not is_play: return # 呼叫遊戲介面 while True: choice_interface = ChoiceInterface(cfg) map_choice, difficulty_choice = choice_interface.update(screen) game_interface = GamingInterface(cfg) game_interface.start(screen, map_path=cfg.MAPPATHS[str(map_choice)], difficulty_path=cfg.DIFFICULTYPATHS[str(difficulty_choice)]) end_interface = EndInterface(cfg) end_interface.update(screen) '''run''' if __name__ == '__main__': main()
1、開發工具啟動
如果你設定了開發工具的環境VScode、sublimeText、notepad+、pycharm什麼的,可以直接在工具中,執行遊戲。
如果沒設定,可以使用命令啟動。
2、命令列啟動 gif
進入程式碼根目錄,按住shift+滑鼠右鍵,選擇 此處執行powershell,然後執行程式碼:python tower.py
即可 執行。
以上就是Python Pygame實戰之塔防遊戲的實現的詳細內容,更多關於Python Pygame塔防遊戲的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45