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

news/2024/11/18 4:28:11/

项目功能:

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

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

总代码累计1100行左右!

地图编辑器:

import pygame
import sys
import csv
import buttonpygame.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_COLSworld_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 += 1if button_col == 3:button_row += 1button_col = 0run = 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为起始位置0scroll = 0with 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 = 0for 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_speedif 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_SIZEy = 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] = -1for event in pygame.event.get():if event.type == pygame.QUIT:run = Falsepygame.quit()sys.exit()# 键盘按键if event.type == pygame.KEYDOWN:if event.key == pygame.K_UP:level += 1if event.key == pygame.K_DOWN and level > 0:level -= 1if event.key == pygame.K_LEFT:scroll_left = Trueif event.key == pygame.K_RIGHT:scroll_right = Trueif event.key == pygame.K_LSHIFT:scroll_speed = 5if event.type == pygame.KEYUP:if event.key == pygame.K_LEFT:scroll_left = Falseif event.key == pygame.K_RIGHT:scroll_right = Falseif event.key == pygame.K_LSHIFT:scroll_speed = 1pygame.display.update()

游戏主运行程序:

import pygame
from pygame import mixer
import sys
import os
import random
import csv
import button
import mathmixer.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] * COLSdata.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 = ammoself.shoot_cooldown = 0 # 冷却self.grenades = grenades # 手雷self.health = 100 # 生命值self.max_health = self.healthself.direction = 1 # 默认方向右self.vel_y = 0 # 垂直self.jump = False # 跳跃self.in_air = True # 是否在空中self.flip = False # 默认左为falseself.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 -= 1def move(self, moving_left, moving_right):# 重置移动变量screen_scroll = 0dx = 0dy = 0# 根据移动变量判断向左还是向右移动if moving_left:dx = -self.speedself.flip = Trueself.direction = -1if moving_right:dx = self.speedself.flip = Falseself.direction = 1# 跳跃if self.jump == True and self.in_air == False:self.vel_y = -11self.jump = Falseself.in_air = True# 使用重力,让其在y方向跳跃高度进行限制self.vel_y += GRAVITYif self.vel_y > 10:self.vel_ydy += 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 *= -1self.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 = 0dy = tile[1].bottom - self.rect.top# 检测与地面顶部的碰撞elif self.vel_y >= 0:self.vel_y = 0self.in_air = Falsedy = tile[1].top - self.rect.bottom# 检测与水面的碰撞if pygame.sprite.spritecollide(self, water_group, False):self.health = 0# 检车与出口标志碰撞level_complete = Falseif 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 += dxself.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 -= dxscreen_scroll = -dxreturn screen_scroll, level_completedef shoot(self):if self.shoot_cooldown == 0 and self.ammo > 0:self.shoot_cooldown = 20bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery,self.direction)bullet_group.add(bullet)#减少弹药self.ammo -= 1shot_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 -= 1if self.idling == False:if self.direction == 1:ai_moving_right = Trueself.idling_counter = 50else:ai_moving_right = Falseai_moving_left = not ai_moving_rightself.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 *= -1self.move_counter *= -1else:self.idling_counter -= 1if self.idling_counter <= 0:self.idling = False# 滚动self.rect.x += screen_scrolldef 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]) - 1else:self.frame_index = 0def update_action(self, new_action):# 判断不同的行动播放不同的动画if new_action != self.action:self.action = new_action# 更新动画设置self.frame_index = 0self.update_time = pygame.time.get_ticks()def check_alive(self):if self.health <= 0:self.health = 0self.speed = 0self.alive = Falseself.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_typeself.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 += 25if player.health > player.max_health:player.health = player.max_healthelif self.item_type == "Ammo":player.ammo += 15elif self.item_type == "Grenade":player.grenades += 3# 删除物品self.kill()# 创建血条类
class HealthBar():def __init__(self, x, y, health, max_health):self.x = xself.y = yself.health = healthself.max_health = max_healthdef draw(self, health):# 更新最新血条self.health = health# 计算血条的比率ratio = self.health / self.max_healthpygame.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 = 10self.image = bullet_imgself.rect = self.image.get_rect()self.rect.center = (x, y)self.direction = directiondef 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 -= 5self.kill()for enemy in enemy_group:if pygame.sprite.spritecollide(enemy, bullet_group, False):if enemy.alive:enemy.health -= 25self.kill()
# 创建手雷
class Grenade(pygame.sprite.Sprite):def __init__(self, x, y, direction):super().__init__()self.timer = 90self.vel_y = -11self.speed = 7self.image = grenade_imgself.rect = self.image.get_rect()self.rect.center = (x, y)self.direction = directionself.width = self.image.get_width()self.height = self.image.get_height()def update(self):self.vel_y += GRAVITYdx = self.direction * self.speeddy = 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 *= -1dx = 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 = 0dy = tile[1].bottom - self.rect.top# 检测与地面顶部的碰撞elif self.vel_y >= 0:self.vel_y = 0self.in_air = Falsedy = tile[1].top - self.rect.bottom# 更新受累的位置self.rect.x += dx + screen_scroll # 手雷扔出也需要加上滚动的量self.rect.y += dy# 手雷爆炸冷却时间self.timer -= 1if 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 -= 10for 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 -= 100if 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_SIZEimg_rect.y = y * TILE_SIZEtile_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_bardef draw(self):for tile in self.obstacle_list:tile[1][0] += screen_scrollscreen.blit(tile[0], tile[1])
# 装饰品类
class Decoration(pygame.sprite.Sprite):def __init__(self, img, x, y):super().__init__()self.image = imgself.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 = imgself.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 = imgself.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 = 0self.image = self.images[self.frame_index]self.rect = self.image.get_rect()self.rect.center = (x, y)self.counter = 0def update(self):# 爆炸加滚动self.rect.x += screen_scrollEXPLOSION_SPEED = 4# 更新爆炸动画self.counter += 1if self.counter >= EXPLOSION_SPEED:self.counter = 0self.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 = directionself.color = colorself.speed = speedself.fade_counter = 0def fade(self):fade_complete = False # 定义判断是否完成覆盖self.fade_counter += self.speedif 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 = Truereturn 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] * COLSworld_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 = Truestart_intro = Trueif exit_button.draw(screen):run = Falseelse: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 = Falseintro_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 -= 1grenade_thrown = Trueif 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 = Truelevel += 1bg_scroll = 0world_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 = 0if death_fade.fade(): # 完成覆盖后才出现按钮中间时间过渡if restart_button.draw(screen):death_fade.fade_counter = 0 # 计数清零start_intro = Truebg_scroll = 0world_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 = Falsepygame.quit()sys.exit()# 键盘按键if event.type == pygame.KEYDOWN:if event.key == pygame.K_a:moving_left = Trueif event.key == pygame.K_d:moving_right = Trueif event.key == pygame.K_SPACE:shoot = Trueif event.key == pygame.K_q:grenade = Trueif event.key == pygame.K_w and player.alive:player.jump = Truejump_fx.play()if event.key == pygame.K_ESCAPE:run = False# 按键释放if event.type == pygame.KEYUP:if event.key == pygame.K_a:moving_left = Falseif event.key == pygame.K_d:moving_right = Falseif event.key == pygame.K_SPACE:shoot = Falseif event.key == pygame.K_q:grenade = Falsegrenade_thrown = Falsepygame.display.update()

