هل تساءلت يومًا عن كيفية تنفيذ وظيفة إعادة التشغيل في ألعاب مثل
Super Meat Boy ؟ تتمثل إحدى طرق تنفيذها في تنفيذ الإدخال بنفس الطريقة التي اتبعها اللاعب ، وهذا بدوره يعني أن الإدخال يجب أن يتم تخزينه بطريقة ما. يمكنك استخدام
نمط الأوامر لهذا وأكثر من ذلك بكثير.
يعد قالب الأوامر مفيدًا أيضًا في إنشاء وظائف "تراجع" و "إعادة" في لعبة استراتيجية.
في هذا البرنامج التعليمي ، ننفذ قالب الأوامر في C # ونستخدمه لتوجيه شخصية الروبوت خلال متاهة ثلاثية الأبعاد. من البرنامج التعليمي سوف تتعلم:
- أساسيات نمط القيادة.
- كيفية تنفيذ نمط القيادة
- كيفية إنشاء قائمة انتظار من أوامر الإدخال وتأخير تنفيذها.
ملاحظة : من المفترض أنك على دراية بالوحدة ولديك معرفة متوسطة بـ C #. في هذا البرنامج التعليمي ، سنعمل مع Unity 2019.1 و C # 7 .
الحصول على العمل
للبدء ، قم بتنزيل
مواد المشروع . قم بفك ضغط الملف وافتح مشروع
Starter في الوحدة.
انتقل إلى
RW / Scenes وافتح المشهد
الرئيسي . يتكون المشهد من روبوت ومتاهة ، وكذلك واجهة مستخدم طرفية تعرض التعليمات. يتم تصميم المستوى في شكل شبكة ، وهو أمر مفيد عندما ننقل الروبوت بصريا عبر المتاهة.
إذا قمت بالنقر فوق "
تشغيل" ، فسنرى أن التعليمات لا تعمل. هذا أمر طبيعي لأننا سنضيف هذه الوظيفة إلى البرنامج التعليمي.
الجزء الأكثر إثارة للاهتمام من المشهد هو GameObject
Bot . حدده في نافذة التسلسل الهرمي من خلال النقر عليه.
في المفتش ، يمكنك أن ترى أنه يحتوي على عنصر
بوت . سنستخدم هذا المكون من خلال إصدار أوامر الإدخال.
ونحن نفهم منطق الروبوت
انتقل إلى
RW / Scripts وافتح البرنامج النصي
Bot في محرر الكود. لا تحتاج إلى معرفة ما يحدث في البرنامج النصي
بوت . لكن ألقِ نظرة على طريقتين:
Move
and
Shoot
. مرة أخرى ، لا يتعين عليك معرفة ما يجري داخل هذه الطرق ، ولكن عليك أن تفهم كيفية استخدامها.
لاحظ أن الأسلوب
Move
يتلقى معلمة إدخال
CardinalDirection
.
CardinalDirection
هو تعداد. يمكن أن يكون عنصر التعداد من النوع
CardinalDirection
Up
أو
Down
أو
Left
أو
Left
. اعتمادًا على
CardinalDirection
المحدد
CardinalDirection
يتحرك الروبوت مربعًا واحدًا على طول الشبكة في الاتجاه المقابل.
طريقة
Shoot
تجبر الروبوت على إطلاق قذائف تدمر
الجدران الصفراء ، ولكنها غير مجدية ضد الجدران الأخرى.
أخيرًا ، ألقِ نظرة على طريقة
ResetToLastCheckpoint
؛ لفهم ما يفعله ، انظر إلى المتاهة. هناك نقاط في المتاهة تسمى
نقطة التفتيش . لتمرير المتاهة ، يحتاج الروبوت إلى الوصول إلى نقطة التحكم
الخضراء .
عندما يخطو الروبوت في نقطة تحكم جديدة ، يصبح
الأخير بالنسبة له.
ResetToLastCheckpoint
يعيد تعيين
ResetToLastCheckpoint
، ونقله إلى نقطة التحكم الأخيرة.
على الرغم من أننا لا نستطيع استخدام هذه الطرق ، إلا أننا سنصلحها قريبًا. للبدء ، تحتاج إلى معرفة نمط تصميم
الأوامر .
ما هو نمط تصميم القيادة؟
نمط القيادة هو واحد من 23 نمطًا من أشكال التصميم الموضحة في كتاب "
أنماط التصميم: عناصر من البرمجيات الموجهة للكائنات القابلة لإعادة الاستخدام" ، والتي كتبها عصابة الأربعة من إريك جاما وريتشارد هيلم ورالف جونسون وجون فليسيديس (GoF ، عصابة الأربعة).
يذكر المؤلفون أن "نمط الأوامر يحتوي على الطلب ككائن ، مما يسمح لنا بمعلمة كائنات أخرى بطلبات مختلفة ، وطلبات قائمة الانتظار أو سجل ، ودعم عمليات عكسية."
نجاح باهر! كيف هذا؟
أفهم أن هذا التعريف ليس بسيطًا جدًا ، لذلك دعونا نحلله.
التغليف يعني أنه يمكن تغليف استدعاء الأسلوب ككائن.
يمكن أن تؤثر الطريقة المُغلفة على العديد من الكائنات وفقًا لمعلمة الإدخال. وهذا ما يسمى
توحيد الكائنات الأخرى.
يمكن حفظ "الأمر" الناتج مع الفرق الأخرى حتى يتم تنفيذها. هذه هي
قائمة انتظار الطلب.
قائمة انتظار الفريقأخيرًا ، تعني إمكانية
الرجوع أن العمليات يمكن التراجع عنها باستخدام وظيفة التراجع.
حسنًا ، لكن كيف ينعكس هذا في الكود؟
سيكون لفئة
الأمر طريقة
تنفيذ ، والتي تستقبل كمعلمة إدخال الكائن (الذي يتم تنفيذ الأمر به) يسمى
المتلقي . وهذا هو ، في الواقع ، يتم
تغليف أسلوب التنفيذ بواسطة فئة الأوامر.
يمكن تمرير العديد من مثيلات فئة الأوامر ككائنات عادية ، أي ، يمكن تخزينها في هياكل البيانات مثل قائمة انتظار أو مكدس ، إلخ.
لتنفيذ أمر ، يجب عليك استدعاء أسلوب التنفيذ. تسمى الفئة التي تبدأ التنفيذ
Invoker .
يحتوي المشروع حاليًا على فصل فارغ يسمى
BotCommand
. في القسم التالي ، سننفذ تنفيذ ما ورد أعلاه للسماح للروبوت بتنفيذ إجراءات باستخدام قالب الأوامر.
تحريك الروبوت
تنفيذ نمط القيادة
في هذا القسم ، ننفذ نمط القيادة. هناك العديد من الطرق لتنفيذه. في هذا البرنامج التعليمي سوف نغطي واحد منهم.
للبدء ، انتقل إلى
RW / Scripts وافتح البرنامج النصي
BotCommand في المحرر. فئة
BotCommand
لا تزال فارغة ، ولكن ليس لفترة طويلة.
أدخل الكود التالي في الفصل:
ما الذي يحدث هنا؟
commandName
استخدام متغير commandName
ببساطة لتخزين اسم الأمر commandName
للقراءة من قبل الإنسان. ليس من الضروري استخدامه في القالب ، لكننا سنحتاجه لاحقًا في البرنامج التعليمي.- يتلقى
BotCommand
وظيفة وسلسلة. سيساعدنا ذلك في إعداد طريقة Execute
لكائن الأوامر name
. - يعرّف المفوض
ExecuteCallback
نوع الأسلوب المغلف. ستُرجع الطريقة المُغلفة فراغًا وتقبل كمعلمة إدخال كائنًا من النوع Bot
(المكون Bot ). - تشير خاصية
Execute
إلى الطريقة المغلفة. سوف نستخدمها لاستدعاء الطريقة المغلفة. - تم تجاوز أسلوب
ToString
لإرجاع commandName
السلسلة. هذا مناسب ، على سبيل المثال ، للاستخدام في واجهة المستخدم.
حفظ التغييرات وهذا كل شيء! لقد نفذنا بنجاح نمط القيادة.
يبقى لاستخدامه.
بناء الفريق
افتح
BotInputHandler في المجلد
RW / Scripts .
هنا سنقوم بإنشاء خمس حالات من
BotCommand
. ستقوم هذه الحالات بتغليف طرق لتحريك GameObject Bot لأعلى ولأسفل ولليسار ولليمين ، وكذلك للتصوير.
لتنفيذ ذلك ، أدخل ما يلي في هذه الفئة:
في كل حالة من هذه الحالات ،
يتم تمرير
طريقة مجهولة المصدر إلى المُنشئ. سيتم تغليف هذه الطريقة المجهولة داخل كائن الأمر المقابل. كما ترون ، يفي توقيع كل من الأساليب المجهولة بالمتطلبات المحددة من قبل المفوض
ExecuteCallback
.
بالإضافة إلى ذلك ، المعلمة الثانية إلى المُنشئ عبارة عن سلسلة تشير إلى اسم الأمر. سيتم إرجاع هذا الاسم بواسطة أسلوب
ToString
لمثيل الأمر. في وقت لاحق سوف نطبقها على واجهة المستخدم.
في الحالات الأربع الأولى ، تستدعي الأساليب المجهولة الأسلوب
Move
على كائن
bot
. ومع ذلك ، معلمات الإدخال الخاصة بهم مختلفة.
تمر
MoveRight
و
MoveLeft
و
MoveLeft
و
MoveRight
Move
المعلمات
CardinalDirection.Up
و
CardinalDirection.Down
و
CardinalDirection.Left
و
CardinalDirection.Right
. كما هو مذكور في قسم
What is Pattern Design Pattern ، فإنهم يشيرون إلى اتجاهات مختلفة لتحرك GameObject Bot.
في المثيل الخامس ، يستدعي الأسلوب المجهول أسلوب
Shoot
للكائن
bot
. بفضل هذا ، سيقوم الروبوت بإطلاق قذيفة أثناء تنفيذ الأمر.
الآن وقد أنشأنا الأوامر ، نحتاج إلى الوصول إليها بطريقة أو بأخرى عندما يقوم المستخدم بإدخال مدخلات.
للقيام بذلك ،
BotInputHandler
التعليمات البرمجية التالية في
BotInputHandler
، مباشرة بعد مثيلات الأوامر:
public static BotCommand HandleInput() { if (Input.GetKeyDown(KeyCode.W)) { return MoveUp; } else if (Input.GetKeyDown(KeyCode.S)) { return MoveDown; } else if (Input.GetKeyDown(KeyCode.D)) { return MoveRight; } else if (Input.GetKeyDown(KeyCode.A)) { return MoveLeft; } else if (Input.GetKeyDown(KeyCode.F)) { return Shoot; } return null; }
إرجاع الأسلوب
HandleInput
مثيل واحد من الأمر اعتماداً على المفتاح ضغط من قبل المستخدم. احفظ التغييرات قبل الانتقال.
تطبيق الأوامر
رائع ، الآن حان الوقت لاستخدام الفرق التي أنشأناها. انتقل إلى
RW / Scripts مرة أخرى وافتح البرنامج النصي
SceneManager في المحرر. في هذه الفئة ، ستلاحظ وجود ارتباط لمتغير
uiManager
من النوع
UIManager
.
توفر فئة
UIManager
طرق مساعدة مفيدة
لواجهة المستخدم الطرفية التي نستخدمها في هذا المشهد. إذا تم استخدام الطريقة من
UIManager
، فسوف يوضح البرنامج التعليمي ما الذي تفعله ، ولكن بشكل عام لأغراضنا ، ليس من الضروري معرفة بنيته الداخلية.
بالإضافة إلى ذلك ، يشير متغير
bot
إلى مكون bot المرفق بـ GameObject
Bot .
أضف الآن الكود التالي إلى فئة
SceneManager
، واستبدله بالتعليق
//1
:
واو ، كم رمز! لكن لا تقلق ؛ نحن جاهزون أخيرًا للإطلاق الحقيقي الأول للمشروع في نافذة اللعبة.
ساوضح الشيفرة لاحقا. تذكر أن تحفظ التغييرات.
تشغيل اللعبة لاختبار قالب الأوامر
الآن حان الوقت للبناء. انقر فوق
تشغيل في محرر الوحدة.
يجب أن تكون قادرًا على إدخال أوامر النقل باستخدام
مفاتيح WASD . لإدخال أمر إطلاق النار ، اضغط على المفتاح
F. لتنفيذ الأوامر ، اضغط على
Enter .
ملاحظة : إلى أن تكتمل عملية التنفيذ ، لا يمكن إدخال أوامر جديدة.
لاحظ أنه يتم إضافة خطوط إلى واجهة المستخدم الطرفية. يشار إلى الفرق في واجهة المستخدم بأسمائها. تم إجراء ذلك بفضل متغير
commandName
.
لاحظ أيضًا كيف يتم تمرير واجهة المستخدم لأعلى قبل التنفيذ وكيف يتم حذف الخطوط أثناء التنفيذ.
نحن ندرس الفرق عن كثب
حان الوقت لتعلم الكود الذي أضفناه في قسم "تطبيق الأوامر":
- تقوم قائمة
botCommands
بتخزين الروابط إلى مثيلات BotCommand
. تذكر أنه لحفظ الذاكرة ، يمكننا فقط إنشاء خمس حالات من الأوامر ، ولكن قد تكون هناك عدة إشارات إلى أمر واحد. بالإضافة إلى ذلك ، يشير متغير executeCoroutine
إلى ExecuteCommandsRoutine
، والذي يتحكم في تنفيذ الأمر. Update
يتحقق إذا كان المستخدم قد ضغط على مفتاح Enter ؛ إذا كان الأمر كذلك ، فإنه يستدعي ExecuteCommands
، وإلا CheckForBotCommands
.- يستخدم
HandleInput
أسلوب HandleInput
الثابت من BotInputHandler
للتحقق مما إذا كان المستخدم قد أكمل الإدخال ، وإذا كان الأمر كذلك ، BotInputHandler
إرجاع الأمر. يتم تمرير الأمر الذي تم AddToCommands
إلى AddToCommands
. ومع ذلك ، إذا تم تنفيذ الأوامر ، أي إذا لم يكن executeRoutine
خاليًا ، فسوف يعود بدون تمرير أي شيء إلى AddToCommands
. وهذا هو ، يحتاج المستخدم إلى الانتظار حتى الانتهاء. - يضيف
AddToCommands
ارتباطًا جديدًا إلى المثيل الذي تم إرجاعه من الأمر في botCommands
. InsertNewText
الأسلوب InsertNewText
للفئة InsertNewText
جديدًا من النص إلى واجهة المستخدم الطرفية. السلسلة النصية عبارة عن سلسلة يتم تمريرها كمعلمة إدخال. في هذه الحالة ، نقوم بتمرير commandName commandName
.- تقوم طريقة
ExecuteCommandsRoutine
بتشغيل ExecuteCommandsRoutine
. ResetScrollToTop
من ResetScrollToTop
يقوم UIManager
واجهة المستخدم لأعلى. يتم ذلك قبل بدء التنفيذ مباشرة.- يحتوي
ExecuteCommandsRoutine
على حلقة للتكرار عبر الأوامر الموجودة في قائمة botCommands
وتنفيذها واحدة تلو الأخرى ، لتمرير كائن bot
إلى الطريقة التي يتم إرجاعها بواسطة خاصية Execute
. بعد كل تنفيذ ، تتم إضافة توقف مؤقت في ثوان CommandPauseTime
. - حذف أسلوب
RemoveFirstTextLine
من UIManager
السطر الأول من النص في واجهة المستخدم الطرفية ، إذا كان موجودًا. وهذا هو ، عند تنفيذ أمر ، تتم إزالة اسمه من واجهة المستخدم. - بعد الانتهاء من جميع الأوامر
botCommands
يتم مسح botCommands
وإعادة ضبط bot إلى آخر نقطة توقف باستخدام ResetToLastCheckpoint
. في النهاية ، executeRoutine
null
ويمكن للمستخدم متابعة إدخال الأوامر.
تطبيق ميزات التراجع والإعادة
قم بتشغيل المشهد مرة أخرى وحاول الوصول إلى نقطة التحكم الخضراء.
ستلاحظ أنه بينما لا يمكننا إلغاء الأمر الذي تم إدخاله. هذا يعني أنه إذا ارتكبت خطأ ، فلن تتمكن من العودة حتى تكمل جميع الأوامر التي تدخلها. يمكنك إصلاح ذلك عن طريق إضافة ميزات
التراجع والإعادة .
ارجع إلى
SceneManager.cs وأضف إعلان المتغير التالي مباشرة بعد إعلان
List for
botCommands
:
private Stack<BotCommand> undoStack = new Stack<BotCommand>();
متغير
undoStack
عبارة عن
مكدس (من عائلة المجموعات) يقوم بتخزين جميع المراجع للأوامر التي يمكن التراجع عنها.
الآن نضيف طريقتين
UndoCommandEntry
و
RedoCommandEntry
التي ستنفذ التراجع
RedoCommandEntry
. في فئة
SceneManager
،
SceneManager
التعليمات البرمجية التالية بعد
ExecuteCommandsRoutine
:
private void UndoCommandEntry() {
دعنا نحلل الكود:
- إذا تم تنفيذ الأوامر أو كانت قائمة
botCommands
فارغة ، فإن طريقة UndoCommandEntry
شيئًا. وإلا ، فإنه يكتب ارتباطًا للأمر الأخير الذي تم إدخاله على مكدس undoStack
. يؤدي ذلك أيضًا إلى إزالة الرابط للأمر من قائمة botCommands
. - تقوم طريقة
RemoveLastTextLine
من UIManager
بإزالة السطر الأخير من النص من واجهة المستخدم الطرفية بحيث تطابق واجهة المستخدم محتويات botCommands
. - إذا كانت مكدس
undoStack
فارغة ، فإن RedoCommandEntry
لا تفعل شيئًا. خلاف ذلك ، فإنه يستخرج الأمر الأخير من الجزء العلوي من undoStack
مرة أخرى إلى قائمة AddToCommands
باستخدام AddToCommands
.
سنضيف الآن إدخال لوحة المفاتيح لاستخدام هذه الوظائف. داخل فئة
SceneManager
نص أسلوب
Update
بالكود التالي:
if (Input.GetKeyDown(KeyCode.Return)) { ExecuteCommands(); } else if (Input.GetKeyDown(KeyCode.U))
- عندما تضغط المفتاح U ، يتم
UndoCommandEntry
الأسلوب UndoCommandEntry
. - عندما تضغط المفتاح R ، يتم
RedoCommandEntry
الأسلوب RedoCommandEntry
.
حافة التعامل مع القضية
عظيم ، لقد انتهينا تقريبا! لكن أولاً ، نحتاج إلى القيام بما يلي:
- عند إدخال أمر جديد ، يجب محو مكدس
undoStack
. - قبل تنفيذ الأوامر ، يجب محو مكدس
undoStack
.
لتنفيذ ذلك ، نحتاج أولاً إلى إضافة طريقة جديدة إلى
SceneManager
. أدخل الطريقة التالية بعد
CheckForBotCommands
:
private void AddNewCommand(BotCommand botCommand) { undoStack.Clear(); AddToCommands(botCommand); }
هذا الأسلوب مسح
undoStack
ثم ثم استدعاء الأسلوب
AddToCommands
.
AddToCommands
الآن المكالمة إلى
AddToCommands
داخل
CheckForBotCommands
التالي:
AddNewCommand(botCommand);
ثم أدخل السطر التالي بعد
if
داخل طريقة
ExecuteCommands
لمسحها قبل تنفيذ أوامر
undoStack
:
undoStack.Clear();
وانتهينا في النهاية!
احفظ عملك قم ببناء المشروع وانقر فوق محرر
Play . أدخل الأوامر كما كان من قبل. اضغط
U لإلغاء الأوامر. اضغط
R لتكرار الأوامر الملغاة.
حاول الوصول إلى نقطة التفتيش الخضراء.
إلى أين تذهب بعد ذلك؟
لمعرفة المزيد حول أنماط التصميم المستخدمة في برمجة الألعاب ، أوصي بأن تدرس
أنماط برمجة ألعاب Robert Nystrom.
لمعرفة المزيد حول تقنيات C # المتقدمة ، خذ دورة
C # Collections و Lambdas و LINQ .
مهمة
كمهمة ، حاول الوصول إلى نقطة التحكم الخضراء في نهاية المتاهة. اختبأت واحدة من الحلول تحت المفسد.
قرار- moveUp × 2
- moveRight × 3
- moveUp × 2
- moveLeft
- تبادل لاطلاق النار
- moveLeft × 2
- moveUp × 2
- moveLeft × 2
- moveDown × 5
- moveLeft
- تبادل لاطلاق النار
- moveLeft
- moveUp × 3
- تبادل لاطلاق النار × 2
- moveUp × 5
- moveRight × 3