综合演示:贪吃蛇游戏开发
文档概述
本演示文档展示了如何使用轻语言(Qing)开发一个完整的网页版贪吃蛇游戏。该游戏利用画板组件进行图形渲染,通过键盘事件控制蛇的移动方向,演示了时钟定时器、数组操作、碰撞检测、随机数生成、动态布局调整等核心编程技术。贪吃蛇是一款经典的休闲游戏,玩家通过控制小蛇移动并吃到食物来获得分数,同时避免撞墙。
- 核心玩法:玩家通过键盘方向键(↑ ↓ ← →)控制小蛇在画布中移动,吃到红色食物即可增加蛇身长度并累计分数
- 游戏规则:
- 小蛇撞墙即游戏结束
- 每吃到一个食物,蛇身长度增加,分数增加 10 分
- 随着蛇身增长,游戏难度逐渐提升(操作空间变小)
- 技术亮点:
- 使用画板组件进行游戏画面的实时渲染
- 利用时钟组件实现游戏主循环(每 150 毫秒更新一次状态)
- 通过键盘事件捕捉玩家操作,动态调整蛇的移动方向
- 响应式布局适配:窗口尺寸变化时自动调整组件位置
涉及组件
- 标签:显示游戏标题、玩法说明和得分信息
- 按钮:开始/重新开始游戏
- 画板:绘制游戏场景(蛇身、食物、得分)
- 时钟类:游戏主循环定时器(每 150 毫秒更新一次)
- 键盘按键被按下事件:捕捉键盘方向键控制蛇的方向
- 窗口尺寸被改变事件:响应窗口大小变化,自动调整布局
- 页面加载完毕事件:页面初始化,设置标题和画板尺寸
涉及函数与 API
| 函数/API | 用途 |
|---|---|
创建时钟(子函数, 间隔时间) | 创建游戏主循环定时器 |
销毁时钟(时钟ID) | 游戏结束时销毁定时器 |
添加成员首(数组, 值) | 在蛇身数组头部插入新蛇头坐标 |
删除成员尾(数组) | 未吃到食物时移除蛇尾,保持蛇身长度不变 |
取随机整数(最小值, 最大值) | 随机生成食物位置 |
置画布尺寸(宽度, 高度) | 设置画板的绘制区域尺寸 |
取实际宽度() | 获取组件的实际渲染宽度 |
事件源.keyCode | 获取键盘按键的键码值 |
置组件水平居中(组件) | 将组件水平居中 |
信息框(消息) | 弹出游戏结束提示框 |
💡 本演示中使用到的画板绘制函数(
填充样式、填充矩形、画笔样式、字体、画文本)请参考画板组件专项文档。
程序代码
界面布局文件:游戏页面.spldg
<?xml version="1.0" encoding="utf-8"?>
<页面 标题="贪吃蛇游戏 - 中文代码版" 宽度="734" 高度="509">
<绝对布局 名称="根布局" 宽度="100" 高度="100">
<!-- 游戏标题 -->
<标签 名称="标签2" 左边="115" 顶边="30" 宽度="600" 高度="30" 文本="🐍 贪吃蛇游戏 - 中文代码版" 字体大小="24" 字体粗细="粗体" />
<!-- 游戏画板 -->
<画板 名称="画板1" 左边="115" 顶边="80" 宽度="600" 高度="300" 背景颜色="#B5C493" />
<!-- 玩法说明 -->
<标签 名称="标签1" 左边="115" 顶边="395" 宽度="350" 高度="65" 文本="🎮 游戏玩法:\n点击「开始游戏」后,按下键盘中的 ↑ ↓ ← → 分别控制小蛇的移动方向。\n🐍 小蛇吃到的食物越多,蛇身越长,得分越高!" 字体大小="14" 文本颜色="#555555" />
<!-- 开始按钮 -->
<按钮 名称="按钮1" 左边="480" 宽度="180" 顶边="395" 高度="40" 文本="🚀 开始游戏" 背景颜色="#1890ff" 文本颜色="#ffffff" 边框圆角="6px" />
</绝对布局>
</页面>
逻辑文件:游戏页面.spl
' ****************************************************************************
' 版权说明:Copyright(C) All Rights Reserved VcnStudio
' 程序版本:V1.0
' 程序作者:VcnStudio
' 联系信息:
' 窗口描述:贪吃蛇游戏 - 中文代码版
' ****************************************************************************
' ========== 游戏核心变量 ==========
' 构建蛇身和食物的小方块尺寸
变量 方块尺寸 = 20
' 记录已经吃到的食物数量(得分)
变量 分数 = 0
' 循环游戏的计时器,负责循环渲染游戏界面
变量 时钟1
' 定义蛇身(数组)数组中的每个项为一个坐标对象
变量 蛇身 = [{X:10, Y:5}]
' 定义食物坐标
变量 食物 = { X:10, Y:10 }
' 定义移动方向(偏移量)
变量 水平偏移 = 1
变量 垂直偏移 = 0
' 定义游戏状态,是否已结束
变量 游戏已结束 = 假
' ========== 页面加载事件 ==========
事件 页面加载完毕()
' 设置页面标题
置页面标题("贪吃蛇游戏网页版 - By:VcnStudio")
' 初始化画板
画板1.置画布尺寸(600, 300)
' 调整页面布局(居中、对齐)
调整页面布局()
结束 事件
' ========== 开始游戏按钮 ==========
事件 按钮1.被单击(源对象, 事件源)
' 重置游戏状态
分数 = 0
游戏已结束 = 假
蛇身 = [{X:10, Y:5}]
食物 = { X:10, Y:10 }
水平偏移 = 1
垂直偏移 = 0
' 更新按钮文字
按钮1.文本 = "⏳ 游戏进行中..."
按钮1.禁用 = 真
' 启动游戏主循环(每 150 毫秒更新一次)
时钟1 = 创建时钟(更新游戏状态, 150)
结束 事件
' ========== 游戏主循环(由时钟触发) ==========
函数 更新游戏状态()
如果(游戏已结束 == 真)
返回
结束 如果
' 计算新的蛇头位置(根据当前方向)
变量 新蛇头坐标 = {
X: 蛇身[0].X + 水平偏移,
Y: 蛇身[0].Y + 垂直偏移
}
' 检测是否撞墙,撞墙则结束游戏
如果(检测是否撞墙(新蛇头坐标))
游戏结束("撞墙了,游戏结束!")
返回
结束 如果
' 检测是否撞到自己(蛇头碰到蛇身)
变量循环(计次 = 0, 取数组成员数(蛇身), 1)
如果(计次 > 0 且 新蛇头坐标.X == 蛇身[计次].X 且 新蛇头坐标.Y == 蛇身[计次].Y)
游戏结束("咬到自己了,游戏结束!")
返回
结束 如果
结束循环
' 将新的蛇头坐标添加到蛇身
添加成员首(蛇身, 新蛇头坐标)
' 检测是否吃到了食物
如果(新蛇头坐标.X == 食物.X 且 新蛇头坐标.Y == 食物.Y)
' 吃到食物,得分增加
分数 = 分数 + 10
' 生成新的食物坐标
' 注意:取随机数的最大值不能超过(画板宽度/方块尺寸)- 1
变量 最大X = 画板1.取渲染宽度() / 方块尺寸 - 1
变量 最大Y = 画板1.取渲染高度() / 方块尺寸 - 1
食物 = {
X: 取随机整数(1, 最大X - 1),
Y: 取随机整数(1, 最大Y - 1)
}
否则
' 没有吃到食物,则移除蛇尾
' 因为上一步已经添加了新的蛇头,没吃到食物需要保证蛇身长度不变
删除成员尾(蛇身)
结束 如果
' 绘制游戏元素
绘制游戏内容()
结束 函数
' ========== 游戏结束处理 ==========
函数 游戏结束(提示信息)
游戏已结束 = 真
销毁时钟(时钟1)
按钮1.文本 = "🔁 重新开始游戏"
按钮1.禁用 = 假
信息框(提示信息 + " 得分:" + 分数)
结束 函数
' ========== 绘制游戏画面 ==========
函数 绘制游戏内容()
' 清空画板内容(每次绘制都需要先清空上一次绘制的内容)
画板1.填充样式 = "#B5C493"
画板1.填充矩形(0, 0, 600, 300)
' ===== 绘制蛇身 =====
画板1.填充样式 = "#2E8B57"
' 蛇头用稍微亮一点的颜色
变量 蛇头 = 蛇身[0]
画板1.填充矩形(蛇头.X * 方块尺寸, 蛇头.Y * 方块尺寸, 方块尺寸 - 2, 方块尺寸 - 2)
' 蛇身用绿色系
画板1.填充样式 = "#32CD32"
变量循环(索引 = 0, 取数组成员数(蛇身), 1)
变量 坐标点 = 蛇身[索引]
画板1.填充矩形(坐标点.X * 方块尺寸, 坐标点.Y * 方块尺寸, 方块尺寸 - 2, 方块尺寸 - 2)
结束循环
' ===== 绘制食物 =====
画板1.填充样式 = "#FF4444"
画板1.填充矩形(食物.X * 方块尺寸, 食物.Y * 方块尺寸, 方块尺寸 - 2, 方块尺寸 - 2)
' ===== 绘制分数(左上角) =====
画板1.画笔样式 = "#333333"
画板1.字体 = "20px Arial"
画板1.画文本("🎯 得分:" + 分数, 10, 25)
' ===== 如果游戏已结束,显示遮罩和提示 =====
如果(游戏已结束)
画板1.填充样式 = "rgba(0,0,0,0.5)"
画板1.填充矩形(0, 0, 600, 300)
画板1.画笔样式 = "#ffffff"
画板1.字体 = "36px Arial"
画板1.画文本("💀 游戏结束", 180, 130)
画板1.字体 = "20px Arial"
画板1.画文本("得分:" + 分数, 240, 180)
结束 如果
结束 函数
' ========== 碰撞检测 ==========
函数 检测是否撞墙(蛇头坐标)
变量 最大X = 画板1.取渲染宽度() / 方块尺寸 - 1
变量 最大Y = 画板1.取渲染高度() / 方块尺寸 - 1
返回 蛇头坐标.X < 0 或 蛇头坐标.X > 最大X 或 蛇头坐标.Y < 0 或 蛇头坐标.Y > 最大Y
结束 函数
' ========== 键盘控制 ==========
事件 键盘按键被按下(事件源)
如果(游戏已结束)
返回
结束 如果
变量 键码 = 事件源.keyCode
判断(键码)
分支 37 ' ← 左
如果(水平偏移 != 1) ' 不能直接掉头
水平偏移 = -1
垂直偏移 = 0
结束 如果
分支 38 ' ↑ 上
如果(垂直偏移 != 1) ' 不能直接掉头
水平偏移 = 0
垂直偏移 = -1
结束 如果
分支 39 ' → 右
如果(水平偏移 != -1) ' 不能直接掉头
水平偏移 = 1
垂直偏移 = 0
结束 如果
分支 40 ' ↓ 下
如果(垂直偏移 != -1) ' 不能直接掉头
水平偏移 = 0
垂直偏移 = 1
结束 如果
结束 判断
结束 事件
' ========== 窗口自适应 ==========
事件 窗口尺寸被改变()
调整页面布局()
结束 事件
' ========== 布局调整函数 ==========
函数 调整页面布局()
' 将画板和标题水平居中
置组件水平居中(画板1, 标签2)
' 按钮对齐到画板右侧
按钮1.右侧对齐到(画板1)
按钮1.顶边对齐到底(画板1, 10)
' 标签1对齐到画板左侧
标签1.左侧对齐到(画板1)
标签1.顶边对齐到底(画板1, 10)
' 标签2顶部边距
标签2.顶边 = 30
结束 函数
游戏运行效果
运行后,你将看到:
- 游戏界面:包含标题、游戏画板、玩法说明和开始按钮
- 游戏流程:
- 点击「开始游戏」按钮,游戏启动
- 使用键盘方向键(↑ ↓ ← →)控制小蛇移动
- 小蛇吃到红色食物,蛇身变长,得分增加 10 分
- 小蛇撞墙或咬到自己时,游戏结束
- 点击「重新开始游戏」按钮可重新开始
- 交互反馈:
- 游戏过程中,蛇头和蛇身使用不同的绿色区分
- 游戏结束时,画板会显示半透明遮罩和游戏结束提示
- 得分实时显示在画板左上角
学习要点
通过本综合演示,你可以学习到以下轻语言编程技术:
| 知识点 | 代码位置 | 说明 |
|---|---|---|
| 画板图形绘制 | 绘制游戏内容() | 使用 填充样式、填充矩形、画笔样式、画文本 等 API 绘制游戏画面 |
| 时钟定时器 | 创建时钟(更新游戏状态, 150) | 创建游戏主循环定时器,每 150 毫秒更新一次游戏状态 |
| 键盘事件 | 键盘按键被按下(事件源) | 捕捉键盘方向键,控制蛇的移动方向 |
| 数组操作 | 添加成员首()、删除成员尾() | 管理蛇身坐标数组,实现蛇的移动逻辑 |
| 碰撞检测 | 检测是否撞墙()、蛇头撞自身检测 | 检测蛇头是否撞墙或撞到自己 |
| 随机数生成 | 取随机整数(最小值, 最大值) | 随机生成食物的位置 |
| 响应式布局 | 调整页面布局()、置组件水平居中() | 窗口尺寸变化时自动调整组件位置 |
| 事件清理 | 销毁时钟(时钟1) | 游戏结束时销毁定时器,避免内存泄漏 |
扩展方向
你可以基于这个游戏继续扩展以下功能:
- 难度递增:随着得分增加,时钟间隔逐渐缩短(蛇移动速度加快)
- 障碍物模式:在画板中随机生成障碍物,蛇碰到障碍物则游戏结束
- 无尽模式:蛇撞墙后从另一侧穿出(类似经典贪吃蛇)
- 暂停/继续:添加空格键暂停游戏功能
- 排行榜:使用本地存储(
保存配置)记录最高分
💡 相关文档:本演示中使用的画板绘制函数、时钟函数、键盘事件等详细用法,请参考对应的专项文档。