TypeScript-左右躲避障碍-神手
学习typescript,第一步应该是学习官方文档,理解最基础的语法。第二步开始用typescript实现一些js+css 或者canvas类型的游行。现在开始我们用ts写跳一跳
前言:
最近微信小程序里面,出现了一个左右手躲避障碍物的游戏:神手。玩了一下觉得很有意思,决定用typescript写一版。
核心点:
1.识别手势动作:双指同时点击,单指随机放开
2.障碍物的成对生成。
3.动画帧的优化
游戏截图
Typescript脚本:
1 //1.创建障碍 2 //2.移动障碍 3 //3.小球控制 4 //4.碰撞检测 5 module game { 6 7 interface FootBall { 8 node: JQuery<HTMLElement>; 9 track: Track; 10 } 11 enum Direction { 12 left, right 13 } 14 enum Track { 15 one, two, three, four 16 } 17 let mask: JQuery<HTMLElement> = $(".game"); 18 let speed: number = 10; 19 let score: number = 0; 20 let rblist: Array<RandBox> = []; 21 let roadList: Array<Road> = []; 22 let ft1: FootBall = { node: $("#ft1"), track: Track.two }; 23 let ft2: FootBall = { node: $("#ft2"), track: Track.three }; 24 //h5的专门适应绘制动画的属性 25 window.requestAnimationFrame = 26 window.requestAnimationFrame || 27 window.webkitRequestAnimationFrame || 28 (function () { 29 return function (callback: Function, element: { __lastTime: number }) { 30 var lastTime = element.__lastTime; 31 if (lastTime === undefined) { 32 lastTime = 0; 33 } 34 var currTime = Date.now(); 35 var timeToCall = Math.max(1, 33 - (currTime - lastTime)); 36 window.setTimeout(callback, timeToCall); 37 element.__lastTime = currTime + timeToCall; 38 }; 39 })(); 40 window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame; 41 let requestAnimationFrameFlag = 0; 42 class MathHelp { 43 /** 44 * 返回范围内随机数[min,max] 45 * @param min 最小值 46 * @param max 最大值 47 */ 48 static RandRange(min: number, max: number): number { 49 return Math.floor(Math.random() * (max - min + 1) + min); 50 } 51 } 52 export class Road { 53 top: number = 0; 54 id: number = 0; 55 heigth: number = document.documentElement.clientHeight; 56 node: JQuery<HTMLElement> = $(''); 57 static num: number = 0; 58 constructor() { 59 this.id = Road.num; 60 this.top = -(Road.num) * this.heigth; 61 this.node = $(`<div id="${this.id}" class="road" style="top:${this.top}px;"></div>`); 62 mask.append(this.node); 63 Road.num++; 64 } 65 move() { 66 this.top += speed; 67 this.node.css({ 68 top: this.top + "px" 69 }); 70 //循环路面 71 if (this.top >= this.heigth) { 72 this.top -= Road.num * this.heigth; 73 } 74 } 75 } 76 export class RandBox { 77 78 left: number = 0; 79 top: number = -100; 80 heigth: number = 80; 81 number = 80; 82 node: JQuery<HTMLElement> = $(''); 83 type: Direction = Direction.left; 84 id: string = "p" + new Date().getTime(); 85 track: Track = Track.one; 86 constructor(p: number = 0) { 87 this.top = p; 88 } 89 createrb(type: Direction) { 90 this.type = type; 91 let r = 0; 92 if (type == Direction.left) { 93 r = MathHelp.RandRange(0, 1); 94 } else { 95 r = MathHelp.RandRange(2, 3); 96 } 97 this.track = r; 98 //计算所属赛道 99 this.left = 70 + 126 * r + (126 - this.width) / 2; 100 this.node = $(`<div id="${this.id}" class='rb'style='left:${this.left}px;top:${this.top}px; background-image:url(img/c${MathHelp.RandRange(1, 4)}.png);'></div>`); 101 mask.append(this.node); 102 } 103 move() { 104 this.top += speed; 105 this.node.css({ 106 top: this.top + "px" 107 }); 108 //碰撞检测 109 if (this.top >= 870 && this.top < 950) { 110 if (this.track == ft1.track || this.track == ft2.track) { 111 scence.gameover(); 112 return false; 113 } 114 } 115 return true; 116 } 117 } 118 119 export class scence { 120 static timer: number; 121 static Init() { 122 //重新开始 123 $(".againBtn").on("click", () => { 124 scence.restart(); 125 }); 126 //创建路面 127 for (let i = 0; i < 3; i++) { 128 let road = new Road(); 129 roadList.push(road); 130 } 131 //最开始给一对障碍,此后是每秒一对 132 scence.makeDoubleRb(450); 133 //开始游戏(可以绑定到开始按钮) 134 scence.start(); 135 } 136 static start() { 137 scence.loadlisten(); 138 //场景平移 139 let move = () => { 140 let status = true; 141 $.each(rblist, (i, item) => { 142 if (!item.move()) { 143 status = false; 144 return false; 145 } 146 147 }); 148 if (status) { 149 $.each(roadList, (i, item) => { 150 item.move(); 151 }); 152 requestAnimationFrameFlag = requestAnimationFrame(move); 153 } else { 154 cancelAnimationFrame(requestAnimationFrameFlag); 155 } 156 } 157 move(); 158 //积分及创建障碍 159 scence.timer = setInterval(() => { 160 score++; 161 speed++; 162 $(".jfb").html(score.toString()); 163 scence.makeDoubleRb(); 164 //移除超出屏幕路障 165 rblist.forEach((item, i) => { 166 167 if (item.top > 1200) { 168 $("#" + item.id).remove(); 169 rblist.splice(i, 1); 170 } 171 }) 172 }, 1000); 173 174 } 175 static gameover() { 176 clearInterval(scence.timer); 177 $(".gameEnd").show(); 178 $(".score").html(score.toString()); 179 scence.removelisten(); 180 //小车回到原始位置 181 ft1.node.animate({ 182 left: "235px" 183 }, 50); 184 ft1.track = Track.two; 185 ft2.node.animate({ 186 left: "360px" 187 }, 50); 188 ft2.track = Track.three; 189 } 190 static restart() { 191 speed = 10; 192 score = 0; 193 $(".rb").remove(); 194 rblist = []; 195 $(".jfb").html(score.toString()); 196 $(".gameEnd").hide(); 197 scence.start(); 198 } 199 //创建成对出现的障碍 200 static makeDoubleRb(top?: number) { 201 let RB1 = new game.RandBox(top); 202 RB1.createrb(Direction.left); 203 rblist.push(RB1); 204 let RB2 = new game.RandBox(top); 205 RB2.createrb(Direction.right); 206 rblist.push(RB2); 207 } 208 private static loadlisten() { 209 document.addEventListener('touchstart', scence.touch, false); 210 document.addEventListener('touchmove', scence.touch, false); 211 document.addEventListener('touchend', scence.touch, false); 212 } 213 private static removelisten() { 214 document.removeEventListener('touchstart', scence.touch, false); 215 document.removeEventListener('touchmove', scence.touch, false); 216 document.removeEventListener('touchend', scence.touch, false); 217 } 218 private static touch(e: TouchEvent) { 219 e.preventDefault(); 220 if (e.type == "touchstart") { 221 if (e.touches.length == 1) { 222 //一指的情况 223 let x1 = e.touches[0].clientX; 224 if (x1 < 320) { 225 //左边 226 ft1.node.animate({ 227 left: "112px" 228 }, 50); 229 ft1.track = Track.one; 230 231 } else { 232 //右边 233 ft2.node.animate({ 234 left: "490px" 235 }, 50); 236 ft2.track = Track.four; 237 } 238 } else if (e.touches.length == 2) { 239 //两指手指的情况 240 let x1 = e.touches[0].clientX; 241 let x2 = e.touches[1].clientX; 242 let a = x1 < 320 ? 0 : 1; 243 let b = x2 < 320 ? 0 : 1; 244 if (a + b == 0) { 245 246 //两指都在左边 247 ft1.node.animate({ 248 left: "112px" 249 }, 50); 250 ft1.track = Track.one; 251 252 } else if (a + b == 1) { 253 //两指一左一右 254 ft1.node.animate({ 255 left: "112px" 256 }, 50); 257 ft1.track = Track.one; 258 ft2.node.animate({ 259 left: "490px" 260 }, 50); 261 ft2.track = Track.four; 262 } else if (a + b == 2) { 263 //两指都在右边 264 ft2.node.animate({ 265 left: "490px" 266 }, 50); 267 ft2.track = Track.four; 268 } 269 } 270 271 } else if (e.type == "touchend") { 272 273 if (e.touches.length == 0) { 274 //放开两指 275 ft1.node.animate({ 276 left: "235px" 277 }, 50); 278 ft1.track = Track.two; 279 ft2.node.animate({ 280 left: "360px" 281 }, 50); 282 ft2.track = Track.three 283 } else if (e.touches.length == 1) { 284 //放开一指 285 let x1 = e.touches[0].clientX; 286 if (x1 > 320) { 287 //放开的左边 288 ft1.node.animate({ 289 left: "235px" 290 }, 50); 291 ft1.track = Track.two; 292 } else { 293 //放开的右边 294 ft2.node.animate({ 295 left: "360px" 296 }, 50); 297 ft2.track = Track.three 298 } 299 } 300 } 301 } 302 } 303 304 } 305 306 game.scence.Init();
html代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script> <script type="text/javascript"> var isios = false; ! function(userAgent) { var screen_w = parseInt(window.screen.width), scale = screen_w / 640; console.log(scale); if(/Android (d+.d+)/.test(userAgent)) { var version = parseFloat(RegExp.$1); document.write(version > 2.3 ? '<meta name="viewport" content="width=640, initial-scale = ' + scale + ',user-scalable=1, minimum-scale = ' + scale + ', maximum-scale = ' + scale + ', target-densitydpi=device-dpi">' : '<meta name="viewport" content="width=640, target-densitydpi=device-dpi">'); } else { isios = true; document.write('<meta name="viewport" content="width=640, initial-scale = ' + scale + ' ,minimum-scale = ' + scale + ', maximum-scale = ' + scale + ', user-scalable=no, target-densitydpi=device-dpi">'); } }(navigator.userAgent); </script> <style> html, body { margin: 0; padding: 0; height: 100%; width: 100%; overflow: hidden; } .game { height: 100%; width: 100%; background-size: 100% auto; position: relative; left: 0; top: 0; } .road{ height: 100%; width: 100%; background: url(img/road.jpg) no-repeat; background-size: 100% 100%; position: absolute; left: 0; top: 0; z-index: 10; } .rb { height: 80px; width: 80px; position: absolute; left: 70px; top: 70px; background-position: left top; background-size: 100% 100%; /* animation: move 5s linear;*/ z-index: 11; } .ft { height: 139px; width: 63px; /*border-radius: 25px;*/ background-image: url(img/tyn.png); background-position: left top; background-size: 100% 100%; position: absolute; bottom: 50px; left: 235px; z-index: 11; } #ft1 { /*animation: football1 1.5s linear infinite;*/ } #ft2 { left: 360px; /*animation: football2 1.5s linear infinite;*/ } @keyframes football2 { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes football1 { from { transform: rotate(0deg); } to { transform: rotate(-360deg); } } @keyframes move { from { top: 0px; } to { top: 1300px; } } .gameEnd { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; background-color: rgba(0, 0, 0, .8); z-index: 999; display: none; } .getScore { width: 492px; height: 760px; background: url(img/getScore.png) no-repeat; background-size: 100% auto; position: absolute; top: 0; right: 0; left: 0; bottom: 0; margin: auto; } .score { color: #dcc226; font-size: 130px; text-align: center; margin-top: 120px; font-weight: 900; } .againBtn { width: 309px; height: 87px; background: url(img/bg.png) no-repeat; background-size: 100% 100%; font-size: 40px; color: #dcc226; text-align: center; border: none; font-weight: 900; position: absolute; left: 50%; margin-left: -154.5px; } .jfb { position: absolute; top: 30px; right: 100px; font-size: 45px; font-weight: 800; color: #FFF; text-align: center; line-height: 45px; z-index: 11; } </style> </head> <body> <div class="game"> <div id="ft1" class="ft"></div> <div id="ft2" class="ft"></div> <div class="jfb">0</div> </div> <div class="gameEnd"> <div class="getScore"> <p class="score">10</p> <button class="againBtn">再来一局</button> </div> </div> <script src="js/game.js" type="text/javascript" charset="utf-8"></script> </body> </html>