使用HTA与VML实现经典打砖块游戏的技术实践
本文介绍一个基于HTA(HTML Application)和VML(Vector Markup Language)编写的经典打砖块游戏——HTAnoid。该游戏由Jean-Luc Antoine于2002年开发,无需任何外部图片即可实现图形界面,完全通过VML绘制砖块、挡板、球体等元素。对于想了解HTA应用开发、VML矢量图形以及VBScript事件驱动编程的开发者,这是一个完整可运行的参考案例。<html xmlns:v="urn:schemas-microsoft-com:vml" XMLNS:t="urn:schemas-microsoft-com:time">
<head>
<HTA:APPLICATION APPLICATIONNAME="HTAnoid" BORDER="thick" BORDERSTYLE="raised" CAPTION="yes" CONTEXTMENU="yes" ID="oHTA" alert(oHTA.applicationName); INNERBORDER="yes" MAXIMIZEBUTTON="yes" MINIMIZEBUTTON="yes" NAVIGABLE="no" SCROLL="no" SCROLLFLAT="no" SELECTION="yes" SHOWINTASKBAR="yes" SINGLEINSTANCE="no" SYSMENU="yes" VERSION="0.5" WINDOWSTATE="normal">
一、HTA声明与窗口配置
上面的HTA声明设置了应用名称为"HTAnoid",边框风格为raised,显示标题栏,禁用导航和滚动,窗口不可调整大小。注意代码中有一段冗余的alert,原意是演示访问applicationName属性,实际运行时可以忽略或删除。窗口初始状态为normal,后续通过VBScript的Init过程重新定位和调整大小:窗口移动到屏幕中央(screen.Width/2-200,0),尺寸设为400×484像素。
二、样式与VML初始化
在<style>中定义了VML的行为(behavior:url(#default#VML)),同时设置body背景色为深蓝(#003159),表格和单元格边框为0像素闪避风格。VML元素(如<v:rect>、<v:oval>、<v:group>、<v:roundrect>)用于绘制游戏界面:
- 顶部渐变背景条
- 左右两侧装饰栏(使用虚线描边和渐变填充)
- 球体(id="balle",椭圆形,位置绝对定位)
- 挡板(id="Raquette",group中包含圆角矩形和渐变矩形)
三、游戏逻辑与VBScript实现
游戏核心代码位于VBScript中,定义常量、全局变量并实现子过程。
1. 常量与变量
- C_ScreenW = 400(屏幕宽度)
- C_NbX = 12,C_NbY = 10(砖块横向12列,纵向10行,但当前关卡仅生成6行)
- Terrain(10,12) 二维数组存储砖块剩余击打次数,初始为0
- RaqX, RaqW, BalleX, BalleY 表示挡板左坐标、宽度、球体坐标
- DirBX, DirBY 球移动方向和速度
- BordDroit = 368(屏幕宽减32,右侧边界)
2. Init过程(页面加载时触发)
- 移动窗口并调整大小
- 计算挡板实际宽度(RaqW = 挡板style.width值转换为像素,约48px,因为30pc=30*1.6=48,但原文使用Replace("pc","")再乘1.6,实际得到48)
- 绘制三条生命指示(使用VML group + roundrect + rect,红色外框、蓝色渐变填充)
- 调用DrawLevel(1)生成第一关砖块
- 设置Playing = False,然后通过setTimeout启动每10ms一次的Gere循环
3. DrawLevel(n)子过程
- 重置Terrain数组
- 根据关卡(目前只有case 1)生成6行砖块:使用insertAdjacentHTML在前面插入TABLE元素,每个砖块绝对定位,top从102开始,每行递增14px,left从16开始每列27px,宽度25px高度10px,颜色依次为silver(银)、red(红)、yellow(黄)、blue(蓝)、magenta(品红)、lightgreen(浅绿)
- 设置初始球坐标BalleY=400,随机方向DirBX(1~3),DirBY(-4~-1,向上)
4. Document_onMouseMove事件
- 鼠标移动时更新挡板位置:RaqX = 鼠标clientX - 挡板一半宽度
- 左边界limit为16,右边界limit为BordDroit - RaqW
- 更新Raquette.style.Left
- 如果Playing=False,球跟随挡板中心(BalleX = RaqX + RaqW/2)
5. Document_onKeyPress事件
- 按P键弹出"pause"提示(暂停功能未完全实现,仅示例)
6. Document_onClick事件
- 点击页面开始游戏:设置Playing = True
7. Gere子过程(游戏主循环)
- 每10ms调用一次,如果Playing=True,则:
- 根据DirBX, DirBY计算球的新位置
- 边界碰撞检测:左右边界(16和BordDroit-10)弹回;上下边界(50和400)弹回
- 更新球的位置(Balle.style.Left和Top)
- 如果Playing=False,则在标题栏显示当前时间(作为简单的屏幕刷新标志)
四、运行与扩展建议
将上述完整代码保存为.hta文件(如htanoid.hta),在Windows系统下双击即可运行(需IE6以上支持,Windows 10/11需启用IE兼容模式或使用旧版Edge的IE模式)。当前版本仅包含一关,球碰到底部时游戏并未结束(未实现生命减少与游戏结束逻辑),挡板与球的碰撞检测也未实现(球只会上下左右反弹,不会因挡板改变方向)。原作者鼓励开发者继续完善:
- 添加球与砖块的碰撞检测,当球碰到砖块时消除该砖块并更新分数
- 实现生命系统:球落到底部时减少一条生命,生命归零则游戏结束
- 增加关卡切换和多级砖块
- 完善暂停功能
- 优化图形:当前使用VML,在后续系统中可改用Canvas或SVG
五、技术影响与分析
HTA是微软在Windows中推出的桌面应用技术,允许用HTML+脚本创建具有系统权限的应用程序。本例展示了HTA与VML结合实现无外部资源二维游戏的能力。虽然HTA已逐渐被UWP和现代Web技术取代,但对于学习早期浏览器编程技巧、理解VML矢量绘制,以及研究如何利用DHTML构建互动应用仍有参考价值。VML的gradient和stroke等特性在当时提供了丰富的视觉效果,而insertAdjacentHTML方法动态创建元素也是一种高效方式。
注意:在现代浏览器中直接运行此代码可能无法显示VML图形,建议在IE兼容模式下测试。若需跨平台,可考虑将逻辑迁移至HTML5 Canvas或WebGL。
完整代码可参考原文链接或从本文代码片段组合得到。开发者可以基于此模板快速搭建自己的HTA小游戏。
Re: 使用HTA与VML实现经典打砖块游戏的技术实践
感谢分享这个经典又有年代感的 HTAnoid 实现,让我想起以前用 HTA 写小工具和 VML 画矢量图形的日子。代码结构清晰,从窗口配置、VML 元素布局到 VBScript 事件驱动,完整展示了早期 Windows 桌面上“免图片”做游戏的一种思路。 几个细节想问一下:当前 Gere 循环里只有墙壁反弹逻辑,如果后续要添加球和砖块碰撞检测,是否打算通过遍历 Terrain 数组并比较球坐标与每个砖块的 left/top/宽高来实现?另外,你提到“球碰到底部游戏未结束”,是不是故意留作扩展,还是初始版本设计如此?对于现代 Windows 的兼容性,有没有遇到 IE 模式下的性能或渲染问题? 不管怎样,这个作品作为教学案例非常有价值,也期待你后续的完善。Re: 使用HTA与VML实现经典打砖块游戏的技术实践
好经典的怀旧技术文!HTA + VML 的组合现在很少见了,但当年在 IE 环境下确实能做出不少轻量级的小游戏。楼主把声明、样式、脚本逻辑都拆解得挺清楚,尤其是砖块的动态生成和事件驱动循环,对入门者来说是个不错的完整案例。 不过楼主也提到了几个未完成的关键点——挡板与球的碰撞检测还没写,球到底部也不会扣生命,这其实是打砖块最核心的机制。有兴趣的话可以自己补上,比如在 Gere 循环里加个判断:当球底部碰到挡板顶部且水平位置在挡板范围内时,让 DirBY 反转并调整 DirBX 根据碰撞位置偏移;如果球底超过 400(底部边界),就减少生命并重置球到挡板中央。这样游戏就能真正玩起来了。 另外,现在 Windows 10/11 要跑 .hta 文件可能需要启用 IE 模式或者用旧版 Edge 的兼容性视图,不然 VML 可能不渲染,这一点也想提醒其他打算试玩的朋友。感谢楼主的分享,期待后续能补完碰撞逻辑的版本!Re: 使用HTA与VML实现经典打砖块游戏的技术实践
这篇文章很经典!HTA + VML 的组合确实是一种老派的 Windows 桌面应用快速开发方案,不需要额外图片就能画出矢量图形,对当年想学习事件驱动和 DOM 操作的开发者很有参考价值。你提到的游戏逻辑残缺部分(碰撞检测、生命管理)也可以作为很好的练习题目:补全挡板碰撞(根据球与挡板的相对位置改变反弹方向)、砖块击中计数(数组状态更新)、生命数和游戏结束判断,以及随机球初始角度。另外建议补充一个键盘方向键控制挡板移动的选项(配合 mouseMove 双控制),更符合经典打砖块的操作习惯。感谢分享这段怀旧但仍有学习意义的代码!
页:
[1]