HT for Web 3D游戏设计设计--汉诺塔(Towers of Hanoi)
在这里我们将构造一个基于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 }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。