تيك تاك تو الجزء 1: Svelte و Canvas 2D

تيك تاك تو الجزء 0: مقارنة Svelte والتفاعل
تيك تاك تو الجزء 1: Svelte و Canvas 2D
تيك تاك تو الجزء 2: عديمي الجنسية التراجع / الإعادة
تيك تاك تو ، الجزء 3: تراجع / الإعادة مع تخزين الأوامر
تيك تاك تو الجزء 4: التفاعل مع القارورة الخلفية باستخدام HTTP

في مقال "المقارنة: Svelte و React" ، حاولت تكرار تطور لعبة Tic Tac Toe. هناك أكملت فقط الجزء الأول من البرنامج التعليمي الأصلي ل React دون دعم تاريخ التحركات. في هذه المقالة ، سنبدأ تطوير هذه اللعبة باستخدام إطار Svelte مع دعم تاريخ التحركات. تاريخ الحركات هو في الواقع نظام تراجع / إعادة. في البرنامج التعليمي React الأصلي ، يتم تطبيق نظام تراجع / إعادة مع تخزين حالة مع وصول عشوائي إلى أي ولاية. عند تطبيق نظام التراجع / الإعادة ، عادة ما يتم استخدام نمط الأوامر ، ويتم تخزين أوامر التراجع / الإعادة في قائمة الأوامر. سنحاول تنفيذ هذا النهج لاحقًا ؛ والآن سننفذ نظام تراجع / إعادة مع تخزين الحالة.


يستخدم التطوير الحل المعماري Flux باستخدام التخزين. هذا قسم منفصل في المقال.


رمز البدء

رمز REPL


App.svelte
<script> import Board from './Board.svelte'; </script> <div class="game"> <div class="game-board"> <Board /> </div> <div class="game-info"> <div class="status">Next player: X</div> <div></div> <ol></ol> </div> </div> <style> .game { font: 14px "Century Gothic", Futura, sans-serif; margin: 20px; display: flex; flex-direction: row; } .game-info { margin-left: 20px; } .status { margin-bottom: 10px; } ol { padding-left: 30px; } </style> 

