एचटीएमएल 5 कैनवस - स्टेप बाय स्टेप एक आर्केड स्कॉलर बनाएं

छवि



प्रस्तावना


यह एक खेल बनाने के लिए एक निर्देश है जो मैंने शाम के एक जोड़े के लिए संकलित किया है। लक्ष्य शैली के योग्य प्रतिनिधि बनाने के लिए इतना नहीं था जितना कि जावास्क्रिप्ट में कैनवस और ओओपी की क्षमताओं का परीक्षण करने के लिए। अधिक दिलचस्प होने के लिए, मैंने शर्त निर्धारित की - स्प्राइट्स के साथ कोई बाहरी फाइल नहीं, सभी ग्राफिक्स अंतर्निहित विधियों का उपयोग करके तैयार किए गए हैं। इसके अलावा, किसी भी ढांचे या पुस्तकालयों का उपयोग नहीं किया जाता है। सिर्फ इसलिए कि इतने छोटे खेल में उनका उपयोग उचित नहीं है IMHO।



सामान्य तौर पर, कैनवास एक युवा मंच है, और इसे क्लासिक गेमिंग अवधारणाओं को पोर्ट करके रुचि का हो सकता है।



कार्य


एक क्लासिक आर्केड स्कॉलर बनाएं, जिसमें विभिन्न प्रकार के दुश्मनों की संख्या होती है जो लहरों में दिखाई देते हैं। शॉट डाउन के लिए दुश्मन अंक देते हैं, सबसे अच्छा परिणाम दर्ज किया जाता है।



क्रियान्वयन


मैं आपको तुरंत चेतावनी देता हूं, लेख काफी लंबा है, क्योंकि मैंने खेल के हर पहलू का वर्णन करने की कोशिश की। अंत में एक काम कर रहे उदाहरण के लिए एक कड़ी।



सबसे पहले, चर घोषित करें:



var c = document.getElementById('canv'); var ctx = c.getContext('2d'); var width = c.width; var height = c.height; var shipx = 100; var shipy = 100; var ship_w = 70;//  var ship_h = 15;// var r_border = width - ship_w;//  ,    var l_border = 0; var t_border = ship_h; var b_border = height; var bgr = new Array;//    var bullets = new Array; var enemies = new Array; var k_down = 0; var k_up = 0; var k_left = 0; var k_right = 0; var fires = 0;//   //   var vx = 0; var vy = 0; var cyclestep = 0; var game_over = 0; var score = 0; var cset = 0;//  
      
      







