إنشاء هوكي لعبة منظمة العفو الدولية استخدام سلوكيات التوجيه: مؤسسة
Arabic (العربية/عربي) translation by Aeni Amalia (you can also view the original English article)
وهناك طرق مختلفة لجعل أي لعبة معينة. عادة ، يختار المطور شيئًا يناسب مهاراته ، باستخدام التقنيات التي يعرفها بالفعل لتحقيق أفضل نتيجة ممكنة. في بعض الأحيان، الناس لا يعرفون حتى الآن أنها تحتاج إلى تقنية معينة – وربما حتى أسهل وأفضل واحد – ببساطة لأنهم يعرفون بالفعل طريقة لإنشاء تلك اللعبة.
في هذه السلسلة من الدروس، سوف تتعلم كيفية إنشاء الذكاء الاصطناعي للعبة هوكي باستخدام مجموعة من التقنيات، مثل السلوكيات التوجيهي، لقد شرحت سابقا كمفاهيم.
ملاحظة: على الرغم من كتابة هذا البرنامج التعليمي باستخدام AS3 و Flash ، يجب أن تكون قادرًا على استخدام نفس الأساليب والمفاهيم في أي بيئة تطوير ألعاب.
مقدمة
الهوكي هو متعة ورياضة شعبية، كلعبة فيديو، وهو يتضمن العديد من المواضيع gamedev، مثل أنماط الحركة والعمل الجماعي (الهجوم والدفاع)، والذكاء الاصطناعي، والتكتيكات. إن لعبة الهوكي القابلة للتشغيل هي مناسبة تمامًا لإظهار مزيج من بعض التقنيات المفيدة.
لمحاكاة الهوكي ميكانيكي، مع الرياضيين يعمل ويتحرك، يشكل تحديا. إذا كانت أنماط الحركة محددة مسبقًا ، حتى مع المسارات المختلفة ، تصبح اللعبة متوقعة (ومملة). كيف يمكننا تنفيذ مثل هذه البيئة الديناميكية مع الحفاظ على السيطرة على ما يجري؟ الجواب: استخدام سلوكيات التوجيه.
السلوكيات التوجيهية تهدف إلى خلق أنماط حركة واقعية مع الملاحة ارتجالية. أنها تستند إلى قوات بسيطة أن يتم الجمع بين كل تحديث اللعبة، حيث تكون الغاية ديناميكية في طبيعتها. وهذا يجعلهم الخيار الأمثل لتنفيذ شيء معقد وديناميكي مثل لعبة الهوكي أو لعبة كرة القدم.
تحديد نطاق عمل
من أجل الوقت والتدريس، دعونا تقليص نطاق اللعبة قليلاً. ستتبع لعبة الهوكي الخاصة بنا مجموعة صغيرة فقط من القواعد الأصلية للرياضة: في لعبتنا لن تكون هناك عقوبات ولا حارس مرمى ، لذا يمكن لكل رياضي التحرك حول الحلبة:



