python大作业高分项目--射击闯关游戏

news2024/11/25 22:58:46

项目功能:

地图编辑器:可以实现玩家自己定义每一关卡的样式和难易程度

运行界面:实现了玩家的移动,跳跃,发射子弹,投掷手雷,以及敌人的AL(移动,发射子弹,扔手雷),同时游戏中有一系列的道具(生命值药箱,子弹补给,手雷补给)以及各种动画和音乐音效,还有各种花草岩石装饰品,以及悬崖和水涡危险地方,更多未知,自己体验就能感受到!

总代码累计1100行左右!

地图编辑器:

import pygame
import sys
import csv
import button

pygame.init()
# 定义一个时钟
clock = pygame.time.Clock()
FPS = 60

# 游戏窗口
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 560
LOWER_MARGIN = 100
SIDE_MAGTIN = 300
screen = pygame.display.set_mode((SCREEN_WIDTH + SIDE_MAGTIN, SCREEN_HEIGHT + LOWER_MARGIN))
pygame.display.set_caption("级别编辑器")

# 定义游戏变量
ROWS = 16
MAX_COLS = 150
TILE_SIZE = SCREEN_HEIGHT // ROWS
TILE_TYPES = 21
level = 1
current_tile = 0
scroll_left = False
scroll_right = False
scroll = 0
scroll_speed = 1

# 加载背景图片
pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha()
pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha()
mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha()
sky_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha()
# 瓷砖瓦片列表
img_list = []
for x in range(TILE_TYPES):
    img = pygame.image.load(f"img/tile/{x}.png")
    img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
    img_list.append(img)

# 创建保存按钮
save_img = pygame.image.load("img/save_btn.png").convert_alpha()
load_img = pygame.image.load("img/load_btn.png").convert_alpha()

# 定义颜色
GREEN = (144, 201, 120)
WHITE = (255, 255, 255)
RED = (200, 25, 25)

#定义字体
font = pygame.font.SysFont("Futura", 30)

# 创建空的瓷砖列表(二维)
world_data = []
for row in range(ROWS):
    r = [-1] * MAX_COLS
    world_data.append(r)

# 创建一个组
for tile in range(0, MAX_COLS):
    world_data[ROWS - 1][tile] = 0

# 在屏幕上显示下一级定义文本显示函数
def draw_text(text, font, text_color, x, y):
    img = font.render(text, True, text_color)
    screen.blit(img, (x, y))


# 创建背景函数
def draw_bg():
    screen.fill(GREEN)
    width = sky_img.get_width()
    for x in range(4):
        screen.blit(sky_img, ((x * width) - scroll * 0.5, 0))
        screen.blit(mountain_img, ((x * width) - scroll * 0.6, SCREEN_HEIGHT - mountain_img.get_height() - 300))
        screen.blit(pine1_img, ((x * width) - scroll * 0.7, SCREEN_HEIGHT - pine1_img.get_height() - 150))
        screen.blit(pine2_img, ((x * width) - scroll * 0.8, SCREEN_HEIGHT - pine2_img.get_height()))

# 绘制格子
def draw_grid():
    # 垂直的线
    for c in range(MAX_COLS + 1):
        pygame.draw.line(screen, WHITE, (c * TILE_SIZE - scroll, 0), (c * TILE_SIZE - scroll, SCREEN_HEIGHT))
    # 水平的线
    for c in range(ROWS + 1):
        pygame.draw.line(screen, WHITE, (0, c * TILE_SIZE), (SCREEN_WIDTH, c * TILE_SIZE))

# 在地图中绘制瓷砖
def draw_world():
    for y, row in enumerate(world_data):
        for x, tile in enumerate(row):
            if tile >= 0:
                screen.blit(img_list[tile], (x * TILE_SIZE - scroll, y * TILE_SIZE))
