Python開發(fā)之射擊闖關(guān)游戲的實現(xiàn)
項目功能
地圖編輯器:可以實現(xiàn)玩家自己定義每一關(guān)卡的樣式和難易程度
運行界面:實現(xiàn)了玩家的移動,跳躍,發(fā)射子彈,投擲手雷,以及敵人的AL(移動,發(fā)射子彈,扔手雷),同時游戲中有一系列的道具(生命值藥箱,子彈補給,手雷補給)以及各種動畫和音樂音效,還有各種花草巖石裝飾品,以及懸崖和水渦危險地方,更多未知,自己體驗就能感受到!
總代碼累計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)
# 創(chuàng)建保存按鈕
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)
# 創(chuàng)建空的瓷磚列表(二維)
world_data = []
for row in range(ROWS):
r = [-1] * MAX_COLS
world_data.append(r)
# 創(chuàng)建一個組
for tile in range(0, MAX_COLS):
world_data[ROWS - 1][tile] = 0
# 在屏幕上顯示下一級定義文本顯示函數(shù)
def draw_text(text, font, text_color, x, y):
img = font.render(text, True, text_color)
screen.blit(img, (x, y))
# 創(chuàng)建背景函數(shù)
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))
# 創(chuàng)建按鈕
# 創(chuàng)建保存和加載數(shù)據(jù)按鈕
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)
# 保存和加載地圖數(shù)據(jù)
if save_button.draw(screen):
# 保存級別數(shù)據(jù)
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):
# 加載地圖級別數(shù)據(jù)
# 重置滾動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))
# 選擇一種瓷磚,獲取右側(cè)瓷磚列表的具體
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
# 在窗口中增加新的瓷磚
# 獲取鼠標(biāo)的位置
pos = pygame.mouse.get_pos()
x = (pos[0] + scroll) // TILE_SIZE
y = pos[1] // TILE_SIZE
# 檢測點擊的區(qū)域,把右側(cè)獲取的瓷片放在地圖中
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("射擊游戲")
# 設(shè)置幀
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
# 定義游戲狀態(tài)
start_game = False
# 定義是否淡入進(jìn)入游戲畫面
start_intro = False
# 定義玩家狀態(tài)變量
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)
# 定義一個顯示文本函數(shù),用來顯示玩家的相關(guān)屬性
def draw_text(text, font, text_color, x, y):
img = font.render(text, True, text_color)
screen.blit(img, (x, y))
# 刷新背景函數(shù),for循環(huán)重復(fù)背景,刷新背景中不同的照片的x坐標(biāo),以此達(dá)到背景動態(tài)效果
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()))
# 重置游戲函數(shù)定義,碰撞”通關(guān)“瓷片時,清空本關(guān)的所有顯示元素
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()
# 創(chuàng)建空的瓷磚列表。二維列表行列
data = []
for row in range(ROWS):
r = [-1] * COLS
data.append(r)
return data
# 創(chuàng)建士兵類(敵人和玩家)
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 # 默認(rèn)方向右
self.vel_y = 0 # 垂直
self.jump = False # 跳躍
self.in_air = True # 是否在空中
self.flip = False # 默認(rèn)左為false
self.animation_list = [] # 動畫列表
self.frame_index = 0 # 索引
self.action = 0 # 選擇動作變量
self.update_time = pygame.time.get_ticks() # 以毫秒為單位獲取時間
# 創(chuàng)建AI特定變量
self.move_counter = 0 # 移動計數(shù),對應(yīng)下文敵人來回徘徊
self.vision = pygame.Rect(0, 0, 150, 20) # 搜索玩家在玩家視線之內(nèi)
self.idling = False # 閑置狀態(tài),對應(yīng)下文AI開槍和扔手雷的狀態(tài)
self.idling_counter = 0 # 閑置計數(shù)
self.grenade_time = pygame.time.get_ticks() # 對應(yīng)下文手雷爆炸時間
# 加載玩家是所有的圖片類型
animation_types = ["Idle", "Run", "Jump", "Death"]
for animation in animation_types:
# 重置臨時列表
temp_list = []
# 統(tǒng)計每種動畫幀數(shù)量
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
# 根據(jù)移動變量判斷向左還是向右移動
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方向跳躍高度進(jìn)行限制
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
# 檢車與出口標(biāo)志碰撞
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
# 在玩家位置的基礎(chǔ)上更新滾動平臺 rect.right 對應(yīng)矩形的左,以此類推
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
# 更新當(dāng)前的幀
self.image = self.animation_list[self.action][self.frame_index]
# 檢測現(xiàn)在的時間更新時間
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
# 檢測如果列表索引超出了動畫幀數(shù)
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
# 更新動畫設(shè)置
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()
# 創(chuàng)建血條類
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()
# 創(chuàng)建手雷
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)
# 爆炸后對任何人在一定的范圍內(nèi)都有傷害
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
# 創(chuàng)建地圖的類
class World():
def __init__(self):
self.obstacle_list = [] # 障礙列表
def process_data(self, data1):
self.level_length = len(data1[0])
# 迭代加載數(shù)據(jù)的每個值
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: # 創(chuàng)建玩家自己
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: # 創(chuàng)建敵人
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:
# 收集醫(yī)藥
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
# 創(chuàng)建水類
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
# 創(chuàng)建出口
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
# 創(chuàng)建爆炸類
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
# 創(chuàng)建淡入淡出
intro_fade = ScreenFade(1, BLACK, 4)
death_fade = ScreenFade(2, PINK, 4)
#創(chuàng)建開始、退出、重置菜單按鈕
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)
#創(chuàng)建群組
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()
# 創(chuàng)建空的瓷磚列表
world_data = []
for row in range(ROWS):
r = [-1] * COLS
world_data.append(r)
# 加載級別數(shù)據(jù)創(chuàng)建地圖
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:
# 發(fā)射子彈
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:
# 加載級別數(shù)據(jù)創(chuàng)建地圖
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(): # 完成覆蓋后才出現(xiàn)按鈕中間時間過渡
if restart_button.draw(screen):
death_fade.fade_counter = 0 # 計數(shù)清零
start_intro = True
bg_scroll = 0
world_data = reset_level()
# 加載級別數(shù)據(jù)創(chuàng)建地圖
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()部分游戲截圖