سيتم استبدال كل هدف "جدار" صغير بدون شبكة. من أجل نقاط، يجب نقل فريق عفريت (قرص) لجعلها تعمل باللمس أي جانب من مرمى المنافس. عندما يسجل شخص ما النتيجة ، سيعيد الفريقان تنظيمهما ، وسيتم وضع العصا في المركز. ستتم إعادة تشغيل المباراة بعد ثوانٍ قليلة.
وفيما يتعلق بالتعامل مع عفريت: إذا كان رياضي، أقول، لقد عفريت، وتطرق من قبل خصم، ويقول ب، ثم ب المكاسب عفريت ويصبح A غير المنقولة لبضع ثوان. إذا كان أي وقت مضى يترك عفريت الحلبة، سيتم وضع في مركز حلبة فورا.
سأستخدم محرك ألعاب Flixel للعناية بالجزء الرسومي من الشفرة. ومع ذلك، سيتم المبسطة رمز محرك أو أغفلت في الأمثلة، الحفاظ على التركيز على اللعبة نفسها.
هيكلة البيئة
دعونا نبدأ مع بيئة اللعبة ، والتي تتكون من حلبة تزلج ، وعدد من الرياضيين ، واثنين من الأهداف. الحلبة وتتكون من أربعة مستطيلات توضع حول منطقة الجليد؛ وسوف تصطدم هذه المستطيلات مع كل ما يمس منها، حيث لا شيء سوف يغادر منطقة الجليد.
سيتم وصف رياضي من قبل
الرياضي:
1 |
public class Athlete |
2 |
{
|
3 |
private var mBoid :Boid; // controls the steering behavior stuff |
4 |
private var mId :int; // a unique identifier for the athelete |
5 |
|
6 |
public function Athlete(thePosX :Number, thePosY :Number, theTotalMass :Number) { |
7 |
mBoid = new Boid(thePosX, thePosY, theTotalMass); |
8 |
}
|
9 |
|
10 |
public function update():void { |
11 |
// Clear all steering forces
|
12 |
mBoid.steering = null; |
13 |
|
14 |
// Wander around
|
15 |
wanderInTheRink(); |
16 |
|
17 |
// Update all steering stuff
|
18 |
mBoid.update(); |
19 |
}
|
20 |
|
21 |
private function wanderInTheRink() :void { |
22 |
var aRinkCenter :Vector3D = getRinkCenter(); |
23 |
|
24 |
// If the distance from the center is greater than 80,
|
25 |
// move back to the center, otherwise keep wandering.
|
26 |
if (Utils.distance(this, aRinkCenter) >= 80) { |
27 |
mBoid.steering = mBoid.steering + mBoid.seek(aRinkCenter); |
28 |
} else { |
29 |
mBoid.steering = mBoid.steering + mBoid.wander(); |
30 |
}
|
31 |
}
|
32 |
}
|
الخاصية mBoid
هي مثال لفئة Void ، وتغليف منطق الرياضيات المستخدم في سلسلة سلوكيات التوجيه. وقد مثيل مبويد، بين عناصر أخرى، موجهات الرياضيات تصف الاتجاه الحالي وتوجيه القوة، والموقف من الكيان.
سيتم استدعاء الأسلوب update () في فئة الرياضي في كل مرة تحديث اللعبة. الآن، فقط مسح أي قوة التوجيه النشط، ويضيف قوة تجول، وأخيراً تدعو mBoid.update(). الأمر السابق يقوم بتحديث جميع منطق السلوك التوجيهي مغلفة داخل مبويد، مما يجعل الرياضي التحرك (باستخدام تكامل أويلر).
ستسمى فئة اللعبة ، المسؤولة عن لعبة الحلقة ، PlayState
. يحتوي على الحلبة ومجموعتين من الرياضيين (مجموعة واحدة لكل فريق) وهدفين اثنين هما:
1 |
public class PlayState |
2 |
{
|
3 |
private var mAthletes :FlxGroup; |
4 |
private var mRightGoal :Goal; |
5 |
private var mLeftGoal :Goal; |
6 |
|
7 |
public function create():void { |
8 |
// Here everything is created and added to the screen.
|
9 |
}
|
10 |
|
11 |
override public function update():void { |
12 |
// Make the rink collide with athletes
|
13 |
collide(mRink, mAthletes); |
14 |
|
15 |
// Ensure all athletes will remain inside the rink.
|
16 |
applyRinkContraints(); |
17 |
}
|
18 |
|
19 |
private function applyRinkContraints() :void { |
20 |
// check if athletes are within the rink
|
21 |
// boundaries.
|
22 |
}
|
23 |
}
|
على افتراض أنه تمت إضافة رياضي واحد للمباراة، أدناه هو نتيجة لكل شيء حتى الآن:
عقب مؤشر الماوس
الرياضي يجب أن يتبع مؤشر الماوس، حيث اللاعب يمكن التحكم في الواقع شيئا. نظرًا لأن مؤشر الماوس يحتوي على موضع على الشاشة ، يمكن استخدامه كوجهة لسلوك الوصول.
سيؤدي سلوك الوصول الرياضي إلى البحث عن موضع المؤشر ، وإبطاء السرعة بسلاسة أثناء اقترابه من المؤشر ، والتوقف في نهاية المطاف هناك.
في فئة الرياضي ، دعنا نستبدل طريقة التجوال بسلوك الوصول:
1 |
public class Athlete |
2 |
{
|
3 |
// (...)
|
4 |
|
5 |
public function update():void { |
6 |
// Clear all steering forces
|
7 |
mBoid.steering = null; |
8 |
|
9 |
// The athlete is controlled by the player,
|
10 |
// so just follow the mouse cursor.
|
11 |
followMouseCursor(); |
12 |
|
13 |
// Update all steering stuff
|
14 |
mBoid.update(); |
15 |
}
|
16 |
|
17 |
private function followMouseCursor() :void { |
18 |
var aMouse :Vector3D = getMouseCursorPosition(); |
19 |
mBoid.steering = mBoid.steering + mBoid.arrive(aMouse, 50); |
20 |
}
|
21 |
}
|
والنتيجة هي رياضي يمكن رأس مؤشر الماوس. منطق الحركة مبنية على السلوكيات التوجيهي، انتقل الرياضيين الحلبة بطريقة مقنعة وسلس.
استخدم مؤشر الماوس لتوجيه الرياضي في العرض التوضيحي أدناه
إضافة والتحكم عفريت
سيتم تمثيل عفريت من قبل صفة الطبقة. هي أهم أجزاء الأسلوب update ()
والخاصية مونير
:
1 |
public class Puck |
2 |
{
|
3 |
public var velocity :Vector3D; |
4 |
public var position :Vector3D; |
5 |
private var mOwner :Athlete; // the athlete currently carrying the puck. |
6 |
|
7 |
public function setOwner(theOwner :Athlete) :void { |
8 |
if (mOwner != theOwner) { |
9 |
mOwner = theOwner; |
10 |
velocity = null; |
11 |
}
|
12 |
}
|
13 |
|
14 |
public function update():void { |
15 |
}
|
16 |
|
17 |
public function get owner() :Athlete { return mOwner; } |
18 |
}
|
بعد نفس المنطق للرياضي ، سيتم استدعاء طريقة التحديث ()
في كل مرة يتم فيها تحديث اللعبة. تحدد الخاصية مونير سواء عفريت في حيازة أي رياضي. إذا كان موينر لاغياً ، فهذا يعني أن عفريت هو "حر" ، وسوف يتحرك ، فينتفض في نهاية المطاف قبالة من حلبة التزلج على الجليد.
إذا لم يكن مونير لاغياً ، فهذا يعني أن العجل يحمله رياضي. وفي هذه الحالة، فإنه سيتم تجاهل أي تدقيق الاصطدام وسوف توضع قوة قبل الرياضي. يمكن تحقيق ذلك باستخدام ناقل السرعة الرياضي، والذي يتطابق أيضا مع الاتجاه الرياضي:



المتجه إلى الأمام هو نسخة من ناقل السرعة الخاص باللاعب الرياضي ، لذلك فهو يشير في نفس الاتجاه. بعد أن قدما هو تطبيع، يمكن تحجيمه بأي قيمة – أقول، 30 – للتحكم في مدى سيتم وضع عفريت قبل الرياضي.
أخيرا ، يتلقى موقف عفريت موقف الرياضي المضافة إلى الأمام ، ووضع عفريت في الموضع المطلوب.
فيما يلي هي الرمز لكل ما:
1 |
public class Puck |
2 |
{
|
3 |
// (...)
|
4 |
|
5 |
private function placeAheadOfOwner() :void { |
6 |
var ahead :Vector3D = mOwner.boid.velocity.clone(); |
7 |
|
8 |
ahead = normalize(ahead) * 30; |
9 |
position = mOwner.boid.position + ahead; |
10 |
}
|
11 |
|
12 |
override public function update():void { |
13 |
if (mOwner != null) { |
14 |
placeAheadOfOwner(); |
15 |
}
|
16 |
}
|
17 |
|
18 |
// (...)
|
19 |
}
|
في فئة PlayState
، يوجد اختبار تصادم للتحقق مما إذا كان العقب يتداخل مع أي رياضي. إذا كان الأمر كذلك ، فإن اللاعب الذي لمس البطة يصبح مالكه الجديد. والنتيجة هي عفريت "العصي" للرياضي. في أدناه التجريبي، دليل الرياضي للمس عفريت في وسط الحلبة ترى في هذا العمل:
ضرب عفريت
لقد حان الوقت لجعل عفريت الانتقال نتيجة للضرب بالعصا. بغض النظر عن الرياضي الذي يحمل العفريت ، فإن كل ما هو مطلوب لمحاكاة ضربة من العصا هو حساب ناقل السرعة الجديد. هذه السرعة الجديدة سوف تحرك العجلة نحو الوجهة المطلوبة.
يمكن توليد ناقل السرعة من خلال ناقل واحد من آخر ؛ ثم ينتقل المتجه الجديد من مكان إلى آخر. هذا بالضبط ما مطلوب لحساب متجه السرعة الجديدة عفريت بعد ضرب:



في الصورة أعلاه ، تكون نقطة الوجهة هي مؤشر الماوس. الموقف الحالي عفريت يمكن استخدامها كنقطة انطلاق، في حين أن النقطة التي ينبغي أن يكون فيها عفريت بعد أنها ضربت العصا يمكن أن تستخدم كنقطة النهاية.
يُظهر الكود الزائف أدناه تنفيذ goFromStickHit ()
، وهو أسلوب في فئة Puck
يقوم بتنفيذ المنطق الموضح في الصورة أعلاه:
1 |
public class Puck |
2 |
{
|
3 |
// (...)
|
4 |
|
5 |
public function goFromStickHit(theAthlete :Athlete, theDestination :Vector3D, theSpeed :Number = 160) :void { |
6 |
// Place the puck ahead of the owner to prevent unexpected trajectories
|
7 |
// (e.g. puck colliding the athlete that just hit it)
|
8 |
placeAheadOfOwner(); |
9 |
|
10 |
// Mark the puck as free (no owner)
|
11 |
setOwner(null); |
12 |
|
13 |
// Calculate the puck's new velocity
|
14 |
var new_velocity :Vector3D = theDestination - position; |
15 |
velocity = normalize(new_velocity) * theSpeed; |
16 |
}
|
17 |
}
|
ينتقل المتجه new_velocity
من الموضع الحالي للقفزة إلى الهدف (theDestination).
بعد ذلك ، يتم تطبيعه وقياسه بواسطة سرعة ، والتي تحدد حجم (طول) من new_velocity.
وتعرف تلك العملية، وبعبارة أخرى، كيف سريع عفريت سوف ينتقل من موضعه الحالي إلى الوجهة. وأخيرا ، يتم استبدال ناقل السرعة سرعة الجرو من قبل new_velocity
.
في فئة PlayState
، يتم استدعاء أسلوب goFromStichHit ()
في كل مرة ينقر فيها اللاعب على الشاشة. عندما يحدث ذلك، يتم استخدام مؤشر الماوس للضرب كالوجهة. ويعتبر النتيجة في هذا العرض التوضيحي:
مشيراً إلى النيابة
حتى الآن ، كان لدينا فقط رياضي واحد يتحرك حول الحلبة. مع إضافة المزيد من الرياضيين ، يجب تنفيذ الذكاء الاصطناعي لجعل كل هؤلاء الرياضيين يبدو وكأنهم أحياء ويفكرون.
ولتحقيق ذلك، سوف نستخدم جهاز دولة محدودة المستندة إلى المكدس (المستند إلى المكدس ولايات ميكرونيزيا الموحدة، لفترة قصيرة). كما سبق ذكره، FSMs تنوعاً ومفيدة لتنفيذ منظمة العفو الدولية في الألعاب.
بالنسبة إلى لعبة الهوكي الخاصة بنا ، ستتم إضافة خاصية تسمى mBrain
إلى صف رياضي:
1 |
public class Athlete |
2 |
{
|
3 |
// (...)
|
4 |
private var mBrain :StackFSM; // controls the AI stuff |
5 |
|
6 |
public function Athlete(thePosX :Number, thePosY :Number, theTotalMass :Number) { |
7 |
// (...)
|
8 |
mBrain = new StackFSM(); |
9 |
}
|
10 |
|
11 |
// (...)
|
12 |
}
|
هذه الخاصية مثيل من StackFSM
، فئة المستخدمة سابقاً في البرنامج التعليمي FSM. فإنه يستخدم مكدس للتحكم في دول منظمة العفو الدولية كيان. يتم وصف كل دولة كوسيلة؛ عندما يتم الضغط دولة إلى المكدس، يصبح الأسلوب النشط، ويسمى أثناء كل لعبة التحديث.
كل دولة سوف تؤدي مهمة محددة، مثل نقل الرياضي نحو عفريت. كل دولة مسؤولة عن إنهاء نفسها، مما يعني أنها مسؤولة عن ظهرت نفسها من المكدس.
يمكن التحكم في اللاعب من قبل اللاعب أو من قبل AI ا
لآن ، لذلك يجب تعديل طريقة التحديث ()
في فئة الرياضي للتحقق من هذا الوضع:
1 |
public class Athlete |
2 |
{
|
3 |
// (...)
|
4 |
|
5 |
public function update():void { |
6 |
// Clear all steering forces
|
7 |
mBoid.steering = null; |
8 |
|
9 |
if (mControlledByAI) { |
10 |
// The athlete is controlled by the AI. Update the brain (FSM) and
|
11 |
// stay away from rink walls.
|
12 |
mBrain.update(); |
13 |
|
14 |
} else { |
15 |
// The athlete is controlled by the player, so just follow
|
16 |
// the mouse cursor.
|
17 |
followMouseCursor(); |
18 |
}
|
19 |
|
20 |
// Update all steering stuff
|
21 |
mBoid.update(); |
22 |
}
|
23 |
}
|
إذا كانت AI نشطة ، يتم تحديث mBrain
، والذي يستدعي طريقة الحالة النشطة حاليًا ، مما يجعل الرياضي يتصرف وفقًا لذلك. إذا كان اللاعب تحت السيطرة ، يتم تجاهل mBrain
جميعًا معًا ويتحرك اللاعب كما يسترشد به اللاعب.
فيما يتعلق بالدول لدفع إلى الدماغ: الآن دعونا تنفيذ اثنين فقط منهم. ستسمح إحدى الدول للرياضي بإعداد نفسه للمباراة ؛ عند التحضير للمباراة ، سينتقل اللاعب إلى موقعه في حلبة التزلج والوقوف بلا حراك ، ويحدق في عفريت. الدولة الأخرى ستجعل الرياضي يقف مكتوفي الأيدي ويقف في البطة.
في المقاطع التالية، سوف ننفذ هذه الدول.
حالة الخمول
إذا كان اللاعب في حالة الخمول ، سيتوقف عن التحرك والتحديق في عفريت. يتم استخدام هذه الدولة عند الرياضي بالفعل في الموقف في الحلبة وهو في انتظار أن يحدث، مثل بدء المباراة شيء.
سيتم ترميز الدولة في فئة الرياضي ، تحت الأسلوب الخالي ():
1 |
public class Athlete |
2 |
{
|
3 |
// (...)
|
4 |
public function Athlete(thePosX :Number, thePosY :Number, theTotalMass :Number, theTeam :FlxGroup) { |
5 |
// (...)
|
6 |
|
7 |
// Tell the brain the current state is 'idle'
|
8 |
mBrain.pushState(idle); |
9 |
}
|
10 |
|
11 |
private function idle() :void { |
12 |
var aPuck :Puck = getPuck(); |
13 |
stopAndlookAt(aPuck.position); |
14 |
}
|
15 |
|
16 |
private function stopAndlookAt(thePoint :Vector3D) :void { |
17 |
mBoid.velocity = thePoint - mBoid.position; |
18 |
mBoid.velocity = normalize(mBoid.velocity) * 0.01; |
19 |
}
|
20 |
}
|
أن هذا الأسلوب لا البوب نفسها من المكدس، سيبقى نشطاً إلى الأبد. في المستقبل، سوف البوب هذه الدولة نفسها لإفساح المجال لدول أخرى، مثل الهجوم، ولكن الآن أنه لا الحيلة.
الأسلوب stopAndStareAt()
يتبع نفس المبدأ يستخدم لحساب السرعة عفريت بعد ضرب. يتم احتساب ناقل من موقف الرياضي إلى موقف عفريت من قبل نقطة - mBoid.position
واستخدامها كالمتجه السرعة الجديدة للرياضي.
هذا ناقل السرعة الجديد سيحرك الرياضي نحو عفريت. لضمان أنه لن يتحرك الرياضي، يتم تحجيم المتجهة من 0.01
، "تقلص" طوله إلى الصفر تقريبا. فإنه يجعل الرياضي وقف التحرك، ولكن يبقى له يحدق في عفريت.
التحضير لمباراة
إذا كان اللاعب في حالة الاستعداد FORMatch
، فسوف ينتقل إلى موقعه الأولي ، ويتوقف هناك بسلاسة. الموقف الأولي هو المكان الذي يجب أن يكون الرياضي على حق قبل بدء المباراة. حيث ينبغي وقف الرياضي في الوجهة، يمكن استخدام السلوك الوصول مرة أخرى:
1 |
public class Athlete |
2 |
{
|
3 |
// (...)
|
4 |
private var mInitialPosition :Vector3D; // the position in the rink where the athlete should be placed |
5 |
|
6 |
public function Athlete(thePosX :Number, thePosY :Number, theTotalMass :Number, theTeam :FlxGroup) { |
7 |
// (...)
|
8 |
mInitialPosition = new Vector3D(thePosX, thePosY); |
9 |
|
10 |
// Tell the brain the current state is 'idle'
|
11 |
mBrain.pushState(idle); |
12 |
}
|
13 |
|
14 |
private function prepareForMatch() :void { |
15 |
mBoid.steering = mBoid.steering + mBoid.arrive(mInitialPosition, 80); |
16 |
|
17 |
// Am I at the initial position?
|
18 |
if (distance(mBoid.position, mInitialPosition) <= 5) { |
19 |
// I'm in position, time to stare at the puck.
|
20 |
mBrain.popState(); |
21 |
mBrain.pushState(idle); |
22 |
}
|
23 |
}
|
24 |
|
25 |
// (...)
|
26 |
}
|
الدولة يستخدم سلوك وصول التحرك الرياضي نحو الموقف المبدئي. إذا كانت المسافة بين اللاعب وموقعه الأولي أقل من 5
، فهذا يعني أن اللاعب قد وصل إلى المكان المرغوب. عندما يحدث هذا ، ستستعد FormFatch
نفسها من المكدس وتدفع الخمول ، مما يجعلها الحالة النشطة الجديدة.
أدناه هو نتيجة استخدام FSM المستندة إلى مكدس للسيطرة على العديد من الرياضيين. مواقع ز الصحافة لوضعها بصورة عشوائية في الحلبة، دفع الدولة بريباريفورماتش:
الاستنتاج
وقدم هذا البرنامج التعليمي الأسس اللازمة لتنفيذ لعبة هوكي استخدام سلوكيات التوجيه وآلات الدولة المحدودة المستندة إلى المكدس. باستخدام مزيج من تلك المفاهيم، رياضي قادرة على التحرك في الحلبة، عقب مؤشر الماوس. ويمكن ضرب الرياضي أيضا عفريت نحو وجهة.
استخدام الدولتين وولايات ميكرونيزيا الموحدة المستندة إلى المكدس، الرياضيين يمكن إعادة تنظيم ونقل إلى موضعها في الحلبة، التحضير للمباراة.
في البرنامج التعليمي التالي ، سوف تتعلم كيفية جعل الرياضيين يهاجمون ، ويحملون العجلة نحو الهدف مع تجنب الخصوم.
مراجع
- العفريت: استاد الهوكي على جرافيكريفير
- العفاريت: لاعبو الهوكي التي كتبها تايلور J Glidden