# 创建按钮
# 创建保存和加载数据按钮
save_button = button.Button(SCREEN_WIDTH // 2, SCREEN_HEIGHT + LOWER_MARGIN - 50, save_img, 1)
load_button = button.Button(SCREEN_WIDTH // 2 + 200, SCREEN_HEIGHT + LOWER_MARGIN - 50, load_img, 1)

# 制作一个按钮瓷片列表
button_list = []
button_col = 0
button_row = 0
for i in range(len(img_list)):
    tile_button = button.Button(SCREEN_WIDTH + (75 * button_col) + 50, 75 * button_row + 50, img_list[i], 1)
    button_list.append(tile_button)
    button_col += 1
    if button_col == 3:
        button_row += 1
        button_col = 0

run = True
while run:
    clock.tick(FPS)
    draw_bg()
    draw_grid()
    draw_world()

    draw_text(f"Level: {level}", font, WHITE, 10, SCREEN_HEIGHT + LOWER_MARGIN - 90)
    draw_text("Press up or Down to change level", font, WHITE, 10, SCREEN_HEIGHT + LOWER_MARGIN - 60)

    # 保存和加载地图数据
    if save_button.draw(screen):
        # 保存级别数据
        with open(f"level{level}_data.csv", "w", newline="") as csvfile:
            writer = csv.writer(csvfile, delimiter = ",")
            for row in world_data:
                writer.writerow(row)
        # with open(f"level{level}_data.csv", "wb") as pickle_out:
        #     pickle.dump(world_data, pickle_out)
    if load_button.draw(screen):
        # 加载地图级别数据
        # 重置滚动scroll为起始位置0
        scroll = 0
        with open(f"level{level}_data.csv", "r", newline="") as csvfile:
            reader = csv.reader(csvfile, delimiter=",")
            for y, row in enumerate(reader):
                for x, tile in enumerate(row):
                    world_data[y][x] = int(tile)


    # 画面板和瓷砖
    pygame.draw.rect(screen, GREEN, (SCREEN_WIDTH, 0, SIDE_MAGTIN, SCREEN_HEIGHT))
    # 选择一种瓷砖,获取右侧瓷砖列表的具体
    button_count = 0
    for button_count, i in enumerate(button_list):
        if i.draw(screen):
            current_tile = button_count
    # 高亮显示选中的瓷砖
    pygame.draw.rect(screen, RED, button_list[current_tile].rect, 3)

    # 滚动地图
    if scroll_left == True and scroll > 0:
        scroll -= 5 * scroll_speed
    if scroll_right == True and scroll < (MAX_COLS * TILE_SIZE) - SCREEN_WIDTH: # 检测最右边的边缘
        scroll += 5 * scroll_speed

    # 在窗口中增加新的瓷砖
    # 获取鼠标的位置
    pos = pygame.mouse.get_pos()
    x = (pos[0] + scroll) // TILE_SIZE
    y = pos[1] // TILE_SIZE

    # 检测点击的区域,把右侧获取的瓷片放在地图中
    if pos[0] < SCREEN_WIDTH and pos[1] < SCREEN_HEIGHT:
        # 更新瓷砖的值
        if pygame.mouse.get_pressed()[0] == 1:
            if world_data[y][x] != current_tile:
                world_data[y][x] = current_tile
        # 删除选中的
        if pygame.mouse.get_pressed()[2] == 1:
            world_data[y][x] = -1

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
            pygame.quit()
            sys.exit()
        # 键盘按键
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                level += 1
            if event.key == pygame.K_DOWN and level > 0:
                level -= 1
            if event.key == pygame.K_LEFT:
                scroll_left = True
            if event.key == pygame.K_RIGHT:
                scroll_right = True
            if event.key == pygame.K_LSHIFT:
                scroll_speed = 5
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                scroll_left = False
            if event.key == pygame.K_RIGHT:
                scroll_right = False
            if event.key == pygame.K_LSHIFT:
                scroll_speed = 1
    pygame.display.update()

游戏主运行程序:

import pygame
from pygame import mixer
import sys
import os
import random
import csv
import button
import math

mixer.init()
pygame.init()
# 画布元素
SCREEN_WIDTH = 800
SCREEN_HEIGHT = int(SCREEN_WIDTH * 0.8)
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("射击游戏")

# 设置帧
clock = pygame.time.Clock()
FPS = 60

# 定义游戏变量
GRAVITY = 0.75
SCROLL_THRESH = 200
ROWS = 16
COLS = 150
TILE_SIZE = SCREEN_HEIGHT // ROWS
TILE_TYPES = 21
MAX_LEVELS = 3
screen_scroll = 0
bg_scroll = 0
level = 1
# 定义游戏状态
start_game = False
# 定义是否淡入进入游戏画面
start_intro = False

# 定义玩家状态变量
moving_left = False
moving_right = False
shoot = False
grenade = False
grenade_thrown = False

#加载音乐和声音
pygame.mixer.music.load("audio/music2.mp3")
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1, 0.0, 3000)
jump_fx = pygame.mixer.Sound("audio/jump.wav")
jump_fx.set_volume(0.5)
shot_fx = pygame.mixer.Sound("audio/shot.wav")
shot_fx.set_volume(0.9)
grenade_fx = pygame.mixer.Sound("audio/grenade.wav")
grenade_fx.set_volume(0.9)


# 加载背景图片
pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha()
pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha()
mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha()
sky_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha()
# 加载按钮图像
start_img = pygame.image.load("img/start_btn.png").convert_alpha()
exit_img = pygame.image.load("img/exit_btn.png").convert_alpha()
restart_img = pygame.image.load("img/restart_btn.png").convert_alpha()

# 加载21种瓷砖图像放在瓷砖图像列表中
img_list = []
for x in range(TILE_TYPES):
   img = pygame.image.load(f"img/Tile/{x}.png")
   img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
   img_list.append(img)
# 加载子弹
bullet_img = pygame.image.load("img/icons/bullet.png").convert_alpha()
grenade_img = pygame.image.load("img/icons/grenade.png").convert_alpha()
# 加载物品
health_box_img = pygame.image.load("img/icons/health_box.png").convert_alpha()
ammo_box_img = pygame.image.load("img/icons/ammo_box.png").convert_alpha()
grenade_box_img = pygame.image.load("img/icons/grenade_box.png").convert_alpha()
item_boxes = {
   "Health": health_box_img,
   "Ammo": ammo_box_img,
   "Grenade": grenade_box_img
}
# 定义颜色
BG = (144, 201, 120)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
PINK = (235, 65, 54)
# 定义字体
font = pygame.font.SysFont("Futura", 30)
# 定义一个显示文本函数,用来显示玩家的相关属性
def draw_text(text, font, text_color, x, y):
   img = font.render(text, True, text_color)
   screen.blit(img, (x, y))
# 刷新背景函数,for循环重复背景,刷新背景中不同的照片的x坐标,以此达到背景动态效果
def draw_bg():
   screen.fill(BG)
   width = sky_img.get_width()
   for x in range(5):
      screen.blit(sky_img, ((x * width) - bg_scroll * 0.5, 0))
      screen.blit(mountain_img, ((x * width) - bg_scroll * 0.6, SCREEN_HEIGHT - mountain_img.get_height() - 300))
      screen.blit(pine1_img, ((x * width) - bg_scroll * 0.7, SCREEN_HEIGHT - pine1_img.get_height() - 150))
      screen.blit(pine2_img, ((x * width) - bg_scroll * 0.8, SCREEN_HEIGHT - pine2_img.get_height()))
# 重置游戏函数定义,碰撞”通关“瓷片时,清空本关的所有显示元素
def reset_level():
   enemy_group.empty()
   bullet_group.empty()
   grenade_group.empty()
   explosion_group.empty()
   item_box_group.empty()
   decoration_group.empty()
   water_group.empty()
   exit_group.empty()
   # 创建空的瓷砖列表。二维列表行列
   data = []
   for row in range(ROWS):
      r = [-1] * COLS
      data.append(r)
   return data
# 创建士兵类(敌人和玩家)
class Soldier(pygame.sprite.Sprite):
   def __init__(self, char_type, x, y, scale, speed, ammo, grenades):
      super().__init__()
      self.alive = True # 定义或者还是死亡变量
      self.char_type = char_type # 获取文件类型样式
      self.speed = speed # 速度
      self.ammo = ammo # 子弹
      self.start_ammo = ammo
      self.shoot_cooldown = 0 # 冷却
      self.grenades = grenades # 手雷
      self.health = 100 # 生命值
      self.max_health = self.health
      self.direction = 1 # 默认方向右
      self.vel_y = 0 # 垂直
      self.jump = False # 跳跃
      self.in_air = True # 是否在空中
      self.flip = False # 默认左为false
      self.animation_list = [] # 动画列表
      self.frame_index = 0 # 索引
      self.action = 0 # 选择动作变量
      self.update_time = pygame.time.get_ticks() # 以毫秒为单位获取时间
      # 创建AI特定变量
      self.move_counter = 0 # 移动计数,对应下文敌人来回徘徊
      self.vision = pygame.Rect(0, 0, 150, 20) # 搜索玩家在玩家视线之内
      self.idling = False # 闲置状态,对应下文AI开枪和扔手雷的状态
      self.idling_counter = 0 # 闲置计数
      self.grenade_time = pygame.time.get_ticks() # 对应下文手雷爆炸时间
      # 加载玩家是所有的图片类型
      animation_types = ["Idle", "Run", "Jump", "Death"]
      for animation in animation_types:
         # 重置临时列表
         temp_list = []
         # 统计每种动画帧数量
         num_of_frames = len(os.listdir(f"img/{char_type}/{animation}"))
         for i in range(num_of_frames):
            img = pygame.image.load(f"img/{char_type}/{animation}/{i}.png").convert_alpha()
            img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
            temp_list.append(img)
         self.animation_list.append(temp_list)
      self.image = self.animation_list[self.action][self.frame_index]
      self.rect = self.image.get_rect()
      self.rect.center = (x, y)# rect=(x,y,w,h)
      self.width = self.image.get_width()
      self.height = self.image.get_height()

   def update(self):
      self.update_animation()
      self.check_alive()
      # 更新冷却时间
      if self.shoot_cooldown > 0:
         self.shoot_cooldown -= 1
   def move(self, moving_left, moving_right):
      # 重置移动变量
      screen_scroll = 0
      dx = 0
      dy = 0
      # 根据移动变量判断向左还是向右移动
      if moving_left:
         dx = -self.speed
         self.flip = True
         self.direction = -1
      if moving_right:
         dx = self.speed
         self.flip = False
         self.direction = 1
      # 跳跃
      if self.jump == True and self.in_air == False:
         self.vel_y = -11
         self.jump = False
         self.in_air = True
      # 使用重力,让其在y方向跳跃高度进行限制
      self.vel_y += GRAVITY
      if self.vel_y > 10:
         self.vel_y
      dy += self.vel_y
      # 检测与地面的碰撞
      for tile in world.obstacle_list:
         # 检测玩家与每个地面瓷砖x方向上的碰撞
         if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
            dx = 0
            # 检测如果是ai机器人碰到墙就返回
            if self.char_type == "enemy":
               self.direction *= -1
               self.move_counter = 0
         # 检车玩家与瓷砖y方向上的碰撞
         if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
            # 检测与地面底部的碰撞
            if self.vel_y < 0:
               self.vel_y = 0
               dy = tile[1].bottom - self.rect.top
            # 检测与地面顶部的碰撞
            elif self.vel_y >= 0:
               self.vel_y = 0
               self.in_air = False
               dy = tile[1].top - self.rect.bottom
      # 检测与水面的碰撞
      if pygame.sprite.spritecollide(self, water_group, False):
         self.health = 0
      # 检车与出口标志碰撞
      level_complete = False
      if pygame.sprite.spritecollide(self, exit_group, False):
         level_complete = True
      # 检测从地图上坠落下来
      if self.rect.bottom > SCREEN_HEIGHT:
         self.health = 0
      # 检测是否走到窗口的边缘,如果走到窗口边缘就不让再走了
      if self.char_type == "player":
         if self.rect.left + dx < 0 or self.rect.right + dx > SCREEN_WIDTH:
            dx = 0
      # 更新矩形的位置
      self.rect.x += dx
      self.rect.y += dy
      # 在玩家位置的基础上更新滚动平台  rect.right 对应矩形的左,以此类推
      if self.char_type == "player":
         if (self.rect.right > SCREEN_WIDTH - SCROLL_THRESH and bg_scroll < world.level_length * TILE_SIZE - SCREEN_WIDTH)\
               or (self .rect.left < SCROLL_THRESH and bg_scroll > abs(dx)):
            self.rect.x -= dx
            screen_scroll = -dx

      return screen_scroll, level_complete

   def shoot(self):
      if self.shoot_cooldown == 0 and self.ammo > 0:
         self.shoot_cooldown = 20
         bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery,
                     self.direction)
         bullet_group.add(bullet)
         #减少弹药
         self.ammo -= 1
         shot_fx.play()
   def ai(self):
      if self.alive and player.alive:
         if self.idling == False and random.randint(1, 100) == 1:
            self.update_action(0) # 选择闲置动作
            self.idling = True
         # ai检测到我方士兵在附近
         if self.vision.colliderect(player.rect):
            # 停止奔跑并面向玩家的时候
            self.update_action(0)
            # 并射击
            self.shoot()
         else:
            # 不定时扔手雷
            now_time = pygame.time.get_ticks()
            if math.sqrt(math.pow(abs(self.rect.centerx - player.rect.centerx), 2) + math.pow(
                  abs(self.rect.centery - player.rect.centery), 2)) < TILE_SIZE * 5:
               if self.grenades > 0:
                  if now_time - self.grenade_time > random.randint(2000, 3000):
                     # 停止奔跑并面向玩家的时候
                     self.update_action(0)
                     self.grenade_time = pygame.time.get_ticks()
                     grenade = Grenade(self.rect.centerx, self.rect.centery, self.direction)
                     grenade_group.add(grenade)
                     self.grenades -= 1

            if self.idling == False:
               if self.direction == 1:
                  ai_moving_right = True
                  self.idling_counter = 50
               else:
                  ai_moving_right = False
               ai_moving_left = not ai_moving_right
               self.move(ai_moving_left, ai_moving_right)
               self.update_action(1) # 选择运动动作
               self.move_counter += 1
               # 更新ai视觉范围作为移动范围
               self.vision.center = (self.rect.centerx + 75 * self.direction, self.rect.centery)
               # pygame.draw.rect(screen, RED, self.vision)
               if self.move_counter > TILE_SIZE:
                  self.direction *= -1
                  self.move_counter *= -1
            else:
               self.idling_counter -= 1
               if self.idling_counter <= 0:
                  self.idling = False

      # 滚动
      self.rect.x += screen_scroll
   def update_animation(self):
      # 更新动画
      ANIMATION_COOLDOWN= 100
      # 更新当前的帧
      self.image = self.animation_list[self.action][self.frame_index]
      # 检测现在的时间更新时间
      if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
         self.update_time = pygame.time.get_ticks()
         self.frame_index += 1
      # 检测如果列表索引超出了动画帧数
      if self.frame_index >= len(self.animation_list[self.action]):
         if self.action == 3:
            self.frame_index = len(self.animation_list[self.action]) - 1
         else:
            self.frame_index = 0
   def update_action(self, new_action):
       # 判断不同的行动播放不同的动画
      if new_action != self.action:
         self.action = new_action
         # 更新动画设置
         self.frame_index = 0
         self.update_time = pygame.time.get_ticks()
   def check_alive(self):
      if self.health <= 0:
         self.health = 0
         self.speed = 0
         self.alive = False
         self.update_action(3)
   def draw(self):
      screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect)
