首页 > 编程语言 >PyZelda 源码解析(全)

PyZelda 源码解析(全)

时间:2024-02-14 22:56:37浏览次数:28  
标签:os self player 源码 pygame import 解析 PyZelda rect

.\Zelda-with-Python\Code\Debug.py


# 导入pygame和os模块
import pygame
import os

# 设置当前工作目录为项目所在的目录,用于导入文件(特别是图片)
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 初始化pygame
pygame.init()

# 创建字体对象,用于在屏幕上显示调试信息
font = pygame.font.Font(None, 30)

# 定义一个函数用于在屏幕上显示调试信息
def debug(info, y = 10, x = 10):
    # 获取当前显示的表面
    display_surface = pygame.display.get_surface()
    # 创建一个包含调试信息的表面
    debug_surf = font.render(str(info), True, "White")
    # 获取调试信息表面的矩形
    debug_rect = debug_surf.get_rect(topleft = (x, y))
    # 在屏幕上绘制一个黑色矩形
    pygame.draw.rect(display_surface, "Black", debug_rect)
    # 在屏幕上显示调试信息
    display_surface.blit(debug_surf, debug_rect)

.\Zelda-with-Python\Code\Enemy.py


# 导入 pygame 模块
import pygame
# 从 Settings 模块中导入所有内容
from Settings import *
# 从 Entity 模块中导入 Entity 类
from Entity import Entity
# 从 Support 模块中导入所有内容
from Support import *
# 导入 os 模块
import os

# 将当前工作目录更改为 Main.py 文件所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

.\Zelda-with-Python\Code\Entity.py


# 从 cmath 模块中导入 rect 函数
from cmath import rect
# 导入 pygame 模块
import pygame
# 从 math 模块中导入 sin 函数
from math import sin
# 导入 os 模块
import os

# 这是用于文件(特别是图片)导入的代码(这行代码将目录更改为项目保存的位置)
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 创建实体类,继承自 pygame.sprite.Sprite 类
class Entity(pygame.sprite.Sprite):
    # 初始化方法
    def __init__(self, groups):
        # 调用父类的初始化方法
        super().__init__(groups)
        # 设置帧索引初始值为 0
        self.frame_index = 0
        # 设置动画速度为 0.15
        self.animation_speed = 0.15
        # 创建一个方向向量
        self.direction = pygame.math.Vector2()
    
    # 移动方法
    def move(self, speed):
        # 如果方向向量的大小不为 0,则将其归一化
        if self.direction.magnitude() != 0:
            self.direction = self.direction.normalize()
        
        # 根据方向向量的值移动碰撞框
        self.hitbox.x += self.direction.x * speed
        self.collision("Horizontal")
        self.hitbox.y += self.direction.y * speed
        self.collision("Vertical")
        self.rect.center = self.hitbox.center

    # 碰撞检测方法
    def collision(self, direction):
        # 如果是水平方向的碰撞检测
        if direction == "Horizontal":
            # 遍历障碍物精灵组中的精灵
            for sprite in self.obstacle_sprites:
                # 如果碰撞框发生碰撞
                if sprite.hitbox.colliderect(self.hitbox):
                    # 根据移动方向调整碰撞框的位置
                    if self.direction.x > 0: # 向右移动
                        self.hitbox.right = sprite.hitbox.left
                    if self.direction.x < 0: # 向左移动
                        self.hitbox.left = sprite.hitbox.right
                        
        # 如果是垂直方向的碰撞检测
        if direction == "Vertical":
            # 遍历障碍物精灵组中的精灵
            for sprite in self.obstacle_sprites:
                # 如果碰撞框发生碰撞
                if sprite.hitbox.colliderect(self.hitbox):
                    # 根据移动方向调整碰撞框的位置
                    if self.direction.y > 0: # 向下移动
                        self.hitbox.bottom = sprite.hitbox.top
                    if self.direction.y < 0: # 向上移动
                        self.hitbox.top = sprite.hitbox.bottom

    # 返回正弦波值的方法
    def wave_value(self):
        # 获取当前时间的正弦值
        value = sin(pygame.time.get_ticks())
        # 如果值大于等于 0,则返回 255,否则返回 0
        if value >= 0:
            return 255
        else:
            return 0

.\Zelda-with-Python\Code\Level.py


# 导入pygame模块
import pygame
# 从Settings模块中导入所有内容
from Settings import *
# 从Tile模块中导入Tile类
from Tile import Tile
# 从Player模块中导入Player类
from Player import Player
# 从Debug模块中导入debug函数
from Debug import debug
# 从Support模块中导入所有内容
from Support import *
# 从random模块中导入choice和randint函数
from random import choice, randint
# 从Weapon模块中导入Weapon类
from Weapon import Weapon
# 从UI模块中导入UI类
from UI import UI
# 从Enemy模块中导入Enemy类
from Enemy import Enemy
# 从Particles模块中导入AnimationPlayer类
from Particles import AnimationPlayer
# 从Magic模块中导入MagicPlayer类
from Magic import MagicPlayer
# 从Upgrade模块中导入Upgrade类
from Upgrade import Upgrade
# 导入os模块
import os

