HTML, CSS और JavaScript में इंटरएक्टिव ऑनलाइन गेम

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

यहाँ परिणाम है:



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



गेमप्ले


खेल का उद्देश्य कीड़े की अपनी कॉलोनी विकसित करना और सभी दुश्मन इकाइयों को नष्ट करना है। बीटल्स बेतरतीब ढंग से खेल के मैदान के चारों ओर दौड़ते हैं, और खिलाड़ी का काम उन्हें विभिन्न भोजन के रूप में बोनस खोजने और उनकी इकाइयों पर हमला करने में मदद करना है।

खेल में बोनस:
कपकेक - 5xp देता है, जब 15xp बीटल गुणा टाइप करता है
सेब - 50hp को पुनर्स्थापित करता है, अगर बीटल पूरी तरह से स्वस्थ है तो यह 15 अतिरिक्त एचपी जोड़ता है
काली मिर्च - हमले को 5MM बढ़ाता है
बलूत - निकटतम दुश्मन पर 2xp और फेंकता है, हिट होने पर ट्रिपल क्षति का सामना करता है
फ्लाई एगारिक - 1xp देता है और आपको ज़हरीले शॉट का उत्पादन करने की अनुमति देता है, जब हिट 1/2 नुकसान पहुंचाता है और पीड़ित को धीमा कर देता है


2 से 4 लोगों से खेल सकते हैं। आप बस विभिन्न ब्राउज़र टैब से सर्वर से कनेक्ट कर सकते हैं, और एक खेल सकते हैं।
आप यहां खेलने की कोशिश कर सकते हैं
गीथूब पर सूत्र।
खेल के साथ पुरालेख

ग्राफिक्स


HTML और CSS निश्चित रूप से प्रदर्शन के मामले में बहुत तेज़ नहीं हैं, जब यह इंटरैक्टिव गेम्स में आवश्यक ग्राफिक्स का प्रतिपादन करने की बात आती है। लेकिन अगर हमारा लक्ष्य एक खिलौना प्रोटोटाइप लिखना है, तो यह विकल्प काम करेगा। अंत में, खेल के मुख्य दृश्य को प्रस्तुत करने के रूप में "संकीर्ण क्षण" को जल्दी से कैनवास पर जल्दी से स्थानांतरित किया जा सकता है।

2 डी गेम में ग्राफिक्स के साथ काम करने के लिए, हमें हिलने, घुमाने और स्केलिंग स्प्राइट के संचालन की आवश्यकता होती है।

अपनी स्थिति निर्धारित करके स्प्राइट को स्थानांतरित करें: पूर्ण और बाएं और शीर्ष को बदलना



स्प्राइट्स को घुमाने के लिए, ट्रांसफॉर्म का उपयोग करें : रोटेट करें । और परिवर्तन के साथ : उत्पत्ति, आप रोटेशन की धुरी निर्दिष्ट कर सकते हैं (डिफ़ॉल्ट रूप से यह स्प्राइट के केंद्र में है)।



पैमाने पर, पृष्ठभूमि-आकार के लिए उचित मूल्य निर्धारित करने से पहले, चौड़ाई और ऊंचाई के गुणों का उपयोग करके स्प्राइट के आकार को बदलें:



हार्डवेयर त्वरण


प्रदर्शन और इसलिए चिकनी एनीमेशन को बेहतर बनाने के लिए, आप ब्राउज़रों को रेंडर करने के लिए GPU का उपयोग करने के लिए मजबूर कर सकते हैं। ऐसा करने के लिए, तीन आयामी वस्तुओं के साथ स्प्राइट्स के साथ काम करें। अब ट्रांसलेशन 3, रोटा 3 डी और स्केल 3 डी के माध्यम से मूविंग, रोटेटिंग और स्केलिंग के संचालन करें:







ये सभी ऑपरेशन "पेंट" में तैयार किए गए कई स्प्राइट्स से गेम में ग्राफिक्स को इकट्ठा करने के लिए पर्याप्त थे।

भौतिक विज्ञान