# 收集物品类
class ItemBox(pygame.sprite.Sprite):
   def __init__(self, item_type, x, y):
      super().__init__()
      self.item_type = item_type
      self.image = item_boxes.get(self.item_type)
      self.rect = self.image.get_rect()
      self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
   def update(self):
      # 滚动
      self.rect.x += screen_scroll
      # 检车士兵与物品的碰撞
      if pygame.sprite.collide_rect(self, player):
         # 检测获取箱子的种类
         if self.item_type == "Health":
            player.health += 25
            if player.health > player.max_health:
               player.health = player.max_health
         elif self.item_type == "Ammo":
            player.ammo += 15
         elif self.item_type == "Grenade":
            player.grenades += 3
         # 删除物品
         self.kill()


# 创建血条类
class HealthBar():
   def __init__(self, x, y, health, max_health):
      self.x = x
      self.y = y
      self.health = health
      self.max_health = max_health
   def draw(self, health):
      # 更新最新血条
      self.health = health
      # 计算血条的比率
      ratio = self.health / self.max_health
      pygame.draw.rect(screen, BLACK, (self.x - 2, self.y - 2, 154, 24))
      pygame.draw.rect(screen, RED, (self.x, self.y, 150, 20))
      pygame.draw.rect(screen, GREEN, (self.x, self.y, 150 * ratio, 20))