# 用于文件(特别是图片)导入的代码(这行代码将目录更改为项目保存的位置)
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 创建YSortCameraGroup类,继承自pygame.sprite.Group类
class YSortCameraGroup(pygame.sprite.Group):
    def __init__(self):
        # 通用设置
        super().__init__()
        self.display_surface = pygame.display.get_surface()
        self.half_width = self.display_surface.get_size()[0] // 2
        self.half_height = self.display_surface.get_size()[1] // 2
        self.offset = pygame.math.Vector2()

        # 创建地板
        self.floor_surf = pygame.image.load("../Graphics/Tilemap/Ground.png").convert()
        self.floor_rect = self.floor_surf.get_rect(topleft = (0, 0))

    # 自定义绘制方法
    def custom_draw(self, player):
        # 获取偏移量
        self.offset.x = player.rect.centerx - self.half_width
        self.offset.y = player.rect.centery - self.half_height

        # 绘制地板
        floor_offset_pos = self.floor_rect.topleft - self.offset
        self.display_surface.blit(self.floor_surf, floor_offset_pos)

        # 对精灵进行排序绘制
        for sprite in sorted(self.sprites(), key = lambda sprite: sprite.rect.centery):
            offset_pos = sprite.rect.topleft - self.offset
            self.display_surface.blit(sprite.image, offset_pos)

    # 更新敌人位置
    def enemy_update(self, player):
        enemy_sprites = [sprite for sprite in self.sprites() if hasattr(sprite, "sprite_type") and sprite.sprite_type == "enemy"]
        for enemy in enemy_sprites:
            enemy.enemy_update(player)

.\Zelda-with-Python\Code\Magic.py


# 导入pygame模块
import pygame
# 从Settings模块中导入所有内容
from Settings import *
# 从random模块中导入randint函数
from random import randint
# 导入os模块
import os

