Files
Sprout-Farm/Scene/SmallGame/SnakeGame.gd

208 lines
4.1 KiB
GDScript

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)