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

原创
小哥 3年前 (2022-12-19) 阅读数 3 #大杂烩

项目特点:

地图编辑器:玩家可以定义每个关卡的风格和难度

运行界面:玩家的移动、跳跃、发射子弹、投掷手榴弹和敌人的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)

创建空平铺列表(2D)

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()

部分游戏截图:

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除