गेम ऑब्जेक्ट को रेंडर करने के अलावा, आपको एक-दूसरे के साथ उनकी बातचीत भी स्थापित करनी होगी।
बुडेसेरेना में, संपूर्ण संपर्क स्प्रिट टकरावों से निपट रहा है।
चूंकि यह सब कुछ यथासंभव सरल करने की योजना है, इसलिए हम खुद को स्कूली गणित तक सीमित रखते हैं।
संभवतः खेलों में सबसे अधिक लगातार गणितीय कार्यों में से एक दो बिंदुओं के बीच की दूरी का पता लगा रहा है। संक्षेप में, कार्य एक त्रिभुज में कर्ण को खोजने के लिए उबलता है:



हमें सूत्र मिलता है:



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



और कुछ और नोट:


वेक्टर वर्ग उदाहरण
//  function Vec (x_, y_) { if (typeof x_ == 'object') { this.setV(x_); return; } this.x= typeof x_ == 'number' ? x_ : 0; this.y= typeof y_ == 'number' ? y_ : 0; } Vec.prototype = { //   0 setZero: function() { this.x = 0.0; this.y = 0.0; }, //   x  y set: function(x_, y_) {this.x=x_; this.y=y_;}, //     setV: function(v) { this.x=vx; this.y=vy; }, //   negative: function(){ return new Vec(-this.x, -this.y); }, //   copy: function(){ return new Vec(this.x,this.y); }, //    add: function(v) { this.x += vx; this.y += vy; return this; }, //   mubtract: function(v) { this.x -= vx; this.y -= vy; return this; }, //    multiply: function(a) { this.x *= a; this.y *= a; return this; }, //    div: function(a) { this.x /= a; this.y /= a; return this; }, //    length: function() { return Math.sqrt(this.x * this.x + this.y * this.y); }, //   (     = 1) normalize: function() { var length = this.length(); if (length < Number.MIN_VALUE) { return 0.0; } var invLength = 1.0 / length; this.x *= invLength; this.y *= invLength; return length; }, //    angle: function () { var x = this.x; var y = this.y; if (x == 0) { return (y > 0) ? (3 * Math.PI) / 2 : Math.PI / 2; } var result = Math.atan(y/x); result += Math.PI/2; if (x < 0) result = result - Math.PI; return result; }, //      (     ) distanceTo: function (v) { return Math.sqrt((vx - this.x) * (vx - this.x) + (vy - this.y) * (vy - this.y)); }, //      x,y     x,y   vectorTo: function (v) { return new Vec(vx - this.x, vy - this.y); }, //      rotate: function (angle) { var length = this.length(); this.x = Math.sin(angle) * length; this.y = Math.cos(angle) * (-length); return this; } }; 



उपयोग किए गए विकास पैटर्न


कुछ शब्दों में, खेल तर्क को निम्नानुसार वर्णित किया जा सकता है: "गेम" वर्ग का एक उद्देश्य है जो खेल की दुनिया का वर्णन करता है जिसमें "गेमऑब्जेक्ट" वर्ग से वस्तुओं-वारिसों की एक सरणी होती है - ये सभी खेल की दुनिया की वस्तुएं हैं। गेम का प्रत्येक गेम फ्रेम सभी गेम ऑब्जेक्ट से गुजरता है और प्रत्येक विधि चरण को कॉल करता है। प्रत्येक ऑब्जेक्ट की चरण विधि यह बताती है कि इस फ्रेम के लिए क्या करना चाहिए (चाल, संभाल टकराव, नष्ट, आदि) ओओपी को लागू करने के लिए, गेम जॉन रेजिग से साधारण जावास्क्रिप्ट इनहेरिटेंस से क्लास ऑब्जेक्ट का उपयोग करता है, जो मिश्रण और स्थैतिक गुणों का समर्थन करने के लिए संशोधित होता है।
खेल में नई वस्तुओं को बनाने के लिए संभवतः सबसे सफल पैटर्न में से एक कारखाना विधि का उपयोग है। लब्बोलुआब यह है कि हम सीधे कॉल के माध्यम से नई वस्तुओं का निर्माण नहीं करेंगे, लेकिन हम एक ऐसी विधि का उपयोग करेंगे जो हमारे लिए ऐसा करती है। कारखाना विधि हमें खेल की दुनिया में एक नई वस्तु को जोड़ने की परेशानी से बचाएगा।

