|
|
|
|
|
|
|
|
import pygame
|
|
|
import os
|
|
|
import random
|
|
|
import csv
|
|
|
import button
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#初始化
|
|
|
pygame.init()
|
|
|
|
|
|
#设置窗口
|
|
|
screen = pygame.display.set_mode((1600,900))
|
|
|
#设置标题
|
|
|
pygame.display.set_caption('喷火龙历险记')
|
|
|
|
|
|
|
|
|
#玩家左右移动
|
|
|
moving_left = False
|
|
|
moving_right = False
|
|
|
#AI左右移动
|
|
|
ai_moving_left = False
|
|
|
ai_moving_right = False
|
|
|
#射击参数
|
|
|
shoot = False
|
|
|
grenade = False
|
|
|
grenade_thrown = False
|
|
|
|
|
|
#设置帧速率
|
|
|
clock = pygame.time.Clock()
|
|
|
FPS = 120
|
|
|
|
|
|
#定义游戏变量
|
|
|
screen_scroll = 0
|
|
|
SCROLL_THRESH = 400
|
|
|
BG_SCROLL = 0
|
|
|
MAX_LEVELS = 3
|
|
|
#重力
|
|
|
Gravity = 0.3
|
|
|
TILE_SIZE = 40
|
|
|
ROWS = 20
|
|
|
COLS = 100
|
|
|
TILE_SIZE = 45
|
|
|
TILE_TYPES = 24
|
|
|
level = 1
|
|
|
|
|
|
start_game = False
|
|
|
|
|
|
|
|
|
start_intro = False
|
|
|
|
|
|
#加载图片
|
|
|
#开始按钮
|
|
|
start_img = pygame.image.load('img/start.png').convert_alpha()
|
|
|
exit_img = pygame.image.load('img/exit.png').convert_alpha()
|
|
|
restart_img = pygame.image.load('img/restart.png').convert_alpha()
|
|
|
#背景
|
|
|
pine1_img = pygame.image.load('img/background/bg1.png').convert_alpha()
|
|
|
pine2_img = pygame.image.load('img/background/bg2.png').convert_alpha()
|
|
|
pine3_img = pygame.image.load('img/background/bg3.png').convert_alpha()
|
|
|
#把地图放进列表
|
|
|
img_list = []
|
|
|
for x in range(TILE_TYPES):
|
|
|
img = pygame.image.load(f'img/background/bg_{x}.gif')
|
|
|
img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
|
|
|
img_list.append(img)
|
|
|
#子弹
|
|
|
bullet_img = pygame.image.load('img/icons/fire.png').convert_alpha()
|
|
|
#手榴弹
|
|
|
grenade_img = pygame.image.load('img/icons/grenade.png').convert_alpha()
|
|
|
#物品拾取
|
|
|
health_box_img = pygame.image.load('img/background/bg_20.gif').convert_alpha()
|
|
|
health_box_img = pygame.transform.scale(health_box_img, (TILE_SIZE, TILE_SIZE))
|
|
|
ammo_box_img = pygame.image.load('img/background/bg_19.gif').convert_alpha()
|
|
|
ammo_box_img = pygame.transform.scale(ammo_box_img, (TILE_SIZE, TILE_SIZE))
|
|
|
grenade_box_img = pygame.image.load('img/background/bg_18.gif').convert_alpha()
|
|
|
grenade_box_img = pygame.transform.scale(grenade_box_img, (TILE_SIZE, TILE_SIZE))
|
|
|
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)
|
|
|
|
|
|
#定义字体
|
|
|
font = pygame.font.SysFont('Futura',30)
|
|
|
|
|
|
def draw_text(text,font,text_col,x,y):
|
|
|
img = font.render(text,True,text_col)
|
|
|
screen.blit(img,(x,y))
|
|
|
|
|
|
|
|
|
#绘制背景
|
|
|
def draw_bg():
|
|
|
screen.fill(BG)
|
|
|
#pygame.draw.line(screen,RED,(0,400),(1600,400))
|
|
|
width = pine1_img.get_width()
|
|
|
for x in range(4):
|
|
|
#screen.blit(pine1_img, ((x * width)- BG_SCROLL * 0.7 , 0))
|
|
|
#screen.blit(pine2_img, ((x * width)- BG_SCROLL * 0.7, 0))
|
|
|
screen.blit(pine3_img, ((x * width) - BG_SCROLL * 0.7, -200))
|
|
|
screen.blit(pine3_img, ((x * width)- BG_SCROLL * 0.7, -200))
|
|
|
#screen.blit(pine1_img, (0,0))
|
|
|
#screen.blit(pine2_img, (0, 0))
|
|
|
#screen.blit(pine3_img, (0, -200))
|
|
|
|
|
|
|
|
|
#重开的函数
|
|
|
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,health,grenades): #所有类开始都用这个
|
|
|
pygame.sprite.Sprite.__init__(self) #初始化
|
|
|
#是否活着
|
|
|
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 = health
|
|
|
#最大生命值
|
|
|
self.max_health = self.health
|
|
|
#移动方向
|
|
|
self.direction = 1
|
|
|
#竖直方向速度
|
|
|
self.vel_y = 0
|
|
|
#翻转图片
|
|
|
self.flip = False
|
|
|
#跳跃检查
|
|
|
self.jump = False
|
|
|
#在空中检查
|
|
|
self.in_air = True
|
|
|
#动画列表
|
|
|
self.animation_list = []
|
|
|
temp_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, 400, 20)
|
|
|
self.idling = False
|
|
|
self.idling_counter = 0
|
|
|
|
|
|
|
|
|
|
|
|
#加载所有图像
|
|
|
animation_types = ['idle','run','jump','death']
|
|
|
for animation in animation_types:
|
|
|
#重置临时图像的列表
|
|
|
temp_list = []
|
|
|
#文件夹中文件数量
|
|
|
num_of_frames = len(os.listdir(f'img/{self.char_type}/{animation}'))
|
|
|
for i in range(num_of_frames):
|
|
|
img = pygame.image.load(f'img/{self.char_type}/{animation}/{i}.gif').convert_alpha() # 加载图片
|
|
|
img = pygame.transform.scale(img, (img.get_width() * scale, 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)
|
|
|
|
|
|
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
|
|
|
|
|
|
#加入重力
|
|
|
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 > 900:
|
|
|
self.health = 0
|
|
|
|
|
|
#检查是否出边缘
|
|
|
if self.char_type == 'player':
|
|
|
if self.rect.left + dx < 0 or self.rect.right + dx > 1600:
|
|
|
dx = 0
|
|
|
|
|
|
|
|
|
#更新矩形
|
|
|
self.rect.x += dx
|
|
|
self.rect.y += dy
|
|
|
#更新滚动基于玩家位置
|
|
|
if self.char_type == 'player':
|
|
|
if (self.rect.right > 1600 - SCROLL_THRESH and BG_SCROLL < (
|
|
|
world.level_length * TILE_SIZE) - 1600) \
|
|
|
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 = 100
|
|
|
bullet = Bullet(self.rect.centerx + (0.7 * self.rect.size[0] * self.direction), self.rect.centery,self.direction)
|
|
|
bullet_group.add(bullet)
|
|
|
#减少弹药
|
|
|
self.ammo -= 1
|
|
|
|
|
|
|
|
|
def ai(self):
|
|
|
if self.alive and player.alive:
|
|
|
#随机
|
|
|
if self.idling == False and random.randint(1,200) == 1:
|
|
|
self.update_action(0)
|
|
|
self.idling = True
|
|
|
self.idling_counter = 50
|
|
|
#是否在玩家旁边
|
|
|
if self.vision.colliderect(player.rect) :
|
|
|
#停止面向玩家
|
|
|
self.update_action(0)
|
|
|
self.shoot()
|
|
|
|
|
|
else:
|
|
|
if self.idling == False:
|
|
|
|
|
|
if self.direction == 1:
|
|
|
ai_moving_right = True
|
|
|
else:
|
|
|
ai_moving_right = False
|
|
|
#防止两边行走
|
|
|
ai_moving_left = not ai_moving_right
|
|
|
self.move(ai_moving_left,ai_moving_right)
|
|
|
#给ai增加动画
|
|
|
self.update_action(1)
|
|
|
self.move_counter += 1
|
|
|
#更新视野
|
|
|
self.vision.center = (self.rect.centerx + 200 * 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
|
|
|
# scroll
|
|
|
self.rect.x += screen_scroll
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_animation(self):
|
|
|
#计时器
|
|
|
ANIMATION_COOLDOWN = 50
|
|
|
#根据当前帧更新图像
|
|
|
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)
|
|
|
#多画一个矩形以检测和ITEM_BOX的碰撞
|
|
|
#自己做了一个只能在敌人头上显示而且只有受到伤害时才显示的血条!
|
|
|
if self.health != self.max_health and self.char_type == 'enemy' and self.health > 0:
|
|
|
# 计算比例
|
|
|
ratio = self.health / self.max_health
|
|
|
# 外边框
|
|
|
pygame.draw.rect(screen, BLACK, (self.rect.centerx - 0.5 * self.rect.size[0]- 2, self.rect.centery - 0.8 * self.rect.size[1]- 2, 64, 10))
|
|
|
# 底色
|
|
|
pygame.draw.rect(screen, RED, (self.rect.centerx - 0.5 * self.rect.size[0], self.rect.centery - 0.8 * self.rect.size[1], 60, 6))
|
|
|
# 外显示
|
|
|
pygame.draw.rect(screen, GREEN, (self.rect.centerx - 0.5 * self.rect.size[0], self.rect.centery - 0.8 * self.rect.size[1],60 * ratio, 6))
|
|
|
|
|
|
|
|
|
class World():
|
|
|
def __init__(self):
|
|
|
self.obstacle_list = []
|
|
|
|
|
|
def process_data(self, data):
|
|
|
self.level_length = len(data[0])
|
|
|
#iterate through each value in level data file
|
|
|
for y, row in enumerate(data):
|
|
|
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 <= 15:
|
|
|
decoration = Decoration(img, x * TILE_SIZE, y * TILE_SIZE)
|
|
|
decoration_group.add(decoration)
|
|
|
elif tile == 16:#create player
|
|
|
player = Soldier('player', x * TILE_SIZE, y * TILE_SIZE, 1.65, 4, 20, 100,10)
|
|
|
health_bar = HealthBar(10, 10, player.health, player.health)
|
|
|
elif tile == 17:#create enemies
|
|
|
enemy = Soldier('enemy', x * TILE_SIZE, y * TILE_SIZE, 1.65, 3, 20, 50, 0)
|
|
|
enemy_group.add(enemy)
|
|
|
elif tile == 18:#create ammo box
|
|
|
item_box = ItemBox('Ammo', x * TILE_SIZE, y * TILE_SIZE)
|
|
|
item_box_group.add(item_box)
|
|
|
elif tile == 19:#create grenade box
|
|
|
item_box = ItemBox('Grenade', x * TILE_SIZE, y * TILE_SIZE)
|
|
|
item_box_group.add(item_box)
|
|
|
elif tile == 20:#create health box
|
|
|
item_box = ItemBox('Health', x * TILE_SIZE, y * TILE_SIZE)
|
|
|
item_box_group.add(item_box)
|
|
|
elif tile == 21:#create exit
|
|
|
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):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
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):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
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):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
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 ItemBox(pygame.sprite.Sprite):
|
|
|
def __init__(self,item_type,x,y):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
self.item_type = item_type
|
|
|
self.image = item_boxes[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 += 25
|
|
|
elif self.item_type == 'Grenade':
|
|
|
player.grenades += 5
|
|
|
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):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
self.speed = 15
|
|
|
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
|
|
|
#检查子弹是否出屏幕
|
|
|
if self.rect.right < 0 or self.rect.left > 1600 - 20:
|
|
|
self.kill()
|
|
|
#检查是否与地图发生碰撞
|
|
|
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循环遍历
|
|
|
for enemy in enemy_group:
|
|
|
if pygame.sprite.spritecollide(enemy, bullet_group, False):
|
|
|
if enemy.alive:
|
|
|
enemy.health -= 10
|
|
|
self.kill()
|
|
|
|
|
|
class Grenade(pygame.sprite.Sprite):
|
|
|
def __init__(self,x,y,direction):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
self.timer = 100
|
|
|
self.vel_y = -11
|
|
|
self.speed = 8
|
|
|
self.image = grenade_img
|
|
|
self.rect = self.image.get_rect()
|
|
|
self.rect.center = (x,y)
|
|
|
self.width = self.image.get_width()
|
|
|
self.height = self.image.get_height()
|
|
|
self.direction = direction
|
|
|
|
|
|
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
|
|
|
#check if below the ground, i.e. thrown up
|
|
|
if self.vel_y < 0:
|
|
|
self.vel_y = 0
|
|
|
dy = tile[1].bottom - self.rect.top
|
|
|
#check if above the ground, i.e. falling
|
|
|
elif self.vel_y >= 0:
|
|
|
self.vel_y = 0
|
|
|
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()
|
|
|
explosion = Explosion(self.rect.x,self.rect.y,0.5)
|
|
|
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 -= 20
|
|
|
|
|
|
for enemy in enemy_group:
|
|
|
if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and \
|
|
|
abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2:
|
|
|
enemy.health -= 25
|
|
|
|
|
|
|
|
|
|
|
|
class Explosion(pygame.sprite.Sprite):
|
|
|
def __init__(self,x,y,scale):
|
|
|
pygame.sprite.Sprite.__init__(self)
|
|
|
self.images = []
|
|
|
for num in range(1,4):
|
|
|
img = pygame.image.load(f'img/explosion/{num}.gif').convert_alpha()
|
|
|
img = pygame.transform.scale(img,(int(img.get_width() * 2), int(img.get_height() * 2)))
|
|
|
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 = 8
|
|
|
#更新爆炸动画
|
|
|
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, colour, speed):
|
|
|
self.direction = direction
|
|
|
self.colour = colour
|
|
|
self.speed = speed
|
|
|
self.fade_counter = 0
|
|
|
|
|
|
|
|
|
def fade(self):
|
|
|
fade_complete = False
|
|
|
self.fade_counter += self.speed
|
|
|
if self.direction == 1:#whole screen fade
|
|
|
pygame.draw.rect(screen, self.colour, (0 - self.fade_counter, 0, 1600 // 2, 900))
|
|
|
pygame.draw.rect(screen, self.colour, (1600 // 2 + self.fade_counter, 0, 1600, 900))
|
|
|
pygame.draw.rect(screen, self.colour, (0, 0 - self.fade_counter, 1600, 900 // 2))
|
|
|
pygame.draw.rect(screen, self.colour, (0, 900 // 2 +self.fade_counter, 1600, 900))
|
|
|
if self.direction == 2:#vertical screen fade down
|
|
|
pygame.draw.rect(screen, self.colour, (0, 0, 1600, 0 + self.fade_counter))
|
|
|
if self.fade_counter >= 1600:
|
|
|
fade_complete = True
|
|
|
|
|
|
return fade_complete
|
|
|
|
|
|
#创造渐变
|
|
|
intro_fade = ScreenFade(1, BLACK, 4)
|
|
|
death_fade = ScreenFade(2, RED, 4)
|
|
|
|
|
|
|
|
|
|
|
|
#创建按钮
|
|
|
start_button = button.Button(1600 // 2 -100, 900 // 2 - 150 , start_img, 2)
|
|
|
exit_button = button.Button(1600 // 2 -70, 900 // 2 + 50 , exit_img, 2)
|
|
|
restart_button = button.Button(1600 // 2 -130, 900 // 2 , 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 x, row in enumerate(reader):
|
|
|
for y, tile in enumerate(row):
|
|
|
world_data[x][y] = 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
|
|
|
if exit_button.draw(screen):
|
|
|
run = False
|
|
|
else:
|
|
|
#绘制背景
|
|
|
draw_bg()
|
|
|
#绘制世界
|
|
|
world.draw()
|
|
|
#展示血量
|
|
|
health_bar.draw(player.health)
|
|
|
#展示子弹
|
|
|
draw_text(f'ammo:{player.ammo}',font,WHITE,10,35)
|
|
|
#展示手雷
|
|
|
draw_text(f'grenade:{player.grenades}', font, WHITE, 10, 60)
|
|
|
#展示血量
|
|
|
draw_text(f'health:{player.health}', font, RED, 10, 85)
|
|
|
|
|
|
#在画之前更新动作
|
|
|
player.update()
|
|
|
player.draw()
|
|
|
|
|
|
for enemy in enemy_group:
|
|
|
enemy.ai()
|
|
|
enemy.draw()
|
|
|
enemy.update()
|
|
|
|
|
|
|
|
|
#更新绘画子弹
|
|
|
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 == True:
|
|
|
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) #2代表跳,在跑和静止之前
|
|
|
elif moving_left or moving_right:
|
|
|
player.update_action(1) #1代表run
|
|
|
else:
|
|
|
player.update_action(0) #0代表idel
|
|
|
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 x, row in enumerate(reader):
|
|
|
for y, tile in enumerate(row):
|
|
|
world_data[x][y] = 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 x, row in enumerate(reader):
|
|
|
for y, tile in enumerate(row):
|
|
|
world_data[x][y] = 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
|
|
|
#键盘输入
|
|
|
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_j:
|
|
|
shoot = True
|
|
|
if event.key == pygame.K_l:
|
|
|
grenade = True
|
|
|
if event.key == pygame.K_ESCAPE:
|
|
|
run = False
|
|
|
if event.key == pygame.K_k and player.alive: #死之后不能跳
|
|
|
player.jump = True
|
|
|
|
|
|
#松开按键
|
|
|
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_j:
|
|
|
shoot = False
|
|
|
if event.key == pygame.K_l:
|
|
|
grenade = False
|
|
|
grenade_thrown = False
|
|
|
|
|
|
pygame.display.update() #更新屏幕
|
|
|
pygame.quit()
|