المرة الأولى التي كتبت فيها لعبة Tetris كانت منذ أكثر من عام، وكنت قد تعلمت لغة البرمجة JS منذ وقت ليس ببعيد.
من أجل تعزيز فهمي لـ JS وإضافة حبي للألعاب، قمت بكتابة Tetris استنادًا إلى القماش باستخدام كود JS الأساسي دون الرجوع إلى أفكار وأكواد الآخرين.
خلال العطلة الصيفية في سنتي الأولى، استخدمت بناء جملة es6 لتحسينه، بما في ذلك السكر النحوي للفصل، ووظائف الأسهم، وما إلى ذلك، لزيادة فهمي لـ es6. يحتوي الكود على أكثر من 400 سطر.
إذا كنت ترغب في إنشاء هذه اللعبة الصغيرة، فيجب عليك أولاً أن تكون على دراية بلوحة H5، ومعالجة مصفوفة js، ومراقبة ومعالجة أحداث لوحة المفاتيح، واستخدام المؤقت، وما إلى ذلك. والباقي عبارة عن معالجة منطقية أساسية.
قواعد اللعبة هي جوهر التعليمات البرمجية لدينا والأولوية القصوى لها
المنطق الأساسي هنا هو تحديد ما إذا كانت الكتلة تتصادم (الكتلة المتحركة حاليًا تصطدم بالكتلة الموضوعة بالفعل بحيث لا يمكن للكتلة المتحركة حاليًا أن تتحرك لأسفل، لأن الكتلة الخاصة بنا تتحرك للأسفل بشكل افتراضي. إذا لم تتمكن من التحرك للأسفل، فسيتم اعتبار المشي للأسفل) ككتلة تم وضعها، ثم يتم إنشاء كتلة جديدة وتستمر في التحرك لأسفل من الموضع الأولي).
وهذا التصادم يحتاج أيضًا إلى تطبيقه عندما تتشوه الكتلة. وبالمثل، إذا اصطدمت الكتلة بكتل أخرى موضوعة أثناء عملية التشوه، فيجب علينا منع الكتلة من التشوه بنجاح.
تم إرفاق الكود، ونرحب بالمناقشات والتصحيحات.
<!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <title>es6-إعادة بناء لعبة Tetris (استنادًا إلى القماش)</title> <style type=text/css> #tetris{ الهامش: 10 بكسل 250 بكسل؛} </style></head><body> <canvas width=700 height=525 id=tetris></canvas> <div id=text style='color: red;font-size: 30px;'>النتيجة الحالية: 0</div> <script type=text/javascript> /** * [تصميم كامل لفئة Tetris بواسطة magic_xiang] * @param {number} الجانب [طول كل جانب مربع (px)، الافتراضي 35] *param {number} العرض [عدد المربعات في صف واحد (الرقم)، الافتراضي 20] * @param {number} الارتفاع [عدد الكتل المضمنة في العمود (الافتراضي 15)] * @param {number} السرعة [سرعة حركة سقوط الكتلة (مللي ثانية)، الافتراضي 400] */ class tetris{ buildor(side=35, width=20, height=15 , speed=400){ this.side = Side // الطول الجانبي لكل كتلة this.width = width // عدد الكتل الموجودة في الصف this.height = height // عدد الكتل الموجودة في العمود هذا .السرعة = السرعة // سرعة الحركة المتساقطة للكتلة this.num_blcok // المتغير الرقمي لنوع الكتلة الحالي this.type_color // متغير السلسلة لنوع اللون الحالي this.ident // معرف setInterval this.direction = 1 // The اتجاه الكتلة، تمت تهيئته إلى 1، الحالة الافتراضية this.grade = 0 // يُستخدم لحساب النتائج this.over = false // ما إذا كانت اللعبة قد انتهت this.arr_bX = [] // قم بتخزين إحداثيات X للكتلة الحالية this.arr_bY = [] // قم بتخزين الإحداثي Y للكتلة الحالية this.arr_store_X = [] // قم بتخزين الإحداثي X لجميع الكتل التي وصلت إلى القاع this.arr_store_Y = [] // قم بتخزين الإحداثي Y لجميع الكتل التي وصلت إلى القاع this. arr_store_color = [] // قم بتخزين جميع الكتل التي وصلت إلى الأسفل لون المربع this.paints = document.getElementById('tetris').getContext('2d') // احصل على الفرشاة self = this } // قم بتغليف طريقة الدهانات لجعل الكود أكثر إيجازًا Paintfr(x, y,scale=1){ this.paints.fillRect(x*this.side, y*this.side,scale*this.side,scale*this.side ) } // تبدأ اللعبة gameStart(){ this.init() this.run() } // عمل التهيئة init(){ this.initBackground() this.initBlock() } // تسقط الكتلة تلقائيًا run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // تهيئة الخريطة initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000 ' // لون تعبئة الخريطة أسود for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints. ClosePath() } // تهيئة موضع الكتلة ولونها initBlock(){ this.paints.beginPath() this.createRandom('rColor') // أنشئ سلسلة ألوان، this.paints.fillStyle = this.type_color this.createRandom('rBlock') // إنشاء أرقام من نوع الكتلة this.arr_bX.forEach((item, Index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints. ClosePath() } // استخدم المصفوفة لـ رسم كتلة drawBlock(color){ this.paints.beginPath() this.paints.fillStyle = color this.arr_bX.forEach((item, Index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints. ClosePath() } // ارسم الكتلة الموضوعة بالفعل drawStaticBlock() ){ this.arr_store_X.forEach((item, Index) => { this.paints.beginPath() this.paints.fillStyle = this.arr_store_color[index] this.paintfr(item, this.arr_store_Y[index], 0.9) this.paints. ClosePath() }) } // أنشئ رقمًا عشوائيًا وأرجع نوع الكتلة أو لونها اكتب createRandom(type){ Let temp = this.width/2-1 if (type == 'rBlock'){ // إذا كانت كتلة، فاكتب this.num_blcok = Math.round(Math.random()*4+1) Switch(this.num_blcok){ الحالة 1: this.arr_bX.push(temp,temp-1,temp, temp+ 1) this.arr_bY.push(0,1,1,1) حالة الاستراحة 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) حالة الاستراحة 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) حالة الاستراحة 4: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) حالة الاستراحة 5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) Break } } if (type == 'rColor'){ //if إنه نوع اللون Let num_color = Math.round(Math.random()*8+1) Switch(num_color){ case 1: this.type_color='#3EF72A' حالة فاصل 2: حالة الفصل this.type_color='yellow' 3: حالة الفصل this.type_color='#2FE0BF' 4: حالة الفصل this.type_color='red' 5: حالة الفصل this.type_color='gray' 6: this.type_color ='#C932C6' حالة الفصل 7: this.type_color= '#FC751B' حالة الفصل 8: this.type_color= حالة الاستراحة '#6E6EDD' 9: this.type_color= '#F4E9E1' Break } } } // تحديد ما إذا كانت الكتل تتصادم (أقل)، وما إذا كانت تعبر الحد الأدنى أثناء التشوه القاضي كوليسيون_داون() { for(let i = 0) ; i < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ // سواء تم تجاوز الحد الأدنى أثناء التشوه return false } if (this.arr_store_X.length != 0) { // تحديد ما إذا كانت الكتل تتصادم (أقل) for(let j = 0; j < this.arr_store_X.length; j++ ){ if (this.arr_bX[i] == this.arr_store_X[j]) { if (this.arr_bY[i] + 1 == this.arr_store_Y[j]) { return false } } } } } return true } // الحكم على ما إذا كانت الكتل تتصادم (اليسار واليمين)، وما إذا كانت تعبر الحدود اليسرى واليمنى أثناء التشوه القاضيCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { // ما إذا تم تجاوز الحد الأيمن أثناء التشوه if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { // ما إذا كان قد تم تجاوز الحد الأيسر أثناء التشوه if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { // تحديد ما إذا كانت الكتل تتصادم (اليسار واليمين) for(let j = 0; j < this.arr_store_X.length; j++){ if (this.arr_bY[i] == this.arr_store_Y[j]) { if (this.arr_bX[i] + num == this.arr_store_X[j]) { return false } } } } } return true } // وظيفة التسريع لـ down_speed_up() { Let flag_all_down = صحيح flag_all_down = this.judgeCollision_down() if (flag_all_down) { this.initBackground() for(let i = 0; i < this.arr_bY.length; i++){ this.arr_bY[i] = this.arr_bY[i] + 1 } } else{ for(let i=0; i < this.arr_bX.length; i++){ this.arr_store_X.push(this.arr_bX[i]) this.arr_store_Y.push(this.arr_bY[i]) this.arr_store_color.push(this.type_color) } this.arr_bX.splice(0,this.arr_bX.length) this.arr_bY.splice(0,this.arr_bY. length) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } // مفتاح الاتجاه هو الحركة اليسرى function move(dir_temp){ this.initBackground() if (dir_temp == 1) { // يمين دع flag_all_right = صحيح flag_all_right = this.judgeCollision_other(1) إذا (flag_all_right) { for(let i = 0; i < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]+1 } } } else{ Let flag_all_left = true flag_all_left = this.judgeCollision_other(-1) if (flag_all_left) { for(let i=0; i < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]-1 } } } this.drawBlock(this.type_color) this.drawStaticBlock() } // مفتاح الاتجاه هو اتجاه تحويل الفضاء function up_change_direction(num_blcok){ if (num_blcok == 5) { return } Let arr_tempX = [] Let arr_tempY = [] // لأنني لا أعرف ما إذا كان التحويل يمكن أن يكون ناجحًا أم لا، أقوم بتخزينه أولاً for(let i = 0;i < this.arr_bX.length; i++){ arr_tempX.push(this.arr_bX[i]) arr_tempY.push(this.arr_bY[i]) } this.direction++ // استخراج إحداثيات المركز وتشويهها بواسطة يجب أن يسود المركز الحالي Let ax_temp = this.arr_bX[0] Let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) // امسح المصفوفة this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { Switch(this.direction%4){ الحالة 1: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp+1,ay_temp+1,ay_temp+1) كسر الحالة 2: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp) this.arr_bY.push(ay_temp,ay_temp, ay_temp-1,ay_temp+1) حالة الاستراحة 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp) حالة الاستراحة 0: this.arr_bX.push(ax_temp,ax_temp,ax_temp, ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp) فاصل } } if (num_blcok == 2) { Switch(this.direction%4){ الحالة 1: this.arr_bX.push(ax_temp,ax_temp -1،ax_temp-1،ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp) فصل الحالة 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp-1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+ 1 ,ay_temp+1) حالة الاستراحة 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp+1) حالة الفصل 0: this.arr_bX.push(ax_temp,ax_temp, ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp-1) فاصل } } if (num_blcok == 3) { Switch(this.direction%4){ الحالة 1: this.arr_bX.push(ax_temp ,ax_temp-1,ax_temp+1,ax_temp+2) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp) كسر الحالة 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+ 2 ) حالة الاستراحة 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+2) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp) حالة الاستراحة 0: this.arr_bX.push(ax_temp,ax_temp,ax_temp, ax_temp) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+2) فاصل } } if (num_blcok == 4) { Switch(this.direction%4){ الحالة 1: this.arr_bX.push(ax_temp) ،ax_temp-1،ax_temp،ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp+1) كسر الحالة 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp+ 1) ,ay_temp,ay_temp-1) فاصل الحالة 3: this.arr_bX.push(ax_temp,ax_temp,ax_temp-1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp-1) حالة الفصل 0: this.arr_bX.push(ax_temp,ax_temp, ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp+1) فاصل } } if (! (this.judgeCollision_other(-1) && this.judgeCollision_down() && this.judgeCollision_other(1) )) { // إذا لم ينجح التحويل، فقم بتنفيذ التعليمة البرمجية التالية this.arr_bX.splice(0, this.arr_bX.length) this.arr_bY.splice(0, this.arr_bY.length) for(let i=0; i< arr_tempX.length; i++){ this.arr_bX.push(arr_tempX[i]) this.arr_bY.push(arr_tempY[i] ]) } } this.drawStaticBlock() } // عندما يكون الصف ممتلئًا، قم بمسح الكتل، ويكون الإحداثي Y للكتلة العلوية هو +1 ClearUnderBlock(){ // احذف الكتل ذات المستوى المنخفض Let arr_row=[] Let line_num if (this.arr_store_X.length != 0) { for(let j = this.height-1; j >= 0; j--) { for (let i = 0; i < this.arr_store_color.length; i++){ if (this.arr_store_Y[i] == j) { arr_row.push(i) } } if (arr_row.length == this.width) { line_num = j Break }else{ arr_row.splice(0, arr_row.length) } } } if (arr_row.length == this.width ) { // احسب درجة الدرجة this.grade++ document.getElementById('text').innerHTML = 'الدرجة الحالية:'+this.grade for(let i = 0; i < arr_row.length; i++){ this.arr_store_X.splice(arr_row[i]-i, 1) this.arr_store_Y.splice(arr_row[i]-i, 1) this.arr_store_color. splice(arr_row[i]-i, 1) } // دع الكتلة العلوية تسقط مسافة واحدة لأسفل for(let i = 0; i < this.arr_store_color.length; i++){ if (this.arr_store_Y[i] < line_num) { this.arr_store_Y[i] = this.arr_store_Y[i]+1 } } } // الحكم على نهاية اللعبة gameover() { for (let i=0; i < this.arr_store_X.length; i++){ إذا (this.arr_store_Y[i] == 0) { ClearInterval(this.ident) this.over = true } } } } Let tetrisObj = new tetris() tetrisObj.gameStart() // وظيفة مفتاح الاتجاه document.onkeydown = (e) => { if (tetrisObj.over) return Switch(e.keyCode){ الحالة 40: // الاتجاه لأسفل tetrisObj.down_speed_up() حالة الاستراحة 32: // مسافة لتغيير الاتجاه tetrisObj.initBackground() // إعادة رسم الخريطة tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) حالة الاستراحة 37: // ترك الاتجاه tetrisObj.initBackground() tetrisObj .تحرك (-1) tetrisObj.drawBlock(tetrisObj.type_color) حالة الاستراحة 39: // الاتجاه صحيح tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) Break } } </script></body></ أتش تي أم أل >
ما ورد أعلاه هو المحتوى الكامل لهذه المقالة وآمل أن يكون مفيدًا لدراسة الجميع وآمل أيضًا أن يدعم الجميع شبكة VeVb Wulin.