उदाहरण के लिए, हम ब्लॉक क्लास का एक ऑब्जेक्ट बनाना चाहते हैं, इसे गेम की दुनिया में शामिल करें और इसे दिए गए स्थान पर रखें:
  game.create('Block', {x: 100, y: 150}); 


विधि कोड बनाएँ:
  create: function (objectName, params) { //             Game.classes //       Game.classes var object = new Game.classes[objectName](params); //     object.id = ++this.idx; //       object.game = this; //        this.objects[object.id] = object; //          //        if (object.isColliding) this.collidingObjects[object.id] = object; //          //     birth,       object.birth(); //    return object; }, 


गेम कार्ड बनाना


इसलिए, जब खेल पहले से ही लिखा गया है, तो मैं इसे कई गेम कार्ड के साथ विविधता देना चाहता हूं। कोड के साथ सभी गेम ऑब्जेक्ट बनाना (विधि के बाद कॉलिंग विधि) बहुत थका देने वाला और प्रिय है। अपना स्वयं का मानचित्र संपादक लिखने में बहुत समय लगेगा। लेकिन एक सरल तरीका है - आप निम्नलिखित दृष्टिकोण को बनाने के लिए एक टेक्स्ट एडिटर या अपने विचारक का उपयोग कर सकते हैं:

  !function () { var WIDTH = 20; var HEIGHT = 12; var B = 'Block'; var P = 'Bonus'; var MAP = [ , , , , , , , , , , , , , , , , , , , , , B, , B, , B, B, B, , B, , , , B, , , , B, B, B, , B, , B, , B, , , , B, , , , B, , , , B, , B, , B, B, B, , B, B, B, , B, , , , B, , , , B, , B, , B, , B, , B, , , , B, , , , B, , , , B, , B, , B, , B, , B, B, B, , B, B, B, , B, B, B, , B, B, B, , , , , , , , , , , , , , , , , , , , , , B, , B, , B, B, B, , B, B, B, , B, B, B, , , , , , B, , B, , B, , B, , B, , B, , B, , B, , , , , , B, B, B, , B, B, B, , B, B, , , B, B, , , , , , , B, , B, , B, , B, , B, , B, , B, , B, , , , , , B, , B, , B, , B, , B, B, B, , B, , B, , P, , , ]; Game.maps['Hello'] = Game.Map.extend({ build: function () { var blockSize = 20; for (var i = 0; i < HEIGHT; i++) { for (var j = 0; j < WIDTH; j++) { var index = WIDTH * i + j; if (MAP[index]) this.game.create(MAP[index], {x: blockSize * j, y: blockSize * i}); } } } }) }(); 

परिणाम:



नेटवर्क कोड


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

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



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

एक आश्चर्य हो सकता है - यदि हम केवल ग्राहक कार्यों को प्रसारित करते हैं, तो यादृच्छिकता के आधार पर वस्तुओं के व्यवहार को कैसे सिंक्रनाइज़ किया जाए? वास्तव में, विभिन्न बोनस पूरी तरह से यादृच्छिक स्थानों में दिखाई देते हैं, लेकिन सभी ग्राहकों के लिए यह एक ही जगह होना चाहिए। बीटल बहुत ही बेतरतीब ढंग से चलते हैं, लगातार अपने रन की दिशा बदल रहे हैं, और इसके अलावा, यह सब "अराजकता" बिल्कुल समान होना चाहिए और सभी को एक ही परिदृश्य का पालन करना चाहिए। इस व्यवहार को सिंक्रनाइज़ करने की समस्या को हल किया जा सकता है ताकि जहां भी यादृच्छिक चर का उपयोग किया जाए, इसके लिए Math.random का उपयोग न करें, बल्कि अपने स्वयं के pseudorandom नंबर जनरेटर (PRNG) का उपयोग करें। लब्बोलुआब यह है - खेल शुरू करने से पहले, सर्वर एक यादृच्छिक संख्या उत्पन्न करता है और इसे प्रत्येक शामिल क्लाइंट को पास करता है। इस नंबर का उपयोग करते हुए, वेज PRNG को इनिशियलाइज़ करता है, जो सभी क्लाइंट्स पर pseudorandom नंबर के समान क्रम का उत्पादन करेगा। ऐसे PRNG का सबसे सरल कार्यान्वयन मिलर पार्क जेनरेटर है।
जेएस कार्यान्वयन:

  var ParkMillerGenerator = function (initializer) { this.a = 16807; this.m = 2147483647; this.val = initializer || Math.round(2147483647 / 3); } ParkMillerGenerator.prototype = { next: function () { this.val = (this.a * this.val) % this.m; return (this.val / 1000000) % 1; } } 


का उपयोग करें:
  var initializer = 333; //   ,        var gen = new ParkMillerGenerator(initializer); //   gen.next(); // 0.5967310000000001 gen.next(); // 0.46109599999999773 gen.next(); // 0.07891199999994569; 


हम नोडज एप्लिकेशन से सेवा बनाते हैं


शायद थोड़ी दूर, लेकिन यह भी एक उपयोगी नोट। जब सर्वर लिखा जाता है, तो इसे निरंतर संचालन के लिए एक सेवा के रूप में लड़ाई मशीन पर चलाना अच्छा होगा। मैं वर्णन करूंगा कि यह एक उदाहरण के रूप में उबंटू का उपयोग करके कैसे किया जा सकता है।
हम /etc/init.d पर जाते हैं और वहां हमारी सेवा के नाम के साथ एक शेल स्क्रिप्ट बनाते हैं, मेरे पास बगसरेना होगा। मैं इस तथ्य पर आपका ध्यान आकर्षित करूंगा कि "BEGIN INIT INFO" से शुरू होने वाला ब्लॉक केवल एक टिप्पणी नहीं है, लेकिन आपको हमारी सेवा की सेटिंग्स को नहीं हटाना चाहिए।

 #!/bin/sh ### BEGIN INIT INFO # Provides: bugsarena # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the bugsarena servers # Description: starts the bugsarena servers ### END INIT INFO #        (  ) NODE=/usr/bin/node DAEMON_SERVER=/home/me/projects/bugs-arena/server/server.js SERVER_PARAMS="name=Arena-Dogfight map=Dogfight port=8090" NAME=bugsarena DESC="bugsarena servers" #    3  - start, stop  restart. #     start() { #  nodejs        pid   start-stop-daemon --start --make-pidfile --background --pidfile /var/run/$NAME-server.pid \ --exec $NODE -- $DAEMON_SERVER $SERVER_PARAMS } stop() { #  nodejs  echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile /var/run/$NAME-server.pid } case "$1" in start) start ;; stop) stop ;; restart) stop sleep 1 start ;; *) echo "Usage: $NAME {start|stop|restart}" >&2 exit 1 ;; esac exit 0 


हम फ़ाइल को निष्पादन योग्य बनाते हैं।
 sudo chmod +x bugsarena 


अब आप कमांड का उपयोग कर सकते हैं
सेवा बगसरेना शुरू
और
सेवा बगसरेना बंद करो
सेवा शुरू करने और रोकने के लिए।
आप सिस्टम स्टार्टअप पर गेम सर्वर स्टार्ट भी कर सकते हैं
अद्यतन- rc.d बगसरे चूक


एक्सएसएस के बारे में मत भूलना!


अंत में, आपको बस ब्राउज़र गेम के लिए एक बहुत ही सरल हमले को याद करना होगा। कल्पना कीजिए कि हमारे पास डिव में खिलाड़ियों की सूची है। और "<स्क्रिप्ट> अलर्ट" ('वासा गेम में प्रवेश करता है!') </ Script> नाम वाला एक खिलाड़ी हमारे गेम में आता है। उनका नाम खिलाड़ियों की सूची के साथ div में जोड़ा जाता है, और सभी ग्राहकों को एक कष्टप्रद चेतावनी संदेश मिलता है। और ये अभी भी फूल हैं। XSS भेद्यता के माध्यम से, आप किसी भी साइट से किसी भी स्क्रिप्ट को सुरक्षित रूप से लोड कर सकते हैं। इसलिए ग्राहकों से प्रेषित डेटा की स्क्रीनिंग के बारे में मत भूलना।

Source: https://habr.com/ru/post/In225669/


All Articles