Trae CN 智能体编写俄罗斯方块游戏
本帖最后由 一曲 于 2026-8-9 20:04 编辑今天,测试Trae CN,
使用火山视窗写一个俄罗斯方块游戏
全程监控,代码自动生成,自动编译,只需手动检查软件运行中的错误。真正实现代码零手动。
<火山程序 类型 = "通常" 版本 = 1 />
包 火山.俄罗斯方块
# ============================================================
# 第一部分:启动类
# 参考:所有例程启动类结构(如 vol-选择夹1 / 你好祖国)
# 基础类 = 窗口程序类,必须定义启动方法,并在其中调用创建主窗口
# ============================================================
类 启动类 <公开 基础类 = 窗口程序类>
{
变量 主窗口对象 <类型 = 我的主窗口 标题 = "俄罗斯方块">
方法 启动方法 <公开 类型 = 整数>
{
// ref: 火山.MFC界面.基本.窗口.创建主窗口
主窗口对象.创建主窗口 ()
返回 (1)
}
}
# ============================================================
# 第二部分:方块形状常量类
# 7种标准俄罗斯方块 × 4种旋转状态 = 28个一维数组(每个16成员=4×4)
# 类型编号约定:
# 0=I(青)1=O(黄)2=T(紫)3=S(绿)4=Z(红)5=L(橙)6=J(蓝)
# 语法参考:vol_win_manual.md P99-P106 数组立即数定义
# 常量 名 <类型 = "整数 []" 值 = { 0,0,0,0,... }>
# ============================================================
类 形状常量 <公开>
{
# ---------- I型(青色,水平长条)----------
常量 I_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 1,1,1,1, 0,0,0,0, 0,0,0,0}>
常量 I_1 <公开 类型 = "整数 []" 值 = {0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0}>
常量 I_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,0, 1,1,1,1, 0,0,0,0}>
常量 I_3 <公开 类型 = "整数 []" 值 = {0,1,0,0, 0,1,0,0, 0,1,0,0, 0,1,0,0}>
# ---------- O型(黄色,正方形,4旋转相同)----------
常量 O_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,0, 0,1,1,0, 0,0,0,0}>
常量 O_1 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,0, 0,1,1,0, 0,0,0,0}>
常量 O_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,0, 0,1,1,0, 0,0,0,0}>
常量 O_3 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,0, 0,1,1,0, 0,0,0,0}>
# ---------- T型(紫色,T字)----------
常量 T_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,1, 0,0,1,0, 0,0,0,0}>
常量 T_1 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,1,1,0, 0,0,1,0}>
常量 T_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,1,1,1, 0,0,0,0}>
常量 T_3 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,0,1,1, 0,0,1,0}>
# ---------- S型(绿色,右斜)----------
常量 S_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,1, 0,1,1,0, 0,0,0,0}>
常量 S_1 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,0,1,1, 0,0,0,1}>
常量 S_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,0, 0,0,1,1, 0,1,1,0}>
常量 S_3 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,0,0, 0,1,1,0, 0,0,1,0}>
# ---------- Z型(红色,左斜)----------
常量 Z_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,0, 0,0,1,1, 0,0,0,0}>
常量 Z_1 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,1, 0,0,1,1, 0,0,1,0}>
常量 Z_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,0, 0,1,1,0, 0,0,1,1}>
常量 Z_3 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,1,1,0, 0,1,0,0}>
# ---------- L型(橙黄色,L字)----------
常量 L_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,1, 0,1,1,1, 0,0,0,0}>
常量 L_1 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,0,1,0, 0,0,1,1}>
常量 L_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,0, 0,1,1,1, 0,1,0,0}>
常量 L_3 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,1,0, 0,0,1,0, 0,0,1,0}>
# ---------- J型(蓝色,反L)----------
常量 J_0 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,1,0,0, 0,1,1,1, 0,0,0,0}>
常量 J_1 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,1, 0,0,1,0, 0,0,1,0}>
常量 J_2 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,0,0, 0,1,1,1, 0,0,0,1}>
常量 J_3 <公开 类型 = "整数 []" 值 = {0,0,0,0, 0,0,1,0, 0,0,1,0, 0,1,1,0}>
}
# ============================================================
# 第三部分:主窗口类
# ============================================================
类 我的主窗口 <基础类 = 窗口 注释 = "俄罗斯方块主窗口" @视窗.布局 = "client_size = \"500, 650\"" 标题 = "俄罗斯方块">
{
# --------------------------------------------------------
# 3.1 界面组件(10个)
# --------------------------------------------------------
# 游戏主画板:10列×20行 × 每格30像素 = 300×600像素
# ref: 画板类定义(vol_win_sys_pkg_026.md P4400起)+ 例程飞机大战主画板
变量 游戏画板 <类型 = 画板 隐藏值属性 = "0" 组件边框 = 单线边框式 自动重画 = 真 可停留焦点 = 真 @视窗.布局 = "id = 101\r\npos = \"25, 25, 302, 602\"">
# 下一方块预览画板:4格×4格 × 30 = 120×120像素
变量 预览画板 <类型 = 画板 隐藏值属性 = "0" 组件边框 = 单线边框式 自动重画 = 真 @视窗.布局 = "id = 102\r\npos = \"350, 25, 122, 122\"">
# 游戏时钟(方块下落定时器,放角落,26×26标准尺寸)
# ref: 时钟例程布局写法
变量 游戏时钟 <类型 = 时钟 隐藏值属性 = "0" @视窗.布局 = "id = 103\r\npos = \"350, 160, 26, 26\"">
# 预览区标题标签
变量 标签_下一块 <类型 = 标签 隐藏值属性 = "0" 横向对齐方式 = 居中 @视窗.布局 = "id = 104\r\npos = \"350, 0, 120, 25\"" 标题 = "下一块">
# 分数显示标签
变量 标签_分数 <类型 = 标签 隐藏值属性 = "0" @视窗.布局 = "id = 105\r\npos = \"350, 200, 120, 30\"" 标题 = "分数:0">
# 等级显示标签
变量 标签_等级 <类型 = 标签 隐藏值属性 = "0" @视窗.布局 = "id = 106\r\npos = \"350, 240, 120, 30\"" 标题 = "等级:1">
# 最高分显示标签
变量 标签_最高分 <类型 = 标签 隐藏值属性 = "0" @视窗.布局 = "id = 110\r\npos = \"350, 270, 120, 30\"" 标题 = "最高分:0">
# 操作说明标签(自动折行 + \n手动换行)
变量 标签_说明 <类型 = 标签 隐藏值属性 = "0" 是否自动折行 = 真 @视窗.布局 = "id = 107\r\npos = \"350, 300, 130, 120\""
标题 = "操作说明:\n← → 移动\n↑ 旋转\n↓ 加速\n空格 直落">
# 重新开始按钮
变量 按钮_重来 <类型 = 按钮 隐藏值属性 = "0" @视窗.布局 = "id = 108\r\npos = \"350, 430, 120, 35\"" 标题 = "重新开始">
# 暂停/继续按钮
变量 按钮_暂停 <类型 = 按钮 隐藏值属性 = "0" @视窗.布局 = "id = 109\r\npos = \"350, 480, 120, 35\"" 标题 = "暂停">
# 闪烁时钟(消行动画用,150ms间隔)
变量 闪烁时钟 <类型 = 时钟 隐藏值属性 = "0" @视窗.布局 = "id = 111\r\npos = \"350, 160, 26, 26\"">
# --------------------------------------------------------
# 3.2 游戏状态成员变量
# --------------------------------------------------------
# 游戏板:整数数组类,10列×20行=200成员,索引=y*10+x
# 值:0=空;1~7对应7种方块类型号(I/O/T/S/Z/L/J+1)
# ref: 整数数组类(vol_win_sys_pkg_081.md P9900起)
变量 游戏板 <类型 = 整数数组类>
# 当前下落方块信息
变量 当前方块类型 <类型 = 整数 注释 = "0~6">
变量 当前方块旋转 <类型 = 整数 注释 = "0~3">
变量 当前方块X <类型 = 整数 注释 = "左上角格坐标(列0~9)">
变量 当前方块Y <类型 = 整数 注释 = "左上角格坐标(行,可为负-表示还在顶部外)">
变量 下一方块类型 <类型 = 整数 注释 = "0~6">
# 统计信息
变量 分数 <类型 = 整数>
变量 等级 <类型 = 整数>
变量 已消行数 <类型 = 整数>
变量 最高分 <类型 = 整数>
# 流程控制
变量 是否暂停 <类型 = 逻辑型 值 = 假>
变量 暂停前周期 <类型 = 整数 值 = 500>
变量 游戏结束 <类型 = 逻辑型 值 = 假>
# 消行动画状态
变量 正在消行 <类型 = 逻辑型 值 = 假>
变量 闪烁计数 <类型 = 整数 值 = 0>
变量 闪烁显示白色 <类型 = 逻辑型 值 = 假>
变量 待消除行号 <类型 = 整数数组类 注释 = "最多4行">
变量 待消除行数 <类型 = 整数 值 = 0>
# --------------------------------------------------------
# 3.3 工具方法
# --------------------------------------------------------
# 初始化游戏:重置所有状态
方法 初始化游戏 <类型 = 整数>
{
// 游戏板重置为 10×20=200 成员,全部清零
// ref: 基本类型数组模板类.重置数组 (vol_win_sys_pkg_081.md P9971)
游戏板.重置数组 (200, 真)
// 统计归零
分数 = 0
等级 = 1
已消行数 = 0
游戏结束 = 假
是否暂停 = 假
暂停前周期 = 500
按钮_暂停.标题 = "暂停"
// 重置消行动画状态
正在消行 = 假
闪烁计数 = 0
闪烁显示白色 = 假
待消除行号.重置数组 (4, 真)
待消除行数 = 0
闪烁时钟.时钟周期 = 0
// 读取最高分
// ref: 文本类.读入文本文件 (vol_win_sys_pkg_081.md P8442)
变量 文件内容 <类型 = 文本型>
文件内容 = 读入文本文件 ("tetris_highscore.txt", -1, 文本编码类型.多字节)
如果 (文件内容 != "")
{
最高分 = 文本到整数 (文件内容)
}
否则
{
最高分 = 0
}
// 取下一方块(首次)
// ref: 算术运算类.取随机数 (vol-飞机大战例程 P19951)
下一方块类型 = 取随机数 (0, 6)
// 生成当前方块
生成新方块 ()
// 设置时钟周期:等级1=500ms
// ref: 时钟.时钟周期属性写 (vol_win_sys_pkg_026.md P7238)
游戏时钟.时钟周期 = 500
// 更新标签显示
更新标签显示 ()
返回 (0)
}
# 根据类型号+旋转状态+4x4内索引,返回该位置是否有方块(1/0)
# 参数:方块类型0~6,旋转0~3,格子索引0~15(4x4=16格)
方法 取形状格值 <类型 = 整数>
参数 方块类型 <类型 = 整数>
参数 旋转状态 <类型 = 整数>
参数 格子索引 <类型 = 整数>
{
// 先做边界钳制,防非法传参
如果 (方块类型 < 0)
{
方块类型 = 0
}
如果 (方块类型 > 6)
{
方块类型 = 6
}
如果 (旋转状态 < 0)
{
旋转状态 = 0
}
如果 (旋转状态 > 3)
{
旋转状态 = 3
}
如果 (格子索引 < 0)
{
格子索引 = 0
}
如果 (格子索引 > 15)
{
格子索引 = 15
}
变量 结果 <类型 = 整数>
结果 = 0
// 根据类型分支
如果 (方块类型 == 0)
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.I_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.I_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.I_2 [格子索引]
}
否则
{
结果 = 形状常量.I_3 [格子索引]
}
}
否则 (方块类型 == 1)
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.O_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.O_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.O_2 [格子索引]
}
否则
{
结果 = 形状常量.O_3 [格子索引]
}
}
否则 (方块类型 == 2)
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.T_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.T_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.T_2 [格子索引]
}
否则
{
结果 = 形状常量.T_3 [格子索引]
}
}
否则 (方块类型 == 3)
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.S_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.S_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.S_2 [格子索引]
}
否则
{
结果 = 形状常量.S_3 [格子索引]
}
}
否则 (方块类型 == 4)
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.Z_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.Z_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.Z_2 [格子索引]
}
否则
{
结果 = 形状常量.Z_3 [格子索引]
}
}
否则 (方块类型 == 5)
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.L_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.L_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.L_2 [格子索引]
}
否则
{
结果 = 形状常量.L_3 [格子索引]
}
}
否则
{
如果 (旋转状态 == 0)
{
结果 = 形状常量.J_0 [格子索引]
}
否则 (旋转状态 == 1)
{
结果 = 形状常量.J_1 [格子索引]
}
否则 (旋转状态 == 2)
{
结果 = 形状常量.J_2 [格子索引]
}
否则
{
结果 = 形状常量.J_3 [格子索引]
}
}
返回 (结果)
}
# 碰撞检测:返回真表示碰撞
# 给定方块(类型,旋转,x,y),检查越界或与游戏板重叠
方法 检测碰撞 <类型 = 逻辑型>
参数 类型 <类型 = 整数>
参数 旋转 <类型 = 整数>
参数 x <类型 = 整数>
参数 y <类型 = 整数>
{
变量 i <类型 = 整数>
变量 cell_x <类型 = 整数>
变量 cell_y <类型 = 整数>
变量 格值 <类型 = 整数>
变量 板值 <类型 = 整数>
// 枚举4×4=16格
// ref: 计次循环用法(例程 vol-飞机大战 P19962)
计次循环 (16)
{
i = 取循环索引 ()
格值 = 取形状格值 (类型, 旋转, i)
如果 (格值 == 0)
{
// 空格子,跳过
到循环尾
}
// 转全局坐标
cell_x = x + i % 4
cell_y = y + i / 4
// 横向越界(左右)
如果 (cell_x < 0 || cell_x >= 10)
{
返回 (真)
}
// 下方越界(落地,y超过底线)
如果 (cell_y >= 20)
{
返回 (真)
}
// 上方越界忽略(新生成块允许y<0)
如果 (cell_y >= 0)
{
// 与游戏板上已有方块重叠?
// ref: 整数数组类.取成员 (vol_win_sys_pkg_081.md P9994)
板值 = 游戏板.取成员 (cell_y * 10 + cell_x)
如果 (板值 != 0)
{
返回 (真)
}
}
}
返回 (假)
}
# 生成新方块:下一方块→当前块,重新随机下一块
# 返回:真=成功;假=生成位置就碰撞(游戏结束)
方法 生成新方块 <类型 = 逻辑型>
{
当前方块类型 = 下一方块类型
当前方块旋转 = 0
// 初始位置:水平居中(x=3,4格宽的话占3~6列);y=-1(让顶部先露一部分)
当前方块X = 3
当前方块Y = -1
// 下一方块重新随机
下一方块类型 = 取随机数 (0, 6)
// 生成位置即碰撞 → 游戏结束
如果 (检测碰撞 (当前方块类型, 当前方块旋转, 当前方块X, 当前方块Y))
{
返回 (假)
}
返回 (真)
}
# --------------------------------------------------------
# 3.4 动作方法
# --------------------------------------------------------
# 左移一格
方法 方块左移 <类型 = 整数>
{
如果 (检测碰撞 (当前方块类型, 当前方块旋转, 当前方块X - 1, 当前方块Y) == 假)
{
当前方块X = 当前方块X - 1
}
返回 (0)
}
# 右移一格
方法 方块右移 <类型 = 整数>
{
如果 (检测碰撞 (当前方块类型, 当前方块旋转, 当前方块X + 1, 当前方块Y) == 假)
{
当前方块X = 当前方块X + 1
}
返回 (0)
}
# 旋转:旋转状态 +1 mod 4
方法 方块旋转 <类型 = 整数>
{
变量 新旋转 <类型 = 整数>
新旋转 = (当前方块旋转 + 1) % 4
如果 (检测碰撞 (当前方块类型, 新旋转, 当前方块X, 当前方块Y) == 假)
{
当前方块旋转 = 新旋转
// ref: 常用功能类.鸣叫 (vol_win_sys_pkg_081.md P16165)
鸣叫 (鸣叫声音类型.特别)
}
返回 (0)
}
# 下落一格:返回真=还可继续下落;假=已落地,已固定
方法 方块下落一格 <类型 = 逻辑型>
{
变量 可继续 <类型 = 逻辑型>
可继续 = 真
// 如果再下一格会碰撞 → 固定方块
如果 (检测碰撞 (当前方块类型, 当前方块旋转, 当前方块X, 当前方块Y + 1))
{
固定方块 ()
// ref: 常用功能类.鸣叫 (vol_win_sys_pkg_081.md P16165)
鸣叫 (鸣叫声音类型.标准)
// 检测满行(仅检测不消除)
待消除行数 = 检测满行 ()
如果 (待消除行数 > 0)
{
// 启动消行闪烁动画
正在消行 = 真
闪烁计数 = 0
闪烁显示白色 = 真
游戏时钟.时钟周期 = 0
闪烁时钟.时钟周期 = 150
}
否则
{
// 无消行,直接生成新方块
如果 (生成新方块 () == 假)
{
游戏结束处理 ()
}
}
可继续 = 假
}
否则
{
// 正常下落
当前方块Y = 当前方块Y + 1
}
返回 (可继续)
}
# 直接落底:循环调用下落一格直到返回假
方法 方块直接落底 <类型 = 整数>
{
变量 可继续 <类型 = 逻辑型>
可继续 = 真
判断循环 (可继续)
{
可继续 = 方块下落一格 ()
}
返回 (0)
}
# 将当前方块4x4中的实心格子写入游戏板数组
方法 固定方块 <类型 = 整数>
{
变量 i <类型 = 整数>
变量 cell_x <类型 = 整数>
变量 cell_y <类型 = 整数>
变量 格值 <类型 = 整数>
变量 类型号1to7 <类型 = 整数>
// 类型号存储=类型+1(0~6 → 1~7,保留0表示空)
类型号1to7 = 当前方块类型 + 1
计次循环 (16)
{
i = 取循环索引 ()
格值 = 取形状格值 (当前方块类型, 当前方块旋转, i)
如果 (格值 == 0)
{
到循环尾
}
cell_x = 当前方块X + i % 4
cell_y = 当前方块Y + i / 4
// 仅在可视范围内写入(y>=0)
如果 (cell_y >= 0 && cell_y < 20 && cell_x >= 0 && cell_x < 10)
{
// ref: 基本类型数组模板类.置成员值 (vol_win_sys_pkg_081.md P10011)
游戏板.置成员值 (cell_y * 10 + cell_x, 类型号1to7)
}
}
返回 (0)
}
# 检测满行:仅检测哪些行已满,存入待消除行号数组,返回满行数
方法 检测满行 <类型 = 整数>
{
变量 满行数 <类型 = 整数>
满行数 = 0
变量 y <类型 = 整数>
y = 19
判断循环 (y >= 0)
{
变量 该行全满 <类型 = 逻辑型>
该行全满 = 真
变量 x <类型 = 整数>
x = 0
判断循环 (x < 10 && 该行全满)
{
如果 (游戏板.取成员 (y * 10 + x) == 0)
{
该行全满 = 假
}
x = x + 1
}
如果 (该行全满)
{
// 记录满行行号
待消除行号.置成员值 (满行数, y)
满行数 = 满行数 + 1
}
y = y - 1
}
返回 (满行数)
}
# 执行消行:将待消除行号中记录的行实际消除(上方下移)
方法 执行消行 <类型 = 整数>
{
变量 i <类型 = 整数>
变量 y <类型 = 整数>
变量 x <类型 = 整数>
变量 yy <类型 = 整数>
// 对每个待消除行执行消除(从下往上处理)
// 先对待消除行号排序(从大到小),避免消除时行号错乱
变量 tmp <类型 = 整数>
变量 j <类型 = 整数>
计次循环 (待消除行数 - 1)
{
i = 取循环索引 ()
j = 0
判断循环 (j < 待消除行数 - 1 - i)
{
如果 (待消除行号.取成员 (j) < 待消除行号.取成员 (j + 1))
{
tmp = 待消除行号.取成员 (j)
待消除行号.置成员值 (j, 待消除行号.取成员 (j + 1))
待消除行号.置成员值 (j + 1, tmp)
}
j = j + 1
}
}
// 逐行消除
计次循环 (待消除行数)
{
i = 取循环索引 ()
y = 待消除行号.取成员 (i)
// 上方所有行下移一行
yy = y
判断循环 (yy > 0)
{
x = 0
计次循环 (10)
{
x = 取循环索引 ()
游戏板.置成员值 (yy * 10 + x, 游戏板.取成员 ((yy - 1) * 10 + x))
}
yy = yy - 1
}
// 顶行清零
x = 0
计次循环 (10)
{
x = 取循环索引 ()
游戏板.置成员值 (x, 0)
}
}
返回 (0)
}
# 根据消除行数更新分数和等级
方法 更新分数等级 <类型 = 整数>
参数 本次消除行数 <类型 = 整数>
{
// 计分规则:1行100 / 2行300 / 3行500 / 4行800
如果 (本次消除行数 == 1)
{
分数 = 分数 + 100
}
否则 (本次消除行数 == 2)
{
分数 = 分数 + 300
}
否则 (本次消除行数 == 3)
{
分数 = 分数 + 500
}
否则 (本次消除行数 == 4)
{
分数 = 分数 + 800
}
已消行数 = 已消行数 + 本次消除行数
// 每消10行升1级
变量 新等级 <类型 = 整数>
新等级 = 已消行数 / 10 + 1
如果 (新等级 != 等级)
{
等级 = 新等级
// 指数加速曲线:500 * 0.85^(等级-1),下限50ms
变量 新周期 <类型 = 整数>
新周期 = 500
计次循环 (等级 - 1)
{
新周期 = 新周期 * 85 / 100
}
如果 (新周期 < 50)
{
新周期 = 50
}
游戏时钟.时钟周期 = 新周期
暂停前周期 = 新周期
}
更新标签显示 ()
返回 (0)
}
# 游戏结束处理
方法 游戏结束处理 <类型 = 整数>
{
游戏结束 = 真
游戏时钟.时钟周期 = 0// 停止时钟
// ref: 常用功能类.鸣叫 (vol_win_sys_pkg_081.md P16165)
鸣叫 (鸣叫声音类型.拒绝)
// 更新最高分
变量 是否破纪录 <类型 = 逻辑型>
是否破纪录 = 假
如果 (分数 > 最高分)
{
最高分 = 分数
是否破纪录 = 真
// ref: 文本类.写出文本文件 (vol_win_sys_pkg_081.md P8471)
写出文本文件 (到文本 (分数), "tetris_highscore.txt", -1, 文本编码类型.多字节)
}
更新标签显示 ()
// 提示:信息框(例程vol-飞机大战P19977参考)
变量 提示文本 <类型 = 文本型>
提示文本 = "游戏结束!\r\n最终分数:" + 到文本 (分数) + "\r\n等级:" + 到文本 (等级) + "\r\n累计消除行数:" + 到文本 (已消行数)
如果 (是否破纪录)
{
提示文本 = 提示文本 + "\r\n恭喜打破最高分纪录!"
}
信息框 (提示文本)
返回 (0)
}
# --------------------------------------------------------
# 3.5 渲染方法
# --------------------------------------------------------
# 根据类型号1~7返回对应颜色整数(刷子填充色)
# 颜色参考:颜色类成员常量 (vol_win_sys_pkg_081.md P11967起)
方法 取方块颜色 <类型 = 整数>
参数 类型号1to7 <类型 = 整数>
{
变量 颜色结果 <类型 = 整数>
颜色结果 = 颜色类.蓝色
如果 (类型号1to7 == 1)
{
颜色结果 = 取颜色值 (0, 255, 255)
}
否则 (类型号1to7 == 2)
{
颜色结果 = 颜色类.黄色
}
否则 (类型号1to7 == 3)
{
颜色结果 = 颜色类.紫色
}
否则 (类型号1to7 == 4)
{
颜色结果 = 颜色类.绿色
}
否则 (类型号1to7 == 5)
{
颜色结果 = 颜色类.红色
}
否则 (类型号1to7 == 6)
{
颜色结果 = 颜色类.橙黄色
}
否则
{
颜色结果 = 颜色类.蓝色
}
返回 (颜色结果)
}
# 绘制单个方格:在指定画板的 (格内X,格内Y) 位置画指定类型颜色的实心方块
# 每格30像素,四周留1像素做网格线,所以画 1~29(宽高28px)
方法 绘制单个方格 <类型 = 整数>
参数 画板对象 <类型 = 画板>
参数 格内X <类型 = 整数>
参数 格内Y <类型 = 整数>
参数 类型号1to7 <类型 = 整数>
{
变量 左x <类型 = 整数>
变量 顶y <类型 = 整数>
变量 右x <类型 = 整数>
变量 底y <类型 = 整数>
左x = 格内X * 30 + 1
顶y = 格内Y * 30 + 1
右x = 格内X * 30 + 29
底y = 格内Y * 30 + 29
// 设置刷子颜色 = 方块颜色(填充内部)
// ref: 画板.刷子颜色属性写 (vol_win_sys_pkg_026.md P4486)
画板对象.刷子颜色 = 取方块颜色 (类型号1to7)
// 设置画笔颜色 = 深**(描边框)
// ref: 画板.画笔颜色属性写 (vol_win_sys_pkg_026.md P4450)
画板对象.画笔颜色 = 颜色类.**
// 画笔粗细=1像素
画板对象.画笔粗细 = 1
// ref: 画板.画矩形 (vol_win_sys_pkg_026.md P4689)
画板对象.画矩形 (左x, 顶y, 右x, 底y)
返回 (0)
}
# 更新分数/等级/最高分标签文字
方法 更新标签显示 <类型 = 整数>
{
// ref: 文本类.到文本(例程普遍使用)
标签_分数.标题 = "分数:" + 到文本 (分数)
标签_等级.标题 = "等级:" + 到文本 (等级)
标签_最高分.标题 = "最高分:" + 到文本 (最高分)
返回 (0)
}
# 重绘整个游戏画面
方法 重绘游戏画面 <类型 = 整数>
{
变量 x <类型 = 整数>
变量 y <类型 = 整数>
变量 板值 <类型 = 整数>
// 第一步:清除游戏画板
// ref: 画板.清除 (vol_win_sys_pkg_026.md P4588)
游戏画板.清除 ()
// 第二步:画网格线(浅**,含外边框共竖11条+横21条)
游戏画板.画笔颜色 = 颜色类.浅**
游戏画板.画笔粗细 = 0
// 竖线:x=0,30,60,...,270,300 共11条(含左右外边框)
计次循环 (11)
{
x = 取循环索引 ()
游戏画板.画直线 (x * 30, 0, x * 30, 600)
}
// 横线:y=0,30,60,...,570,600 共21条(含上下外边框)
计次循环 (21)
{
y = 取循环索引 ()
游戏画板.画直线 (0, y * 30, 300, y * 30)
}
// 第三步:遍历游戏板,画所有已落下的方块
// 如果正在消行闪烁且显示白色,满行画白色
变量 i <类型 = 整数>
计次循环 (200)
{
变量 idx <类型 = 整数>
idx = 取循环索引 ()
板值 = 游戏板.取成员 (idx)
如果 (板值 != 0)
{
x = idx % 10
y = idx / 10
// 检查该行是否在待消除行号中
变量 是满行 <类型 = 逻辑型>
是满行 = 假
如果 (正在消行 && 闪烁显示白色)
{
计次循环 (待消除行数)
{
i = 取循环索引 ()
如果 (待消除行号.取成员 (i) == y)
{
是满行 = 真
}
}
}
如果 (是满行)
{
// 画白色方块
游戏画板.刷子颜色 = 颜色类.白色
游戏画板.画笔颜色 = 颜色类.**
游戏画板.画笔粗细 = 1
游戏画板.画矩形 (x * 30 + 1, y * 30 + 1, x * 30 + 29, y * 30 + 29)
}
否则
{
绘制单个方格 (游戏画板, x, y, 板值)
}
}
}
// 第四步:画降落阴影 + 当前方块(若游戏未结束且未在消行动画中)
如果 (游戏结束 == 假 && 正在消行 == 假)
{
// 4a: 计算阴影位置(方块落底后的Y坐标)
变量 影子Y <类型 = 整数>
影子Y = 当前方块Y
判断循环 (检测碰撞 (当前方块类型, 当前方块旋转, 当前方块X, 影子Y + 1) == 假)
{
影子Y = 影子Y + 1
}
// 4b: 画阴影(仅当影子Y > 当前方块Y时才画)
如果 (影子Y > 当前方块Y)
{
变量 格值 <类型 = 整数>
变量 cell_x <类型 = 整数>
变量 cell_y <类型 = 整数>
计次循环 (16)
{
i = 取循环索引 ()
格值 = 取形状格值 (当前方块类型, 当前方块旋转, i)
如果 (格值 == 0)
{
到循环尾
}
cell_x = 当前方块X + i % 4
cell_y = 影子Y + i / 4
如果 (cell_y >= 0 && cell_x >= 0 && cell_x < 10 && cell_y < 20)
{
// 画浅**半透明效果(用浅**填充+**边框)
游戏画板.刷子颜色 = 颜色类.浅**
游戏画板.画笔颜色 = 颜色类.**
游戏画板.画笔粗细 = 1
游戏画板.画矩形 (cell_x * 30 + 1, cell_y * 30 + 1, cell_x * 30 + 29, cell_y * 30 + 29)
}
}
}
// 4c: 画当前下落方块
{
变量 格值2 <类型 = 整数>
变量 cell_x2 <类型 = 整数>
变量 cell_y2 <类型 = 整数>
计次循环 (16)
{
i = 取循环索引 ()
格值2 = 取形状格值 (当前方块类型, 当前方块旋转, i)
如果 (格值2 == 0)
{
到循环尾
}
cell_x2 = 当前方块X + i % 4
cell_y2 = 当前方块Y + i / 4
如果 (cell_y2 >= 0 && cell_x2 >= 0 && cell_x2 < 10 && cell_y2 < 20)
{
绘制单个方格 (游戏画板, cell_x2, cell_y2, 当前方块类型 + 1)
}
}
}
}
// 第五步:同步更新预览画板
重绘预览画面 ()
返回 (0)
}
# 重绘下一方块预览画面
方法 重绘预览画面 <类型 = 整数>
{
预览画板.清除 ()
// 画预览网格线(4×4格,30像素/格,共120×120像素,含外边框共5竖5横)
变量 px <类型 = 整数>
变量 py <类型 = 整数>
预览画板.画笔颜色 = 颜色类.浅**
预览画板.画笔粗细 = 0
// 竖线:x=0,30,60,90,120 共5条(含左右外边框)
计次循环 (5)
{
px = 取循环索引 ()
预览画板.画直线 (px * 30, 0, px * 30, 120)
}
// 横线:y=0,30,60,90,120 共5条(含上下外边框)
计次循环 (5)
{
py = 取循环索引 ()
预览画板.画直线 (0, py * 30, 120, py * 30)
}
// 下一方块居中绘制
变量 i <类型 = 整数>
变量 格值 <类型 = 整数>
变量 gx <类型 = 整数>
变量 gy <类型 = 整数>
计次循环 (16)
{
i = 取循环索引 ()
格值 = 取形状格值 (下一方块类型, 0, i)
如果 (格值 == 0)
{
到循环尾
}
gx = i % 4
gy = i / 4
绘制单个方格 (预览画板, gx, gy, 下一方块类型 + 1)
}
返回 (0)
}
# --------------------------------------------------------
# 3.6 事件接收方法
# --------------------------------------------------------
# 主窗口创建完毕事件:初始化游戏 + 获取焦点
# 事件签名参考:例程 vol-选择夹2 P186 / vol-文件浏览 P482
# 方法 {类名}_创建完毕 <接收事件 类型 = 整数>
# 参数 来源对象 <类型 = {类名}>
# 参数 标记值 <类型 = 整数>
方法 我的主窗口_创建完毕 <接收事件 类型 = 整数>
参数 来源对象 <类型 = 我的主窗口>
参数 标记值 <类型 = 整数>
{
初始化游戏 ()
重绘游戏画面 ()
重绘预览画面 ()
// 获取焦点到游戏画板,确保能收到键盘事件
// ref: 窗口组件.获取焦点 (vol_win_sys_pkg_026.md P10120)
// 画板默认不接收焦点,需配合"可停留焦点=真"属性
游戏画板.获取焦点 ()
返回 (0)
}
# 时钟周期事件:游戏时钟和闪烁时钟共用,通过来源对象区分
# ref: 时钟例程 P564 事件签名(两个时钟共用一个事件方法)
方法 时钟_周期事件 <接收事件 类型 = 整数>
参数 来源对象 <类型 = 时钟>
参数 标记值 <类型 = 整数>
{
如果 (来源对象 == 游戏时钟)
{
// 游戏时钟:方块下落
如果 (游戏结束 == 假 && 是否暂停 == 假 && 正在消行 == 假)
{
方块下落一格 ()
重绘游戏画面 ()
}
}
否则 (来源对象 == 闪烁时钟)
{
// 闪烁时钟:消行动画
闪烁计数 = 闪烁计数 + 1
如果 (闪烁显示白色)
{
闪烁显示白色 = 假
}
否则
{
闪烁显示白色 = 真
}
重绘游戏画面 ()
// 4次切换(2次闪烁)后执行消行
如果 (闪烁计数 >= 4)
{
闪烁时钟.时钟周期 = 0
正在消行 = 假
执行消行 ()
更新分数等级 (待消除行数)
// ref: 常用功能类.鸣叫 (vol_win_sys_pkg_081.md P16165)
鸣叫 (鸣叫声音类型.感叹)
// 生成新方块
如果 (生成新方块 () == 假)
{
游戏结束处理 ()
}
// 恢复游戏时钟
游戏时钟.时钟周期 = 暂停前周期
重绘游戏画面 ()
}
}
返回 (0)
}
# 键盘按下事件:处理方向键 + 空格
# ref: 窗口组件.按下某键 (vol_win_sys_pkg_026.md P9905)
# 按键码:左光标键/右光标键/下光标键/上光标键/空格键
# (vol_win_sys_pkg_081.md P13056/P13063/P13069/P13076/P13025)
方法 窗口组件_按下某键 <接收事件 类型 = 整数>
参数 来源对象 <类型 = 窗口组件>
参数 标记值 <类型 = 整数>
参数 键代码 <类型 = 整数>
参数 辅Zhu键状态 <类型 = 整数>
{
如果 (游戏结束 == 假 && 是否暂停 == 假 && 正在消行 == 假)
{
如果 (键代码 == 按键码.左光标键)
{
方块左移 ()
重绘游戏画面 ()
}
否则 (键代码 == 按键码.右光标键)
{
方块右移 ()
重绘游戏画面 ()
}
否则 (键代码 == 按键码.上光标键)
{
方块旋转 ()
重绘游戏画面 ()
}
否则 (键代码 == 按键码.下光标键)
{
方块下落一格 ()
重绘游戏画面 ()
}
否则 (键代码 == 按键码.空格键)
{
方块直接落底 ()
重绘游戏画面 ()
}
}
// 返回1:表示本事件已处理完成,不再传递给父窗口
返回 (1)
}
# 按钮被单击事件:重新开始 / 暂停切换
# ref: 所有按钮事件签名(如时钟例程 P580)
方法 按钮_被单击 <接收事件 类型 = 整数>
参数 来源对象 <类型 = 按钮>
参数 标记值 <类型 = 整数>
{
如果 (来源对象 == 按钮_重来)
{
初始化游戏 ()
重绘游戏画面 ()
重绘预览画面 ()
游戏画板.获取焦点 ()
}
否则 (来源对象 == 按钮_暂停)
{
如果 (游戏结束 == 假)
{
如果 (是否暂停)
{
// 恢复
是否暂停 = 假
游戏时钟.时钟周期 = 暂停前周期
按钮_暂停.标题 = "暂停"
}
否则
{
// 暂停
暂停前周期 = 游戏时钟.时钟周期
游戏时钟.时钟周期 = 0
是否暂停 = 真
按钮_暂停.标题 = "继续"
}
}
游戏画板.获取焦点 ()
}
返回 (0)
}
}
用哪个技能书的吗 白雪皑皑0306 发表于 2026-8-8 12:26
用哪个技能书的吗
以官方资料为主,自己编写技能书。 精品,学习学习:) 完成度很高吖,比我用DeepSeek写出来得效果好耶 不错,学习了 厉害了 直接让Trae用主流语言和架构写会更快。。。
页:
[1]