进一步完善服务器功能,添加远程命令系统,踢人系统

This commit is contained in:
2025-08-15 13:20:01 +08:00
parent ea42a1563d
commit 4bc5673726
99 changed files with 24189 additions and 552 deletions

View File

@@ -85,5 +85,45 @@ offset_bottom = 79.25
theme_override_font_sizes/font_size = 40
text = "刷新"
[node name="All_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
offset_left = 217.5
offset_top = 12.5
offset_right = 305.5
offset_bottom = 75.5
theme_override_font_sizes/font_size = 40
text = "全部"
[node name="Pet_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
offset_left = 310.0
offset_top = 13.75
offset_right = 398.0
offset_bottom = 76.75
theme_override_font_sizes/font_size = 40
text = "宠物"
[node name="Crop_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
offset_left = 397.5
offset_top = 13.75
offset_right = 485.5
offset_bottom = 76.75
theme_override_font_sizes/font_size = 40
text = "作物"
[node name="Farm_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
offset_left = 485.0
offset_top = 13.75
offset_right = 573.0
offset_bottom = 76.75
theme_override_font_sizes/font_size = 40
text = "农场"
[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"]
[connection signal="pressed" from="RefreshButton" to="." method="_on_refresh_button_pressed"]
[connection signal="pressed" from="All_Filter" to="." method="_on_all_filter_pressed"]
[connection signal="pressed" from="Pet_Filter" to="." method="_on_pet_filter_pressed"]
[connection signal="pressed" from="Crop_Filter" to="." method="_on_crop_filter_pressed"]
[connection signal="pressed" from="Farm_Filter" to="." method="_on_farm_filter_pressed"]

View File

@@ -15,7 +15,6 @@ corner_detail = 20
shadow_size = 20
[node name="ItemStorePanel" type="Panel"]
visible = false
offset_left = 58.0
offset_top = 79.0
offset_right = 1598.0
@@ -87,4 +86,48 @@ offset_bottom = 75.5
theme_override_font_sizes/font_size = 40
text = "刷新"
[node name="All_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
layout_mode = 2
offset_left = 217.5
offset_top = 12.5
offset_right = 305.5
offset_bottom = 75.5
theme_override_font_sizes/font_size = 40
text = "全部"
[node name="Pet_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
layout_mode = 2
offset_left = 310.0
offset_top = 13.75
offset_right = 398.0
offset_bottom = 76.75
theme_override_font_sizes/font_size = 40
text = "宠物"
[node name="Crop_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
layout_mode = 2
offset_left = 397.5
offset_top = 13.75
offset_right = 485.5
offset_bottom = 76.75
theme_override_font_sizes/font_size = 40
text = "作物"
[node name="Farm_Filter" type="Button" parent="."]
custom_minimum_size = Vector2(60, 60)
layout_mode = 2
offset_left = 485.0
offset_top = 13.75
offset_right = 573.0
offset_bottom = 76.75
theme_override_font_sizes/font_size = 40
text = "农场"
[connection signal="pressed" from="RefreshButton" to="." method="_on_refresh_button_pressed"]
[connection signal="pressed" from="All_Filter" to="." method="_on_all_filter_pressed"]
[connection signal="pressed" from="Pet_Filter" to="." method="_on_pet_filter_pressed"]
[connection signal="pressed" from="Crop_Filter" to="." method="_on_crop_filter_pressed"]
[connection signal="pressed" from="Farm_Filter" to="." method="_on_farm_filter_pressed"]

View File

@@ -0,0 +1,179 @@
[gd_scene load_steps=4 format=3 uid="uid://jjlgdnoo8e52"]
[ext_resource type="Script" uid="uid://dvaah0n1ia1a3" path="res://Script/BigPanel/PlayGamePanel.gd" id="1_i6cj7"]
[ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="2_wlugc"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7i23t"]
border_width_left = 15
border_width_top = 15
border_width_right = 15
border_width_bottom = 15
corner_detail = 20
shadow_size = 20
[node name="PlayGamePanel" type="Panel"]
offset_right = 1399.0
offset_bottom = 720.0
theme_override_styles/panel = SubResource("StyleBoxFlat_7i23t")
script = ExtResource("1_i6cj7")
[node name="Title" type="Label" parent="."]
layout_mode = 0
offset_top = 17.0
offset_right = 1401.0
offset_bottom = 86.0
theme_override_colors/font_color = Color(0.560784, 1, 0, 1)
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 20
theme_override_font_sizes/font_size = 50
text = "玩玩小游戏"
horizontal_alignment = 1
vertical_alignment = 1
[node name="ScrollContainer" type="ScrollContainer" parent="."]
layout_mode = 0
offset_left = 15.0
offset_top = 88.0
offset_right = 1384.0
offset_bottom = 707.0
horizontal_scroll_mode = 0
[node name="Grid" type="GridContainer" parent="ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
columns = 5
[node name="VBox" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "贪吃蛇"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox"]
layout_mode = 2
texture = ExtResource("2_wlugc")
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击游玩"
[node name="VBox2" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox2"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "贪吃蛇"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox2"]
layout_mode = 2
texture = ExtResource("2_wlugc")
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox2"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击游玩"
[node name="VBox3" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox3"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "贪吃蛇"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox3"]
layout_mode = 2
texture = ExtResource("2_wlugc")
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox3"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击游玩"
[node name="VBox4" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox4"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "贪吃蛇"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox4"]
layout_mode = 2
texture = ExtResource("2_wlugc")
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox4"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击游玩"
[node name="VBox5" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox5"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "贪吃蛇"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox5"]
layout_mode = 2
texture = ExtResource("2_wlugc")
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox5"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击游玩"

View File

@@ -0,0 +1,223 @@
[gd_scene load_steps=4 format=3 uid="uid://byxhjyyaahs6q"]
[ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="1_mv0g4"]
[ext_resource type="Script" uid="uid://btm2je8pg7rgk" path="res://Script/BigPanel/SpecialFarmPanel.gd" id="1_n2mxy"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7i23t"]
border_width_left = 15
border_width_top = 15
border_width_right = 15
border_width_bottom = 15
corner_detail = 20
shadow_size = 20
[node name="SpecialFarmPanel" type="Panel"]
offset_right = 1399.0
offset_bottom = 720.0
theme_override_styles/panel = SubResource("StyleBoxFlat_7i23t")
script = ExtResource("1_n2mxy")
[node name="QuitButton" type="Button" parent="."]
custom_minimum_size = Vector2(80, 80)
layout_mode = 0
offset_left = 1303.0
offset_top = 17.0
offset_right = 1383.0
offset_bottom = 97.0
theme_override_font_sizes/font_size = 50
text = "X"
[node name="Title" type="Label" parent="."]
layout_mode = 0
offset_top = 17.0
offset_right = 1401.0
offset_bottom = 86.0
theme_override_colors/font_color = Color(0.560784, 1, 0, 1)
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 20
theme_override_font_sizes/font_size = 50
text = "神秘农场"
horizontal_alignment = 1
vertical_alignment = 1
[node name="ScrollContainer" type="ScrollContainer" parent="."]
layout_mode = 0
offset_left = 15.0
offset_top = 88.0
offset_right = 1384.0
offset_bottom = 707.0
horizontal_scroll_mode = 0
[node name="Grid" type="GridContainer" parent="ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
columns = 5
[node name="FlowerFarm" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/FlowerFarm"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "花卉农场"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/FlowerFarm"]
layout_mode = 2
texture = ExtResource("1_mv0g4")
[node name="FlowerFarmButton" type="Button" parent="ScrollContainer/Grid/FlowerFarm"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击访问"
[node name="FruitFarm" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/FruitFarm"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "瓜果飘香"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/FruitFarm"]
layout_mode = 2
texture = ExtResource("1_mv0g4")
[node name="FruitFarmButton" type="Button" parent="ScrollContainer/Grid/FruitFarm"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击访问"
[node name="HybridFarm" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/HybridFarm"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "杂交基地"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/HybridFarm"]
layout_mode = 2
texture = ExtResource("1_mv0g4")
[node name="HybridFarmButton" type="Button" parent="ScrollContainer/Grid/HybridFarm"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击访问"
[node name="LuckyFarm" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/LuckyFarm"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "幸运农场"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/LuckyFarm"]
layout_mode = 2
texture = ExtResource("1_mv0g4")
[node name="LuckyFarmButton" type="Button" parent="ScrollContainer/Grid/LuckyFarm"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击访问"
[node name="RiceFarm" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/RiceFarm"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "稻香"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/RiceFarm"]
layout_mode = 2
texture = ExtResource("1_mv0g4")
[node name="RiceFarmButton" type="Button" parent="ScrollContainer/Grid/RiceFarm"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击访问"
[node name="WheatFarm" type="VBoxContainer" parent="ScrollContainer/Grid"]
layout_mode = 2
alignment = 1
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/WheatFarm"]
layout_mode = 2
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
theme_override_constants/outline_size = 15
theme_override_constants/shadow_outline_size = 15
theme_override_font_sizes/font_size = 35
text = "小麦谷"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/WheatFarm"]
layout_mode = 2
texture = ExtResource("1_mv0g4")
[node name="WheatFarmButton" type="Button" parent="ScrollContainer/Grid/WheatFarm"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "点击访问"
[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"]
[connection signal="pressed" from="ScrollContainer/Grid/FlowerFarm/FlowerFarmButton" to="." method="_on_flower_farm_button_pressed"]
[connection signal="pressed" from="ScrollContainer/Grid/FruitFarm/FruitFarmButton" to="." method="_on_fruit_farm_button_pressed"]
[connection signal="pressed" from="ScrollContainer/Grid/HybridFarm/HybridFarmButton" to="." method="_on_hybrid_farm_button_pressed"]
[connection signal="pressed" from="ScrollContainer/Grid/LuckyFarm/LuckyFarmButton" to="." method="_on_lucky_farm_button_pressed"]
[connection signal="pressed" from="ScrollContainer/Grid/RiceFarm/RiceFarmButton" to="." method="_on_rice_farm_button_pressed"]
[connection signal="pressed" from="ScrollContainer/Grid/WheatFarm/WheatFarmButton" to="." method="_on_wheat_farm_button_pressed"]

307
Scene/SmallGame/2048Game.gd Normal file
View File

@@ -0,0 +1,307 @@
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)

View File

@@ -0,0 +1 @@
uid://5dkxyuiuuhxk

View File

@@ -0,0 +1,102 @@
[gd_scene load_steps=2 format=3 uid="uid://dn0f1hjolxori"]
[ext_resource type="Script" uid="uid://5dkxyuiuuhxk" path="res://Scene/SmallGame/2048Game.gd" id="1_4d5vb"]
[node name="2048Game" type="Panel"]
offset_right = 1400.0
offset_bottom = 720.0
script = ExtResource("1_4d5vb")
[node name="GameBoard" type="Panel" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -200.0
offset_top = -200.0
offset_right = 200.0
offset_bottom = 200.0
grow_horizontal = 2
grow_vertical = 2
[node name="ScoreLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 50.0
offset_top = -100.0
offset_right = 250.0
offset_bottom = -70.0
grow_vertical = 0
text = "分数: 0"
[node name="BestLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 50.0
offset_top = -70.0
offset_right = 250.0
offset_bottom = -40.0
grow_vertical = 0
text = "最高分: 0"
[node name="GameOverLabel" type="Label" parent="."]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -150.0
offset_top = -50.0
offset_right = 150.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
text = "游戏结束
按R重新开始"
horizontal_alignment = 1
vertical_alignment = 1
[node name="WinLabel" type="Label" parent="."]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -150.0
offset_top = -100.0
offset_right = 150.0
offset_bottom = -50.0
grow_horizontal = 2
grow_vertical = 2
text = "恭喜达到2048
按C继续游戏"
horizontal_alignment = 1
vertical_alignment = 1
[node name="ControlsLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -300.0
offset_top = -150.0
offset_right = -50.0
offset_bottom = -50.0
grow_horizontal = 0
grow_vertical = 0
text = "操作说明:
WASD或方向键 - 移动
R - 重新开始
C - 继续游戏(达到2048后)"

317
Scene/SmallGame/PushBox.gd Normal file
View File

@@ -0,0 +1,317 @@
extends Panel
# 游戏常量
const CELL_SIZE = 40
# 地图元素
enum CellType {
EMPTY, # 空地
WALL, # 墙壁
TARGET, # 目标点
BOX, # 箱子
PLAYER, # 玩家
BOX_ON_TARGET # 箱子在目标点上
}
# 颜色配置
const CELL_COLORS = {
CellType.EMPTY: Color.WHITE,
CellType.WALL: Color.DARK_GRAY,
CellType.TARGET: Color.LIGHT_BLUE,
CellType.BOX: Color.SADDLE_BROWN,
CellType.PLAYER: Color.GREEN,
CellType.BOX_ON_TARGET: Color.DARK_GREEN
}
# 关卡数据
const LEVELS = [
# 关卡1 - 简单入门
[
"########",
"#......#",
"#..##..#",
"#..#*..#",
"#..#$..#",
"#..@...#",
"#......#",
"########"
],
# 关卡2 - 两个箱子
[
"########",
"#......#",
"#.*$*..#",
"#......#",
"#..$...#",
"#..@...#",
"#......#",
"########"
],
# 关卡3 - 稍微复杂
[
"##########",
"#........#",
"#..####..#",
"#..*##*..#",
"#..$$....#",
"#........#",
"#...@....#",
"#........#",
"##########"
],
# 关卡4 - 更复杂的布局
[
"############",
"#..........#",
"#..######..#",
"#..*....#..#",
"#..$....#..#",
"#.......#..#",
"#..#....#..#",
"#..*....#..#",
"#..$......@#",
"#..........#",
"############"
]
]
# 游戏变量
var current_level = 0
var level_data = []
var player_pos = Vector2()
var moves = 0
var level_completed = false
var map_width = 0
var map_height = 0
# 节点引用
@onready var game_area = $GameArea
@onready var level_label = $LevelLabel
@onready var moves_label = $MovesLabel
@onready var win_label = $WinLabel
func _ready():
# 设置游戏区域样式
game_area.modulate = Color(0.9, 0.9, 0.9)
# 初始化游戏
init_level()
func init_level():
# 重置游戏状态
level_completed = false
moves = 0
# 加载当前关卡
load_level(current_level)
# 更新UI
update_ui()
win_label.visible = false
queue_redraw()
func load_level(level_index: int):
if level_index >= LEVELS.size():
level_index = LEVELS.size() - 1
var level_strings = LEVELS[level_index]
map_height = level_strings.size()
map_width = level_strings[0].length()
# 初始化关卡数据
level_data.clear()
for y in range(map_height):
var row = []
for x in range(map_width):
row.append(CellType.EMPTY)
level_data.append(row)
# 解析关卡字符串
for y in range(map_height):
var line = level_strings[y]
for x in range(line.length()):
var char = line[x]
match char:
'#': # 墙壁
level_data[y][x] = CellType.WALL
'*': # 目标点
level_data[y][x] = CellType.TARGET
'$': # 箱子
level_data[y][x] = CellType.BOX
'@': # 玩家
level_data[y][x] = CellType.PLAYER
player_pos = Vector2(x, y)
'+': # 箱子在目标点上
level_data[y][x] = CellType.BOX_ON_TARGET
'.': # 空地
level_data[y][x] = CellType.EMPTY
func _input(event):
if event is InputEventKey and event.pressed:
if level_completed:
match event.keycode:
KEY_N:
next_level()
KEY_R:
init_level()
return
# 移动控制
var direction = Vector2.ZERO
match event.keycode:
KEY_UP, KEY_W:
direction = Vector2(0, -1)
KEY_DOWN, KEY_S:
direction = Vector2(0, 1)
KEY_LEFT, KEY_A:
direction = Vector2(-1, 0)
KEY_RIGHT, KEY_D:
direction = Vector2(1, 0)
KEY_R:
init_level()
return
KEY_P:
prev_level()
return
KEY_N:
next_level()
return
if direction != Vector2.ZERO:
move_player(direction)
func move_player(direction: Vector2):
var new_pos = player_pos + direction
# 检查边界
if new_pos.x < 0 or new_pos.x >= map_width or new_pos.y < 0 or new_pos.y >= map_height:
return
var target_cell = level_data[new_pos.y][new_pos.x]
# 检查是否撞墙
if target_cell == CellType.WALL:
return
# 检查是否推箱子
if target_cell == CellType.BOX or target_cell == CellType.BOX_ON_TARGET:
var box_new_pos = new_pos + direction
# 检查箱子新位置是否有效
if box_new_pos.x < 0 or box_new_pos.x >= map_width or box_new_pos.y < 0 or box_new_pos.y >= map_height:
return
var box_target_cell = level_data[box_new_pos.y][box_new_pos.x]
# 箱子不能推到墙上或其他箱子上
if box_target_cell == CellType.WALL or box_target_cell == CellType.BOX or box_target_cell == CellType.BOX_ON_TARGET:
return
# 移动箱子
var was_on_target = (target_cell == CellType.BOX_ON_TARGET)
var moving_to_target = (box_target_cell == CellType.TARGET)
# 更新箱子原位置
if was_on_target:
level_data[new_pos.y][new_pos.x] = CellType.TARGET
else:
level_data[new_pos.y][new_pos.x] = CellType.EMPTY
# 更新箱子新位置
if moving_to_target:
level_data[box_new_pos.y][box_new_pos.x] = CellType.BOX_ON_TARGET
else:
level_data[box_new_pos.y][box_new_pos.x] = CellType.BOX
# 移动玩家
# 恢复玩家原位置(检查是否在目标点上)
var level_strings = LEVELS[current_level]
if player_pos.y < level_strings.size() and player_pos.x < level_strings[player_pos.y].length():
var original_char = level_strings[player_pos.y][player_pos.x]
if original_char == '*': # 玩家原来在目标点上
level_data[player_pos.y][player_pos.x] = CellType.TARGET
else:
level_data[player_pos.y][player_pos.x] = CellType.EMPTY
else:
level_data[player_pos.y][player_pos.x] = CellType.EMPTY
# 更新玩家位置
player_pos = new_pos
level_data[player_pos.y][player_pos.x] = CellType.PLAYER
# 增加步数
moves += 1
# 检查是否过关
check_win_condition()
# 更新UI和重绘
update_ui()
queue_redraw()
func check_win_condition():
# 检查是否所有箱子都在目标点上
for y in range(map_height):
for x in range(map_width):
if level_data[y][x] == CellType.BOX:
return # 还有箱子不在目标点上
# 所有箱子都在目标点上,过关!
level_completed = true
win_label.visible = true
func next_level():
if current_level < LEVELS.size() - 1:
current_level += 1
init_level()
func prev_level():
if current_level > 0:
current_level -= 1
init_level()
func update_ui():
level_label.text = "关卡: " + str(current_level + 1)
moves_label.text = "步数: " + str(moves)
func _draw():
if not game_area:
return
# 获取游戏区域位置
var area_pos = game_area.position
# 计算起始绘制位置(居中)
var start_x = area_pos.x + (game_area.size.x - map_width * CELL_SIZE) / 2
var start_y = area_pos.y + (game_area.size.y - map_height * CELL_SIZE) / 2
# 绘制地图
for y in range(map_height):
for x in range(map_width):
var cell_x = start_x + x * CELL_SIZE
var cell_y = start_y + y * CELL_SIZE
var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE)
var cell_type = level_data[y][x]
var color = CELL_COLORS[cell_type]
# 绘制单元格
draw_rect(rect, color, true)
# 绘制边框
draw_rect(rect, Color.BLACK, false, 1)
# 特殊处理:如果是玩家在目标点上,需要先绘制目标点
if cell_type == CellType.PLAYER:
# 检查玩家下面是否有目标点(通过检查原始关卡数据)
var level_strings = LEVELS[current_level]
if y < level_strings.size() and x < level_strings[y].length():
var original_char = level_strings[y][x]
if original_char == '*': # 玩家在目标点上
draw_rect(rect, CELL_COLORS[CellType.TARGET], true)
draw_rect(rect, Color.BLACK, false, 1)
# 再绘制玩家(半透明)
var player_color = CELL_COLORS[CellType.PLAYER]
player_color.a = 0.8
draw_rect(rect, player_color, true)

View File

@@ -0,0 +1 @@
uid://b1t5fxyg0jjx5

View File

@@ -0,0 +1,85 @@
[gd_scene load_steps=2 format=3 uid="uid://cq14axh7bovu6"]
[ext_resource type="Script" uid="uid://b1t5fxyg0jjx5" path="res://Scene/SmallGame/PushBox.gd" id="1_qdfdf"]
[node name="PushBox" type="Panel"]
offset_right = 1402.0
offset_bottom = 720.0
script = ExtResource("1_qdfdf")
[node name="GameArea" type="Panel" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -300.0
offset_top = -250.0
offset_right = 300.0
offset_bottom = 250.0
grow_horizontal = 2
grow_vertical = 2
[node name="LevelLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 50.0
offset_top = -100.0
offset_right = 200.0
offset_bottom = -70.0
grow_vertical = 0
text = "关卡: 1"
[node name="MovesLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 50.0
offset_top = -70.0
offset_right = 200.0
offset_bottom = -40.0
grow_vertical = 0
text = "步数: 0"
[node name="WinLabel" type="Label" parent="."]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -150.0
offset_top = -50.0
offset_right = 150.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
text = "恭喜过关!
按N进入下一关
按R重新开始"
horizontal_alignment = 1
vertical_alignment = 1
[node name="ControlsLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -300.0
offset_top = -150.0
offset_right = -50.0
offset_bottom = -50.0
grow_horizontal = 0
grow_vertical = 0
text = "操作说明:
WASD或方向键 - 移动
R - 重新开始当前关卡
N - 下一关(过关后)
P - 上一关"

View File

@@ -0,0 +1,207 @@
extends Panel
# 游戏常量
const GRID_SIZE = 20
const GRID_WIDTH = 30
const GRID_HEIGHT = 30
# 方向枚举
enum Direction {
UP,
DOWN,
LEFT,
RIGHT
}
# 游戏变量
var snake_body = []
var snake_direction = Direction.RIGHT
var next_direction = Direction.RIGHT
var food_position = Vector2()
var score = 0
var game_over = false
# 节点引用
@onready var game_area = $GameArea
@onready var score_label = $ScoreLabel
@onready var game_over_label = $GameOverLabel
@onready var game_timer = $GameTimer
func _ready():
# 连接定时器信号
game_timer.timeout.connect(_on_game_timer_timeout)
# 设置游戏区域样式
game_area.modulate = Color(0.2, 0.2, 0.2, 1.0)
# 初始化游戏
init_game()
func init_game():
# 重置游戏状态
game_over = false
score = 0
snake_direction = Direction.RIGHT
next_direction = Direction.RIGHT
# 初始化蛇身
snake_body.clear()
snake_body.append(Vector2(5, 5))
snake_body.append(Vector2(4, 5))
snake_body.append(Vector2(3, 5))
# 生成食物
generate_food()
# 更新UI
update_score()
game_over_label.visible = false
# 启动定时器
game_timer.start()
func _input(event):
if event is InputEventKey and event.pressed:
if game_over:
if event.keycode == KEY_SPACE:
init_game()
return
# 控制蛇的方向
match event.keycode:
KEY_UP, KEY_W:
if snake_direction != Direction.DOWN:
next_direction = Direction.UP
KEY_DOWN, KEY_S:
if snake_direction != Direction.UP:
next_direction = Direction.DOWN
KEY_LEFT, KEY_A:
if snake_direction != Direction.RIGHT:
next_direction = Direction.LEFT
KEY_RIGHT, KEY_D:
if snake_direction != Direction.LEFT:
next_direction = Direction.RIGHT
func _on_game_timer_timeout():
if game_over:
return
# 更新方向
snake_direction = next_direction
# 移动蛇
move_snake()
# 检查碰撞
check_collisions()
# 重绘游戏
queue_redraw()
func move_snake():
var head = snake_body[0]
var new_head = head
# 根据方向计算新的头部位置
match snake_direction:
Direction.UP:
new_head = Vector2(head.x, head.y - 1)
Direction.DOWN:
new_head = Vector2(head.x, head.y + 1)
Direction.LEFT:
new_head = Vector2(head.x - 1, head.y)
Direction.RIGHT:
new_head = Vector2(head.x + 1, head.y)
# 添加新头部
snake_body.insert(0, new_head)
# 检查是否吃到食物
if new_head == food_position:
# 增加分数
score += 10
update_score()
# 生成新食物
generate_food()
else:
# 移除尾部
snake_body.pop_back()
func check_collisions():
var head = snake_body[0]
# 检查边界碰撞
if head.x < 0 or head.x >= GRID_WIDTH or head.y < 0 or head.y >= GRID_HEIGHT:
game_over = true
show_game_over()
return
# 检查自身碰撞
for i in range(1, snake_body.size()):
if head == snake_body[i]:
game_over = true
show_game_over()
return
func generate_food():
var attempts = 0
while attempts < 100: # 防止无限循环
food_position = Vector2(
randi() % GRID_WIDTH,
randi() % GRID_HEIGHT
)
# 确保食物不在蛇身上
var food_on_snake = false
for segment in snake_body:
if segment == food_position:
food_on_snake = true
break
if not food_on_snake:
break
attempts += 1
func update_score():
score_label.text = "分数: " + str(score)
func show_game_over():
game_timer.stop()
game_over_label.visible = true
func _draw():
if not game_area:
return
# 获取游戏区域的位置和大小
var area_pos = game_area.position
var area_size = game_area.size
# 计算网格大小
var cell_width = area_size.x / GRID_WIDTH
var cell_height = area_size.y / GRID_HEIGHT
# 绘制蛇身
for i in range(snake_body.size()):
var segment = snake_body[i]
var rect = Rect2(
area_pos.x + segment.x * cell_width,
area_pos.y + segment.y * cell_height,
cell_width - 1,
cell_height - 1
)
# 头部用不同颜色
var color = Color.GREEN if i == 0 else Color.LIME_GREEN
draw_rect(rect, color)
# 绘制食物
var food_rect = Rect2(
area_pos.x + food_position.x * cell_width,
area_pos.y + food_position.y * cell_height,
cell_width - 1,
cell_height - 1
)
draw_rect(food_rect, Color.RED)

View File

@@ -0,0 +1 @@
uid://rut5cdp1l3nh

View File

@@ -0,0 +1,57 @@
[gd_scene load_steps=2 format=3 uid="uid://grpkpy4duwac"]
[ext_resource type="Script" uid="uid://rut5cdp1l3nh" path="res://Scene/SmallGame/SnakeGame.gd" id="1_o070e"]
[node name="SnakeGame" type="Panel"]
offset_right = 1403.0
offset_bottom = 726.0
script = ExtResource("1_o070e")
[node name="GameArea" type="Panel" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -300.0
offset_top = -300.0
offset_right = 300.0
offset_bottom = 300.0
grow_horizontal = 2
grow_vertical = 2
[node name="ScoreLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 20.0
offset_top = -50.0
offset_right = 200.0
offset_bottom = -20.0
grow_vertical = 0
text = "分数: 0"
[node name="GameOverLabel" type="Label" parent="."]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -100.0
offset_top = -50.0
offset_right = 100.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
text = "游戏结束
按空格重新开始"
horizontal_alignment = 1
vertical_alignment = 1
[node name="GameTimer" type="Timer" parent="."]
wait_time = 0.2
autostart = true

333
Scene/SmallGame/Tetris.gd Normal file
View File

@@ -0,0 +1,333 @@
extends Panel
# 游戏常量
const BOARD_WIDTH = 10
const BOARD_HEIGHT = 20
const CELL_SIZE = 30
# 方块类型
enum PieceType {
I, O, T, S, Z, J, L
}
# 方块形状定义
const PIECE_SHAPES = {
PieceType.I: [
[[1, 1, 1, 1]],
[[1], [1], [1], [1]]
],
PieceType.O: [
[[1, 1], [1, 1]]
],
PieceType.T: [
[[0, 1, 0], [1, 1, 1]],
[[1, 0], [1, 1], [1, 0]],
[[1, 1, 1], [0, 1, 0]],
[[0, 1], [1, 1], [0, 1]]
],
PieceType.S: [
[[0, 1, 1], [1, 1, 0]],
[[1, 0], [1, 1], [0, 1]]
],
PieceType.Z: [
[[1, 1, 0], [0, 1, 1]],
[[0, 1], [1, 1], [1, 0]]
],
PieceType.J: [
[[1, 0, 0], [1, 1, 1]],
[[1, 1], [1, 0], [1, 0]],
[[1, 1, 1], [0, 0, 1]],
[[0, 1], [0, 1], [1, 1]]
],
PieceType.L: [
[[0, 0, 1], [1, 1, 1]],
[[1, 0], [1, 0], [1, 1]],
[[1, 1, 1], [1, 0, 0]],
[[1, 1], [0, 1], [0, 1]]
]
}
# 方块颜色
const PIECE_COLORS = {
PieceType.I: Color.CYAN,
PieceType.O: Color.YELLOW,
PieceType.T: Color.MAGENTA,
PieceType.S: Color.GREEN,
PieceType.Z: Color.RED,
PieceType.J: Color.BLUE,
PieceType.L: Color.ORANGE
}
# 游戏变量
var board = []
var current_piece = null
var current_piece_pos = Vector2()
var current_piece_rotation = 0
var next_piece_type = PieceType.I
var score = 0
var level = 1
var lines_cleared = 0
var game_over = false
var drop_time = 1.0
# 节点引用
@onready var game_area = $GameArea
@onready var next_piece_area = $NextPieceArea
@onready var score_label = $ScoreLabel
@onready var level_label = $LevelLabel
@onready var lines_label = $LinesLabel
@onready var game_over_label = $GameOverLabel
@onready var drop_timer = $DropTimer
func _ready():
# 连接定时器信号
drop_timer.timeout.connect(_on_drop_timer_timeout)
# 设置游戏区域样式
game_area.modulate = Color(0.1, 0.1, 0.1, 1.0)
next_piece_area.modulate = Color(0.2, 0.2, 0.2, 1.0)
# 初始化游戏
init_game()
func init_game():
# 重置游戏状态
game_over = false
score = 0
level = 1
lines_cleared = 0
drop_time = 1.0
# 初始化游戏板
board.clear()
for y in range(BOARD_HEIGHT):
var row = []
for x in range(BOARD_WIDTH):
row.append(0)
board.append(row)
# 生成第一个方块
next_piece_type = randi() % PieceType.size()
spawn_new_piece()
# 更新UI
update_ui()
game_over_label.visible = false
# 启动定时器
drop_timer.wait_time = drop_time
drop_timer.start()
func _input(event):
if event is InputEventKey and event.pressed:
if game_over:
if event.keycode == KEY_SPACE:
init_game()
return
if not current_piece:
return
# 控制方块
match event.keycode:
KEY_A:
move_piece(-1, 0)
KEY_D:
move_piece(1, 0)
KEY_S:
move_piece(0, 1)
KEY_W:
rotate_piece()
KEY_SPACE:
drop_piece()
func spawn_new_piece():
current_piece = {
"type": next_piece_type,
"rotation": 0
}
current_piece_pos = Vector2(BOARD_WIDTH / 2 - 1, 0)
current_piece_rotation = 0
# 生成下一个方块
next_piece_type = randi() % PieceType.size()
# 检查游戏是否结束
if not can_place_piece(current_piece_pos, current_piece_rotation):
game_over = true
show_game_over()
func move_piece(dx: int, dy: int):
var new_pos = current_piece_pos + Vector2(dx, dy)
if can_place_piece(new_pos, current_piece_rotation):
current_piece_pos = new_pos
queue_redraw()
func rotate_piece():
var new_rotation = (current_piece_rotation + 1) % get_piece_rotations(current_piece.type)
if can_place_piece(current_piece_pos, new_rotation):
current_piece_rotation = new_rotation
queue_redraw()
func drop_piece():
while can_place_piece(current_piece_pos + Vector2(0, 1), current_piece_rotation):
current_piece_pos.y += 1
place_piece()
func can_place_piece(pos: Vector2, rotation: int) -> bool:
var shape = get_piece_shape(current_piece.type, rotation)
for y in range(shape.size()):
for x in range(shape[y].size()):
if shape[y][x] == 1:
var board_x = pos.x + x
var board_y = pos.y + y
# 检查边界
if board_x < 0 or board_x >= BOARD_WIDTH or board_y >= BOARD_HEIGHT:
return false
# 检查碰撞
if board_y >= 0 and board[board_y][board_x] != 0:
return false
return true
func place_piece():
var shape = get_piece_shape(current_piece.type, current_piece_rotation)
for y in range(shape.size()):
for x in range(shape[y].size()):
if shape[y][x] == 1:
var board_x = current_piece_pos.x + x
var board_y = current_piece_pos.y + y
if board_y >= 0:
board[board_y][board_x] = current_piece.type + 1
# 检查并清除完整的行
clear_lines()
# 生成新方块
spawn_new_piece()
queue_redraw()
func clear_lines():
var lines_to_clear = []
# 找到完整的行
for y in range(BOARD_HEIGHT):
var is_full = true
for x in range(BOARD_WIDTH):
if board[y][x] == 0:
is_full = false
break
if is_full:
lines_to_clear.append(y)
# 清除行并下移
for line_y in lines_to_clear:
board.erase(board[line_y])
var new_row = []
for x in range(BOARD_WIDTH):
new_row.append(0)
board.insert(0, new_row)
# 更新分数和等级
if lines_to_clear.size() > 0:
lines_cleared += lines_to_clear.size()
score += lines_to_clear.size() * 100 * level
# 每10行提升一个等级
level = lines_cleared / 10 + 1
drop_time = max(0.1, 1.0 - (level - 1) * 0.1)
drop_timer.wait_time = drop_time
update_ui()
func get_piece_shape(type: PieceType, rotation: int) -> Array:
return PIECE_SHAPES[type][rotation]
func get_piece_rotations(type: PieceType) -> int:
return PIECE_SHAPES[type].size()
func update_ui():
score_label.text = "分数: " + str(score)
level_label.text = "等级: " + str(level)
lines_label.text = "消除行数: " + str(lines_cleared)
func show_game_over():
drop_timer.stop()
game_over_label.visible = true
func _on_drop_timer_timeout():
if game_over or not current_piece:
return
if can_place_piece(current_piece_pos + Vector2(0, 1), current_piece_rotation):
current_piece_pos.y += 1
queue_redraw()
else:
place_piece()
func _draw():
if not game_area:
return
# 获取游戏区域的位置
var area_pos = game_area.position
# 绘制游戏板
for y in range(BOARD_HEIGHT):
for x in range(BOARD_WIDTH):
var cell_value = board[y][x]
if cell_value > 0:
var rect = Rect2(
area_pos.x + x * CELL_SIZE,
area_pos.y + y * CELL_SIZE,
CELL_SIZE - 1,
CELL_SIZE - 1
)
var color = PIECE_COLORS[cell_value - 1]
draw_rect(rect, color)
# 绘制当前方块
if current_piece:
var shape = get_piece_shape(current_piece.type, current_piece_rotation)
for y in range(shape.size()):
for x in range(shape[y].size()):
if shape[y][x] == 1:
var board_x = current_piece_pos.x + x
var board_y = current_piece_pos.y + y
if board_y >= 0:
var rect = Rect2(
area_pos.x + board_x * CELL_SIZE,
area_pos.y + board_y * CELL_SIZE,
CELL_SIZE - 1,
CELL_SIZE - 1
)
var color = PIECE_COLORS[current_piece.type]
draw_rect(rect, color)
# 绘制网格线
for x in range(BOARD_WIDTH + 1):
var start_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y)
var end_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y + BOARD_HEIGHT * CELL_SIZE)
draw_line(start_pos, end_pos, Color.GRAY, 1)
for y in range(BOARD_HEIGHT + 1):
var start_pos = Vector2(area_pos.x, area_pos.y + y * CELL_SIZE)
var end_pos = Vector2(area_pos.x + BOARD_WIDTH * CELL_SIZE, area_pos.y + y * CELL_SIZE)
draw_line(start_pos, end_pos, Color.GRAY, 1)
# 绘制下一个方块预览
var next_area_pos = next_piece_area.position
var next_shape = get_piece_shape(next_piece_type, 0)
for y in range(next_shape.size()):
for x in range(next_shape[y].size()):
if next_shape[y][x] == 1:
var rect = Rect2(
next_area_pos.x + 20 + x * 20,
next_area_pos.y + 50 + y * 20,
19,
19
)
var color = PIECE_COLORS[next_piece_type]
draw_rect(rect, color)

View File

@@ -0,0 +1 @@
uid://yhljjw1pbovq

120
Scene/SmallGame/Tetris.tscn Normal file
View File

@@ -0,0 +1,120 @@
[gd_scene load_steps=2 format=3 uid="uid://c3nuuwu5te3bu"]
[ext_resource type="Script" uid="uid://yhljjw1pbovq" path="res://Scene/SmallGame/Tetris.gd" id="1_s1q1g"]
[node name="Tetris" type="Panel"]
offset_right = 1403.0
offset_bottom = 721.0
script = ExtResource("1_s1q1g")
[node name="GameArea" type="Panel" parent="."]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 50.0
offset_top = -300.0
offset_right = 350.0
offset_bottom = 300.0
grow_vertical = 2
[node name="NextPieceArea" type="Panel" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -200.0
offset_top = 50.0
offset_right = -50.0
offset_bottom = 200.0
grow_horizontal = 0
[node name="NextPieceLabel" type="Label" parent="NextPieceArea"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -50.0
offset_top = 10.0
offset_right = 50.0
offset_bottom = 40.0
grow_horizontal = 2
text = "下一个"
horizontal_alignment = 1
[node name="ScoreLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -200.0
offset_top = 220.0
offset_right = -50.0
offset_bottom = 250.0
grow_horizontal = 0
text = "分数: 0"
horizontal_alignment = 1
[node name="LevelLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -200.0
offset_top = 260.0
offset_right = -50.0
offset_bottom = 290.0
grow_horizontal = 0
text = "等级: 1"
horizontal_alignment = 1
[node name="LinesLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -200.0
offset_top = 300.0
offset_right = -50.0
offset_bottom = 330.0
grow_horizontal = 0
text = "消除行数: 0"
horizontal_alignment = 1
[node name="GameOverLabel" type="Label" parent="."]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -100.0
offset_top = -50.0
offset_right = 100.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
text = "游戏结束
按空格重新开始"
horizontal_alignment = 1
vertical_alignment = 1
[node name="DropTimer" type="Timer" parent="."]
autostart = true
[node name="ControlsLabel" type="Label" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -200.0
offset_top = 400.0
offset_right = -50.0
offset_bottom = 500.0
grow_horizontal = 0
text = "操作说明:
A/D - 左右移动
S - 快速下降
W - 旋转
空格 - 瞬间下降"