class Bullet(pygame.sprite.Sprite):
   def __init__(self, x, y, direction):
      super().__init__()
      self.speed = 10
      self.image = bullet_img
      self.rect = self.image.get_rect()
      self.rect.center = (x, y)
      self.direction = direction
   def update(self):
      # 移动子弹
      self.rect.x += (self.direction * self.speed) + screen_scroll  # 子弹射出也要一起移动
      # 检测子弹与地面瓷砖的碰撞
      for tile in world.obstacle_list:
         if tile[1].colliderect(self.rect):
            self.kill()
      # 检测子弹的碰撞
      if pygame.sprite.spritecollide(player, bullet_group, False):
         if player.alive:
            player.health -= 5
            self.kill()
      for enemy in enemy_group:
         if pygame.sprite.spritecollide(enemy, bullet_group, False):
            if enemy.alive:
               enemy.health -= 25
               self.kill()
# 创建手雷
class Grenade(pygame.sprite.Sprite):
   def __init__(self, x, y, direction):
      super().__init__()
      self.timer = 90
      self.vel_y = -11
      self.speed = 7
      self.image = grenade_img
      self.rect = self.image.get_rect()
      self.rect.center = (x, y)
      self.direction = direction
      self.width = self.image.get_width()
      self.height = self.image.get_height()
   def update(self):
      self.vel_y += GRAVITY
      dx = self.direction * self.speed
      dy = self.vel_y

      # 检测手雷与每个瓷砖的碰撞
      for tile in world.obstacle_list:
         # 检测与瓷砖墙壁的碰撞
         if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
            self.direction *= -1
            dx = self.direction * self.speed
         # 检测与y方向上的碰撞
         if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
            self.speed = 0
            # 检测与地面底部的碰撞向下反弹
            if self.vel_y < 0:
               self.vel_y = 0
               dy = tile[1].bottom - self.rect.top
            # 检测与地面顶部的碰撞
            elif self.vel_y >= 0:
               self.vel_y = 0
               self.in_air = False
               dy = tile[1].top - self.rect.bottom
      # 更新受累的位置
      self.rect.x += dx + screen_scroll # 手雷扔出也需要加上滚动的量
      self.rect.y += dy
      # 手雷爆炸冷却时间
      self.timer -= 1
      if self.timer <= 0:
         self.kill()
         grenade_fx.play()
         explosion = Explosion(self.rect.x, self.rect.y, 0.8)
         explosion_group.add(explosion)
         # 爆炸后对任何人在一定的范围内都有伤害
         if abs(self.rect.centerx - player.rect.centerx) < TILE_SIZE * 2 and \
            abs(self.rect.centery - player.rect.centery) < TILE_SIZE * 2:
            player.health -= 10
         for enemy in enemy_group:
            # if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE and \
            #  abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE:
            #  enemy.health -= 100
            if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and \
               abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2:
               enemy.health -= 50
