Python Pygame实战之塔防游戏的实现
作者:LexSaints 发布时间:2021-12-15 08:57:22
标签:Python,Pygame,塔防,游戏
一、环境要求
windows系统,python3.6+
安装模块
pip install pyqt5
pip install pygame
二、游戏介绍
1、游戏目标
按照关卡,设计不同的塔防地图(博主只设计了三关,有兴趣的同学,学会之后,可以自己画地图),设置三种炮台,每种炮台发射不同的炮弹,每种炮弹对敌人杀伤力不一样,每种
炮台的价格也不一样。玩家通过钱币购买炮台,并设有剪出炮台的操作(有点类似植物大战僵尸里的铲子)。敌人成功抵达塔楼,游戏结束。
2、先上游戏效果图
三、完整开发流程
1、项目主结构
首先,先整理一下项目的主结构,其实看一下主结构,基本就清晰了
2、详细配置
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'),
}
3、定义敌人、塔楼、 * 的类
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
4、游戏开始:选择难度地图
在这里我们把游戏主界面初始化,并把三个难度的地图列出来,让玩家选择。
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
5、游戏开始界面
包括开始按钮,退出游戏等操作
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
6、游戏运行
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)
7、游戏暂停
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
8、游戏结束及分数
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
9、引入音频、图片、地图、难度json
启动游戏主程序
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
即可 运行。
来源:https://lexsaints.blog.csdn.net/article/details/115672270


猜你喜欢
- 1、官网下载,并解压https://dev.mysql.com/downloads/mysql/2、设置环境变量配置MYSQL_HOME为M
- js汉字简繁转换源代码:<html> <head> <title>汉字简繁转换工具_asp之家</
- 自动追踪算法,在我们设计2D射击类游戏时经常会用到,这个听起来很高大上的东西,其实也并不是军事学的专利,在数学上解决的话需要去解微分方程,这
- 前言Tkinter(即 tk interface) 是 Python 标准 GUI 库,简称 “Tk&rdquo
- 迄今为止,导出/导入工具集仍是跨多个平台转移数据所需劳动强度最小的首选实用工具,尽管人们常常抱怨它速度太慢。导入只是将每条记录从导出转储文件
- Python可是真强大。但他具体是怎么强大的,让我们一点一点来了解吧(小编每天晚上下班回家会抽时间看看教程,多充实下自己也是好的)。废话不多
- 时间对象(Date())比较简单,本文旨在为初学者入门使用,大牛可略过!本文承接基础知识实例,说一下实例的要求:在页面中输出当前客户端时间(
- 本文为大家分享了mysql 8.0.16 压缩包安装配置方法,供大家参考,具体内容如下运行环境:Windows 10 x641、下载zip安
- 我们自己鼓捣mysql时,总免不了会遇到这个问题:插入中文字符出现乱码,虽然这是运维先给配好的环境,但是在自己机子上玩的时候咧,
- 本文实例讲述了SQL Server存储过程中编写事务处理的方法。分享给大家供大家参考,具体如下:SQL Server中数据库事务处理是相当有
- 我们之前写的代码都没有对前端input框输入的数据做验证,我们今天来看下,如果做form表单的数据的验证在views文件做验证首先用文字描述
- 通过视图来访问数据,其优点是非常明显的。如可以起到数据保密、保证数据的逻辑独立性、简化查询操作等等。但是,话说回来,SQL Server数据
- 题记:django如果要并和原有的数据库,那么就需要把现有数据库的表写入model.py中。一,在setting.py中配置好连接数据库的参
- 在项目中发现这样一个问题:sqlserver数据库编码为gbk,使用python3.4+pymssql 查询,中文乱码,经过一番思考问题解决
- 印象中最早看老外个人网站就挺纳闷,怎么人家都没有www,这样也可以?经过不断尝试,我发现确实不录入www要快捷的多,但不清楚怎么能做到。几年
- 闲来无事,想通过python来实现一些简单的游戏辅助脚本,而游戏辅助脚本的主要原理就是通过程序来查找游戏程序窗口,模拟实现鼠标点击和键盘按键
- 本文实例讲述了Golang算法之田忌赛马问题实现方法。分享给大家供大家参考,具体如下:【田忌赛马问题】输入:输入有多组测试数据。 每组测试数
- 本文实例讲述了python中bisect模块用法,分享给大家供大家参考。具体方法分析如下:这个模块只有几个函数,一旦决定使用二分搜索时,立马
- 呃,看到这个标题,我们可以首先将IE系浏览器无视了。我承认,我是有极简主义倾向的,我希望能够使用最少的代码和图片做更多的事情。虽然CSS3仅
- 最近的业务中涉及到这样一个需求,在线培训的系统需要知道用户对某个在线预览的页面追踪用户的预览时长。初步我们首先想到借助 Vue 页面的生命周