|
|
|
@ -0,0 +1,347 @@
|
|
|
|
|
import pgzrun
|
|
|
|
|
from pgzero.actor import Actor
|
|
|
|
|
from pgzero.clock import clock
|
|
|
|
|
from pgzero.loaders import sounds
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GRID_SIZE = 48 # 格子尺寸
|
|
|
|
|
WIDTH = GRID_SIZE * 11 # 屏幕宽度
|
|
|
|
|
HEIGHT = GRID_SIZE * 9 # 屏幕高度
|
|
|
|
|
size=WIDTH,HEIGHT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
level = 1 # 游戏关卡值
|
|
|
|
|
level_images=[] #关卡图片
|
|
|
|
|
level_complete = False # 游戏过关标记
|
|
|
|
|
MAX_LEVEl = 3
|
|
|
|
|
game_over = False
|
|
|
|
|
display_game_over = False
|
|
|
|
|
start=False#开始游戏按钮
|
|
|
|
|
level_num_flag=False#选关窗口判定
|
|
|
|
|
|
|
|
|
|
ui_game_over = Actor("pushbox_game_over",(WIDTH//2,HEIGHT//2))
|
|
|
|
|
|
|
|
|
|
# 方向字典,存储各方向对应的坐标偏移值
|
|
|
|
|
dirs = {"right":(1, 0), "left":(-1, 0),
|
|
|
|
|
"up":(0, -1), "down":(0, 1), "none":(0, 0)}
|
|
|
|
|
|
|
|
|
|
# 游戏界面元素
|
|
|
|
|
ui_level_up = Actor("pushbox_level_up", (WIDTH//2, HEIGHT//2))
|
|
|
|
|
#开始界面
|
|
|
|
|
gui_background_image=Actor("gui_background",topleft=(0,0))
|
|
|
|
|
btn_start_game=Actor("start",(WIDTH-290, HEIGHT-160))
|
|
|
|
|
btn_exit_game=Actor("finish",(WIDTH-275,HEIGHT-60))
|
|
|
|
|
#绘制关卡
|
|
|
|
|
for i in range(3):
|
|
|
|
|
level_image=Actor("level"+str(i + 1), (0, HEIGHT // 2 + 80 * i))
|
|
|
|
|
level_images.append(level_image)
|
|
|
|
|
|
|
|
|
|
# 从文件读取地图数据
|
|
|
|
|
def load_map(map_file):
|
|
|
|
|
map_array =[]
|
|
|
|
|
with open(map_file, "r") as map_file:
|
|
|
|
|
lines = map_file.readlines()
|
|
|
|
|
for line in lines:
|
|
|
|
|
line = line.replace(" ", "") # 去掉空格
|
|
|
|
|
line = line.split(",") # 将文本行转换为字符列表
|
|
|
|
|
line = list(map(int, line)) # 将字符转化成整数
|
|
|
|
|
map_array.append(line)
|
|
|
|
|
return map_array
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化地图,生成游戏角色
|
|
|
|
|
def setup_screen(mapdata):
|
|
|
|
|
global walls, floors, boxes, targets, player
|
|
|
|
|
walls = [] # 墙壁列表
|
|
|
|
|
floors= [] # 地板列表
|
|
|
|
|
boxes = [] # 箱子列表
|
|
|
|
|
targets = [] # 目标点列表
|
|
|
|
|
for row in range(len(mapdata)):
|
|
|
|
|
for col in range(len(mapdata[row])):
|
|
|
|
|
x = col * GRID_SIZE
|
|
|
|
|
y = row * GRID_SIZE
|
|
|
|
|
floors.append(Actor("pushbox_floor", topleft=(x, y)))
|
|
|
|
|
if mapdata[row][col] == 1:
|
|
|
|
|
walls.append(Actor("pushbox_wall", topleft=(x, y)))
|
|
|
|
|
elif mapdata[row][col] == 2:
|
|
|
|
|
box = Actor("pushbox_box", topleft=(x, y))
|
|
|
|
|
box.placed = False
|
|
|
|
|
boxes.append(box)
|
|
|
|
|
elif mapdata[row][col] == 3:
|
|
|
|
|
player = Actor("pushbox_down", topleft=(x, y))
|
|
|
|
|
elif mapdata[row][col] == 4:
|
|
|
|
|
targets.append(Actor("pushbox_target", topleft=(x, y)))
|
|
|
|
|
elif mapdata[row][col] == 6:
|
|
|
|
|
targets.append(Actor("pushbox_target", topleft=(x, y)))
|
|
|
|
|
box = Actor("pushbox_box_hit", topleft=(x, y))
|
|
|
|
|
box.placed = True
|
|
|
|
|
boxes.append(box)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 加载地图,初始化关卡
|
|
|
|
|
def setup_level(level):
|
|
|
|
|
if level<=0:
|
|
|
|
|
return
|
|
|
|
|
mapdata = load_map("maps/map" + str(level) + ".txt")
|
|
|
|
|
setup_screen(mapdata)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 处理键盘按下事件
|
|
|
|
|
def on_key_down(key):
|
|
|
|
|
if level_complete or game_over:
|
|
|
|
|
return
|
|
|
|
|
if key==keys.C:
|
|
|
|
|
undo()
|
|
|
|
|
return
|
|
|
|
|
if key == keys.O:
|
|
|
|
|
setup_level(level)
|
|
|
|
|
return
|
|
|
|
|
if key == keys.RIGHT:
|
|
|
|
|
player.direction = "right"
|
|
|
|
|
player.image = "pushbox_right"
|
|
|
|
|
elif key == keys.LEFT:
|
|
|
|
|
player.direction = "left"
|
|
|
|
|
player.image = "pushbox_left"
|
|
|
|
|
elif key == keys.DOWN:
|
|
|
|
|
player.direction = "down"
|
|
|
|
|
player.image = "pushbox_down"
|
|
|
|
|
elif key == keys.UP:
|
|
|
|
|
player.direction = "up"
|
|
|
|
|
player.image = "pushbox_up"
|
|
|
|
|
else:
|
|
|
|
|
player.direction = "none"
|
|
|
|
|
player_move()
|
|
|
|
|
player_collision()
|
|
|
|
|
|
|
|
|
|
#按钮点击
|
|
|
|
|
def on_mouse_down(pos):
|
|
|
|
|
global start,level_num_flag,level
|
|
|
|
|
if btn_start_game.collidepoint(pos):
|
|
|
|
|
start=True
|
|
|
|
|
level_num_flag = True
|
|
|
|
|
elif btn_exit_game.collidepoint(pos):
|
|
|
|
|
exit()
|
|
|
|
|
else:
|
|
|
|
|
for i in range(len(level_images)):
|
|
|
|
|
if level_images[i].collidepoint(pos) and level_num_flag and level:
|
|
|
|
|
if i == 0:
|
|
|
|
|
level_num1()
|
|
|
|
|
setup_level(level)
|
|
|
|
|
if i == 1:
|
|
|
|
|
level_num2()
|
|
|
|
|
setup_level(level)
|
|
|
|
|
elif i == 2:
|
|
|
|
|
level_num3()
|
|
|
|
|
setup_level(level)
|
|
|
|
|
level_num_flag = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#游戏关卡绘制
|
|
|
|
|
level_image_bg=Actor("gui_background",topleft=(0,0))
|
|
|
|
|
def level_draw():
|
|
|
|
|
level_image_bg.draw()
|
|
|
|
|
#关卡1、2、3
|
|
|
|
|
def level_num1():
|
|
|
|
|
global level
|
|
|
|
|
level = 1
|
|
|
|
|
def level_num2():
|
|
|
|
|
global level
|
|
|
|
|
level = 2
|
|
|
|
|
def level_num3():
|
|
|
|
|
global level
|
|
|
|
|
level = 3
|
|
|
|
|
|
|
|
|
|
# 移动玩家角色
|
|
|
|
|
moves=[]
|
|
|
|
|
box_moves = []
|
|
|
|
|
def player_move():
|
|
|
|
|
player.oldx = player.x
|
|
|
|
|
player.oldy = player.y
|
|
|
|
|
if player.direction is None:
|
|
|
|
|
dx, dy = 0, 0
|
|
|
|
|
else:
|
|
|
|
|
dx, dy = dirs[player.direction]
|
|
|
|
|
player.x += dx * GRID_SIZE
|
|
|
|
|
player.y += dy * GRID_SIZE
|
|
|
|
|
moves.append((player.direction, -dx * GRID_SIZE, -dy * GRID_SIZE))
|
|
|
|
|
# 记录箱子移动前的位置
|
|
|
|
|
for box in boxes:
|
|
|
|
|
box.oldx = box.x
|
|
|
|
|
box.oldy = box.y
|
|
|
|
|
|
|
|
|
|
# 撤销最后一次移动操作
|
|
|
|
|
def undo():
|
|
|
|
|
global moves, box_moves, boxes
|
|
|
|
|
if moves:
|
|
|
|
|
last_move = moves.pop()
|
|
|
|
|
direction, dx, dy = last_move
|
|
|
|
|
player.direction = opposite_dir(direction)
|
|
|
|
|
player.x += dx
|
|
|
|
|
player.y += dy
|
|
|
|
|
player_collision() # 处理碰撞
|
|
|
|
|
# 检查是否有箱子的移动记录,如果有,则撤销箱子的移动
|
|
|
|
|
if box_moves:
|
|
|
|
|
last_box_move = box_moves.pop()
|
|
|
|
|
box,bx, by = last_box_move
|
|
|
|
|
box.x += bx
|
|
|
|
|
box.y += by
|
|
|
|
|
box_collision(box) # 重新处理箱子碰撞
|
|
|
|
|
# 获取相反的方向
|
|
|
|
|
def opposite_dir(direction):
|
|
|
|
|
if direction == "right":
|
|
|
|
|
return "left"
|
|
|
|
|
elif direction == "left":
|
|
|
|
|
return "right"
|
|
|
|
|
elif direction == "up":
|
|
|
|
|
return "down"
|
|
|
|
|
elif direction == "down":
|
|
|
|
|
return "up"
|
|
|
|
|
|
|
|
|
|
# 玩家角色的碰撞检测与处理
|
|
|
|
|
def player_collision():
|
|
|
|
|
# 玩家与墙壁的碰撞
|
|
|
|
|
if player.collidelist(walls) != -1:
|
|
|
|
|
player.x = player.oldx
|
|
|
|
|
player.y = player.oldy
|
|
|
|
|
return
|
|
|
|
|
# 检测玩家与箱子的碰撞
|
|
|
|
|
index = player.collidelist(boxes)
|
|
|
|
|
# 如果玩家没有碰到箱子,返回
|
|
|
|
|
if index == -1:
|
|
|
|
|
return
|
|
|
|
|
# 如果玩家碰到箱子,箱子先移动一格
|
|
|
|
|
box = boxes[index]
|
|
|
|
|
box.oldx = box.x
|
|
|
|
|
box.oldy = box.y
|
|
|
|
|
dx, dy = dirs[player.direction]
|
|
|
|
|
box.x += dx * GRID_SIZE
|
|
|
|
|
box.y += dy * GRID_SIZE
|
|
|
|
|
current_box = box
|
|
|
|
|
# 记录箱子的移动
|
|
|
|
|
box_moves.append((current_box, -dx * GRID_SIZE, -dy * GRID_SIZE))
|
|
|
|
|
# 如果箱子碰到了墙壁或者其他箱子,玩家和箱子都退回原来的位置
|
|
|
|
|
if box_collision(box) == True:
|
|
|
|
|
box.x = box.oldx
|
|
|
|
|
box.y = box.oldy
|
|
|
|
|
player.x = player.oldx
|
|
|
|
|
player.y = player.oldy
|
|
|
|
|
return
|
|
|
|
|
sounds.fall.play()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 箱子角色的碰撞检测与处理
|
|
|
|
|
def box_collision(box):
|
|
|
|
|
# 箱子与墙壁的碰撞,返回True
|
|
|
|
|
if box.collidelist(walls) != -1:
|
|
|
|
|
return True
|
|
|
|
|
# 箱子与其他箱子的碰撞,返回True
|
|
|
|
|
if box_collision_check(box):
|
|
|
|
|
return True
|
|
|
|
|
for bx in boxes:
|
|
|
|
|
if box == bx:
|
|
|
|
|
continue
|
|
|
|
|
if box.colliderect(bx):
|
|
|
|
|
return True
|
|
|
|
|
check_target(box)
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def box_collision_check(box):
|
|
|
|
|
for bx in boxes:
|
|
|
|
|
if box == bx:
|
|
|
|
|
continue
|
|
|
|
|
if box.colliderect(bx):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 检测箱子是否放置在目标点上
|
|
|
|
|
def check_target(box):
|
|
|
|
|
if box.collidelist(targets) != -1:
|
|
|
|
|
box.image = "pushbox_box_hit"
|
|
|
|
|
box.placed = True
|
|
|
|
|
else:
|
|
|
|
|
box.image = "pushbox_box"
|
|
|
|
|
box.placed = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 判断是否过关
|
|
|
|
|
def check_level_up():
|
|
|
|
|
for box in boxes:
|
|
|
|
|
if not box.placed:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 设置新的关卡
|
|
|
|
|
def set_next_level():
|
|
|
|
|
global level_complete, level
|
|
|
|
|
level_complete = False
|
|
|
|
|
level += 1
|
|
|
|
|
setup_level(level)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 更新游戏逻辑
|
|
|
|
|
def update():
|
|
|
|
|
global level_complete,game_over
|
|
|
|
|
if level_complete or game_over:
|
|
|
|
|
return
|
|
|
|
|
if check_level_up():
|
|
|
|
|
if level == MAX_LEVEl:
|
|
|
|
|
game_over = True
|
|
|
|
|
sounds.win.play()
|
|
|
|
|
clock.schedule(set_game_over,1)
|
|
|
|
|
else:
|
|
|
|
|
level_complete = True
|
|
|
|
|
sounds.win.play()
|
|
|
|
|
clock.schedule(set_next_level, 2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制游戏图像
|
|
|
|
|
def draw():
|
|
|
|
|
screen.fill((255, 255, 255))
|
|
|
|
|
if not start:
|
|
|
|
|
# screen.clear()
|
|
|
|
|
gui_background_image.draw()
|
|
|
|
|
btn_start_game.draw()
|
|
|
|
|
btn_exit_game.draw()
|
|
|
|
|
return
|
|
|
|
|
elif level_num_flag and start:
|
|
|
|
|
# screen.clear()
|
|
|
|
|
level_draw()
|
|
|
|
|
level_menu_draw()
|
|
|
|
|
for level_image in level_images:
|
|
|
|
|
level_image.draw()
|
|
|
|
|
return
|
|
|
|
|
if game_over & display_game_over:
|
|
|
|
|
ui_game_over.draw()
|
|
|
|
|
return
|
|
|
|
|
for floor in floors:
|
|
|
|
|
floor.draw()
|
|
|
|
|
for wall in walls:
|
|
|
|
|
wall.draw()
|
|
|
|
|
for target in targets:
|
|
|
|
|
target.draw()
|
|
|
|
|
for box in boxes:
|
|
|
|
|
box.draw()
|
|
|
|
|
screen.draw.text("Level " + str(level), topleft=(20, 20),
|
|
|
|
|
fontsize=30, color="black")
|
|
|
|
|
player.draw()
|
|
|
|
|
if level_complete:
|
|
|
|
|
ui_level_up.draw()
|
|
|
|
|
|
|
|
|
|
def set_game_over():
|
|
|
|
|
global display_game_over
|
|
|
|
|
display_game_over = True
|
|
|
|
|
|
|
|
|
|
def level_menu_draw():
|
|
|
|
|
if level_num_flag:
|
|
|
|
|
for level_image in level_images:
|
|
|
|
|
level_image.x=WIDTH//2
|
|
|
|
|
else:
|
|
|
|
|
for level_image in level_images:
|
|
|
|
|
level_image.x=-50
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setup_level(level)
|
|
|
|
|
pgzrun.go()
|