Chinaunix首页 | 论坛 | 博客
  • 博客访问: 951073
  • 博文数量: 110
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1997
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-15 22:39
个人简介

HT for Web 3D. HT for modern web and mobile app development. www.hightopo.com

文章分类

全部博文(110)

文章存档

2020年(1)

2018年(28)

2017年(21)

2016年(10)

2015年(28)

2014年(19)

2013年(3)

我的朋友

分类: HTML5

2015-01-12 19:06:18

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

汉诺塔的游戏规则及递归算法分析请参考。

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

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

 

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

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

 

 

所有代码和运行效果如下: 

点击(此处)折叠或打开

  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. function init(){
  36.     dataModel = new ht.DataModel();
  37.     g3d = new ht.graph3d.Graph3dView(dataModel);
  38.     view = g3d.getView();
  39.     view.className = 'main';
  40.     document.body.appendChild(view);
  41.     window.addEventListener('resize', function (e) {
  42.         g3d.invalidate();
  43.     }, false);

  44.     g3d.setEye([0, cylinderHeight * 2, floorX * sin(2*PI/360*60)]);

  45.     // 初始化节点
  46.     initNodes();

  47.     moveAnimation();
  48. }

  49. /**
  50.  * 构造游戏移动队列
  51.  * diskQuantity:圆盘个数
  52.  * positionA:起点
  53.  * positionB:中转点
  54.  * positionC:终点
  55.  * */
  56. function buildRunOrder(diskQuantity, positionA, positionB, positionC){
  57.     if (diskQuantity == 1) {
  58.         runOrder.push([positionA, positionC]);
  59.     } else {
  60.         buildRunOrder(diskQuantity - 1, positionA, positionC, positionB);
  61.         buildRunOrder(1, positionA, positionB, positionC);
  62.         buildRunOrder(diskQuantity - 1, positionB, positionA, positionC);
  63.     }
  64. }

  65. /**
  66.  * 移动动画
  67.  * positionA:起点
  68.  * positionC:终点
  69.  * */
  70. function moveAnimation(positionA, positionC){
  71.     if(!positionA){
  72.         var poses = runOrder.shift();
  73.         if(!poses){
  74.             setTimeout(reset, 500);
  75.         }else{
  76.             moveAnimation(positions[poses[0]], positions[poses[1]]);
  77.         }
  78.     }else {
  79.         var barrel = positionA.barrels.pop();
  80.         var position = positionC.cylinder.p3(),
  81.             barPos = barrel.getPosition3d();
  82.         position[1] = position[1] + floorY + barrelHeight * positionC.barrels.length - cylinderHeight / 2;
  83.         setPolylinePoints(polyline, barPos, position);
  84.         params.action = function (v, t) {
  85.             var length = g3d.getLineLength(polyline),
  86.                 offset = g3d.getLineOffset(polyline, length * v),
  87.                 point = offset.point,
  88.                 px = point.x,
  89.                 py = point.y,
  90.                 pz = point.z;
  91.             barrel.p3(px, py, pz);
  92.         };
  93.         params.finishFunc = function () {
  94.             positionC.barrels.push(barrel);
  95.             var poses = runOrder.shift();
  96.             if (!poses) {
  97.                 moveAnimation();
  98.             } else {
  99.                 moveAnimation(positions[poses[0]], positions[poses[1]]);
  100.             }
  101.         };
  102.         anim = ht.Default.startAnim(params);
  103.     }
  104. }

  105. /**
  106.  * 重置游戏
  107.  * */
  108. function reset(){
  109.     if(positions[0].barrels.length == 0){
  110.         positions[0].barrels = positions[2].barrels;
  111.     }
  112.     positions[2].barrels = [];
  113.     for(var i = 0, len = positions[0].barrels.length; i < len; i++){
  114.         var pos = positions[0].cylinder.p3();
  115.         pos[1] = pos[1] + floorY + i * barrelHeight - cylinderHeight / 2;
  116.         positions[0].barrels[i].p3(pos);
  117.     }
  118.     buildRunOrder(barNum, 0, 1, 2);
  119.     setTimeout(moveAnimation, 500);
  120. }

  121. /**
  122.  * 初始化节点
  123.  * */
  124. function initNodes(){
  125.     // 底盘
  126.     floor = createNode([0, floorY / 2, 0], [floorX, floorY, floorZ]).s({
  127.         'shape3d': 'box',
  128.         '3d.movable': false
  129.     });

  130.     // 创建柱子
  131.     for(var i = 0, len = 3; i < len; i++){
  132.         positions[i].cylinder = createNode(positions[i].position, [20, cylinderHeight, 20], floor).s({
  133.             'shape3d': 'cylinder',
  134.             'shape3d.color': '#E5BB77',
  135.             '3d.movable': false
  136.         });
  137.     }

  138.     // 创建圆盘
  139.     createBarrels(barNum, positions[0].cylinder);

  140.     // 创建圆盘运行轨迹
  141.     polyline = new ht.Polyline();
  142.     polyline.setSegments([1, 2, 4, 2]);
  143.     polyline.s({
  144.         'shape.background': null,
  145.         'shape.border.color': 'rgba(0,0,0,0)',
  146.         'shape.border.gradient.color': 'rgba(0,0,0,0)',
  147.         'shape.border.pattern': [20, 10],
  148.         'shape3d.resolution': 50
  149.     });
  150.     dataModel.add(polyline);
  151. }

  152. /**
  153.  * 设置路线节点
  154.  * */
  155. function setPolylinePoints(polyline, from, to){
  156.     polyline.setPoints([
  157.         {x: from[0], y: from[2], e: from[1]},
  158.         {x: from[0], y: from[2], e: cylinderHeight},
  159.         {x: from[0], y: from[2], e: cylinderHeight + 60},
  160.         {x: to[0], y: to[2], e: cylinderHeight + 60},
  161.         {x: to[0], y: to[2], e: cylinderHeight},
  162.         {x: to[0], y: to[2], e: to[1]}
  163.     ]);
  164.     return polyline;
  165. }

  166. /**
  167.  * 创建圆盘
  168.  * barNum:圆盘个数
  169.  * host:吸附节点
  170.  * */
  171. function createBarrels(barNum, host){
  172.     // 圆盘初始x位置
  173.     var pos = host.p3();

  174.     for(var i = barNum, j = 0; i > 0; i--, j++){
  175.         pos[1] = barrelHeight * j + floorY;
  176.         positions[0].barrels.push(createBarrel(pos, [1, barrelHeight, 1], barrelMinORadius + i*poorRadius, barrelIRadius, host).s({
  177.             'shape3d.color': randomColor(),
  178.             '3d.movable': false
  179.         }));
  180.     }
  181. }

  182. /**
  183.  * 创建节点
  184.  * p3:节点位置
  185.  * s3:节点大小
  186.  * host:吸附节点
  187.  * */
  188. function createNode(p3, s3, host){
  189.     var node = new ht.Node();
  190.     node.p3(p3);
  191.     node.s3(s3);
  192.     node.setHost(host);
  193.     node.s({
  194.         'wf.visible': 'selected',
  195.         'wf.color': '#FF6B10',
  196.         'wf.width': 2,
  197.         'wf.short': true
  198.     });
  199.     dataModel.add(node);
  200.     return node;
  201. }

  202. /**
  203.  * 创建空心圆柱
  204.  * p3:圆桶位置
  205.  * s3:圆桶大小
  206.  * oRadius:圆桶外径
  207.  * iRadius:圆桶内径
  208.  * host:吸附节点
  209.  * */
  210. function createBarrel(p3, s3, oRadius, iRadius, host){
  211.     return createNode(p3, s3, host).s({
  212.         'shape3d': ht.Default.createRingModel([
  213.             oRadius, 1,
  214.             oRadius, 0,
  215.             iRadius, 0,
  216.             iRadius, 1,
  217.             oRadius, 1
  218.         ], null, 20, false, false, 70)
  219.     });
  220. }


阅读(3006) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~