मुझे लगता है कि यह स्पष्ट है कि उपरोक्त चर का उपयोग किस लिए किया जाता है। यदि नहीं, तो यह कोड से और स्पष्ट होगा। अपनी कक्षाएं बनाएँ:



 function enemy(hp,dx,dy,type,x,y,width){ //  this.hp = hp; this.type = type; this.x = x; this.y = y; this.width = width; this.hwidth = width/2; this.dx = dx; this.dy = dy; } function bgObj(x,y,speed){ //   this.x = x; this.y = y; this.speed = speed; } function bullet(x,y){ //  this.x = x; this.y = y; this.dx = 12; this.dy = 0; }
      
      







हॉकिंग पैरामीटर पर ध्यान दें, जो वस्तु की चौड़ाई से आधे से अधिक कुछ भी नहीं है। यह जांचने के लिए इस्तेमाल किया जाएगा कि क्या गोलियां किसी दुश्मन की वस्तु से टकराईं। ठीक है, dx और डाई इसी समन्वय अक्षों के साथ वस्तु की गति है।



खिलाड़ी के अंकों पर डेटा, निश्चित रूप से, कुकीज़ में संग्रहीत किया जाएगा। सुविधा के लिए, उन्हें लिखने वाले फ़ंक्शन की घोषणा करें:



 function setCookie(name,value){ var d = new Date(); d.setDate(d.getDate()+1); document.cookie = name + "="+ escape(value)+";expires="+d.toGMTString(); }
      
      







जैसा कि आप देख सकते हैं, वे एक दिन संग्रहीत होते हैं। ठीक एक दिन क्यों ... मैं नहीं जानता। उन्हें केवल तब तक की आवश्यकता होती है जब तक कि php स्क्रिप्ट (या जो कुछ भी आपका पेज चालू है) उनकी तुलना पहले से रिकॉर्ड किए गए रिकॉर्ड से करता है।

तो, खेल का मुख्य कार्य ड्रा है ():



 function draw(){ //   if (k_left) vx -= 2; else if (k_right) vx +=2; if (k_up) vy -=4; else if (k_down) vy +=4; //  if(vx > 7) vx = 7; if(vy > 5) vy = 5; if (vy < -5) vy = -5; if (vx < -5) vx = -5; // if (fires == 1 && cyclestep % 8 == 0 && game_over != 1){ var b = new bullet(shipx+74,shipy-14); bullets.push(b); } draw_bg(); shoot(); if (game_over != 1) draw_ship(); move_ship(); draw_enemies(); enemy_ai(); if(game_over == 1){ ctx.fillStyle = "rgb(72,118,255)"; ctx.font = "bold 30px Arial"; ctx.textBaseline = "top"; ctx.fillText("GAME OVER",130,150); if(cset != 1){ var uname = prompt('Enter your name:','player'); if (uname == null || uname == "") uname = 'player'; setCookie('username',uname); setCookie('score',score); cset = 1; } } cyclestep++; if (cyclestep == 128) make_wave(1,4,30); if (cyclestep == 256){ cyclestep = 0; make_wave(2,4,20); } }
      
      







साइक्लेस्टेप वेरिएबल इमेज रेंडरिंग फंक्शन की वर्तमान पुनरावृति को धारण करता है। जैसा कि आप देख सकते हैं, इस चर का उपयोग किया जाता है, उदाहरण के लिए, दबाए गए फायर कुंजी को संसाधित करते समय, जब बुलेट केवल तभी बाहर निकलता है जब वर्तमान पुनरावृत्ति को शेष के बिना 8 से विभाजित किया जाए। यह इसलिए किया जाता है ताकि गोलियां एक के बाद एक लाइन से बाहर न निकलें। बेशक, यह बफर बनाने के लिए अधिक सही होगा ताकि गोलियां गायब न हों, लेकिन एक अतिरिक्त सरणी बनाने के लिए नहीं, यह कार्यान्वयन काफी स्वीकार्य है। इसके अलावा, मैं आपको अपने रूप में नियुक्त करूंगा, और इस तरह के खेलों में मैं लगभग कभी भी फायर कुंजी जारी नहीं करता।



अब पृष्ठभूमि ड्रा समारोह:



 function draw_bg(){ var distance; //""    ctx.fillStyle = "rgb(0,0,0)"; ctx.fillRect(0,0,width,height); for (var i = 0; i < bgr.length; i++){ distance = bgr[i].speed*40; if (distance < 100) distance = 100; //   ctx.fillStyle = "rgb("+distance+","+distance+","+distance+")"; //,    -   ctx.fillRect(bgr[i].x, bgr[i].y,1,1); bgr[i].x -=bgr[i].speed; if (bgr[i].x < 0){ //     ,   (  ) bgr[i].x += width; bgr[i].y = Math.floor(Math.random() * height); bgr[i].speed = Math.floor (Math.random() * 4) + 1; } } }
      
      







सरणी में तारों को जोड़ें और हर 40 मिलीसेकंड पर मुख्य ड्रॉ फ़ंक्शन शुरू करने के लिए आवृत्ति सेट करें:



 for (var i = 1; i < 50; i++){ var b = new bgObj(Math.floor(Math.random()*height),Math.floor(Math.random()*width),Math.floor(Math.random()*4)+1); bgr.push(b); } setInterval("draw();", 40);
      
      







अब आप उन फ़ंक्शंस पर टिप्पणी कर सकते हैं जो अभी भी ड्रॉ में अपरिभाषित हैं () और एप्लिकेशन चलाएं। चलते सितारों के साथ एक पृष्ठभूमि दिखाई देनी चाहिए। अब जहाज खींचना और अर्जित अंक:



 function draw_ship(){ // var sbpaint = ctx.createLinearGradient(shipx,shipy,shipx,shipy-15);// sbpaint.addColorStop(0,'rgb(220,220,230)'); sbpaint.addColorStop(1,'rgb(170,170,180)'); ctx.fillStyle = sbpaint; ctx.beginPath(); ctx.moveTo(shipx,shipy); ctx.lineTo(shipx+60,shipy); ctx.lineTo(shipx+50,shipy-15); ctx.lineTo(shipx+10,shipy-15); ctx.lineTo(shipx,shipy); ctx.fill(); // var gpaint = ctx.createLinearGradient(shipx+50,shipy-12,shipx+70,shipy-12); gpaint.addColorStop(0,'rgb(190,190,200)'); gpaint.addColorStop(1,'rgb(120,120,130)'); ctx.fillStyle = gpaint; ctx.beginPath(); ctx.moveTo(shipx+50,shipy-13); ctx.lineTo(shipx+70,shipy-13); ctx.lineTo(shipx+70,shipy-8); ctx.lineTo(shipx+50,shipy-8); ctx.lineTo(shipx+50,shipy-13); ctx.fill(); //  ctx.fillStyle = "rgb(58,95,205)"; ctx.font = "14px Arial"; ctx.textBaseline = "top"; ctx.fillText("Score:"+score,3,3); }
      
      







बेशक, सब कुछ पहले कागज पर खींचा गया था। यहाँ, उदाहरण के लिए, एक जहाज का एक योजनाबद्ध प्रतिनिधित्व है (पिक्सेल में आयाम):



छवि



चूँकि मैंने बाहरी फ़ाइलों का उपयोग नहीं करने का वादा किया था, इसलिए मैं केवल प्राइमेटिव्स ही बना सकता हूं। लेकिन, ताकि खेल 80 के दशक की शुरुआत से मेहमान की तरह न दिखे, मैं उन पर ग्रेडिएंट थोपता हूं।



स्थान बचाने के लिए, मैं निम्नलिखित कई कार्य एक पंक्ति में दूंगा:



 function move_ship(){//  -   shipx += vx; shipy +=vy; if (shipx>r_border){ shipx = r_border; vx = 0; } if (shipx<l_border){ shipx = l_border; vx = 0; } if (shipy>b_border){ shipy = b_border; vy = 0; } if (shipy<t_border){ shipy = t_border; vy = 0; } } function shoot(){ var dead_bullets = new Array; for (var i = 0; i < bullets.length; i++) { ctx.fillStyle = "rgb(173,216,230)"; ctx.fillRect(bullets[i].x,bullets[i].y,12,2);// , 122 //     if (bullets[i].x > width) dead_bullets.push(i); //    for (var j = 0;j < enemies.length;j++){ if (enemies[j].type > 0){ if (bullets[i].x >= enemies[j].x-enemies[j].hwidth && bullets[i].x < enemies[j].x+enemies[j].hwidth && bullets[i].y >= enemies[j].y-enemies[j].hwidth && bullets[i].y < enemies[j].y+enemies[j].hwidth){ enemies[j].hp--; } if(enemies[j].hp < 0){ enemies[j].type = -1; } } } bullets[i].x += bullets[i].dx; bullets[i].y += bullets[i].dy; } // ""  for (var i = dead_bullets.length-1; i >= 0; i--){ bullets.splice(dead_bullets[i],1); } } function make_wave(type,count,ewidth){ var h = Math.floor(Math.random()*(height-40))+40; for (var i = 0;i < count;i++){ var n = new enemy(2,Math.floor(Math.random()* -4)-1,0,type,width+i*20,h+i*21,ewidth); enemies.push(n); } }
      
      







दुश्मन और गोली के बीच टकराव की जाँच करने का कार्य पहली नज़र में हास्यास्पद नहीं लगता है - यह दुश्मन की आधी चौड़ाई के पैरामीटर का उपयोग करता है। लेकिन ध्यान रखें कि x के साथ परिपत्र दुश्मनों के तहत फ़ंक्शन तेज किया गया था, सर्कल के केंद्र में y निर्देशांक और चौड़ाई का कुख्यात आधा त्रिज्या है। दुश्मन का नकारात्मक प्रकार एक विस्फोट है। यह व्रज़िन के बाकी ड्राइंग के साथ वर्णित है:



 function draw_enemies(){ var dead_bad = new Array; for (var i = 1;i < enemies.length; i++){ // 1 -    if(enemies[i].type == 1){ var rg = ctx.createRadialGradient(enemies[i].x,enemies[i].y,0,enemies[i].x,enemies[i].y,enemies[i].hwidth); rg.addColorStop(0,"rgba(130,130,130,0.4)"); rg.addColorStop(0.5,"rgba(125,125,125,0.5)"); rg.addColorStop(1,"rgba(120,120,120,"+enemies[i].hp*0.4+")"); ctx.fillStyle = rg; ctx.beginPath(); ctx.arc(enemies[i].x,enemies[i].y,15,0,Math.PI*2,true); ctx.fill(); } // 2 -  if(enemies[i].type == 2){ var rg = ctx.createRadialGradient(enemies[i].x+10,enemies[i].y-10,0,enemies[i].x+10,enemies[i].y-10,enemies[i].width); rg.addColorStop(0,"rgba(240,240,0,"+enemies[i].hp*0.4+")"); rg.addColorStop(1,"rgba(240,240,0,0.6"); ctx.fillStyle = rg; ctx.beginPath(); ctx.moveTo(enemies[i].x,enemies[i].y); ctx.lineTo(enemies[i].x+10,enemies[i].y-20); ctx.lineTo(enemies[i].x+20,enemies[i].y); ctx.lineTo(enemies[i].x,enemies[i].y); ctx.fill(); } //!  if(enemies[i].type < 0){ ctx.fillStyle="rgb(250,250,250)"; ctx.beginPath(); ctx.arc(enemies[i].x,enemies[i].y,enemies[i].type * -4,0,Math.PI*2,true); ctx.fill(); } if(enemies[i].type < 0) enemies[i].type--; if(enemies[i].type < -4){ dead_bad.push(i); score+=2; } if(enemies[i].x + enemies[i].width < 0) dead_bad.push(i); if(enemies[i].y + 5 < 0) dead_bad.push(i); if(enemies[i].y > height+enemies[i].width) dead_bad.push(i); if(enemies[i].x < shipx+60 && enemies[i].x > shipx && enemies[i].y < shipy+15 && enemies[i].y > shipy) game_over = 1; enemies[i].x += enemies[i].dx; enemies[i].y += enemies[i].dy; } for (var i = 0;i < dead_bad.length;i++){ enemies.splice(dead_bad[i],1); } } function enemy_ai(){ for (var i = 0;i < enemies.length;i++){ if(enemies[i].type == 2){ if(cyclestep % 4 == 0){ if(shipy > enemies[i].y && enemies[i].y+20 < height && enemies[i].dy < 4 && enemies[i].x < width-100) enemies[i].dy++; if(shipy < enemies[i].y && enemies[i].y-20 > 0 && enemies[i].dy > -4 && enemies[i].x < width-100) enemies[i].dy--; } } } }
      
      







विस्फोट, जैसा कि आप देखते हैं, 4 पुनरावृत्तियों के रूप में कई जारी है, व्यास में बढ़ रहा है, और फिर गायब हो जाता है। मैं खिलाड़ी और त्रिकोण के AI के साथ टकराव की जांच का वर्णन नहीं करता, मुझे लगता है कि सब कुछ स्पष्ट है (त्रिकोण y समन्वय के साथ खिलाड़ी का पीछा कर रहा है)। और अंत में, कीस्ट्रोक्स के प्रसंस्करण:

 function get_key_down(e){ if (e.keyCode == 37) k_left = 1; if (e.keyCode == 38) k_up = 1; if (e.keyCode == 39) k_right = 1; if (e.keyCode == 40) k_down = 1; if(e.keyCode == 32) fires = 1; } function get_key_up(e){ if (e.keyCode == 37) k_left = 0; if (e.keyCode == 38) k_up = 0; if (e.keyCode == 39) k_right = 0; if (e.keyCode == 40) k_down = 0; if(e.keyCode == 32) fires = 0; }
      
      







वह सब है! एक कामकाजी उदाहरण यहाँ है । मैं कुकीज़ को निकालने और उन्हें सर्वर पर एक फ़ाइल पर लिखने / फ़ाइल को स्क्रीन पर पढ़ने की प्रक्रिया को छोड़ देता हूं ताकि लेख बिल्कुल भी न खिले।



अंतभाषण




बेशक, इस उदाहरण में काफी सुधार किया जा सकता है। आप कठिनाई स्तर, दुश्मन, बॉस जोड़ सकते हैं, जहाज को फिर से खोलना (ऊपर से एक अर्धवृत्त खींचना - और आपको एक पूर्ण उड़ान तश्तरी मिलती है)।



आपका ध्यान देने के लिए धन्यवाद।



सारांश - निष्कर्ष

क्रोम में पुरानी क्रमिक समस्याएं हैं, इसलिए यह प्रदर्शन के अनुसार नहीं किया गया। त्रुटि अब पकड़ा गया है और एक साधारण भरण प्रदर्शित किया जाता है, यदि एक ढाल समस्या के साथ। यह एक छोटी कुकी समस्या को ठीक करने के लिए बनी हुई है, लेकिन यह बात नहीं है। मुख्य बात - कैनवस अभी भी बहुत कच्चा है और कुछ आंतरिक रेंडरिंग विधियों की तुलना में स्प्राइट्स को संभालना बेहतर लगता है। प्रयोग समाप्त माना जा सकता है।

सभी परीक्षकों को धन्यवाद।



All Articles