# 创建地图的类
class World():
   def __init__(self):
      self.obstacle_list = [] # 障碍列表
   def process_data(self, data1):
      self.level_length = len(data1[0])
      # 迭代加载数据的每个值
      for y, row in enumerate(data1):
         for x, tile in enumerate(row):
            if tile >= 0:
               img = img_list[tile]
               img_rect = img.get_rect()
               img_rect.x = x * TILE_SIZE
               img_rect.y = y * TILE_SIZE
               tile_data = (img, img_rect)
               if tile >= 0 and tile <= 8:  # 地面泥块
                  self.obstacle_list.append(tile_data)
               elif tile >= 9 and tile <= 10: # 水
                  water = Water(img, x * TILE_SIZE, y * TILE_SIZE)
                  water_group.add(water)
               elif tile >= 11 and tile <= 14: # 装饰类型的
                  decoration = Decoration(img, x * TILE_SIZE, y * TILE_SIZE)
                  decoration_group.add(decoration)
               elif tile == 15: # 创建玩家自己
                  player = Soldier("player", x * TILE_SIZE, y * TILE_SIZE, 1.65, 5, 30, 10)
                  health_bar = HealthBar(10, 10, player.health, player.health)
               elif tile == 16: # 创建敌人
                  enemy = Soldier("enemy", x * TILE_SIZE, y * TILE_SIZE, 1.65, 2, 20, 5)
                  enemy_group.add(enemy)
               elif tile == 17:
                  # 收集弹药
                  item_box = ItemBox("Ammo", x * TILE_SIZE, y * TILE_SIZE)
                  item_box_group.add(item_box)
               elif tile == 18:
                  # 收集手雷
                  item_box = ItemBox("Grenade", x * TILE_SIZE, y * TILE_SIZE)
                  item_box_group.add(item_box)
               elif tile == 19:
                  # 收集医药
                  item_box = ItemBox("Health", x * TILE_SIZE, y * TILE_SIZE)
                  item_box_group.add(item_box)
               elif tile == 20: # 出口
                  exit = Exit(img, x * TILE_SIZE, y * TILE_SIZE)
                  exit_group.add(exit)
      return player, health_bar
   def draw(self):
      for tile in self.obstacle_list:
         tile[1][0] += screen_scroll
         screen.blit(tile[0], tile[1])
# 装饰品类
class Decoration(pygame.sprite.Sprite):
   def __init__(self, img, x, y):
      super().__init__()
      self.image = img
      self.rect = self.image.get_rect()
      self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
   def update(self):
      self.rect.x += screen_scroll
# 创建水类
class Water(pygame.sprite.Sprite):
   def __init__(self, img, x, y):
      super().__init__()
      self.image = img
      self.rect = self.image.get_rect()
      self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
   def update(self):
      self.rect.x += screen_scroll