部分游戏截图:

 

 

 

 

 

 

 


http://www.ppmy.cn/news/3824.html

相关文章

【大数据技术Hadoop+Spark】MapReduce概要、思想、编程模型组件、工作原理详解(超详细)

MapReduce是Hadoop系统核心组件之一&#xff0c;它是一种可用于大数据并行处理的计算模型、框架和平台&#xff0c;主要解决海量数据的计算&#xff0c;是目前分布式计算模型中应用较为广泛的一种。 一、MapReduce核心思想 MapReduce的核心思想是“分而治之”。所谓“分而治之…

PostgreSQL的学习心得和知识总结(一百一十七)|语法级自上而下完美实现MySQL数据库的 label:loop 的实现方案

目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《PostgreSQL数据库内核分析》 2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》 3、PostgreSQL数据库仓库链接,点击前往 4、日本著名PostgreSQL数据库专家 铃木启修 网站…

测试用例等级怎么划分?别再傻傻的一脸懵逼

我们都知道测试工程师最基本的能力便是编写测试用例&#xff0c;可是看似简单的用例&#xff0c;后面其实蕴含这个很多人忽略的细节&#xff0c;今天就来说测试里面所蕴含的很多细节。 很多时候不只是测试和测试用例息息相关&#xff0c;开发&#xff0c;产品也有的时候对于测试…

类加载器、类加载器的过程、类加载的分类、双亲委派模型

文章目录1.类加载器1.1类加载器1.2类加载的过程1.3类加载的分类1.4 双亲委派模型1.5 ClassLoader 中的两个方法1.类加载器 1.1类加载器 作用 负责将.class文件&#xff08;存储的物理文件&#xff09;加载到内存中 1.2类加载的过程 类加载时机 创建类的实例&#xff08;对象…

新产品开发流程管理_以市场为驱动

第1章 创新的挑战 1.1 挑战&#xff1a;如何真正做到创新 企业都有不凡的成长目标。 引发成长的四种来源&#xff1a; **- 市场成长 市场份额增长新市场的出现收购** 以上四种来源获得企业成长困难且代价高。 **市场成长&#xff1a;**很多工业化国家和行业中的市场是成熟…

Linux常用命令总结(建议收藏)

文章目录一、文件管理1、cat&#xff1a;查看文件内容案例1&#xff1a;输出内容行数2、chmod&#xff1a;是控制用户对文件的权限的命令案例1&#xff1a;&#xff1a;将user文件修改成用户、组、其他用户都可以读写可执行的权限3、diff&#xff1a;用于比较文件的差异4、find…

MySql SQL语句的执行过程

一、客户端的数据库驱动与数据库连接池&#xff1a; &#xff08;1&#xff09;客户端与数据库进行通信前&#xff0c;通过数据库驱动与MySQL建立连接&#xff0c;建立完成之后&#xff0c;就发送SQL语句 &#xff08;2&#xff09;为了减少频繁创建和销毁连接造成系统性能的…

【Processing】我给网友 “战场小包” 做了他的 “自画像”.

前言 突然疫情放开了&#xff0c;在掘金里认识的一个掘友&#xff08;战场小包&#xff09;&#xff0c;突然今天找我。 &#xff1a;寻思啥事呢&#xff0c;原来找我做个自画像。 &#xff1a;行&#xff01;没问题&#xff01; &#xff1a;结果等半天&#xff08;一晚上到今…