extends Node2D
# 游戏设置
const GRID_SIZE = 3 # 3x3 网格
const TILE_SIZE = 100 # 每个拼图块大小
const BOARD_SIZE = TILE_SIZE * GRID_SIZE
# 游戏状态
var tiles = [] # 存储所有拼图块
var selected_tile = null # 当前选中的拼图块
var is_solved = false
func _ready():
# 创建游戏板
create_game_board()
# 洗牌拼图
shuffle_tiles()
# 创建游戏板
func create_game_board():
# 尝试加载指定位置的图片
var image = Image.new()
var err = image.load("res://puzzle_image.png")
# 如果图片加载失败,创建默认图片
if err != OK:
push_warning("无法加载图片 res://puzzle_image.png,将使用默认图片")
image = Image.create(300, 300, false, Image.FORMAT_RGBA8)
image.fill(Color(0.2, 0.3, 0.8))
image.fill_rect(Rect2i(50, 50, 200, 50), Color(1, 1, 1)) # 顶部矩形
image.fill_rect(Rect2i(100, 150, 100, 100), Color(1, 1, 1)) # 中间方块
image.fill_rect(Rect2i(50, 250, 200, 30), Color(1, 1, 1)) # 底部矩形
# 创建所有拼图块
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
# 计算索引
var index = y * GRID_SIZE + x
# 创建拼图块纹理
var tile_image = image.get_region(Rect2i(
x * TILE_SIZE,
y * TILE_SIZE,
TILE_SIZE,
TILE_SIZE
))
var tile_texture = ImageTexture.create_from_image(tile_image)
# 创建拼图块
var tile = TextureButton.new()
tile.texture_normal = tile_texture
tile.custom_minimum_size = Vector2(TILE_SIZE, TILE_SIZE)
tile.size_flags_horizontal = Control.SIZE_FILL
tile.size_flags_vertical = Control.SIZE_FILL
tile.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
# 设置位置
tile.position = Vector2(x * TILE_SIZE, y * TILE_SIZE)
# 存储位置信息
tile.set_meta("grid_pos", Vector2(x, y))
tile.set_meta("index", index)
# 连接点击事件
tile.connect("pressed", _on_tile_pressed.bind(tile))
add_child(tile)
tiles.append(tile)
# 洗牌拼图
func shuffle_tiles():
# 只与相邻拼图块交换
for i in range(100):
var random_tile = tiles[randi() % tiles.size()]
var neighbors = get_adjacent_tiles(random_tile)
if neighbors.size() > 0:
var neighbor_tile = neighbors[randi() % neighbors.size()]
swap_tiles(random_tile, neighbor_tile)
# 获取相邻的拼图块
func get_adjacent_tiles(tile: TextureButton) -> Array:
var adjacent_tiles = []
var grid_pos = tile.get_meta("grid_pos")
# 检查四个方向:上、下、左、右
var directions = [
Vector2(0, -1), # 上
Vector2(0, 1), # 下
Vector2(-1, 0), # 左
Vector2(1, 0) # 右
]
for dir in directions:
var neighbor_pos = grid_pos + dir
# 检查位置是否有效
if neighbor_pos.x >= 0 and neighbor_pos.x < GRID_SIZE and \
neighbor_pos.y >= 0 and neighbor_pos.y < GRID_SIZE:
# 找到对应位置的拼图块
for neighbor in tiles:
if neighbor.get_meta("grid_pos") == neighbor_pos:
adjacent_tiles.append(neighbor)
break
return adjacent_tiles
# 交换两个拼图块
func swap_tiles(tile1: TextureButton, tile2: TextureButton):
# 交换位置
var temp_pos = tile1.position
tile1.position = tile2.position
tile2.position = temp_pos
# 交换网格位置元数据
var temp_grid_pos = tile1.get_meta("grid_pos")
tile1.set_meta("grid_pos", tile2.get_meta("grid_pos"))
tile2.set_meta("grid_pos", temp_grid_pos)
# 检查拼图是否完成
func check_completion() -> bool:
# 检查所有拼图块是否在正确位置
for tile in tiles:
var current_pos = tile.get_meta("grid_pos")
var correct_pos = Vector2(tile.get_meta("index") % GRID_SIZE, tile.get_meta("index") / GRID_SIZE)
if current_pos != correct_pos:
return false
return true
# 拼图块点击事件
func _on_tile_pressed(tile: TextureButton):
if is_solved:
return
# 如果没有选中的拼图块,选择当前拼图块
if selected_tile == null:
selected_tile = tile
tile.modulate = Color(0.8, 0.8, 1.0) # 高亮显示选中的拼图块
else:
# 如果点击了同一个拼图块,取消选择
if selected_tile == tile:
selected_tile.modulate = Color(1, 1, 1) # 恢复颜色
selected_tile = null
return
# 获取选中的拼图块位置
var selected_pos = selected_tile.get_meta("grid_pos")
var tile_pos = tile.get_meta("grid_pos")
# 检查是否相邻
var is_adjacent = false
if selected_pos.x == tile_pos.x: # 同一列
if abs(selected_pos.y - tile_pos.y) == 1:
is_adjacent = true
elif selected_pos.y == tile_pos.y: # 同一行
if abs(selected_pos.x - tile_pos.x) == 1:
is_adjacent = true
if is_adjacent:
# 交换两个拼图块
swap_tiles(selected_tile, tile)
# 检查是否完成
if check_completion():
is_solved = true
show_completion_message()
else:
# 显示提示信息
show_hint("只能与相邻图片交换位置")
# 恢复颜色
selected_tile.modulate = Color(1, 1, 1)
tile.modulate = Color(1, 1, 1)
# 取消选择
selected_tile = null
# 显示提示信息
func show_hint(message: String):
# 创建提示标签
var hint = Label.new()
hint.text = message
hint.position = Vector2(50, BOARD_SIZE + 20)
hint.add_theme_font_size_override("font_size", 24)
hint.add_theme_color_override("font_color", Color.YELLOW)
add_child(hint)
# 设置定时器移除提示
var timer = Timer.new()
timer.wait_time = 1.5 # 1.5秒后移除提示
timer.one_shot = true
timer.timeout.connect(func():
hint.queue_free()
timer.queue_free()
)
add_child(timer)
timer.start()
# 显示完成消息
func show_completion_message():
# 创建完成消息
var message = Label.new()
message.text = "拼图完成!"
message.position = Vector2(50, BOARD_SIZE + 20)
message.add_theme_font_size_override("font_size", 36)
message.add_theme_color_override("font_color", Color.GREEN)
add_child(message)
# 创建重新开始按钮
var restart_button = Button.new()
restart_button.text = "重新开始"
restart_button.position = Vector2(150, BOARD_SIZE + 80)
restart_button.custom_minimum_size = Vector2(100, 40)
restart_button.pressed.connect(_on_restart_pressed)
add_child(restart_button)
# 重新开始游戏
func _on_restart_pressed():
# 清除UI元素
for child in get_children():
if child is Label or child is Button:
child.queue_free()
# 重置游戏状态
is_solved = false
selected_tile = null
# 重置拼图块位置
for tile in tiles:
var index = tile.get_meta("index")
var grid_x = index % GRID_SIZE
var grid_y = index / GRID_SIZE
# 设置位置
tile.position = Vector2(grid_x * TILE_SIZE, grid_y * TILE_SIZE)
tile.set_meta("grid_pos", Vector2(grid_x, grid_y))
tile.modulate = Color(1, 1, 1) # 确保颜色正常
# 重新洗牌
shuffle_tiles()
使用说明
准备图片:
在项目根目录添加300×300像素的puzzle_image.png
创建场景:
创建Node2D节点
将上述代码复制到脚本中
运行游戏:
点击第一张拼图块(会高亮显示)
点击相邻(上下左右)的第二张拼图块
两张拼图块会互换位置
尝试交换不相邻的图片会显示提示
完成拼图后显示胜利消息