在这里我们将构造一个基于HT for Web的HTML5+JavaScript来实现汉诺塔游戏。

汉诺塔的游戏规则及递归算法分析请参考http://en.wikipedia.org/wiki/Tower_of_Hanoi

知道了汉诺塔的规则和算法,现在就开始创建元素。用HT for Web(http://www.hightopo.com)现有的3D模板创建底盘和3根柱子不是问题,问题是要创建若干个中空的圆盘。一开始的想法是:创建一个圆柱体,将圆柱体的上下两端隐藏,设置柱面的宽度来实现圆盘的效果,经过多次尝试并查阅相关api文档,发现柱面是没有厚度的,改方法不可行。

后来在HT for Web自定义3D模型的WebGL应用(http://www.hightopo.com/blog/381.html)受到启发,圆盘的形成就是在xy平面上的一个矩形,根据y轴旋转一周产生的,通过查阅相关文档,最总决定采用ht.Default.createRingModel方法来创建圆盘模型,然后在创建node的时候通过shape3d属性引用创建好的模型。

在逻辑实现上,采用了栈的先进后出的原理,对圆柱上的圆盘做顺序控制,确保每次移动的圆盘都是最小的圆盘。

在算法上,采用的是递归算法,通过递归算法,将搬迁过程一步一步记录下来,再采用堆的原理一步一步地执行搬迁过工作。

http://v.youku.com/v_show/id_XODcwMTk4MDI4.html

  1. var barNum = 5, // 圆盘个数
  2. cylinderHeight = barNum * 20 + 40, // 圆柱高度
  3. barrelMinORadius = 50, // 圆盘最大外半径
  4. barrelIRadius = 10, // 圆盘内半径
  5. poorRadius = 20, // 圆盘外半径差值
  6. barrelMaxORadius = barrelMinORadius + barNum * poorRadius,
  7. barrelHeight = 20, // 圆盘高
  8. barPadding = 20, // 柱体之间的间隙
  9. floorX = barrelMaxORadius * 6 + barPadding * 4, // 底盘长
  10. floorY = 20, // 底盘高
  11. floorZ = 2 * barrelMaxORadius + barPadding * 2, // 底盘宽
  12. // 柱体集
  13. positions = [
  14. {
  15. barrels: [],
  16. position: [-(2*barrelMaxORadius + barPadding), cylinderHeight / 2 + 1, 0]
  17. },{
  18. barrels: [],
  19. position: [0, cylinderHeight / 2 + 1, 0]
  20. },{
  21. barrels: [],
  22. position: [(2*barrelMaxORadius + barPadding), cylinderHeight / 2 + 1, 0]
  23. }
  24. ],
  25. runOrder = [], // 圆盘移动顺序集
  26. // 动画参数
  27. params = {
  28. delay: 10,
  29. duration: 500,
  30. easing: Easing['easeBoth']
  31. };
  32.  
  33. /**
  34. * 初始化程序
  35. * */
  36. function init(){
  37. dataModel = new ht.DataModel();
  38. g3d = new ht.graph3d.Graph3dView(dataModel);
  39. view = g3d.getView();
  40. view.className = 'main';
  41. document.body.appendChild(view);
  42. window.addEventListener('resize', function (e) {
  43. g3d.invalidate();
  44. }, false);
  45.  
  46. g3d.setEye([0, cylinderHeight * 2, floorX * sin(2*PI/360*60)]);
  47.  
  48. // 初始化节点
  49. initNodes();
  50.  
  51. moveAnimation();
  52. }
  53.  
  54. /**
  55. * 构造游戏移动队列
  56. * diskQuantity:圆盘个数
  57. * positionA:起点
  58. * positionB:中转点
  59. * positionC:终点
  60. * */
  61. function buildRunOrder(diskQuantity, positionA, positionB, positionC){
  62. if (diskQuantity == 1) {
  63. runOrder.push([positionA, positionC]);
  64. } else {
  65. buildRunOrder(diskQuantity - 1, positionA, positionC, positionB);
  66. buildRunOrder(1, positionA, positionB, positionC);
  67. buildRunOrder(diskQuantity - 1, positionB, positionA, positionC);
  68. }
  69. }
  70.  
  71. /**
  72. * 移动动画
  73. * positionA:起点
  74. * positionC:终点
  75. * */
  76. function moveAnimation(positionA, positionC){
  77. if(!positionA){
  78. var poses = runOrder.shift();
  79. if(!poses){
  80. setTimeout(reset, 500);
  81. }else{
  82. moveAnimation(positions[poses[0]], positions[poses[1]]);
  83. }
  84. }else {
  85. var barrel = positionA.barrels.pop();
  86. var position = positionC.cylinder.p3(),
  87. barPos = barrel.getPosition3d();
  88. position[1] = position[1] + floorY + barrelHeight * positionC.barrels.length - cylinderHeight / 2;
  89. setPolylinePoints(polyline, barPos, position);
  90. params.action = function (v, t) {
  91. var length = g3d.getLineLength(polyline),
  92. offset = g3d.getLineOffset(polyline, length * v),
  93. point = offset.point,
  94. px = point.x,
  95. py = point.y,
  96. pz = point.z;
  97. barrel.p3(px, py, pz);
  98. };
  99. params.finishFunc = function () {
  100. positionC.barrels.push(barrel);
  101. var poses = runOrder.shift();
  102. if (!poses) {
  103. moveAnimation();
  104. } else {
  105. moveAnimation(positions[poses[0]], positions[poses[1]]);
  106. }
  107. };
  108. anim = ht.Default.startAnim(params);
  109. }
  110. }
  111.  
  112. /**
  113. * 重置游戏
  114. * */
  115. function reset(){
  116. if(positions[0].barrels.length == 0){
  117. positions[0].barrels = positions[2].barrels;
  118. }
  119. positions[2].barrels = [];
  120. for(var i = 0, len = positions[0].barrels.length; i < len; i++){
  121. var pos = positions[0].cylinder.p3();
  122. pos[1] = pos[1] + floorY + i * barrelHeight - cylinderHeight / 2;
  123. positions[0].barrels[i].p3(pos);
  124. }
  125. buildRunOrder(barNum, 0, 1, 2);
  126. setTimeout(moveAnimation, 500);
  127. }
  128.  
  129. /**
  130. * 初始化节点
  131. * */
  132. function initNodes(){
  133. // 底盘
  134. floor = createNode([0, floorY / 2, 0], [floorX, floorY, floorZ]).s({
  135. 'shape3d': 'box',
  136. '3d.movable': false
  137. });
  138.  
  139. // 创建柱子
  140. for(var i = 0, len = 3; i < len; i++){
  141. positions[i].cylinder = createNode(positions[i].position, [20, cylinderHeight, 20], floor).s({
  142. 'shape3d': 'cylinder',
  143. 'shape3d.color': '#E5BB77',
  144. '3d.movable': false
  145. });
  146. }
  147.  
  148. // 创建圆盘
  149. createBarrels(barNum, positions[0].cylinder);
  150.  
  151. // 创建圆盘运行轨迹
  152. polyline = new ht.Polyline();
  153. polyline.setSegments([1, 2, 4, 2]);
  154. polyline.s({
  155. 'shape.background': null,
  156. 'shape.border.color': 'rgba(0,0,0,0)',
  157. 'shape.border.gradient.color': 'rgba(0,0,0,0)',
  158. 'shape.border.pattern': [20, 10],
  159. 'shape3d.resolution': 50
  160. });
  161. dataModel.add(polyline);
  162. }
  163.  
  164. /**
  165. * 设置路线节点
  166. * */
  167. function setPolylinePoints(polyline, from, to){
  168. polyline.setPoints([
  169. {x: from[0], y: from[2], e: from[1]},
  170. {x: from[0], y: from[2], e: cylinderHeight},
  171. {x: from[0], y: from[2], e: cylinderHeight + 60},
  172. {x: to[0], y: to[2], e: cylinderHeight + 60},
  173. {x: to[0], y: to[2], e: cylinderHeight},
  174. {x: to[0], y: to[2], e: to[1]}
  175. ]);
  176. return polyline;
  177. }
  178.  
  179. /**
  180. * 创建圆盘
  181. * barNum:圆盘个数
  182. * host:吸附节点
  183. * */
  184. function createBarrels(barNum, host){
  185. // 圆盘初始x位置
  186. var pos = host.p3();
  187.  
  188. for(var i = barNum, j = 0; i > 0; i--, j++){
  189. pos[1] = barrelHeight * j + floorY;
  190. positions[0].barrels.push(createBarrel(pos, [1, barrelHeight, 1], barrelMinORadius + i*poorRadius, barrelIRadius, host).s({
  191. 'shape3d.color': randomColor(),
  192. '3d.movable': false
  193. }));
  194. }
  195. }
  196.  
  197. /**
  198. * 创建节点
  199. * p3:节点位置
  200. * s3:节点大小
  201. * host:吸附节点
  202. * */
  203. function createNode(p3, s3, host){
  204. var node = new ht.Node();
  205. node.p3(p3);
  206. node.s3(s3);
  207. node.setHost(host);
  208. node.s({
  209. 'wf.visible': 'selected',
  210. 'wf.color': '#FF6B10',
  211. 'wf.width': 2,
  212. 'wf.short': true
  213. });
  214. dataModel.add(node);
  215. return node;
  216. }
  217.  
  218. /**
  219. * 创建空心圆柱
  220. * p3:圆桶位置
  221. * s3:圆桶大小
  222. * oRadius:圆桶外径
  223. * iRadius:圆桶内径
  224. * host:吸附节点
  225. * */
  226. function createBarrel(p3, s3, oRadius, iRadius, host){
  227. return createNode(p3, s3, host).s({
  228. 'shape3d': ht.Default.createRingModel([
  229. oRadius, 1,
  230. oRadius, 0,
  231. iRadius, 0,
  232. iRadius, 1,
  233. oRadius, 1
  234. ], null, 20, false, false, 70)
  235. });
  236. }

HT for Web 3D游戏设计设计--汉诺塔(Towers of Hanoi)的更多相关文章

  1. 汉诺塔问题(Hanoi Tower)递归算法解析(Python实现)

    汉诺塔问题 1.问题来源:汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从上往下从小到大顺序摞着64片黄金圆盘.上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根 ...

  2. 汉诺塔 Tower of Hanoi

    假设柱子标为A,B.C.要由A搬至C,在仅仅有一个盘子时,就将它直接搬至C:当有两个盘子,就将B作为辅助柱.假设盘数超过2个.将第二个下面的盘子遮起来,就非常easy了.每次处理两个盘子,也就是:A- ...

  3. what' the python之递归函数、二分算法与汉诺塔游戏

    what's the 递归? 递归函数的定义:在函数里可以再调用函数,如果这个调用的函数是函数本身,那么就形成了一个递归函数. 递归的最大深度为997,这个是程序强制定义的,997完全可以满足一般情况 ...

  4. 汉诺塔 Hanoi Tower

    电影<猩球崛起>刚开始的时候,年轻的Caesar在玩一种很有意思的游戏,就是汉诺塔...... 汉诺塔源自一个古老的印度传说:在世界的中心贝拿勒斯的圣庙里,一块黄铜板上插着三支宝石针.印度 ...

  5. 【Python实践-3】汉诺塔问题递归求解(打印移动步骤及计算移动步数)

    # -*- coding: utf-8 -*- #汉诺塔移动问题 # 定义move(n,a,b,c)函数,接受参数n,表示3个柱子A.B.C中第1个柱子A的盘子数量 # 然后打印出把所有盘子从A借助B ...

  6. 学C记录(理解递归问题之汉诺塔)

    汉诺游戏规则如下: 1.有三根相邻的柱子,标号为A,B,C. 2.A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘. 3.现在把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘 ...

  7. 【学习】Python解决汉诺塔问题

    参考文章:http://www.cnblogs.com/dmego/p/5965835.html   一句话:学程序不是目的,理解就好:写代码也不是必然,省事最好:拿也好,查也好,解决问题就好!   ...

  8. c++汉诺塔相关知识总结1

    困扰已久,难以攻克的汉诺塔总结来啦 Part One 汉诺塔到底是什么呢? 汉诺塔(Tower of Hanoi)源于印度传说中,大梵天创造世界时造了三根金钢石柱子,其中一根柱子自底向上叠着64片黄金 ...

  9. 基于HTML5的WebGL设计汉诺塔3D游戏

    在这里我们将构造一个基于HT for Web的HTML5+JavaScript来实现汉诺塔游戏. http://hightopo.com/demo/hanoi_20151106/index.html ...

随机推荐

  1. 细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一

    细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一(转) ADO.NET Entity Framework        ADO.NET Entity Framework 是微软以 ADO.N ...

  2. 初中级Javascript程序员必修学习目录

    很多人总感觉javascript无法入门,笔者在这里写一下自己的学习过程,以及个人认为的最佳看书过程,只要各位能按照本人所说步骤走下去,不用很长时间,坚持个3个月,你的js层级会提高一个档次,无他,唯 ...

  3. linux 2.6 驱动笔记(二)

    字符设备驱动 linux 2.6的字符驱动由cdev结构体描述,具体参考globalmem例子,代码主要分以下几部分: 1. 定义一个字符类型设备驱动的结构体 struct globalmem_dev ...

  4. EF6(CodeFirst)+MySql开发遇到的坑

    最近一不小心偷个懒就已经过了好几个月了,真是惭愧惭愧,出来混终究是要还的,我还是把”脱坑指南“写完吧,-_-~~.点我打开上篇博客 0x001.架构名”dbo”の殇 坑之首也,当提架构名,在mssql ...

  5. Flume采集处理日志文件

    Flume简介 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据:同时,Flume提供对数据 ...

  6. 走进AngularJs(五)自定义指令----(下)

    自定义指令学习有段时间了,学了些纸上谈兵的东西,还没有真正的写个指令出来呢...所以,随着学习的接近尾声,本篇除了介绍剩余的几个参数外,还将动手结合使用各参数,写个真正能用的指令出来玩玩. 我们在自定 ...

  7. JavaScript思维导图—正则表达式

    JavaScript思维导图-来自@王子墨http://julying.com/blog/the-features-of-javascript-language-summary-maps/

  8. java中的Static class

    Java中的类可以是static吗?答案是可以.在java中我们可以有静态实例变量.静态方法.静态块.类也可以是静态的. java允许我们在一个类里面定义静态类.比如内部类(nested class) ...

  9. C语言再学习之内存对齐

    昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵.一番google后,终于明白,这个宏的作用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧: #define _I ...

  10. C#入门基础三

    封装:简化用户接口,隐藏实现细节. get{return 属性值:} set{属性值 = value:} 继承:子类继承父类所有非私有成员.继承具有传递性,单根性. 隐式继承:用引号(:)实现. 显示 ...