Board.svelte
 <script> import { onMount } from 'svelte'; export let width = 3; export let height = 3; export let cellWidth = 34; export let cellHeight = 34; export let colorStroke = "#999"; let boardWidth = 1 + (width * cellWidth); let boardHeight = 1 + (height * cellHeight); let canvas; onMount(() => { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, boardWidth, boardHeight); ctx.beginPath(); // vertical lines for (let x = 0; x <= boardWidth; x += cellWidth) { ctx.moveTo(0.5 + x, 0); ctx.lineTo(0.5 + x, boardHeight); } // horizontal lines for (let y = 0; y <= boardHeight; y += cellHeight) { ctx.moveTo(0, 0.5 + y); ctx.lineTo(boardWidth, 0.5 + y); } // draw the board ctx.strokeStyle = colorStroke; ctx.stroke(); ctx.closePath(); }); </script> <canvas bind:this={canvas} width={boardWidth} height={boardHeight} ></canvas> 

يعرض رمز البدء شبكة فارغة. يتم عرضه باستخدام عنصر قماش HTML5. لمزيد من المعلومات حول استخدام هذا العنصر ، راجع المقال السابق تطوير Breakout على Svelte . كيفية رسم شبكة تجسست هنا . يمكن إعادة استخدام مكون اللوحة في الألعاب الأخرى. عن طريق تغيير متغيرات العرض والارتفاع ، يمكنك تغيير حجم الشبكة ؛ من خلال تغيير قيم متغيرات cellWidth و cellHeight ، يمكنك تغيير حجم الخلية.


ملء الخلايا مع الأصفار

رمز REPL
في وظيفة onMount () أضاف إخراج الأصفار في الخلايا بعد إخراج الشبكة. وبعض الأرقام السحرية المتعلقة بقيم تحديد المواقع في الخلايا.


 ctx.beginPath(); ctx.font = "bold 22px Century Gothic"; let d = 8; for (let i = 0; i < height; i+=1) { for (let j = 0; j < width; j+=1) { ctx.fillText("O", j * cellWidth + d + 1, (i + 1) * cellHeight - d); } } ctx.closePath(); 

إضافة تخزين الدولة

رمز REPL
في هذا القسم ، نتحقق من تغييرات الحالة باستخدام مستودع المستخدم . تمت إضافة مساحة تخزين إضافية في ملف store.js منفصل ، يتم استيراد هذا التخزين إلى كلا المكونين: التطبيق واللوحة . يتم تعريف أساليب state1 و state2 التي تغير حالة اللعبة في هذا المخزون.


 import { writable } from 'svelte/store'; function createState() { const { subscribe, set, update } = writable(Array(9).fill('O')); return { subscribe, state1: () => set(Array(9).fill('1')), state2: () => set(Array(9).fill('2')), }; } export const state = createState(); 

إضافة زرين الحالة 1 والدولة 2 إلى مكون التطبيق. من خلال النقر على الأزرار ، ندعو الأساليب المقابلة في المستودع.


 <button on:click={state.state1}>State 1</button> <button on:click={state.state2}>State 2</button> 

في مكون "اللوحة" ، قمت بتغيير خط إخراج الأصفار إلى إخراج البيانات من وحدة التخزين الخاصة بهم. هنا نستخدم الاشتراك التلقائي في التخزين .


 ctx.fillText($state[k], j * cellWidth + d + 1, (i + 1) * cellHeight - d); 

في هذه المرحلة ، يتم ملء حقل اللعبة بالأصفار بشكل افتراضي ، انقر فوق الزر State 1 - الحقل مليء بوحدات ، وانقر فوق الزر State 2 - الحقل مليء بالثغرات.


تعبئة خلية بنقرة ماوس

رمز REPL
في مخزن الحالة ، أضفت طريقة setCell () التي تملأ الخلية المحددة بصليب.


 setCell: (i) => update(a => {a[i] = 'X'; return a;}), 

تمت إضافة معالج أحداث النقر بالماوس إلى لوحة الرسم القماشية ، وهنا نحدد فهرس الخلية ونستدعي طريقة setCell () لتخزين الحالة .


 function handleClick(event) { let x = Math.trunc((event.offsetX + 0.5) / cellWidth); let y = Math.trunc((event.offsetY + 0.5) / cellHeight); let i = y * width + x; state.setCell(i); } 

بشكل افتراضي ، يتم ملء ملعب اللعب بالأصفار ، نضغط على أي خلية ، يتم استبدال الصفر بصلب.


تاريخ التحركات

رمز REPL
واسمحوا لي أن أذكرك بأننا نعمل حاليًا على تشغيل نظام تراجع / إعادة مع تخزين حالة مع وصول عشوائي.


 import { writable } from 'svelte/store'; class History { constructor() { this.history = new Array; this.current = -1; } currentState() { return this.history[this.current]; } push(state) { // TODO: remove all redo states this.current++; this.history.push(state); } } function createHistory() { const { subscribe, set, update } = writable(new History); return { subscribe, push: (state) => update(h => { h.push(state); return h; }), }; } export const history = createHistory(); 

تم حذف مخزن الحالة ، وتمت إضافة مخزن المحفوظات لتخزين محفوظات التحركات. نحن تصف ذلك باستخدام فئة التاريخ . لتخزين الولايات ، استخدم صفيف السجل . في بعض الأحيان ، عند تطبيق نظام التراجع / الإعادة ، يتم استخدام مجموعتي LIFO: التراجع عن المكدس والإعادة. نحن نستخدم مجموعة محفوظات واحدة لتخزين الحالات في المحفوظات . يتم استخدام الخاصية الحالية لتحديد الحالة الحالية للعبة. جميع الحالات الموجودة في السجل من بداية الصفيف إلى الحالة مع الفهرس الحالي ، إذا جاز التعبير ، موجودة في قائمة تراجع ، وجميع الحالات الأخرى في قائمة الإعادة. بتقليل أو زيادة الخاصية الحالية ، بمعنى آخر ، تنفيذ أوامر تراجع أو إعادة ، نختار الحالة أقرب إلى بداية اللعبة أو إلى نهايتها. لم يتم تنفيذ أساليب التراجع والإعادة بعد ، وسيتم إضافتها لاحقًا. تمت إضافة أساليب CurrentState () و push () إلى فئة المحفوظات . تقوم طريقة currentState () بإرجاع الحالة الحالية للعبة ، باستخدام طريقة push () نضيف حالة جديدة إلى سجل الحركة.


في مكون التطبيق ، أزلنا أزرار الحالة 1 والدولة 2 . وأضاف زر ضغط :


 <button on:click={() => history.push(Array(9).fill($history.current + 1))}>Push</button> 

بالنقر فوق هذا الزر ، تتم إضافة حالة جديدة إلى محفوظات التحركات ، يتم تعبئة صفيف المحفوظات ببساطة مع قيمة فهرس الحالة الحالية الحالية .


في مكون لوحة ، عرض الحالة الحالية من تاريخ التحركات. إليك كيفية استخدام الاشتراك التلقائي في التخزين :


 ctx.fillText($history.currentState()[k], j * cellWidth + d + 1, (i + 1) * cellHeight - d); 

في طريقة الدفع الخاصة بمخزن المحفوظات ، يمكنك إضافة الإخراج إلى وحدة تحكم المستعرض ومشاهدة كيف يتغير بعد النقر على زر الضغط .


 h.push(state); console.log(h); return h; 

النقر على خلية

رمز REPL
في مكون التطبيق ، تمت إزالة زر الضغط .


لقد تم تعريف طريقة clickCell في مخزن المحفوظات . هنا نقوم بإنشاء نسخة كاملة من حالة اللعبة ، وتغيير حالة الخلية المحددة وإضافة حالة جديدة إلى سجل الحركة:


 clickCell: (i) => update(h => { // create a copy of the current state const state = h.currentState().slice(); // change the value of the selected cell to X state[i] = 'X'; // add the new state to the history h.push(state); console.log(h.history); return h; }), 

في مكون اللوحة ، تمت إضافة استدعاء طريقة التخزين callClick () إلى وظيفة handleClick () :


 history.clickCell(i); 

في وحدة التحكم بالمتصفح هنا ، يمكننا أيضًا أن نرى كيف تتغير حالة محفوظات الحركات بعد كل نقرة بالماوس.


في المقالات التالية ، سننهي اللعبة حتى النهاية ، مع واجهة لإلغاء / إرجاع الخطوات والوصول العشوائي إلى أي خطوة من اللعبة. النظر في تنفيذ نظام التراجع / الإعادة باستخدام نمط تصميم القيادة. النظر في التفاعل مع الخلفية ، لاعب سوف تتنافس مع وكيل الفكرية في الخلفية.


هندسة التدفق

في تطوير هذه اللعبة ، يلاحظ استخدام الحل Flux المعماري . يتم تنفيذ الإجراءات كطرق في تعريف مخزن المحفوظات في ملف stores.js . يوجد مستودع محفوظات ، موصوف في شكل فئة السجل. يتم تنفيذ طرق العرض كمكونات التطبيق والمجلس . بالنسبة لي ، كل هذا هو نفس العمارة MVC ، عرض الجانب. الإجراءات - تحكم ، تخزين - نموذج ، عرض - عرض. توصيفات كلا العمارة تتزامن عمليا.


مستودع جيثب

https://github.com/nomhoi/tic-tac-toe-part1


تثبيت اللعبة على الكمبيوتر المحلي:


 git clone https://github.com/nomhoi/tic-tac-toe-part1.git cd tic-tac-toe-part1 npm install npm run dev 

نطلق اللعبة في متصفح على العنوان: http: // localhost: 5000 / .

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


All Articles