Files
Sprout-Farm/Scene/SmallGame/2048Game.gd

308 lines
6.5 KiB
GDScript

extends Panel
# 游戏常量
const GRID_SIZE = 4
const CELL_SIZE = 90
const CELL_MARGIN = 10
# 数字颜色配置
const NUMBER_COLORS = {
0: Color.TRANSPARENT,
2: Color(0.93, 0.89, 0.85),
4: Color(0.93, 0.88, 0.78),
8: Color(0.95, 0.69, 0.47),
16: Color(0.96, 0.58, 0.39),
32: Color(0.96, 0.49, 0.37),
64: Color(0.96, 0.37, 0.23),
128: Color(0.93, 0.81, 0.45),
256: Color(0.93, 0.80, 0.38),
512: Color(0.93, 0.78, 0.31),
1024: Color(0.93, 0.77, 0.25),
2048: Color(0.93, 0.76, 0.18)
}
const TEXT_COLORS = {
2: Color.BLACK,
4: Color.BLACK,
8: Color.WHITE,
16: Color.WHITE,
32: Color.WHITE,
64: Color.WHITE,
128: Color.WHITE,
256: Color.WHITE,
512: Color.WHITE,
1024: Color.WHITE,
2048: Color.WHITE
}
# 游戏变量
var grid = []
var score = 0
var best_score = 0
var game_over = false
var won = false
var can_continue = true
# 节点引用
@onready var game_board = $GameBoard
@onready var score_label = $ScoreLabel
@onready var best_label = $BestLabel
@onready var game_over_label = $GameOverLabel
@onready var win_label = $WinLabel
func _ready():
# 设置游戏板样式
game_board.modulate = Color(0.7, 0.6, 0.5)
# 初始化游戏
init_game()
func init_game():
# 重置游戏状态
game_over = false
won = false
can_continue = true
score = 0
# 初始化网格
grid.clear()
for y in range(GRID_SIZE):
var row = []
for x in range(GRID_SIZE):
row.append(0)
grid.append(row)
# 添加两个初始数字
add_random_number()
add_random_number()
# 更新UI
update_ui()
hide_labels()
queue_redraw()
func _input(event):
if event is InputEventKey and event.pressed:
if game_over:
if event.keycode == KEY_R:
init_game()
return
if won and not can_continue:
if event.keycode == KEY_C:
can_continue = true
win_label.visible = false
return
# 移动控制
var moved = false
match event.keycode:
KEY_UP, KEY_W:
moved = move_up()
KEY_DOWN, KEY_S:
moved = move_down()
KEY_LEFT, KEY_A:
moved = move_left()
KEY_RIGHT, KEY_D:
moved = move_right()
KEY_R:
init_game()
return
if moved:
add_random_number()
update_ui()
check_game_state()
queue_redraw()
func move_left() -> bool:
var moved = false
for y in range(GRID_SIZE):
var row = grid[y].duplicate()
var new_row = merge_line(row)
if new_row != grid[y]:
moved = true
grid[y] = new_row
return moved
func move_right() -> bool:
var moved = false
for y in range(GRID_SIZE):
var row = grid[y].duplicate()
row.reverse()
var new_row = merge_line(row)
new_row.reverse()
if new_row != grid[y]:
moved = true
grid[y] = new_row
return moved
func move_up() -> bool:
var moved = false
for x in range(GRID_SIZE):
var column = []
for y in range(GRID_SIZE):
column.append(grid[y][x])
var new_column = merge_line(column)
var changed = false
for y in range(GRID_SIZE):
if grid[y][x] != new_column[y]:
changed = true
grid[y][x] = new_column[y]
if changed:
moved = true
return moved
func move_down() -> bool:
var moved = false
for x in range(GRID_SIZE):
var column = []
for y in range(GRID_SIZE):
column.append(grid[y][x])
column.reverse()
var new_column = merge_line(column)
new_column.reverse()
var changed = false
for y in range(GRID_SIZE):
if grid[y][x] != new_column[y]:
changed = true
grid[y][x] = new_column[y]
if changed:
moved = true
return moved
func merge_line(line: Array) -> Array:
# 移除零
var filtered = []
for num in line:
if num != 0:
filtered.append(num)
# 合并相同数字
var merged = []
var i = 0
while i < filtered.size():
if i < filtered.size() - 1 and filtered[i] == filtered[i + 1]:
# 合并
var new_value = filtered[i] * 2
merged.append(new_value)
score += new_value
i += 2
else:
merged.append(filtered[i])
i += 1
# 填充零到指定长度
while merged.size() < GRID_SIZE:
merged.append(0)
return merged
func add_random_number():
var empty_cells = []
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
if grid[y][x] == 0:
empty_cells.append(Vector2(x, y))
if empty_cells.size() > 0:
var random_cell = empty_cells[randi() % empty_cells.size()]
var value = 2 if randf() < 0.9 else 4
grid[random_cell.y][random_cell.x] = value
func check_game_state():
# 检查是否达到2048
if not won:
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
if grid[y][x] == 2048:
won = true
can_continue = false
win_label.visible = true
return
# 检查是否游戏结束
if not can_move():
game_over = true
if score > best_score:
best_score = score
game_over_label.visible = true
func can_move() -> bool:
# 检查是否有空格
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
if grid[y][x] == 0:
return true
# 检查是否可以合并
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
var current = grid[y][x]
# 检查右边
if x < GRID_SIZE - 1 and grid[y][x + 1] == current:
return true
# 检查下面
if y < GRID_SIZE - 1 and grid[y + 1][x] == current:
return true
return false
func update_ui():
score_label.text = "分数: " + str(score)
best_label.text = "最高分: " + str(best_score)
func hide_labels():
game_over_label.visible = false
win_label.visible = false
func _draw():
if not game_board:
return
# 获取游戏板位置
var board_pos = game_board.position
# 绘制网格背景
for y in range(GRID_SIZE):
for x in range(GRID_SIZE):
var cell_x = board_pos.x + x * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN
var cell_y = board_pos.y + y * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN
var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE)
# 绘制单元格背景
draw_rect(rect, Color(0.8, 0.7, 0.6), true)
# 绘制数字
var value = grid[y][x]
if value > 0:
# 绘制数字背景
var bg_color = NUMBER_COLORS.get(value, Color.GOLD)
draw_rect(rect, bg_color, true)
# 绘制数字文本
var text = str(value)
var font_size = 24 if value < 100 else (20 if value < 1000 else 16)
var text_color = TEXT_COLORS.get(value, Color.WHITE)
# 获取默认字体
var font = ThemeDB.fallback_font
# 计算文本尺寸
var text_size = font.get_string_size(text, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size)
# 计算文本位置(居中)
var text_pos = Vector2(
cell_x + (CELL_SIZE - text_size.x) / 2,
cell_y + (CELL_SIZE - text_size.y) / 2 + text_size.y
)
# 绘制文本
draw_string(font, text_pos, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color)