# 这是用于文件(特别是图像)导入的(这一行将目录更改为项目保存的位置)
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 创建魔法玩家类
class MagicPlayer:
    def __init__(self, animation_player):
        # 初始化动画播放器
        self.animation_player = animation_player
        # 初始化声音字典
        self.sounds = {
            "heal": pygame.mixer.Sound("../Audio/Heal.wav"), 
            "flame": pygame.mixer.Sound("../Audio/Fire.wav")
        }

    # 治愈方法
    def heal(self, player, strength, cost, groups):
        # 如果玩家能量大于等于治愈消耗
        if player.energy >= cost:
            # 播放治愈声音
            self.sounds["heal"].play()
            # 增加玩家生命值
            player.health += strength
            # 减少玩家能量
            player.energy -= cost
            # 如果玩家生命值大于等于最大生命值
            if player.health >= player.stats["health"]:
                player.health = player.stats["health"]
            # 创建光环粒子效果
            self.animation_player.create_particles("aura", player.rect.center, groups)
            # 创建治愈粒子效果
            self.animation_player.create_particles("heal", player.rect.center + pygame.math.Vector2(0, -60), groups)

    # 火焰方法
    def flame(self, player, cost, groups):
        # 如果玩家能量大于等于火焰消耗
        if player.energy >= cost:
            # 减少玩家能量
            player.energy -= cost
            # 播放火焰声音
            self.sounds["flame"].play()

            # 根据玩家状态确定火焰方向
            if player.status.split("_")[0] == "right": direction = pygame.math.Vector2(1, 0)
            elif player.status.split("_")[0] == "left": direction = pygame.math.Vector2(-1, 0)
            elif player.status.split("_")[0] == "up": direction = pygame.math.Vector2(0, -1)
            else: direction = pygame.math.Vector2(0, 1)

            # 创建火焰粒子效果
            for i in range(1, 6):
                if direction.x: # 水平
                    offset_x = (direction.x * i) * TILESIZE
                    x = player.rect.centerx + offset_x + randint(-TILESIZE // 3, TILESIZE // 3)
                    y = player.rect.centery + randint(-TILESIZE // 3, TILESIZE // 3)
                    self.animation_player.create_particles("flame", (x, y), groups)
                else: # 垂直
                    offset_y = (direction.y * i) * TILESIZE
                    x = player.rect.centerx + randint(-TILESIZE // 3, TILESIZE // 3)
                    y = player.rect.centery + offset_y + randint(-TILESIZE // 3, TILESIZE // 3)
                    self.animation_player.create_particles("flame", (x, y), groups)

.\Zelda-with-Python\Code\Main.py


# 导入pygame和sys模块
import pygame, sys
# 从Settings模块中导入所有内容
from Settings import *
# 从Level模块中导入Level类
from Level import Level
# 导入os模块
import os

# 更改工作目录到项目所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__))

# 创建游戏屏幕并调用一些类
class Game:
    def __init__(self):
        # 初始化pygame
        pygame.init()
        # 创建屏幕并设置宽度和高度
        self.screen = pygame.display.set_mode((WIDTH, HEIGTH))
        # 设置窗口标题
        pygame.display.set_caption("Zelda with Python")
        # 加载游戏图标
        pygame_icon = pygame.image.load("../Graphics/Test/Player.png")
        pygame.display.set_icon(pygame_icon)
        # 创建时钟对象
        self.clock = pygame.time.Clock()

        # 创建Level对象
        self.level = Level()

        # 加载音乐
        main_sound = pygame.mixer.Sound("../Audio/Main.ogg")
        main_sound.set_volume(0.5)
        main_sound.play(loops = -1)

    # 游戏运行循环
    def run(self):
        while True:
            # 处理事件
            for event in pygame.event.get():
                # 如果事件类型为退出,则退出游戏
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                # 如果按下键盘上的m键,则切换菜单状态
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_m:
                        self.level.toggle_menu()
            
            # 填充屏幕颜色
            self.screen.fill(WATER_COLOR)
            # 运行关卡
            self.level.run()
            # 更新屏幕
            pygame.display.update()
            # 控制帧率
            self.clock.tick(FPS)

# 如果该文件被直接运行,则创建Game对象并运行游戏
if __name__ == "__main__":
    game = Game()
    game.run()

.\Zelda-with-Python\Code\Particles.py


# 导入pygame模块
import pygame
# 从Support模块中导入import_folder函数
from Support import import_folder
# 从random模块中导入choice函数
from random import choice
# 导入os模块
import os

# 改变当前工作目录到脚本所在目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 定义动画播放器类
class AnimationPlayer:
    def __init__(self):
        # 定义frames属性,包含不同动画类型的帧
        self.frames = {
            # Magic
            "flame": import_folder("../Graphics/Particles/Flame/Frames"),
            "aura": import_folder("../Graphics/Particles/Aura"),
            "heal": import_folder("../Graphics/Particles/Heal/Frames"),
            # Attacks 
            "claw": import_folder("../Graphics/Particles/Claw"),
            "slash": import_folder("../Graphics/Particles/Slash"),
            "sparkle": import_folder("../Graphics/Particles/Sparkle"),
            "leaf_attack": import_folder("../Graphics/Particles/leaf_attack"),
            "thunder": import_folder("../Graphics/Particles/Thunder"),
            # Monster Deaths
            "squid": import_folder("../Graphics/Particles/smoke_orange"),
            "raccoon": import_folder("../Graphics/Particles/Raccoon"),
            "spirit": import_folder("../Graphics/Particles/Nova"),
            "bamboo": import_folder("../Graphics/Particles/Bamboo"),
            # Leafs
            "leaf":(
                import_folder("../Graphics/Particles/Leaf1"),
                import_folder("../Graphics/Particles/Leaf2"),
                import_folder("../Graphics/Particles/Leaf3"),
                import_folder("../Graphics/Particles/Leaf4"),
                import_folder("../Graphics/Particles/Leaf5"),
                import_folder("../Graphics/Particles/Leaf6"),
                self.reflect_images(import_folder("../Graphics/Particles/Leaf1")),
                self.reflect_images(import_folder("../Graphics/Particles/Leaf2")),
                self.reflect_images(import_folder("../Graphics/Particles/Leaf3")),
                self.reflect_images(import_folder("../Graphics/Particles/Leaf4")),
                self.reflect_images(import_folder("../Graphics/Particles/Leaf5")),
                self.reflect_images(import_folder("../Graphics/Particles/Leaf6"))
                )
            }

    # 反转图像
    def reflect_images(self, frames):
        new_frames = []
        for frame in frames:
            flipped_frame = pygame.transform.flip(frame, True, False)
            new_frames.append(flipped_frame)
        return new_frames

    # 创建草粒子效果
    def create_grass_particles(self, pos, groups):
        animation_frames = choice(self.frames["leaf"])
        ParticleEffect(pos, animation_frames, groups)

    # 创建粒子效果
    def create_particles(self, animation_type, pos, groups):
        animation_frames = self.frames[animation_type]
        ParticleEffect(pos, animation_frames, groups)

# 粒子效果类
class ParticleEffect(pygame.sprite.Sprite):
    def __init__(self, pos, animation_frames, groups):
        super().__init__(groups)
        self.sprite_type = "magic"
        self.frame_index = 0
        self.animation_speed = 0.15
        self.frames = animation_frames
        self.image = self.frames[self.frame_index]
        self.rect = self.image.get_rect(center = pos)

    # 动画
    def animate(self):
        self.frame_index += self.animation_speed
        if self.frame_index >= len(self.frames):
            self.kill()
        else:
            self.image = self.frames[int(self.frame_index)]

    # 更新
    def update(self):
        self.animate()

.\Zelda-with-Python\Code\Player.py


# 导入 pygame 模块
import pygame
# 从 Support 模块中导入 import_folder 函数
from Support import import_folder
# 从 Settings 模块中导入所有内容
from Settings import *
# 从 Entity 模块中导入 Entity 类
from Entity import Entity
# 导入 os 和 sys 模块
import os, sys

# 将当前工作目录更改为 Main.py 文件所在的目录
# 这是为了在 Main.py 中进行文件导入时的路径设置
os.chdir(os.path.dirname(os.path.abspath(__file__)))

.\Zelda-with-Python\Code\Settings.py


# 导入 os 模块
import os

# 更改当前工作目录为当前文件所在目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 游戏设置
WIDTH = 1280  # 游戏窗口宽度
HEIGTH = 720  # 游戏窗口高度
FPS = 60  # 游戏帧率
TILESIZE = 64  # 地图瓦片大小
HITBOX_OFFSET = {  # 不同对象的碰撞框偏移量
	"player": -26,
	"object": -40,
	"grass": -10,
	"invisible": 0
	}

# 用户界面设置
BAR_HEIGHT = 20  # 柱状图高度
HEALTH_BAR_WIDTH = 200  # 生命值柱状图宽度
ENERGY_BAR_WIDTH = 140  # 能量值柱状图宽度
ITEM_BOX_SIZE = 80  # 物品框大小
UI_FONT = "../Graphics/Font/Joystix.ttf"  # 用户界面字体
UI_FONT_SIZE = 18  # 用户界面字体大小

# 通用颜色
WATER_COLOR = "#71ddee"  # 水的颜色
UI_BG_COLOR = "#222222"  # 用户界面背景颜色
UI_BORDER_COLOR = "#111111"  # 用户界面边框颜色
TEXT_COLOR = "#EEEEEE"  # 文本颜色

# 用户界面颜色
HEALTH_COLOR = "Red"  # 生命值颜色
ENERGY_COLOR = "Blue"  # 能量值颜色
UI_BORDER_COLOR_ACTIVE = "Gold"  # 用户界面激活时的边框颜色

# 升级菜单
TEXT_COLOR_SELECTED = "#111111"  # 选中文本颜色
BAR_COLOR = "#EEEEEE"  # 柱状图颜色
BAR_COLOR_SELECTED = "#111111"  # 选中柱状图颜色
UPGRADE_BG_COLOR_SELECTED = "#EEEEEE"  # 选中升级菜单背景颜色

# 武器
weapon_data = {
	"sword": {"cooldown": 100, "damage": 15, "graphic": "../Graphics/Weapons/Sword/Full.png"},  # 剑的冷却时间、伤害和图像路径
	"lance": {"cooldown": 400, "damage": 30, "graphic": "../Graphics/Weapons/Lance/Full.png"},  # 枪的冷却时间、伤害和图像路径
	"axe": {"cooldown": 300, "damage": 20, "graphic": "../Graphics/Weapons/Axe/Full.png"},  # 斧头的冷却时间、伤害和图像路径
	"rapier": {"cooldown": 50, "damage": 8, "graphic": "../Graphics/Weapons/Rapier/Full.png"},  # 细剑的冷却时间、伤害和图像路径
	"sai": {"cooldown": 80, "damage": 10, "graphic": "../Graphics/Weapons/Sai/Full.png"}  # 菜刀的冷却时间、伤害和图像路径
    }

# 魔法
magic_data = {
	"flame": {"strength": 5, "cost": 20, "graphic": "../Graphics/Particles/Flame/Fire.png"},  # 火焰的强度、消耗和图像路径
	"heal": {"strength": 20, "cost": 10, "graphic": "../Graphics/Particles/Heal/Heal.png"}  # 治疗的强度、消耗和图像路径
	}

# 敌人
monster_data = {
	"squid": {"health": 100, "exp": 180, "damage": 20, "attack_type": "slash", "attack_sound": "../Audio/Attack/Slash.wav", "speed": 3, "resistance": 3, "attack_radius": 80, "notice_radius": 360},  # 鱿鱼的生命值、经验值、伤害等信息
	"raccoon": {"health": 300, "exp": 300, "damage": 40, "attack_type": "claw", "attack_sound": "../Audio/Attack/Claw.wav", "speed": 2, "resistance": 3, "attack_radius": 120, "notice_radius": 400},  # 浣熊的生命值、经验值、伤害等信息
	"spirit": {"health": 100, "exp": 200, "damage": 8, "attack_type": "thunder", "attack_sound": "../Audio/Attack/Fireball.wav", "speed": 4, "resistance": 3, "attack_radius": 60, "notice_radius": 350},  # 精灵的生命值、经验值、伤害等信息
	"bamboo": {"health": 70, "exp": 150, "damage": 6, "attack_type": "leaf_attack", "attack_sound": "../Audio/Attack/Slash.wav", "speed": 3, "resistance": 3, "attack_radius": 50, "notice_radius": 300}  # 竹子的生命值、经验值、伤害等信息
	}

.\Zelda-with-Python\Code\Support.py


# 从csv模块中导入reader函数
from csv import reader
# 导入os模块
import os
# 从os模块中导入walk函数
from os import walk
# 导入pygame模块

# 用于将CSV文件导入Python以及其他相关操作

# 这是用于文件(特别是图片)导入的代码(这行将目录更改为项目保存的位置)
os.chdir(os.path.dirname(os.path.abspath(__file__)))


def import_csv_layout(path): 
    # 创建一个空的地形地图列表
    terrain_map = []

    # 打开指定路径的文件
    with open(path) as level_map:
        # 使用CSV reader函数读取文件,以逗号为分隔符
        layout = reader(level_map, delimiter = ",")

        # 遍历CSV文件的每一行
        for row in layout:
            # 将每一行转换为列表,并添加到地形地图列表中
            terrain_map.append(list(row))
        
        # 返回地形地图列表
        return terrain_map


def import_folder(path):
    # 创建一个空的表面列表
    surface_list = []
    
    # 遍历指定路径下的所有文件和子目录
    for _, __, img_files in walk(path):
        # 遍历每个图片文件
        for image in img_files:
            # 拼接完整的文件路径
            full_path = path + "/" + image
            # 加载图片并转换为alpha通道
            image_surf = pygame.image.load(full_path).convert_alpha()
            # 将图片表面添加到表面列表中
            surface_list.append(image_surf)
    # 返回表面列表
    return surface_list

.\Zelda-with-Python\Code\Tile.py


# 导入pygame模块
import pygame
# 从Settings模块中导入所有内容
from Settings import *
# 导入os模块
import os

# 将当前工作目录更改为Main.py所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 创建Tile类,继承自pygame.sprite.Sprite类
class Tile(pygame.sprite.Sprite):
    # 初始化方法,接受位置、组、精灵类型和表面作为参数
    def __init__(self, pos, groups, sprite_type, surface = pygame.Surface((TILESIZE, TILESIZE))):
        # 调用父类的初始化方法
        super().__init__(groups)

        # 设置精灵类型
        self.sprite_type = sprite_type
        # 根据精灵类型获取y轴偏移量
        y_offset = HITBOX_OFFSET[sprite_type]
        # 设置精灵的图像
        self.image = surface
        
        # 根据精灵类型设置精灵的矩形位置
        if sprite_type == "object":
            self.rect = self.image.get_rect(topleft = (pos[0], pos[1] - TILESIZE))
        else:
            self.rect = self.image.get_rect(topleft = pos)
        # 设置精灵的碰撞框
        self.hitbox = self.rect.inflate(0, y_offset)

.\Zelda-with-Python\Code\UI.py


# 导入pygame模块
import pygame
# 从Settings模块中导入所有内容
from Settings import *
# 导入os模块
import os

# 改变当前工作目录到项目所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 创建UI类
class UI:
    def __init__(self):
        
        # General
        # 获取显示表面
        self.display_surface = pygame.display.get_surface()
        # 创建字体对象
        self.font = pygame.font.Font(UI_FONT, UI_FONT_SIZE)

        # Bar Setup
        # 创建血条矩形对象
        self.health_bar_rect = pygame.Rect(10, 10, HEALTH_BAR_WIDTH, BAR_HEIGHT)
        # 创建能量条矩形对象
        self.energy_bar_rect = pygame.Rect(10, 34, ENERGY_BAR_WIDTH, BAR_HEIGHT)

        # Convert Weapon Dictionary
        # 将武器字典中的图像转换为pygame图像对象
        self.weapon_graphics = []
        for weapon in weapon_data.values():
            path = weapon["graphic"]
            weapon = pygame.image.load(path).convert_alpha()
            self.weapon_graphics.append(weapon)

        # Convert Magic Dictionary
        # 将魔法字典中的图像转换为pygame图像对象
        self.magic_graphics = []
        for magic in magic_data.values():
            magic = pygame.image.load(magic["graphic"]).convert_alpha()
            self.magic_graphics.append(magic)

    # 显示条形图
    def show_bar(self, current, max_amount, bg_rect, color):
        # 绘制背景
        pygame.draw.rect(self.display_surface, UI_BG_COLOR, bg_rect)

        # 将状态转换为像素
        ratio = current / max_amount
        current_width = bg_rect.width * ratio
        current_rect = bg_rect.copy()
        current_rect.width = current_width

        # 绘制条形图
        pygame.draw.rect(self.display_surface, color, current_rect)
        pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, bg_rect, 3)

    # 显示经验值
    def show_exp(self, exp):
        text_surf = self.font.render(str(int(exp)), False, TEXT_COLOR)
        x = self.display_surface.get_size()[0] - 20
        y = self.display_surface.get_size()[1] - 20
        text_rect = text_surf.get_rect(bottomright = (x, y))

        pygame.draw.rect(self.display_surface, UI_BG_COLOR, text_rect.inflate(20, 20))
        self.display_surface.blit(text_surf, text_rect)
        pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, text_rect.inflate(20, 20), 3)

    # 选择框
    def selection_box(self, left, top, has_switched):
        bg_rect = pygame.Rect(left, top, ITEM_BOX_SIZE, ITEM_BOX_SIZE)
        pygame.draw.rect(self.display_surface, UI_BG_COLOR, bg_rect)
        if has_switched:
            pygame.draw.rect(self.display_surface, UI_BORDER_COLOR_ACTIVE, bg_rect, 3)
        else:
            pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, bg_rect, 3)
        return bg_rect

    # 武器叠加
    def weapon_overlay(self, weapon_index, has_switched):
        bg_rect = self.selection_box(10, 630, has_switched) # 武器框
        weapon_surf = self.weapon_graphics[weapon_index]
        weapon_rect = weapon_surf.get_rect(center = bg_rect.center)

        self.display_surface.blit(weapon_surf, weapon_rect)

    # 魔法叠加
    def magic_overlay(self, magic_index, has_switched):
        bg_rect = self.selection_box(100, 630, has_switched) # 魔法框
        magic_surf = self.magic_graphics[magic_index]
        magic_rect = magic_surf.get_rect(center = bg_rect.center)

        self.display_surface.blit(magic_surf, magic_rect)

    # 显示
    def display(self, player):
        self.show_bar(player.health, player.stats["health"], self.health_bar_rect, HEALTH_COLOR)
        self.show_bar(player.energy, player.stats["energy"], self.energy_bar_rect, ENERGY_COLOR)

        self.show_exp(player.exp)

        self.weapon_overlay(player.weapon_index, not player.can_switch_weapon)
        self.magic_overlay(player.magic_index, not player.can_switch_magic)

.\Zelda-with-Python\Code\Upgrade.py


# 导入必要的模块
import imp  # 引入模块
from traceback import print_tb  # 从模块中引入特定函数
import pygame  # 导入pygame模块
from Settings import *  # 从Settings模块中导入所有内容
import os  # 导入os模块

# 更改工作目录到项目所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__))

# 定义Upgrade类
class Upgrade:
    def __init__(self, player):
        # 通用设置
        self.display_surface = pygame.display.get_surface()  # 获取显示表面
        self.player = player  # 设置玩家对象
        self.attribute_nr = len(player.stats)  # 获取玩家属性数量
        self.attribute_names = list(player.stats.keys())  # 获取玩家属性名称列表
        self.max_values = list(player.max_stats.values())  # 获取玩家最大属性值列表
        self.font = pygame.font.Font(UI_FONT, UI_FONT_SIZE)  # 设置字体

        # 创建物品
        self.height = self.display_surface.get_size()[1] * 0.8  # 设置高度
        self.width = self.display_surface.get_size()[0] // 6  # 设置宽度
        self.create_items()  # 创建物品

        # 选择系统
        self.selection_index = 0  # 初始化选择索引
        self.selection_time = None  # 初始化选择时间
        self.can_move = True  # 初始化可移动状态

    # 输入处理
    def input(self):
        keys = pygame.key.get_pressed()  # 获取按键状态

        if self.can_move:
            if keys[pygame.K_RIGHT] and self.selection_index < self.attribute_nr - 1:  # 如果按下右键且选择索引小于属性数量减1
                self.selection_index += 1  # 选择索引加1
                self.can_move = False  # 设置为不可移动
                self.selection_time = pygame.time.get_ticks()  # 获取当前时间
            elif keys[pygame.K_LEFT] and self.selection_index >= 1:  # 如果按下左键且选择索引大于等于1
                self.selection_index -= 1  # 选择索引减1
                self.can_move = False  # 设置为不可移动
                self.selection_time = pygame.time.get_ticks()  # 获取当前时间

            if keys[pygame.K_SPACE]:  # 如果按下空格键
                self.can_move = False  # 设置为不可移动
                self.selection_time = pygame.time.get_ticks()  # 获取当前时间
                self.item_list[self.selection_index].trigger(self.player)  # 触发物品效果

    # 选择冷却
    def selection_cooldown(self):
        if not self.can_move:  # 如果可以移动
            current_time = pygame.time.get_ticks()  # 获取当前时间
            if current_time - self.selection_time >= 300:  # 如果当前时间减去选择时间大于等于300
                self.can_move = True  # 设置为可移动

    # 创建物品
    def create_items(self):
        self.item_list = []  # 初始化物品列表

        for item, index in enumerate(range(self.attribute_nr)):  # 遍历属性数量
            # 水平位置
            full_width = self.display_surface.get_size()[0]  # 获取显示表面宽度
            increment = full_width // self.attribute_nr  # 计算增量
            left = (item * increment) + (increment - self.width) // 2  # 计算左边距

            # 垂直位置
            top = self.display_surface.get_size()[1] * 0.1  # 计算顶部距离

            # 创建对象
            item = Item(left, top, self.width, self.height, index, self.font)  # 创建物品对象
            self.item_list.append(item)  # 将物品对象添加到列表中

    # 显示
    def display(self):
        self.input()  # 处理输入
        self.selection_cooldown()  # 处理选择冷却

        for index, item in enumerate(self.item_list):  # 遍历物品列表
            # 获取属性
            name = self.attribute_names[index]  # 获取属性名称
            value = self.player.get_value_by_index(index)  # 获取属性值
            max_value = self.max_values[index]  # 获取最大属性值
            cost = self.player.get_cost_by_index(index)  # 获取花费
            item.display(self.display_surface, self.selection_index, name, value, max_value, cost)  # 显示物品


# 物品类
class Item:
    def __init__(self, l, t, w, h, index, font):
        self.rect = pygame.Rect(l, t, w, h)  # 设置矩形
        self.index = index  # 设置索引
        self.font = font  # 设置字体

    # 显示名称
    def display_names(self, surface, name, cost, selected):
        color = TEXT_COLOR_SELECTED if selected else TEXT_COLOR  # 根据选择状态设置颜色

        # 标题
        title_surf = self.font.render(name, False, color)  # 渲染标题
        title_rect = title_surf.get_rect(midtop=self.rect.midtop + pygame.math.Vector2(0, 20))  # 设置标题位置

        # 花费
        cost_surf = self.font.render(f"{int(cost)}", False, color)  # 渲染花费
        cost_rect = cost_surf.get_rect(midbottom=self.rect.midbottom - pygame.math.Vector2(0, 20))  # 设置花费位置

        # 绘制
        surface.blit(title_surf, title_rect)  # 绘制标题
        surface.blit(cost_surf, cost_rect)  # 绘制花费

    # 显示进度条
    def display_bar(self, surface, value, max_value, selected):
        # 绘制设置
        top = self.rect.midtop + pygame.math.Vector2(0, 60)  # 设置顶部位置
        bottom = self.rect.midbottom - pygame.math.Vector2(0, 60)  # 设置底部位置
        color = BAR_COLOR_SELECTED if selected else BAR_COLOR  # 根据选择状态设置颜色

        # 进度条设置
        full_height = bottom[1] - top[1]  # 计算总高度
        relative_number = (value / max_value) * full_height  # 计算相对数值
        value_rect = pygame.Rect(top[0] - 15, bottom[1] - relative_number, 30, 10)  # 设置数值矩形

        # 绘制元素
        pygame.draw.line(surface, color, top, bottom, 5)  # 绘制线条
        pygame.draw.rect(surface, color, value_rect)  # 绘制矩形

    # 触发效果
    def trigger(self, player):
        upgrade_attribute = list(player.stats.keys())[self.index]  # 获取升级属性

        if player.exp >= player.upgrade_cost[upgrade_attribute] and player.stats[upgrade_attribute] < player.max_stats[upgrade_attribute]:  # 如果玩家经验大于等于升级花费且属性小于最大属性
            player.exp -= player.upgrade_cost[upgrade_attribute]  # 减去升级花费
            player.stats[upgrade_attribute] *= 1.2  # 属性增加20%
            player.upgrade_cost[upgrade_attribute] *= 1.4  # 升级花费增加40%

        if player.stats[upgrade_attribute] > player.max_stats[upgrade_attribute]:  # 如果属性大于最大属性
            player.stats[upgrade_attribute] = player.max_stats[upgrade_attribute]  # 将属性设置为最大属性

    # 显示
    def display(self, surface, selection_num, name, value, max_value, cost):
        if self.index == selection_num:  # 如果索引等于选择索引
            pygame.draw.rect(surface, UPGRADE_BG_COLOR_SELECTED, self.rect)  # 绘制选中背景
            pygame.draw.rect(surface, UI_BORDER_COLOR, self.rect, 4)  # 绘制边框
        else:
            pygame.draw.rect(surface, UI_BG_COLOR, self.rect)  # 绘制背景
            pygame.draw.rect(surface, UI_BORDER_COLOR, self.rect, 4)  # 绘制边框

        self.display_names(surface, name, cost, self.index == selection_num)  # 显示名称
        self.display_bar(surface, value, max_value, self.index == selection_num)  # 显示进度条

.\Zelda-with-Python\Code\Weapon.py


# 导入必要的模块
import os
import pygame

# 这是用于导入文件(特别是图片)的部分(这一行将目录更改为项目保存的位置)
os.chdir(os.path.dirname(os.path.abspath(__file__)))

# 创建武器类,继承自 pygame.sprite.Sprite 类
class Weapon(pygame.sprite.Sprite):
    def __init__(self, player, groups):
        super().__init__(groups)
        self.sprite_type = "weapon"
        # 获取玩家的状态(方向)
        direction = player.status.split("_")[0]

        # 图形
        full_path = f"../Graphics/Weapons/{player.weapon}/{direction}.png"
        # 加载武器图片并转换为透明度格式
        self.image = pygame.image.load(full_path).convert_alpha()

        # 放置
        if direction == "right":
            # 如果方向向右,则将武器放置在玩家矩形的右侧中间位置
            self.rect = self.image.get_rect(midleft = player.rect.midright + pygame.math.Vector2(0, 16))
        elif direction == "left":
            # 如果方向向左,则将武器放置在玩家矩形的左侧中间位置
            self.rect = self.image.get_rect(midright = player.rect.midleft + pygame.math.Vector2(0, 16))
        elif direction == "down":
            # 如果方向向下,则将武器放置在玩家矩形的底部中间位置
            self.rect = self.image.get_rect(midtop = player.rect.midbottom + pygame.math.Vector2(-10, 0))
        else:
            # 如果方向向上,则将武器放置在玩家矩形的顶部中间位置
            self.rect = self.image.get_rect(midbottom = player.rect.midtop + pygame.math.Vector2(-10, 0))

标签:os,self,player,源码,pygame,import,解析,PyZelda,rect
From: https://www.cnblogs.com/apachecn/p/18015785

相关文章

  • 2024九省联考数学备用卷选填解析
    答案\(1-5\)\(CADCA\)\(6-8\)\(DBD\)\(9\)\(ABD\)\(10\)\(AC\)\(11\)\(ACD\)\(12.\)\({(\frac{\sqrt{6}}{3},\frac{\sqrt{6}}{3}),(-\frac{\sqrt{6}}{3},-\frac{\sqrt{6}}{3})}\)\(13.\)奇\(\pi\)\(14.\)\(4+\frac{......
  • Ubuntu 中通过源码安装 Python3.x 环境
    最近在个人前后端分离项目时候,后端接口程序fastapi,在部署的时候,需要Pyhton3.8以上的环境,但ubuntu默认的是2.7于是想通过源码安装的方式进行环境搭建.下载官网下载二进制源码安装包:https://www.python.org/downloads/source/wegthttps://www.python.org/ftp/......
  • 通过解析库探究函数式抽象代价
    前因在春节前了解到Rust语言有一个叫nom的解析库它可以让你创建安全的解析器,而不会占用内存或影响性能。它依靠Rust强大的类型系统和内存安全来生成既正确又高效的解析器,并使用函数,宏和特征来抽象出容易出错的管道。nom核心是解析器组合器,而解析器组合器是高阶函数,可以......
  • Torrent文件结构解析
    Torrent文件内的数据结构分为以下几部分:announce:Tracker的主服务器announce-list:Tracker服务器列表comment:种子文件的注释comment.utf-8:种子文件注释的utf-8编码creationdate:种子文件建立的时间,是从1970年1月1日00:00:00到现在的秒数。encoding:种子文件的默认编码,比如GB......
  • Python通过Lxml库解析网络爬虫抓取到的html
    ​Lxml是基于libxml2解析库的Python封装。libxml2是使用C语言编写的,解析速度很好,不过安装起来稍微有点复杂。安装说明可以参考(http://Lxml.de/installation.html),在CentOS7上中文安装说明(http://www.cjavapy.com/article/64/),使用lxml库来解析网络爬虫抓取到的HTML是一种非常......
  • 探索C语言的内存魔法:动态内存管理解析
    ✨✨欢迎大家来到贝蒂大讲堂✨✨......
  • 通达信美布林主图指标公式源码
    {股票指标}SS:=ma(C,30)>REF(MA(C,30),1);SS1:=MA(C,20)>REF(MA(C,20),1);ADS:=MA(C,10)>REF(MA(C,10),1);VAR1:=CLOSE>REF(CLOSE,1)ANDCLOSE>REF(CLOSE,2);VAR2:=REF(VAR1,1)ANDCLOSE<=REF(CLOSE,1)ANDCLOSE>=REF(CLOSE,2);VAR3:=REF(VAR......
  • 通达信封神买点指标公式源码副图
    {股票指标}短期:100*(C-LLV(L,5))/(HHV(C,5)-LLV(L,5)),COLORWHITE;中期:100*(C-LLV(L,10))/(HHV(C,10)-LLV(L,10)),COLORYELLOW;中长期:100*(C-LLV(L,20))/(HHV(C,20)-LLV(L,20)),COLORmaGENTA;长期:100*(C-LLV(L,30))/(HHV(C,30)-LLV(L,30)),COLORRED,LINETHICK2;VARD:=......
  • 通达信小牛抄底选股指标公式源码副图
    {股票指标}X:=REF(((HHV(HIGH,55)-LLV(LOW,55))/LLV(LOW,55)),1)<=(25/100)ANDCLOSE>=REF(HHV(HIGH,55),1)ANDBARSCOUNT(CLOSE)>55;X1:=Abs(ma(C,10)/MA(C,20)-1)<0.01;X2:=ABS(MA(C,5)/MA(C,10)-1)<0.01;X3:=ABS(MA(C,5)/MA(C,20)-1)<0.01;X4:=O/......
  • 通达信分时量能起爆指标公式源码副图
    {股票指标}M12:=36;N1:=13;N:=100;M:=5;SHORT:=21;LONG:=44;MID:=11;N2:=11;NN:=2400;HD:=HHV(V,NN);LD:=LLV(V,NN);最高:=CONst(HD),NODRAW;最低:=CONST(LD),NODRAW;HLmaX:=最高;HLMIN:=最低;HLMN1:=(HLMAX+HLMIN)/2;HLMN2:=HLMAX-HLMIN;HLMN3:=(100-0)/2*1......