相信大家都玩过飞翔的小鸟吧,当然,可能已经有很多人因为这个游戏砸了不少手机。吼吼。
废话不多说,回到主题,源码如下。
博客园上传空间大小有限制,没法上传了,需要打包源码的朋友们请留言邮箱地址。当然还有,不要忘了点赞哦~谢谢大家的支持。
直接上源码:一共是三个文件:页面、js、css。
HTML(index.html)页面源码如下:
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <title>飞翔的字母 - 孤影</title>
6 <link rel="stylesheet" href="style.css" media="screen" type="text/css" />
7 </head>
8
9 <body>
10 <div id="canvasContainer"></div>
11 <span id="textInputSpan">
12 1.输入内容,2.点击屏幕开始游戏,请输入内容(最大8个字符):
13 <input id="textInput" maxlength="10" type="text" width="150" />
14 <button onclick="changeText()">确定!</button>
15 </span>
16 <script src="index.js"></script>
17 </body>
18 </html>
JavaScript脚本文件(index.js)如下:
1
2 (function (window){
3
4 var Sakri = window.Sakri || {};
5 window.Sakri = window.Sakri || Sakri;
6
7 Sakri.MathUtil = {};
8
9 //used for radiansToDegrees and degreesToRadians
10 Sakri.MathUtil.PI_180 = Math.PI/180;
11 Sakri.MathUtil.ONE80_PI = 180/Math.PI;
12
13 //precalculations for values of 90, 270 and 360 in radians
14 Sakri.MathUtil.PI2 = Math.PI*2;
15 Sakri.MathUtil.HALF_PI = Math.PI/2;
16
17
18 //return number between 1 and 0
19 Sakri.MathUtil.normalize = function(value, minimum, maximum){
20 return (value - minimum) / (maximum - minimum);
21 };
22
23 //map normalized number to values
24 Sakri.MathUtil.interpolate = function(normValue, minimum, maximum){
25 return minimum + (maximum - minimum) * normValue;
26 };
27
28 //map a value from one set to another
29 Sakri.MathUtil.map = function(value, min1, max1, min2, max2){
30 return Sakri.MathUtil.interpolate( Sakri.MathUtil.normalize(value, min1, max1), min2, max2);
31 };
32
33 Sakri.MathUtil.getRandomNumberInRange = function(min, max){
34 return min + Math.random() * (max - min);
35 };
36
37 Sakri.MathUtil.getRandomIntegerInRange = function(min, max){
38 return Math.round(Sakri.MathUtil.getRandomNumberInRange(min, max));
39 };
40
41
42 }(window));
43
44 (function (window){
45
46 var Sakri = window.Sakri || {};
47 window.Sakri = window.Sakri || Sakri;
48
49 Sakri.Geom = {};
50
51 //==================================================
52 //=====================::POINT::====================
53 //==================================================
54
55 Sakri.Geom.Point = function (x,y){
56 this.x = isNaN(x) ? 0 : x;
57 this.y = isNaN(y) ? 0 : y;
58 };
59
60 Sakri.Geom.Point.prototype.clone = function(){
61 return new Sakri.Geom.Point(this.x,this.y);
62 };
63
64 Sakri.Geom.Point.prototype.update = function(x, y){
65 this.x = isNaN(x) ? this.x : x;
66 this.y = isNaN(y) ? this.y : y;
67 };
68
69 Sakri.Geom.Point.prototype.equals = function(point){
70 return this.x==point.x && this.y==point.y;
71 };
72
73 Sakri.Geom.Point.prototype.toString = function(){
74 return "{x:"+this.x+" , y:"+this.y+"}";
75 };
76
77
78
79 //==================================================
80 //===================::RECTANGLE::==================
81 //==================================================
82
83 Sakri.Geom.Rectangle = function (x, y, width, height){
84 this.update(x, y, width, height);
85 };
86
87 Sakri.Geom.Rectangle.prototype.update = function(x, y, width, height){
88 this.x = isNaN(x) ? 0 : x;
89 this.y = isNaN(y) ? 0 : y;
90 this.width = isNaN(width) ? 0 : width;
91 this.height = isNaN(height) ? 0 : height;
92 };
93
94
95 Sakri.Geom.Rectangle.prototype.getRight = function(){
96 return this.x + this.width;
97 };
98
99 Sakri.Geom.Rectangle.prototype.getBottom = function(){
100 return this.y + this.height;
101 };
102
103 Sakri.Geom.Rectangle.prototype.getCenterX = function(){
104 return this.x + this.width/2;
105 };
106
107 Sakri.Geom.Rectangle.prototype.getCenterY = function(){
108 return this.y + this.height/2;
109 };
110
111 Sakri.Geom.Rectangle.prototype.containsPoint = function(x, y){
112 return x >= this.x && y >= this.y && x <= this.getRight() && y <= this.getBottom();
113 };
114
115
116 Sakri.Geom.Rectangle.prototype.clone = function(){
117 return new Sakri.Geom.Rectangle(this.x, this.y, this.width, this.height);
118 };
119
120 Sakri.Geom.Rectangle.prototype.toString = function(){
121 return "Rectangle{x:"+this.x+" , y:"+this.y+" , width:"+this.width+" , height:"+this.height+"}";
122 };
123
124 }(window));
125
126 (function (window){
127
128 var Sakri = window.Sakri || {};
129 window.Sakri = window.Sakri || Sakri;
130
131 Sakri.CanvasTextUtil = {};
132
133 //returns the biggest font size that best fits into rect
134 Sakri.CanvasTextUtil.getFontSizeForRect = function(string, fontProps, rect, canvas, fillStyle){
135 if(!canvas){
136 var canvas = document.createElement("canvas");
137 }
138 if(!fillStyle){
139 fillStyle = "#000000";
140 }
141 var context = canvas.getContext('2d');
142 context.font = fontProps.getFontString();
143 context.textBaseline = "top";
144
145 var copy = fontProps.clone();
146 //console.log("getFontSizeForRect() 1 : ", copy.fontSize);
147 context.font = copy.getFontString();
148 var width = context.measureText(string).width;
149 //console.log(width, rect.width);
150
151 //SOME DISAGREEMENT WHETHER THIS SHOOULD BE WITH && or ||
152 if(width < rect.width){
153 while(context.measureText(string).width < rect.width || copy.fontSize*1.5 < rect.height){
154 copy.fontSize++;
155 context.font = copy.getFontString();
156 }
157 }else if(width > rect.width){
158 while(context.measureText(string).width > rect.width || copy.fontSize*1.5 > rect.height){
159 copy.fontSize--;
160 context.font = copy.getFontString();
161 }
162 }
163 //console.log("getFontSizeForRect() 2 : ", copy.fontSize);
164 return copy.fontSize;
165 }
166
167 //=========================================================================================
168 //==============::CANVAS TEXT PROPERTIES::====================================
169 //========================================================
170
171 Sakri.CanvasTextProperties = function(fontWeight, fontStyle, fontSize, fontFace){
172 this.setFontWeight(fontWeight);
173 this.setFontStyle(fontStyle);
174 this.setFontSize(fontSize);
175 this.fontFace = fontFace ? fontFace : "sans-serif";
176 };
177
178 Sakri.CanvasTextProperties.NORMAL = "normal";
179 Sakri.CanvasTextProperties.BOLD = "bold";
180 Sakri.CanvasTextProperties.BOLDER = "bolder";
181 Sakri.CanvasTextProperties.LIGHTER = "lighter";
182
183 Sakri.CanvasTextProperties.ITALIC = "italic";
184 Sakri.CanvasTextProperties.OBLIQUE = "oblique";
185
186
187 Sakri.CanvasTextProperties.prototype.setFontWeight = function(fontWeight){
188 switch (fontWeight){
189 case Sakri.CanvasTextProperties.NORMAL:
190 case Sakri.CanvasTextProperties.BOLD:
191 case Sakri.CanvasTextProperties.BOLDER:
192 case Sakri.CanvasTextProperties.LIGHTER:
193 this.fontWeight = fontWeight;
194 break;
195 default:
196 this.fontWeight = Sakri.CanvasTextProperties.NORMAL;
197 }
198 };
199
200 Sakri.CanvasTextProperties.prototype.setFontStyle = function(fontStyle){
201 switch (fontStyle){
202 case Sakri.CanvasTextProperties.NORMAL:
203 case Sakri.CanvasTextProperties.ITALIC:
204 case Sakri.CanvasTextProperties.OBLIQUE:
205 this.fontStyle = fontStyle;
206 break;
207 default:
208 this.fontStyle = Sakri.CanvasTextProperties.NORMAL;
209 }
210 };
211
212 Sakri.CanvasTextProperties.prototype.setFontSize = function(fontSize){
213 if(fontSize && fontSize.indexOf && fontSize.indexOf("px")>-1){
214 var size = fontSize.split("px")[0];
215 fontProperites.fontSize = isNaN(size) ? 24 : size;//24 is just an arbitrary number
216 return;
217 }
218 this.fontSize = isNaN(fontSize) ? 24 : fontSize;//24 is just an arbitrary number
219 };
220
221 Sakri.CanvasTextProperties.prototype.clone = function(){
222 return new Sakri.CanvasTextProperties(this.fontWeight, this.fontStyle, this.fontSize, this.fontFace);
223 };
224
225 Sakri.CanvasTextProperties.prototype.getFontString = function(){
226 return this.fontWeight + " " + this.fontStyle + " " + this.fontSize + "px " + this.fontFace;
227 };
228
229 }(window));
230
231
232 window.requestAnimationFrame =
233 window.__requestAnimationFrame ||
234 window.requestAnimationFrame ||
235 window.webkitRequestAnimationFrame ||
236 window.mozRequestAnimationFrame ||
237 window.oRequestAnimationFrame ||
238 window.msRequestAnimationFrame ||
239 (function () {
240 return function (callback, element) {
241 var lastTime = element.__lastTime;
242 if (lastTime === undefined) {
243 lastTime = 0;
244 }
245 var currTime = Date.now();
246 var timeToCall = Math.max(1, 33 - (currTime - lastTime));
247 window.setTimeout(callback, timeToCall);
248 element.__lastTime = currTime + timeToCall;
249 };
250 })();
251
252 var readyStateCheckInterval = setInterval( function() {
253 if (document.readyState === "complete") {
254 clearInterval(readyStateCheckInterval);
255 init();
256 }
257 }, 10);
258
259 //========================
260 //general properties for demo set up
261 //========================
262
263 var canvas;
264 var context;
265 var canvasContainer;
266 var htmlBounds;
267 var bounds;
268 var minimumStageWidth = 300;
269 var minimumStageHeight = 300;
270 var maxStageWidth = 800;
271 var maxStageHeight = 1100;
272 var resizeTimeoutId = -1;
273 //var stats;
274
275 function init(){
276 canvasContainer = document.getElementById("canvasContainer");
277 window.onresize = resizeHandler;
278 //stats = new Stats();
279 //canvasContainer.appendChild( stats.getDisplayElement() );
280 window.addEventListener( "keydown", keyUpEventHandler, false )
281 commitResize();
282 }
283
284 function getWidth( element ){return Math.max(element.scrollWidth,element.offsetWidth,element.clientWidth );}
285 function getHeight( element ){return Math.max(element.scrollHeight,element.offsetHeight,element.clientHeight );}
286
287 //avoid running resize scripts repeatedly if a browser window is being resized by dragging
288 function resizeHandler(){
289 context.clearRect(0,0,canvas.width, canvas.height);
290 clearTimeout(resizeTimeoutId);
291 clearTimeoutsAndIntervals();
292 resizeTimeoutId = setTimeout(commitResize, 300 );
293 }
294
295 function commitResize(){
296 if(canvas){
297 canvasContainer.removeChild(canvas);
298 }
299 canvas = document.createElement('canvas');
300 canvas.style.position = "absolute";
301 context = canvas.getContext("2d");
302 canvasContainer.appendChild(canvas);
303
304 htmlBounds = new Sakri.Geom.Rectangle(0,0, getWidth(canvasContainer) , getHeight(canvasContainer));
305 if(htmlBounds.width >= maxStageWidth){
306 canvas.width = maxStageWidth;
307 canvas.style.left = htmlBounds.getCenterX() - (maxStageWidth/2)+"px";
308 }else{
309 canvas.width = htmlBounds.width;
310 canvas.style.left ="0px";
311 }
312 if(htmlBounds.height > maxStageHeight){
313 canvas.height = maxStageHeight;
314 canvas.style.top = htmlBounds.getCenterY() - (maxStageHeight/2)+"px";
315 }else{
316 canvas.height = htmlBounds.height;
317 canvas.style.top ="0px";
318 }
319 bounds = new Sakri.Geom.Rectangle(0,0, canvas.width, canvas.height);
320 context.clearRect(0,0,canvas.width, canvas.height);
321
322 if(bounds.width<minimumStageWidth || bounds.height<minimumStageHeight){
323 stageTooSmallHandler();
324 return;
325 }
326
327 var textInputSpan = document.getElementById("textInputSpan");
328 var textInputSpanY = (canvas.height - canvas.height*.85)/2 + 15;//15 is an estimate for half of textInputHeight
329 textInputSpan.style.top = htmlBounds.getCenterY() + (bounds.height/2) - textInputSpanY +"px";
330 textInputSpan.style.left = (htmlBounds.getCenterX() - getWidth(textInputSpan)/2)+"px";
331
332 startDemo();
333 }
334
335 function stageTooSmallHandler(){
336 var warning = "Sorry, bigger screen required :(";
337 context.font = "bold normal 24px sans-serif";
338 context.fillText(warning, bounds.getCenterX() - context.measureText(warning).width/2, bounds.getCenterY()-12);
339 }
340
341
342
343
344 //========================
345 //Demo specific properties
346 //========================
347
348
349 var HOME = 0;
350 var GAME = 1;
351 var GAME_OVER = 2;
352 var gameState;
353 var scrollSpeed = 3;
354 var score;
355 var fontProperties = new Sakri.CanvasTextProperties(Sakri.CanvasTextProperties.BOLD, null, 100);
356
357 var word = "张董";
358
359 function startDemo(){
360 canvas.addEventListener('touchstart', handleUserTap, false);
361 canvas.addEventListener('mousedown', handleUserTap, false);
362
363 var logoText = "飞翔的字母";
364 if(!logoCanvas){
365 logoCanvas = document.createElement("canvas");
366 logoCanvasBG = document.createElement("canvas");
367 }
368 createLogo("飞翔的字母", logoCanvas, logoCanvasBG);
369 if(!gameOverCanvas){
370 gameOverCanvas = document.createElement("canvas");
371 gameOverCanvasBG = document.createElement("canvas");
372 }
373 createLogo("行了 到此为止吧", gameOverCanvas, gameOverCanvasBG);
374
375 createGroundPattern();
376 createBird();
377 createTubes();
378 createCityGraphic();
379 score = 0;
380 gameState = HOME;
381 loop();
382 }
383
384 function loop(){
385 switch(gameState){
386 case HOME:
387 renderHome();
388 break;
389 case GAME :
390 renderGame();
391 break;
392 case GAME_OVER:
393 renderGameOver();
394 break;
395 }
396 //stats.tick();
397 }
398
399 function handleUserTap(event){
400 switch(gameState){
401 case HOME:
402 gameState = GAME;
403 break;
404 case GAME :
405 birdYSpeed = -tapBoost;
406 break;
407 case GAME_OVER:
408 commitResize();
409 break;
410 }
411 if(event){
412 event.preventDefault();
413 }
414 }
415
416 function keyUpEventHandler(event){
417 //event.keyCode == 32 -> Space
418 if(event.keyCode == 38){
419 handleUserTap(event);
420 }
421 }
422
423 function renderHome(){
424 context.clearRect(0, 0, canvas.width, canvas.height);
425 renderGroundPattern();
426 renderLogo();
427 renderInstructions();
428 window.requestAnimationFrame(loop, canvas);
429 }
430
431 function renderGame(){
432 context.clearRect(0, 0, canvas.width, canvas.height);
433 updateTubes();
434 renderTubes();
435 updateBird();
436 if(!characters.length){
437 gameOverHandler();
438 return;
439 }
440 renderBird();
441 renderGroundPattern();
442 updateScore();
443 renderScore();
444 window.requestAnimationFrame(loop, canvas);
445 }
446
447 function gameOverHandler(){
448 context.clearRect(0, 0, canvas.width, canvas.height);
449 gameState = GAME_OVER;
450 renderGameOver();
451 }
452
453 function renderGameOver(){
454
455 //game over logo
456 context.drawImage(gameOverCanvas, bounds.getCenterX() - logoCanvas.width/2, canvas.height *.2);
457
458 var instruction = "点击重新任性、";
459 context.font = "bold normal 24px sans-serif";
460 context.fillStyle = "#FFFFFF";
461 context.fillText(instruction, bounds.getCenterX() - context.measureText(instruction).width/2, canvas.height *.25 + gameOverCanvas.height);
462 renderScore();
463
464 //window.requestAnimationFrame(loop, canvas);
465 }
466
467 function renderLogo(){
468 logoCurrentY += logoDirection;
469 context.drawImage(logoCanvas, bounds.getCenterX() - logoCanvas.width/2, logoCurrentY);
470 if(logoCurrentY <= logoY || logoCurrentY >= logoMaxY){
471 logoDirection *= -1;
472 }
473 }
474
475 function renderInstructions(){
476 var instruction = "飞翔的字母 - 孤影";
477 context.font = "bold normal 24px sans-serif";
478 context.fillStyle = "#FFFFFF";
479 context.fillText(instruction, bounds.getCenterX() - context.measureText(instruction).width/2, canvas.height *.2);
480 }
481
482 function renderScore(){
483 context.font = fontProperties.getFontString();
484 context.fillStyle = "#FFFFFF";
485 context.strokeStyle = "#000000";
486 context.lineWidth = 3;
487 var x = bounds.getCenterX() - context.measureText(score).width/2;
488 var y = bounds.height*.1;
489 context.fillText(score, x, y);
490 context.strokeText(score, x, y);
491 }
492
493 //========================================================================
494 //========================:: LOGO ::======================================
495 //========================================================================
496
497 var logoCanvas;
498 var logoCanvasBG;
499
500 var gameOverCanvas;
501 var gameOverCanvasBG;
502
503 var logoY;
504 var logoCurrentY;
505 var logoMaxY;
506 var logoDirection;
507
508 function createLogo(logoText, logoCanvas, logoCanvassBG){
509 logoCanvas.width = logoCanvasBG.width = canvas.width;
510 logoCanvas.height = logoCanvasBG.height = canvas.height / 4;
511 logoCurrentY = logoY = canvas.height * .25;
512 logoMaxY = canvas.height * .35;
513 logoDirection = 1;
514 var logoContext = logoCanvas.getContext("2d");
515 logoContext.textBaseline = "top";
516 var textRect = new Sakri.Geom.Rectangle(0, 0, logoCanvas.width * .8, logoCanvas.height);
517 var logoFontProps = fontProperties.clone();
518 logoFontProps.fontSize = Sakri.CanvasTextUtil.getFontSizeForRect(logoText, fontProperties, textRect);
519
520
521 var logoBGContext = logoCanvasBG.getContext("2d");
522 logoBGContext.fillStyle = "#f5eea5";
523 logoBGContext.fillRect(0, 0, logoCanvasBG.width, logoCanvasBG.height);
524 logoBGContext.fillStyle = "#9ce358";
525 logoBGContext.fillRect(0, logoFontProps.fontSize/2, logoCanvasBG.width, logoCanvasBG.height);
526
527 logoContext.font = logoFontProps.getFontString();
528 logoContext.fillStyle = logoContext.createPattern(logoCanvasBG, "repeat-x");
529 logoContext.strokeStyle = "#000000";
530 logoContext.lineWidth = 3;
531 var x = logoCanvas.width/2 - logoContext.measureText(logoText).width/2;
532 var y = logoFontProps.fontSize/2;
533 logoContext.fillText(logoText, x, 0);
534 logoContext.strokeText(logoText, x, 0);
535 }
536
537 //========================================================================
538 //========================:: BIRD ::==================================
539 //========================================================================
540
541 var birdCanvas;
542 var birdYSpeed = 0;
543 var gravity = 1;
544 var tapBoost = 12;
545 var birdSize = 60;
546
547 function updateBird(){
548 characters[0].y += birdYSpeed;
549 birdYSpeed += gravity;
550
551 //floor
552 if(characters[0].y >= groundGraphicRect.y - birdCanvas.height){
553 characters[0].y = groundGraphicRect.y - birdCanvas.height;
554 birdYSpeed = 0;
555 }
556 //celing
557 if(characters[0].y<=0){
558 characters[0].y = 1;
559 birdYSpeed = 0;
560 }
561 //tube collision
562 if(!isHit && checkTubesCollision()){
563 context.fillStyle = "#FFFFFF";
564 context.fillRect(0,0,canvas.width, canvas.height);
565 removeCharacter();
566 isHit = true;
567 }
568 }
569
570 var currentTube;
571 var isHit = false;
572 var ffScoreBugFix = 0;// for some reason the score would fire multiple times on firefox
573
574 function updateScore(){
575 if(ffScoreBugFix>10 && currentTube.topRect.getRight() < characters[0].x){
576 if(!isHit){
577 score++;
578 }
579 isHit = false;
580 var index = tubes.indexOf(currentTube) + 1;
581 index %= tubes.length;
582 currentTube = tubes[index];
583 ffScoreBugFix = 0;
584 }
585 ffScoreBugFix++;
586 }
587
588 function renderBird(){
589 context.drawImage(characters[0].image, characters[0].x, characters[0].y );
590 for(var i = 1; i < characters.length; i++){
591 characters[i].y = characters[i-1].y - (characters[i-1].y - characters[i].y) * .9;
592 context.drawImage(characters[i].image, characters[i].x, characters[i].y );
593 }
594 }
595
596 function removeCharacter(){
597 if(characters.length==1){
598 //game over
599 gameState = GAME_OVER;
600 }
601 for(var i=0; i<characters.length-1;i++){
602 characters[i].image = characters[i+1].image;
603 }
604 characters.pop();
605 }
606
607 function checkTubesCollision(){
608 for(var i= 0; i<tubes.length;i++){
609 if(checkTubeCollision(tubes[i])){
610 return true;
611 }
612 }
613 return false;
614 }
615
616
617 var collisionPoint = new Sakri.Geom.Point();
618 var birdPoints = [];
619
620 function checkTubeCollision(tube){
621 birdPoints[0] = characters[0].x;
622 birdPoints[1] = characters[0].y;
623 birdPoints[2] = characters[0].x + birdSize;
624 birdPoints[3] = characters[0].y;
625 birdPoints[4] = characters[0].x + birdSize;
626 birdPoints[5] = characters[0].y + birdSize;
627 birdPoints[6] = characters[0].x;
628 birdPoints[7] = characters[0].y + birdSize;
629 for(var i=0; i<8; i+=2){
630 collisionPoint.x = birdPoints[i];
631 collisionPoint.y = birdPoints[i+1];
632 if(tube.topRect.containsPoint(collisionPoint.x, collisionPoint.y) || tube.bottomRect.containsPoint(collisionPoint.x, collisionPoint.y)){
633 return true;
634 }
635 }
636 return false;
637 }
638
639 var characters;
640 var birdFontProperties = new Sakri.CanvasTextProperties(Sakri.CanvasTextProperties.BOLD, null, 50);
641
642 function createBird(){
643
644 if(!birdCanvas){
645 birdCanvas = document.createElement("canvas");
646 }
647 birdCanvas.width = birdSize;
648 birdCanvas.height = birdSize;
649
650 characters = [];
651 characters[0] = {}
652 characters[0].x = canvas.width / 3;
653 characters[0].y = groundGraphicRect.y / 2;
654 characters[0].image = createCharacterImage(word.charAt(word.length - 1));
655
656 var x = characters[0].x -(birdCanvas.width + birdCanvas.width*.2);
657 for(var i=1; i<word.length ; i++){
658 characters[i] = {};
659 characters[i].x = x;
660 characters[i].y = characters[0].y;
661 x -= (birdCanvas.width + birdCanvas.width*.2);
662 characters[i].image = createCharacterImage(word.charAt(word.length - i - 1));
663 }
664 }
665
666 function createCharacterImage(character){
667 var birdContext = birdCanvas.getContext("2d");
668 birdContext.textBaseline = "top";
669
670 birdContext.font = birdFontProperties.getFontString();
671 birdContext.fillStyle = "#d5bb22";
672 birdContext.fillRect(0, 0, birdSize, birdSize/2);
673 birdContext.fillStyle = "#e97b13";
674 birdContext.fillRect(0, birdSize/2, birdSize, birdSize/2);
675 //hilite
676 birdContext.fillStyle = "#e0e9a9";
677 birdContext.fillRect(0, 0, birdSize, 6);
678 //"mouth"
679 birdContext.fillStyle = "#da473b";
680 birdContext.fillRect(0, birdSize - 10, birdSize, birdSize);
681
682 birdContext.lineWidth = 3;
683 birdContext.strokeStyle = "#4d2f3b";
684 birdContext.strokeRect(2, 2, birdSize-4, birdSize-4);
685
686 birdContext.fillStyle = "#e8fcd6";
687 birdContext.fillText(character, birdSize/2 - birdContext.measureText(character).width/2, 0);
688 birdContext.strokeText(character, birdSize/2 - birdContext.measureText(character).width/2, 0);
689
690 var image = new Image();
691 image.width = birdSize;
692 image.height = birdSize;
693 image.src = birdCanvas.toDataURL();
694 return image;
695 }
696
697
698 //========================================================================
699 //========================:: TUBES ::==================================
700 //========================================================================
701
702 var tubeGapHeight = 230;//needs some logic
703 var tubesGapWidth;
704 var tubes;
705 var tubeWidth = 100;//needs some logic
706 var minTubeHeight = 50;//needs some logic
707
708 function updateTubes(){
709 for(var i= 0; i<tubes.length;i++){
710 updateTube(tubes[i]);
711 }
712 }
713
714 function updateTube(tube){
715 tube.topRect.x -= scrollSpeed;
716 tube.bottomRect.x = tube.topRect.x;
717 if(tube.topRect.x <= -tubeWidth ){
718 tube.topRect.x = tube.bottomRect.x = canvas.width;
719 renderTube(tube);
720 }
721 }
722
723
724 function renderTubes(){
725 for(var i= 0; i<tubes.length;i++){
726 context.drawImage(tubes[i].canvas, tubes[i].bottomRect.x, 0);
727 }
728 }
729
730 function createTubes(){
731 tubes = [];
732 var totalTubes = 2;
733 tubesGapWidth = Math.floor(canvas.width/totalTubes);
734
735 for(var i = 0; i < totalTubes; i++){
736 tubes[i] = {};
737 tubes[i].canvas = document.createElement("canvas");
738 tubes[i].topRect = new Sakri.Geom.Rectangle(canvas.width+(i * tubesGapWidth));
739 tubes[i].bottomRect = new Sakri.Geom.Rectangle(canvas.width+(i * tubesGapWidth));
740 renderTube(tubes[i]);
741 }
742 currentTube = tubes[0];
743 }
744
745 var tubeOutlineColor = "#534130";
746 var tubeMainColor = "#75be2f";
747 var tubeCapHeight = 40;
748
749 function renderTube(tube){
750 tube.canvas.width = tubeWidth;
751 tube.canvas.height = groundGraphicRect.y;
752
753 tube.bottomRect.width = tube.topRect.width = tubeWidth;
754 tube.topRect.y = 0;
755 tube.topRect.height = minTubeHeight + Math.round(Math.random()*(groundGraphicRect.y-tubeGapHeight-minTubeHeight*2));
756
757 tube.bottomRect.y = tube.topRect.getBottom() + tubeGapHeight;
758 tube.bottomRect.height = groundGraphicRect.y - tube.bottomRect.y - 1;//minus one for stroke
759
760 var tubeContext = tube.canvas.getContext("2d");
761 tubeContext.lineWidth = 2;
762 //top tube
763 renderTubeElement(tubeContext , 3, 0, tubeWidth-6, tube.topRect.height);
764 renderTubeElement(tubeContext , 1, tube.topRect.getBottom() - tubeCapHeight, tubeWidth-2, tubeCapHeight);
765
766 //bottom tube
767 renderTubeElement(tubeContext , 3, tube.bottomRect.y, tubeWidth-6, tube.bottomRect.height);
768 renderTubeElement(tubeContext , 1, tube.bottomRect.y, tubeWidth-2, tubeCapHeight);
769 }
770
771 function renderTubeElement(ctx, x, y, width, height){
772 ctx.fillStyle = tubeMainColor;
773 ctx.fillRect(x, y, width, height);
774 ctx.fillStyle = "#9de85a";
775 ctx.fillRect(x, y, width*.25, height);
776
777 ctx.fillStyle = "#d9f881";
778 ctx.fillRect(x+width *.05, y, width *.05, height);
779
780 ctx.fillStyle = "#547e25";
781 ctx.fillRect(x+width- width * .1, y, width *.1, height);
782 ctx.fillRect(x+width- width * .2, y, width *.05, height);
783
784 ctx.strokeRect(x, y, width, height);
785 }
786
787
788 //========================================================================
789 //========================:: CITY BG ::==================================
790 //========================================================================
791
792 var cityGraphicCanvas;
793
794 function createCityGraphic(){
795
796 if(cityGraphicCanvas){
797 canvasContainer.removeChild(cityGraphicCanvas);
798 }
799 cityGraphicCanvas = document.createElement("canvas");
800 cityGraphicCanvas.style.position = "absolute";
801 cityGraphicCanvas.style.left = canvas.style.left;
802 cityGraphicCanvas.style.top = canvas.style.top;
803 cityGraphicCanvas.width = canvas.width;
804 cityGraphicCanvas.height = canvas.height;
805 var cgContext = cityGraphicCanvas.getContext("2d");
806 var cityGraphicHeight = canvas.height * .25;
807
808 //fill with blue sky
809 cgContext.fillStyle = "#71c5cf";
810 cgContext.fillRect(0, 0, canvas.width, canvas.height);
811
812 cgContext.fillStyle = "#e9fad8";
813
814 cgContext.save();
815 cgContext.translate(0, groundGraphicRect.y - cityGraphicHeight);
816
817 //CLOUDS
818 var maxCloudRadius = cityGraphicHeight * .4;
819 var minCloudRadius = maxCloudRadius * .5;
820
821 for(iterator=0; iterator<canvas.width; iterator+=minCloudRadius){
822 cgContext.beginPath();
823 cgContext.arc( iterator , maxCloudRadius, Sakri.MathUtil.getRandomNumberInRange(minCloudRadius, maxCloudRadius), 0, Sakri.MathUtil.PI2);
824 cgContext.closePath();
825 cgContext.fill();
826 }
827
828 cgContext.fillRect(0,maxCloudRadius, canvas.width, cityGraphicHeight );
829
830 //HOUSES
831 var houseWidth;
832 var houseHeight;
833 cgContext.fillStyle = "#deefcb";
834 for(iterator=0; iterator<canvas.width; iterator+=(houseWidth+8)){
835 houseWidth = 20 + Math.floor(Math.random()*30);
836 houseHeight = Sakri.MathUtil.getRandomNumberInRange(cityGraphicHeight *.5 , cityGraphicHeight - maxCloudRadius *.8);
837 cgContext.fillRect(iterator, cityGraphicHeight - houseHeight, houseWidth, houseHeight);
838 }
839
840 cgContext.fillStyle = "#dff1c4";
841 cgContext.strokeStyle = "#9fd5d5";
842 cgContext.lineWidth = 3;
843 for(iterator=0; iterator<canvas.width; iterator+=(houseWidth+8)){
844 houseWidth = 20 + Math.floor(Math.random()*30);
845 houseHeight = Sakri.MathUtil.getRandomNumberInRange(cityGraphicHeight *.5 , cityGraphicHeight - maxCloudRadius *.8);
846 cgContext.fillRect(iterator, cityGraphicHeight - houseHeight, houseWidth, houseHeight);
847 cgContext.strokeRect(iterator, cityGraphicHeight - houseHeight, houseWidth, houseHeight);
848 }
849
850 //TREES
851 var maxTreeRadius = cityGraphicHeight * .3;
852 var minTreeRadius = maxTreeRadius * .5;
853 var radius;
854 var strokeStartRadian = Math.PI + Math.PI/4;
855 var strokeEndRadian = Math.PI + Math.PI/4;
856 cgContext.fillStyle = "#81e18b";
857 cgContext.strokeStyle = "#72c887";
858 for(iterator=0; iterator<canvas.width; iterator+=minTreeRadius){
859 cgContext.beginPath();
860 radius = Sakri.MathUtil.getRandomNumberInRange(minCloudRadius, maxCloudRadius)
861 cgContext.arc( iterator , cityGraphicHeight, radius, 0, Sakri.MathUtil.PI2);
862 cgContext.closePath();
863 cgContext.fill();
864
865 cgContext.beginPath();
866 cgContext.arc( iterator , cityGraphicHeight, radius, strokeStartRadian, strokeEndRadian);
867 cgContext.closePath();
868 cgContext.stroke();
869 }
870
871 cgContext.restore();
872 //sand
873 cgContext.fillStyle = sand;
874 cgContext.fillRect(0,groundGraphicRect.y, canvas.width, canvas.height);
875
876 canvasContainer.insertBefore(cityGraphicCanvas, canvasContainer.firstChild);
877 }
878
879
880 //========================================================================
881 //========================:: GROUND ::==================================
882 //========================================================================
883
884 var groundX = 0;
885 function renderGroundPattern(){
886 context.drawImage(groundPatternCanvas, groundX, groundGraphicRect.y);
887 groundX -= scrollSpeed;
888 groundX %= 16;
889 }
890
891
892 //colors
893 var groundLightGreen = "#97e556";
894 var groundDarkGreen = "#73be29";
895 var groundDarkerGreen = "#4b7e19";
896 var groundShadow = "#d1a649";
897 var groundBorder = "#4c3f48";
898 var sand = "#dcd795";
899 var groundGraphicRect = new Sakri.Geom.Rectangle();
900 var groundPatternCanvas;
901
902 function createGroundPattern(){
903 groundGraphicRect.y = canvas.height*.85;
904 if(!groundPatternCanvas){
905 groundPatternCanvas = document.createElement("canvas");
906 }
907 groundPatternCanvas.width = 16;
908 groundPatternCanvas.height = 16;
909 var groundContext = groundPatternCanvas.getContext("2d");
910 groundContext.fillStyle = groundLightGreen;
911 groundContext.fillRect(0,0,16,16);
912
913 //diagonal graphic
914 groundContext.fillStyle = groundDarkGreen;
915 groundContext.beginPath();
916 groundContext.moveTo(8,3);
917 groundContext.lineTo(16,3);
918 groundContext.lineTo(8,13);
919 groundContext.lineTo(0,13);
920 groundContext.closePath();
921 groundContext.fill();
922
923 //top border
924 groundContext.fillStyle = groundBorder;
925 groundContext.globalAlpha = .2;
926 groundContext.fillRect(0,0,16,1);
927 groundContext.globalAlpha = 1;
928 groundContext.fillRect(0,1,16,1);
929 groundContext.globalAlpha = .6;
930 groundContext.fillRect(0,2,16,1);
931
932 //hilite
933 groundContext.fillStyle = "#FFFFFF";
934 groundContext.globalAlpha = .3;
935 groundContext.fillRect(0,3,16,2);
936
937 //bottom border
938 groundContext.fillStyle = groundDarkerGreen;
939 groundContext.globalAlpha = .3;
940 groundContext.fillRect(0,10,16,3);
941 groundContext.globalAlpha = 1;
942 groundContext.fillRect(0,11,16,1);
943
944 //shadow
945 groundContext.fillStyle = groundShadow;
946 groundContext.fillRect(0,13,16,3);
947
948 var groundPattern = context.createPattern(groundPatternCanvas, "repeat-x");
949
950 groundPatternCanvas.width = canvas.width + 16;
951 groundPatternCanvas.height = 16;
952
953 groundContext.fillStyle = groundPattern;
954 groundContext.fillRect(0, 0, groundPatternCanvas.width, 16);
955
956 }
957
958 function clearTimeoutsAndIntervals(){
959 gameState = -1;
960 }
961
962 var maxCharacters = 8;
963
964 function changeText(){
965 var textInput = document.getElementById("textInput");
966 if(textInput.value && textInput.text!=""){
967 if(textInput.value.length > maxCharacters){
968 alert("Sorry, there is only room for "+maxCharacters+" characters. Try a shorter name.");
969 return;
970 }
971 if(textInput.value.indexOf(" ")>-1){
972 alert("Sorry, no support for spaces right now :(");
973 return;
974 }
975 word = textInput.value;
976 clearTimeoutsAndIntervals();
977 animating = false;
978 setTimeout(commitResize, 100);
979 }
980 }