# 创建出口
class Exit(pygame.sprite.Sprite):
   def __init__(self, img, x, y):
      super().__init__()
      self.image = img
      self.rect = self.image.get_rect()
      self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
   def update(self):
      self.rect.x += screen_scroll

# 创建爆炸类
class Explosion(pygame.sprite.Sprite):
   def __init__(self, x, y, scale):
      super().__init__()
      self.images = []
      for num in range(1, 6):
         img = pygame.image.load(f"img/explosion/exp{num}.png").convert_alpha()
         img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
         self.images.append(img)
      self.frame_index = 0
      self.image = self.images[self.frame_index]
      self.rect = self.image.get_rect()
      self.rect.center = (x, y)
      self.counter = 0

   def update(self):
      # 爆炸加滚动
      self.rect.x += screen_scroll
      EXPLOSION_SPEED = 4
      # 更新爆炸动画
      self.counter += 1
      if self.counter >= EXPLOSION_SPEED:
         self.counter = 0
         self.frame_index += 1
         # 检测爆炸完成后删除爆炸
         if self.frame_index >= len(self.images):
            self.kill()
         else:
            self.image = self.images[self.frame_index]


class ScreenFade():
   def __init__(self, direction, color, speed):
      self.direction = direction
      self.color = color
      self.speed = speed
      self.fade_counter = 0

   def fade(self):
      fade_complete = False # 定义判断是否完成覆盖
      self.fade_counter += self.speed
      if self.direction == 1: #所有类型的淡入淡出
         pygame.draw.rect(screen, self.color, (0 - self.fade_counter, 0, SCREEN_WIDTH // 2, SCREEN_HEIGHT)) # 向左拉开序幕
         pygame.draw.rect(screen, self.color, (SCREEN_WIDTH // 2 + self.fade_counter, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) # 向右拉开序幕
         pygame.draw.rect(screen, self.color, (0, 0 - self.fade_counter, SCREEN_WIDTH, SCREEN_HEIGHT // 2))
         pygame.draw.rect(screen, self.color, (0, SCREEN_HEIGHT // 2 + self.fade_counter, SCREEN_WIDTH, SCREEN_HEIGHT))
      if self.direction == 2: # 垂直向下淡入
         pygame.draw.rect(screen, self.color, (0, 0, SCREEN_WIDTH, 0 + self.fade_counter))
      if self.fade_counter >= SCREEN_WIDTH:
         fade_complete = True
      return fade_complete

# 创建淡入淡出
intro_fade = ScreenFade(1, BLACK, 4)
death_fade = ScreenFade(2, PINK, 4)

#创建开始、退出、重置菜单按钮
start_button = button.Button(SCREEN_WIDTH // 2 - 130, SCREEN_HEIGHT // 2 - 150, start_img, 1)
exit_button = button.Button(SCREEN_WIDTH // 2 - 110, SCREEN_HEIGHT // 2 + 50, exit_img, 1)
restart_button = button.Button(SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 - 50, restart_img, 1)

#创建群组
enemy_group = pygame.sprite.Group()
bullet_group = pygame.sprite.Group()
grenade_group = pygame.sprite.Group()
explosion_group = pygame.sprite.Group()
item_box_group = pygame.sprite.Group()
decoration_group = pygame.sprite.Group()
water_group = pygame.sprite.Group()
exit_group = pygame.sprite.Group()
# 创建空的瓷砖列表
world_data = []
for row in range(ROWS):
   r = [-1] * COLS
   world_data.append(r)
# 加载级别数据创建地图
with open(f"level{level}_data.csv", newline="") as csvfile:
   reader = csv.reader(csvfile, delimiter=",")
   for y, row in enumerate(reader):
      for x, tile in enumerate(row):
         world_data[y][x] = int(tile)
world = World()
player, health_bar = world.process_data(world_data)
run = True
while run:
   clock.tick(FPS)
   if start_game == False:
      # 显示主菜单
      # 画菜单
      screen.fill(BG)
      # 增加按钮
      if start_button.draw(screen):
         start_game = True
         start_intro = True
      if exit_button.draw(screen):
         run = False
   else:
      draw_bg()
      # 显示地图
      world.draw()
      # 显示血条
      health_bar.draw(player.health)
      # 显示弹药量
      draw_text("AMMO: ", font, WHITE, 10, 35)
      for x in range(player.ammo):
         screen.blit(bullet_img, (90 + (x * 10), 40))
      # 显示手雷量
      draw_text("GRENADES: ", font, WHITE, 10, 60)
      for x in range(player.grenades):
         screen.blit(grenade_img, (135 + (x * 15), 60))
      # 显示血条量
      # draw_text(f"AMMO: {player.ammo}", font, WHITE, 10, 35)

      player.update()
      player.draw()

      for enemy in enemy_group:
         enemy.ai()
         enemy.update()
         enemy.draw()

      # 更新和画组
      bullet_group.update()
      grenade_group.update()
      explosion_group.update()
      item_box_group.update()
      decoration_group.update()
      water_group.update()
      exit_group.update()
      bullet_group.draw(screen)
      grenade_group.draw(screen)
      explosion_group.draw(screen)
      item_box_group.draw(screen)
      decoration_group.draw(screen)
      water_group.draw(screen)
      exit_group.draw(screen)
      # 显示淡入
      if start_intro:
         if intro_fade.fade():
            start_intro = False
            intro_fade.fade_counter = 0

      # 更新玩家的动作
      if player.alive:
         # 发射子弹
         if shoot:
            player.shoot()
         # 扔手雷
         elif grenade and grenade_thrown == False and player.grenades > 0:
            grenade = Grenade(player.rect.centerx + (0.5 * player.rect.size[0] * player.direction),
                          player.rect.top, player.direction)
            grenade_group.add(grenade)
            player.grenades -= 1
            grenade_thrown = True

         if player.in_air:
            player.update_action(2)
         elif moving_left or moving_right:
            player.update_action(1)
         else:
            player.update_action(0)

         screen_scroll, level_complete = player.move(moving_left, moving_right)
         bg_scroll -= screen_scroll
         # 检测玩家是否通过该级别,把二位列表的具体位置(某一行的某一列)赋值
         if level_complete:
            start_intro = True
            level += 1
            bg_scroll = 0
            world_data = reset_level()
            if level <= MAX_LEVELS:
               # 加载级别数据创建地图
               with open(f"level{level}_data.csv", newline="") as csvfile:
                  reader = csv.reader(csvfile, delimiter=",")
                  for y, row in enumerate(reader):
                     for x, tile in enumerate(row):
                        world_data[y][x] = int(tile)

               world = World()
               player, health_bar = world.process_data(world_data)

      else:
         screen_scroll = 0
         if death_fade.fade(): # 完成覆盖后才出现按钮中间时间过渡
            if restart_button.draw(screen):
               death_fade.fade_counter = 0 # 计数清零
               start_intro = True
               bg_scroll = 0
               world_data = reset_level()
               # 加载级别数据创建地图
               with open(f"level{level}_data.csv", newline="") as csvfile:
                  reader = csv.reader(csvfile, delimiter=",")
                  for y, row in enumerate(reader):
                     for x, tile in enumerate(row):
                        world_data[y][x] = int(tile)

               world = World()
               player, health_bar = world.process_data(world_data)


   for event in pygame.event.get():
      # 退出游戏
      if event.type == pygame.QUIT:
         run = False
         pygame.quit()
         sys.exit()
      # 键盘按键
      if event.type == pygame.KEYDOWN:
         if event.key == pygame.K_a:
            moving_left = True
         if event.key == pygame.K_d:
            moving_right = True
         if event.key == pygame.K_SPACE:
            shoot = True
         if event.key == pygame.K_q:
            grenade = True
         if event.key == pygame.K_w and player.alive:
            player.jump = True
            jump_fx.play()
         if event.key == pygame.K_ESCAPE:
            run = False
      # 按键释放
      if event.type == pygame.KEYUP:
         if event.key == pygame.K_a:
            moving_left = False
         if event.key == pygame.K_d:
            moving_right = False
         if event.key == pygame.K_SPACE:
            shoot = False
         if event.key == pygame.K_q:
            grenade = False
            grenade_thrown = False


   pygame.display.update()

部分游戏截图:

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/90454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Word处理控件Aspose.Words功能演示:在 Python 中将 HTML 转换为 PNG、JPEG、BMP、GIF 或 TIFF 图像

Aspose API支持流行文件格式处理&#xff0c;并允许将各类文档导出或转换为固定布局文件格式和最常用的图像/多媒体格式。 HTML &#xff08;超文本标记语言&#xff09;是所有浏览器都支持的主要网页文件格式。它经常用于将数据和信息显示为网页。在某些情况下&#xff0c;我们…

迎接工业互联网的龙卷风暴,软通动力绘制了一张转型地图

《绿野仙踪》一书的开始&#xff0c;主角多萝西被一股龙卷风卷起来&#xff0c;从此离开了平凡无奇的堪萨斯州&#xff0c;来到神奇的奥兹国。这种让人一步登天、进入仙境的“龙卷风暴”&#xff0c;也在科技行业不停上演。在微型计算机和个人电脑PC这两场大型龙卷风市场中&…

cdr最新软件下载2023中文版电脑64位免费安装包

CorelDRAW Graphics Suite2023涵盖了全部CorelDRAW图形处理组件&#xff0c;是一款智能高效的平面设计软件&#xff0c;广泛应用于排版印刷、矢量图形编辑及网页设计等领域&#xff0c;30多年来无数优秀的设计师通过CorelDRAW大胆展现真我&#xff0c;交付了出众的创意作品&…

web前端期末大作业——仿小米商城电商平台(6页) html+css+javascript网页设计实例 企业网站制作

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

【面试】RabbitMQ面试题答案整理

1、RabbitMQ routing路由模式 1、 消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息; 2、 根据业务功能定义路由字符串 3、 从系统的…

关于IPv6升级改造的政策文件汇总-中科三方

一、《推进互联网协议第六版&#xff08;IPv6&#xff09;规模部署行动计划》 发布时间&#xff1a;2017年11月 发文单位&#xff1a;中共中央办公厅、国务院办公厅 主要内容&#xff1a;用5到10年时间&#xff0c;形成下一代互联网自主技术体系和产业生态&#xff0c;建成全…

Matlab论文插图绘制模板第70期—带误差棒的柱状图(Bar with Errorbar)

在之前的文章中&#xff0c;分享了一系列Matlab柱状图的绘制模板&#xff1a; 这一次&#xff0c;再来分享一种特殊的柱状图&#xff1a;带误差棒的柱状图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab论文插图绘制模板系列&#xff0c;旨在降低大家使用Matl…

直播倒计时 1 天 | SOFAChannel#31 RPC 框架设计的考和量

SOFARPC 是蚂蚁集团开源的一款基于 Java 实现的 RPC 服务框架&#xff0c;为应用之间提供远程服务调用能力&#xff0c;具有高可伸缩性&#xff0c;高容错性&#xff0c;目前蚂蚁集团所有的业务的相互间的 RPC 调用都是采用 SOFARPC。SOFARPC 为用户提供了负载均衡&#xff0c;…

全网唯一,不忽悠的ChatGPT

Datawhale干货 作者&#xff1a;Ben&#xff0c;中山大学&#xff0c;Datawhale成员最近ChatGPT火出圈了&#xff0c;它和前阵子的Stable Diffusion&#xff08;AIGC&#xff09;一样成为社交媒体上人们津津乐道的话题。“ChatGPT要取代谷歌搜索了&#xff1f;”“ChatGPT要让程…

制作 Python Docker 镜像的最佳实践

概述 &#x1f4da;️Reference: 制作容器镜像的最佳实践 这篇文章是关于制作 Python Docker 容器镜像的最佳实践。(2022 年 12 月更新&#xff09; 最佳实践的目的一方面是为了减小镜像体积&#xff0c;提升 DevOps 效率&#xff0c;另一方面是为了提高安全性。希望对各位有所…

无忧·企业文档,为企业客户的场景而思考

作为无忧企业文档的设计者&#xff0c;常常被问到一个问题&#xff0c;这个和腾X文档有什么区别&#xff1f;其实这个问题我口头回答了很多次&#xff0c;这次&#xff0c;我将这个问题做个记录与分析。 主要是有以下几个方面不同&#xff1a; 1、客户定位不同 2、核心功能不…

Java基础疑难点梳理(泛型到反射9章内容)

文章目录1. 泛型2. 基础类库3. 集合4. 异常5. 注解6. JDBC7. IO流8. 网络编程9. 类加载和反射1. 泛型 静态方法的形参&#xff0c;静态变量&#xff0c;静态代码块中不能使用泛型&#xff08;因为即使泛型不同&#xff0c;还是同一个类&#xff0c;静态变量是属于类的&#xff…

锂电池实验室规划设计方案 | 喜格SICOLAB

锂电池实验室规划设计方案 | 喜格SICOLAB 锂电池生产基本工序 正负极混料、涂布、辊压、干燥、裁切、卷绕或叠片、封装、注液、化成、检测、出货等。锂电池实验室设计标准 1、《电子工业洁净厂房设计规范》GB50472-2008 2、《锂离子电池工厂设计标准》GB 51377-2019 《工业建筑…

在 Python 中使用 cv2 进行嗜睡检测

大家好&#xff0c;在这个博客中&#xff0c;我们将构建一个嗜睡检测应用程序&#xff0c;它将检测视频中的人是否变得昏昏欲睡。这是一个非常有趣且简单的项目&#xff0c;代码甚至不到 80 行&#xff0c;让我们开始吧看看最终输出注意——你不会在这里听到警报声&#xff0c;…

Stm32旧版库函数12——定时器的使用\time1

#include "stm32f10x_lib.h" #include "usart.h" #include "delay.h" #include "sys_config.h" #include <math.h> //Keil library #define uchar unsigned char #define uint unsigned int //通用定时器中断初始…

SpringCloud(6)— RabbitMQ(消息队列)

SpringCloud&#xff08;6&#xff09;— RabbitMQ(消息队列) 一 初识MQ 1.同步通信与异步通信 1.同步通信的问题 同步调用的优点在于时效性高&#xff0c;可以立即得到结果 微服务之间基于Feign的调用属于同步方式&#xff0c;存在一些问题 耦合性&#xff1a;业务较多时&a…

硅片检测半导体运动台控制器的设计

多自由度精密运动平台是半导体行业中硅片制造和检测过程里至关重要的问题&#xff0c;采用直线电机和音圈电机等先进驱动方式的精密运动平台相对传统滚珠丝杠旋转电机运动的运动平台&#xff0c;具有精度高、响应快、寿命长、免维护和结构简单等诸多优点&#xff0c;优势十分明…

为什么感觉现在很少有黑客了?

有一个词语&#xff0c;人人不是它&#xff0c;却人人都提及它&#xff0c;他就是&#xff1a;黑客&#xff01; 黑客&#xff0c;这个我们从小就接触的工种&#xff0c;总是衣服全身黑衣、眼戴墨镜、冷酷无情、超级厉害的形象&#xff0c;关键是&#xff0c;只要应用崩了/数据…

一颗芯片是怎样诞生的

目录 从沙子到单晶硅 单晶硅切片 电路实现 芯片的封装 芯片属于半导体。半导体是介于导体和绝缘体之间的一类物质&#xff0c;元素周期表中硅、锗、硒、硼的单质都属于半导体。这些单质通过掺杂其他元素生成的一些化合物&#xff0c;也属于半导体的范畴。 P型半导体是在单…

Linux安装phpstudy(无联网版)

下载install.sh Centos安装脚本&#xff0c;直接在浏览器打开https://notdocker.xp.cn/install.sh下载install.sh #官方安装命令 yum install -y wget && wget -O install.sh https://notdocker.xp.cn/install.sh && sh install.sh #命令解析 yum install -y …