到此這篇關(guān)于Python開發(fā)之射擊闖關(guān)游戲的實現(xiàn)的文章就介紹到這了,更多相關(guān)Python射擊闖關(guān)游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡單介紹Python的Tornado框架中的協(xié)程異步實現(xiàn)原理
這篇文章主要介紹了簡單介紹Python的Tornado框架中的協(xié)程異步實現(xiàn)原理,作者基于Python的生成器講述了Tornado異步的特點,需要的朋友可以參考下2015-04-04
Python連接Oracle數(shù)據(jù)庫的操作指南
Oracle數(shù)據(jù)庫是一種強大的企業(yè)級關(guān)系數(shù)據(jù)庫管理系統(tǒng)(RDBMS),而Python是一門流行的編程語言,兩者的結(jié)合可以提供出色的數(shù)據(jù)管理和分析能力,本教程將詳細(xì)介紹如何在Python中連接Oracle數(shù)據(jù)庫,并演示常見的數(shù)據(jù)庫任務(wù),需要的朋友可以參考下2023-11-11
python正則表達(dá)中的re庫常用方法總結(jié)
這篇文章主要介紹了python正則表達(dá)中的re庫常用方法總結(jié)文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-08-08
Python實現(xiàn)刪除列表中滿足一定條件的元素示例
這篇文章主要介紹了Python實現(xiàn)刪除列表中滿足一定條件的元素,結(jié)合具體實例形式對比分析了Python針對列表元素的遍歷、復(fù)制、刪除等相關(guān)操作技巧,需要的朋友可以參考下2017-06-06
python中datetime模塊中strftime/strptime函數(shù)的使用
這篇文章主要介紹了python中datetime模塊中strftime/strptime函數(shù)的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07

