typescript 简版跳一跳
学习typescript,第一步应该是学习官方文档,理解最基础的语法。第二步开始用typescript实现一些js+css 或者canvas类型的游行。现在开始我们用ts写跳一跳
核心点:1.场景的随机创建
2.旗子的跳动
3.落脚点的判断,重点要提及的是射线判断法,参见博客
4.场景平移
5.游戏重置
6.销毁场景外方块
Ts代码:
1 //1.创建底座:
2 //2.创建跳棋:
3 //3.点击移动
4 //4.开始按压动画,
5 //5.放开动画
6 //6.跳动
7 //7.是否跳对
8 //8.移动场景
9 //9.创建新底座
10 module Jump {
11 interface Pos {
12 x: number;
13 y: number;
14 }
15 enum Flag {
16 on, in, out
17 }
18 enum Direction {
19 left,
20 right
21 }
22 let direction: Direction = Direction.right;
23 let diamondsList: diamonds[] = [];
24 let mask: JQuery<HTMLElement> = $(".game-p");
25 let chess: Chess;
26 let score:number=0;
27 class MathHelp {
28 /**
29 * 返回范围内随机数[min,max]
30 * @param min 最小值
31 * @param max 最大值
32 */
33 static RandRange(min: number, max: number): number {
34 return Math.floor(Math.random() * (max - min + 1) + min);
35 }
36 /**
37 * 根据角度求对边长度。Math.sin(randian)
38 * @param r 斜边长
39 * @param angle 角度
40 */
41 static righttriangle(r: number, angle: number): number {
42 return Math.sin(Math.PI / 180 * angle) * r;
43 }
44 /**
45 * 射线法判断点是否在多边形内部
46 * @param p 待判断的点
47 * @param poly 多边形顶点
48 */
49 static rayCasting(p: Pos, poly: Pos[]): Flag {
50 var px = p.x,
51 py = p.y,
52 flag = false
53
54 for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
55 var sx = poly[i].x,
56 sy = poly[i].y,
57 tx = poly[j].x,
58 ty = poly[j].y
59
60 // 点与多边形顶点重合
61 if ((sx === px && sy === py) || (tx === px && ty === py)) {
62 return Flag.on;
63 }
64
65 // 判断线段两端点是否在射线两侧
66 if ((sy < py && ty >= py) || (sy >= py && ty < py)) {
67 // 线段上与射线 Y 坐标相同的点的 X 坐标
68 var x = sx + (py - sy) * (tx - sx) / (ty - sy)
69
70 // 点在多边形的边上
71 if (x === px) {
72 return Flag.on;
73 }
74
75 // 射线穿过多边形的边界
76 if (x > px) {
77 flag = !flag
78 }
79 }
80 }
81
82 // 射线穿过多边形边界的次数为奇数时点在多边形内
83 return flag ? Flag.in : Flag.out;
84 }
85
86 }
87 class diamonds {
88
89 private width: number = 180;
90 private height: number = 180;
91 id: string = "p" + new Date().getTime();;
92 node: JQuery<HTMLElement>;
93 left: number = 0;
94 top: number = 0;
95 constructor(isauto: boolean = true, isRe = true, left: number = 0, top: number = 0) {
96 this.left = left;
97 this.top = top;
98 if (isauto) {
99
100 let dl = MathHelp.RandRange(200, 300);
101 let x = MathHelp.righttriangle(dl, 57);
102 let y = MathHelp.righttriangle(dl, 33);
103 let lastbox = diamondsList[diamondsList.length - 1];
104 if (isRe) {
105 if (direction == Direction.left) {
106 direction = Direction.right;
107 } else if (direction == Direction.right) {
108 direction = Direction.left;
109 }
110 }
111 if (direction == Direction.right) {
112 this.left = lastbox.left + x;
113 } else {
114 this.left = lastbox.left - x;
115 }
116 this.top = lastbox.top - y;
117 }
118 this.node = $(`<div id='${this.id}' class='gb' style="top:${this.top}px;left:${this.left}px">
119 <div class='box' style="background:url(img/c${MathHelp.RandRange(1, 4)}.png)"></div>
120 <div class='shadw'></div>
121 </div>`);
122 mask.append(this.node);
123
124 }
125 GetPointList(): Pos[] {
126 var result = [{
127 x: this.left,
128 y: this.top + 57
129 }, {
130 x: this.left + (this.width / 2),
131 y: this.top + 4
132 }, {
133 x: this.left + this.width,
134 y: this.top + 57
135 }, {
136 x: this.left + (this.width / 2),
137 y: this.top + (57 * 2 - 4)
138 }];
139 return result;
140 }
141 move(p: Pos) {
142 this.node.css({
143 left: p.x + "px",
144 top: p.y + "px",
145 transition: "",
146 transform: ""
147 });
148 }
149 }
150 class Chess {
151 private width: number = 180;
152 private height: number = 182;
153 left: number = 49;
154 top: number = 531;
155 node: JQuery<HTMLElement>;
156 constructor() {
157
158 this.node =$(`<div class="chess gb"></div>`);
159 mask.append(this.node);
160 }
161 jump(pt: Pos, call: Function) {
162 let numb = 0;
163 let t1 = setInterval(() => {
164
165 if (numb == 0) {
166 this.node.animate({
167 left: pt.x + "px",
168 top: pt.y + "px"
169 }, 504);
170
171 }
172 this.node.css({
173 "background-position": "-" + this.width * numb + "px" + " 0px"
174 });
175 numb++;
176 if (numb >= 11) {
177 window.clearInterval(t1);
178 call();
179 }
180
181 }, 42)
182
183 }
184 GetCenter(): Pos {
185 return {
186 x: this.left + this.width / 2,
187 y: this.top + this.height
188 }
189 }
190 move(p: Pos) {
191 this.node.css({
192 left: p.x + "px",
193 top: p.y + "px",
194 transition: "",
195 transform: ""
196 });
197 }
198 }
199
200 class Game {
201 static distince = 0;
202 static time: number;
203 /**
204 * 初始化游戏场景
205 */
206 static scence() {
207 let d1 = new diamonds(false, false, 50, 650);
208 diamondsList.push(d1);
209 let d = new diamonds(true, false);
210 diamondsList.push(d);
211 Game.loadlisten();
212 chess = new Chess();
213 $(".againBtn").on("click",()=>{
214 //重置
215 Game.GameRest();
216
217 });
218 }
219 /**
220 * 重置游戏
221 */
222 static GameRest(){
223 $(".gameEnd").css({
224 "z-index":-1
225 });
226 diamondsList=[];
227 score=0;
228 $(".jfb").html(score.toString());
229 $(".gb").remove();
230 direction=Direction.right;
231 Game.scence();
232
233 }
234 static CreateSP() {
235 let d = new diamonds();
236 diamondsList.push(d);
237 Game.loadlisten();
238 }
239 private static loadlisten() {
240 document.addEventListener('touchstart', Game.touch, false);
241 document.addEventListener('touchmove', Game.touch, false);
242 document.addEventListener('touchend', Game.touch, false);
243 }
244 private static removelisten() {
245 document.removeEventListener('touchstart', Game.touch, false);
246 document.removeEventListener('touchmove', Game.touch, false);
247 document.removeEventListener('touchend', Game.touch, false);
248 }
249 private static touch(e: Event) {
250 e.preventDefault();
251 let currentDiamonds = diamondsList[diamondsList.length - 2];
252 if (e.type == "touchstart") {
253 //挤压形变动画
254 let scaley = 1;
255 let chessy = 0;
256 Game.distince = 0;
257 Game.time = setInterval(() => {
258 if (scaley > 0.7) {
259 scaley -= 0.01;
260 }
261 if (chessy < 30) {
262 chessy++;
263 chess.node.css({
264 "transform": " scaleX(" + (1 + chessy * 0.006) + ") scaleY(" + (1 - (chessy * 0.007)) + ")"
265 })
266 }
267 Game.distince++;
268 currentDiamonds.node.css({
269 "transform-origin": "bottom center",
270 "transform": "scaleY(" + scaley + ")"
271 });
272
273 }, 50);
274
275 } else if (e.type == "touchend") {
276 //1.底座还原动画
277 //2.旗子动画轨迹
278 //3.落脚点判断
279 //4.场景平移,创建新底座,移除画外底座
280 currentDiamonds.node.css({
281 "transform": "scaleY(1)"
282 });
283 clearInterval(Game.time);
284 Game.removelisten();
285 Game.Jump(Game.distince);
286
287 }
288 }
289 private static Jump(distince: number) {
290 let dl = distince * 17;
291 let x = MathHelp.righttriangle(dl, 57);
292 let y = MathHelp.righttriangle(dl, 33);
293 if (direction == Direction.left) {
294 x = -x;
295 }
296 chess.left = chess.left + x;
297 chess.top = chess.top - y;
298 chess.jump({ x: chess.left, y: chess.top }, () => {
299 let p = chess.GetCenter();
300 let last = diamondsList[diamondsList.length - 1];
301 let poly = last.GetPointList();
302 //判断是否跳在基座上
303 let result = MathHelp.rayCasting(p, poly);
304 if (result == Flag.in) {
305 direction = Math.random() > 0.5 ? 1 : 0;
306 Game.moveblock();
307 score++;
308 $(".jfb").html(score.toString());
309 } else {
310 //game over
311 $(".gameEnd").css({
312 "z-index":999
313 });
314 $(".score").html(score.toString());
315 console.log("game over!");
316 }
317 })
318
319 }
320 /**
321 * 移动场景
322 */
323 private static moveblock() {
324 let last = diamondsList[diamondsList.length - 1];
325 let p1: Pos;
326 let x: number = 0;
327 let y: number = 0;
328 //以左右标准基座为左边平移
329 if (direction == Direction.left) {
330 p1 = { x: 50, y: 650 };
331 } else {
332 p1 = { x: 400, y: 650 };
333 }
334 x = p1.x - last.left;
335 y = p1.y - last.top;
336
337 $(".gb").css({
338 "transform": "translate(" + x + "px," + y + "px)",
339 "transition": "transform 0.9s"
340 });
341
342
343 setTimeout(() => {
344 //更新class中left,top属性
345 $.each(diamondsList, (a, b) => {
346 b.left = b.left + x;
347 b.top = b.top + y;
348 b.move({ x: b.left, y: b.top });
349 });
350 chess.left = chess.left + x;
351 chess.top = chess.top + y;
352 chess.move({
353 x: chess.left,
354 y: chess.top
355 });
356 Game.Destroy();
357 //创建新底座
358 Game.CreateSP();
359 }, 1100)
360 }
361 //销毁画外的底座
362 private static Destroy(){
363 diamondsList.forEach((item,i,list)=>{
364
365 if(item.top>1008){
366 diamondsList.splice(i,1);
367 $("#"+item.id).remove();
368 }
369 })
370 }
371 }
372 Game.scence();
373
374 }
View Code
html:
1 <!DOCTYPE html>
2 <html>
3
4 <head>
5 <meta charset="utf-8" />
6 <title>Typescript跳一跳</title>
7 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
8 <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
9 <link rel="stylesheet" type="text/css" href="css/ts.css" />
10 <script type="text/javascript">
11 var isios = false;
12 ! function(userAgent) {
13 var screen_w = parseInt(window.screen.width),
14 scale = screen_w / 640;
15 if(/Android (\d+\.\d+)/.test(userAgent)) {
16 var version = parseFloat(RegExp.$1);
17 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">');
18 } else {
19 isios = true;
20 document.write('<meta name="viewport" content="width=640, initial-scale = ' + scale + ' ,minimum-scale = ' + scale + ', maximum-scale = ' + scale + ', user-scalable=no, target-densitydpi=device-dpi">');
21 }
22 }(navigator.userAgent);
23 </script>
24 </head>
25
26 <body>
27
28 <!--游戏页面-->
29 <div class="game">
30
31 <div class="game-p">
32 <!--<div class="chess gb"></div>
33 <div id="one" class="gb">
34 <div class="box"></div>
35 <div class="shadw"></div>
36 </div>-->
37 </div>
38 <img class="logo" src="img/logo.png" />
39 <div class="jfb">0</div>
40 <div class="toolp">距离开启宝箱还有5步</div>
41 </div>
42
43 <div class="gameEnd">
44
45 <div class="getScore">
46
47 <p class="score">10</p>
48 <button class="againBtn">再来一局</button>
49
50 </div>
51
52 </div>
53
54 <script src="js/myjump.js" type="text/javascript" charset="utf-8"></script>
55 </body>
56
57 </html>
View Code
css:
1 html,
2 body {
3 margin: 0;
4 padding: 0;
5 height: 100%;
6 min-height: 1008px;
7 }
8
9 .game {
10 height: 100%;
11 width: 100%;
12 position: relative;
13 left: 0;
14 top: 0;
15 }
16
17
18 .logo {
19 position: absolute;
20 left: 30px;
21 top: 26px;
22 }
23
24 .jfb {
25 position: absolute;
26 top: 30px;
27 right: 60px;
28 font-size: 100px;
29 font-weight: 900;
30 color: #4f4e3f;
31 text-align: center;
32 line-height: 100px;
33 }
34
35 .toolp {
36 position: absolute;
37 bottom: 5%;
38 left: 30%;
39 width: 40%;
40 height: 50px;
41 border-radius: 50px;
42 color: #FFFFFF;
43 text-align: center;
44 font-size: 20px;
45 line-height: 50px;
46 background-color: #4a764e;
47 }
48 .game-p {
49 height: 100%;
50 width: 100%;
51 position: absolute;
52 left: 0;
53 top: 0;
54 background-color: #8aad8e;
55 }
56 .gb{
57 position: absolute;
58 left: 50px;
59 top: 650px;
60
61 }
62 .box{
63 height: 180px;
64 width: 180px;
65 background-image:url(../img/c1.png);
66 background-size: 100% 100%;
67 z-index: 2;
68 position: absolute;
69 }
70 .shadw{
71 position: absolute;
72 left: 120px;
73 top: 0;
74 height: 133px;
75 width: 234px;
76 background-image: url(../img/s2.png);
77 background-size: 100% 100%;
78 opacity: 0.3;
79 transform: translateY(35px) translateX(-180px);
80 transform-origin: bottom center;
81 }
82 .chess{
83 width: 182px;
84 height: 227px;
85 background-image: url(../img/e3.png);
86 background-position:0 0;
87 position: absolute;
88 top: 531px;
89 left: 49px;
90 z-index: 3;
91
92 }
93 .gameEnd{
94 position: absolute;
95 top: 0;
96 left: 0;
97 width: 100%;
98 height: 100%;
99 overflow: hidden;
100 background-color: rgba(0,0,0,.8);
101 z-index: -1;
102 }
103 .getScore{
104 width: 492px;
105 height: 760px;
106 background: url(../img/getScore.png) no-repeat;
107 background-size: 100% auto;
108 position: absolute;
109 top: 0;
110 right: 0;
111 left: 0;
112 bottom: 0;
113 margin: auto;
114 }
115 .score{
116 color: #dcc226;
117 font-size: 130px;
118 text-align: center;
119 margin-top: 120px;
120 font-weight: 900;
121
122 }
123 .againBtn{
124 width: 309px;
125 height: 87px;
126 background: url(../img/bg.png) no-repeat;
127 background-size: 100% 100%;
128 font-size: 40px;
129 color: #dcc226;
130 text-align: center;
131 border: none;
132 font-weight: 900;
133 position: absolute;
134 left: 50%;
135 margin-left: -154.5px;
136 }
View Code
源码:留下邮箱,看到就发。