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



كما يتضح من الصورة، الظروف للتبديل بين الدول فقط سيستند عفريت المسافة والملكية. على سبيل المثال،
فريق عفريت
أو عفريت بعيداً جداً.
عملية الهجوم سيتكون من أربعة دول: الخمول، الهجوم
، وستيلبو
ك، وبورسويبوك
. حالة الخمول تم تنفيذها
بالفعل في البرنامج التعليمي السابق
، وهو نقطة الانطلاق لهذه العملية. من هناك، سوف التبديل
إلى الهجوم إذا كان الفريق عفريت، إلى ستيلبوك إذا كان الفريق المنافس عفريت، رياضي أو إلى بورسويبوك إذا كان عفريت
لا مالك وإغلاق
ما يكفي ليتم جمعها.
ويمثل الدولة هجوم
حركة هجومية. بينما في تلك الدولة، سيحاول رياضي يحمل الصولجان (المسماة
الزعيم) للوصول إلى مرمى المنافس. سيتم نقل زملائه على طول، في محاولة لدعم العمل.
الدولة ستيلبوك يمثل شيئا بين دفاعية
وهي حركة هجومية. بينما في تلك الدولة، ستركز رياضي على متابعة الخصم تحمل عفريت. الهدف استعادة عفريت، حتى يمكن بدء الفريق مهاجمة مرة أخرى.
وأخيراً، بورسويبوك الدولة لا تتصل بالهجوم أو الدفاع؛ وسوف توجه الرياضيين فقط عندما عفريت لا مالك. بينما في تلك الدولة، سيحاول رياضي
للحصول على الصولجان أن تتحرك بحرية على الحلبة (على سبيل المثال، بعد أن أصيبت بعصا لشخص ما).
تحديث حالة الخمول
حالة الخمول التي تم تنفيذها سابقا قد لا توجد انتقالات. نظراً لهذه الحالة هو نقطة الانطلاق لمنظمة العفو الدولية كلها، دعونا تحديثه وجعله قادراً على التبديل
إلى دول أخرى.
وقد حالة الخمول التحولات
الثلاثة:



إذا كان فريق الرياضي عفريت
، خمول ينبغي أن برزت من الدماغ
ويجب أن يدفع الهجوم. وبالمثل
، إذا كان الفريق المنافس عفريت، خمول ينبغي الاستعاضة ستيالبوك
. الانتقال المتبقية يحدث عندما لا أحد يملك
عفريت وقريب من الرياضي؛ وفي هذه الحالة، ينبغي دفع بورسويبوك في المخ.
الإصدار المحدث من الخمول
كما يلي (جميع الدول الأخرى ستنفذ في وقت لاحق):
1 |
class Athlete { |
2 |
// (...)
|
3 |
private function idle() :void { |
4 |
var aPuck :Puck = getPuck(); |
5 |
|
6 |
stopAndlookAt(aPuck); |
7 |
|
8 |
// This is a hack to help test the AI.
|
9 |
if (mStandStill) return; |
10 |
|
11 |
// Does the puck has an owner?
|
12 |
if (getPuckOwner() != null) { |
13 |
// Yeah, it has.
|
14 |
mBrain.popState(); |
15 |
|
16 |
if (doesMyTeamHaveThePuck()) { |
17 |
// My team just got the puck, it's attack time!
|
18 |
mBrain.pushState(attack); |
19 |
} else { |
20 |
// The opponent team got the puck, let's try to steal it.
|
21 |
mBrain.pushState(stealPuck); |
22 |
}
|
23 |
} else if (distance(this, aPuck) < 150) { |
24 |
// The puck has no owner and it is nearby. Let's pursue it.
|
25 |
mBrain.popState(); |
26 |
mBrain.pushState(pursuePuck); |
27 |
}
|
28 |
}
|
29 |
|
30 |
private function attack() :void { |
31 |
}
|
32 |
|
33 |
private function stealPuck() :void { |
34 |
}
|
35 |
|
36 |
private function pursuePuck() :void { |
37 |
}
|
38 |
}
|
دعونا المضي قدما في تنفيذ الدول الأخرى.
متابعة عفريت
والآن بعد أن اكتسبت بعض التصور
حول البيئة الرياضي وهي قادرة على التحول من الخمول إلى أي دولة، دعونا نركز على متابعة عفريت عندما يحتوي على لا مالك.
رياضي ستتحول إلى بورسويبوك فورا بعد بدء
المباراة، لأنه سيتم وضع عفريت في وسط الحلبة مع لا مالك. وقد بورسويبوك
الدولة التحولات الثلاثة:



الفترة الانتقالية الأولى هو عفريت بعيداً جداً، وأنه يحاول محاكاة ما يحدث في لعبة حقيقية فيما يتعلق مطاردة عفريت. لأسباب استراتيجية، عادة
هو الأقرب إلى عفريت الرياضي الذي يحاول القبض عليه، بينما الآخرين الانتظار أو في محاولة لمساعدة.
دون التبديل إلى الخمول عند عفريت بعيد
المنال، ستتابع كل رياضي تسيطر عليها منظمة العفو الدولية عفريت في
نفس الوقت، حتى لو كانوا بعيداً عن ذلك. عن طريق فحص المسافة بين الرياضي
وعفريت، بورسويبوك نفسها من الدماغ للملوثات العضوية الثابتة ويدفع خمول عند عفريت بعيد جداً، مما يعني الرياضي فقط "تخلى" تتابع عفريت:
1 |
class Athlete { |
2 |
// (...)
|
3 |
|
4 |
private function pursuePuck() :void { |
5 |
var aPuck :Puck = getPuck(); |
6 |
|
7 |
if (distance(this, aPuck) > 150) { |
8 |
// Puck is too far away from our current position, so let's give up
|
9 |
// pursuing the puck and hope someone will be closer to get the puck
|
10 |
// for us.
|
11 |
mBrain.popState(); |
12 |
mBrain.pushState(idle); |
13 |
} else { |
14 |
// The puck is close, let's try to grab it.
|
15 |
}
|
16 |
}
|
17 |
|
18 |
// (...)
|
19 |
}
|
عندما تكون العربة قريبة ، يجب على الرياضي أن يلاحقها ، والتي يمكن تحقيقها بسهولة مع سلوك البحث. باستخدام وضع الصولجان كوجهة البحث ، سوف يتابع الرياضي ببراعة القرص ويضبط مساره مع تحرك الصولجان:
1 |
class Athlete { |
2 |
// (...) |
3 |
private function pursuePuck() :void { |
4 |
var aPuck :Puck = getPuck(); |
5 |
|
6 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
7 |
|
8 |
if (distance(this, aPuck) > 150) { |
9 |
// Puck is too far away from our current position, so let's give up |
10 |
// pursuing the puck and hope someone will be closer to get the puck |
11 |
// for us. |
12 |
mBrain.popState(); |
13 |
mBrain.pushState(idle); |
14 |
} else { |
15 |
// The puck is close, let's try to grab it. |
16 |
if (aPuck.owner == null) { |
17 |
// Nobody has the puck, it's our chance to seek and get it! |
18 |
mBoid.steering = mBoid.steering + mBoid.seek(aPuck.position); |
19 |
|
20 |
} else { |
21 |
// Someone just got the puck. If the new puck owner belongs to my team, |
22 |
// we should switch to 'attack', otherwise I should switch to 'stealPuck' |
23 |
// and try to get the puck back. |
24 |
mBrain.popState(); |
25 |
mBrain.pushState(doesMyTeamHaveThePuck() ? attack : stealPuck); |
26 |
} |
27 |
} |
28 |
} |
29 |
} |
التحولات المتبقيين في حالة pursPuck
، الفريق لديه الصولجان وخصم
لديه عفريت ، ترتبط باك الوقوع أثناء عملية المتابعة
. إذا كان شخص ما يمسك بطرده ، يجب على الرياضي أن يمارس حالة pursPuck
ويدفع واحدة جديدة إلى الدماغ.
تعتمد الدولة التي يجب دفعها على ملكية الصولجان. إذا كانت الدعوة إلى doMyTeamHaveThePuck
() تعود حقيقية ، فهذا يعني أن أحد زملائه حصل على كرة ، لذا يجب على الرياضي دفع الهجوم
، وهو ما يعني أن الوقت قد حان لوقف السعي والقفز
والبدء في التحرك نحو هدف الخصم. إذا حصل الخصم على كرة صغيرة ، يجب على الرياضي دفع stealPuck
، الأمر الذي سيجعل الفريق يحاول استعادة العفريت.
كتعزيز صغير ، يجب أن لا يبقى الرياضيون
قريبين من بعضهم البعض خلال فترة المتابعة ، لأن حركة المتابعة "المزدحمة" غير طبيعية. إن إضافة الفصل إلى قوة توجيه الدولة (الخط 6 في
الكود أعلاه) يضمن للرياضيين الحفاظ على الحد الأدنى من المسافة فيما بينهم.
والنتيجة هي أن فريق قادر على متابعة عفريت. من أجل الاختبار ، في هذا العرض ، يتم وضع عفريت في وسط الحلبة كل بضع ثوان ، لجعل الرياضيين يتحركون باستمرار:
مهاجمة مع عفريت
بعد الحصول على عفريت ، يجب على الرياضي
وفريقه التحرك نحو هدف الخصم للتسجيل. هذا هو الهدف من حالة الهجوم:



لدى حالة الهجوم
تحويلين اثنين فقط: الخصم لديه عفريت و عفريت ليس لديه مالك. بما أن الدولة مصممة فقط لجعل الرياضيين يتحركون نحو هدف الخصم ، فليس
هناك ما يبرهن على الاستمرار في مهاجمتك إذا لم تكن لعبة الحظ ليست تحت ملكية الفريق.
فيما يتعلق بالحركة نحو هدف المنافس: يجب أن يتصرف اللاعب الرياضي الذي يحمل البطة (القائد) وزملاؤه لمساعدته بطريقة مختلفة. يجب على الزعيم الوصول إلى هدف الخصم ، وينبغي أن يساعده زملاؤه على طول الطريق.
يمكن تنفيذ ذلك من خلال التحقق مما إذا كان اللاعب الذي يدير الكود يحتوي على:
1 |
class Athlete { |
2 |
// (...)
|
3 |
private function attack() :void { |
4 |
var aPuckOwner :Athlete = getPuckOwner(); |
5 |
|
6 |
// Does the puck have an owner?
|
7 |
if (aPuckOwner != null) { |
8 |
// Yeah, it has. Let's find out if the owner belongs to the opponents team.
|
9 |
if (doesMyTeamHaveThePuck()) { |
10 |
if (amIThePuckOwner()) { |
11 |
// My team has the puck and I am the one who has it! Let's move
|
12 |
// towards the opponent's goal.
|
13 |
mBoid.steering = mBoid.steering + mBoid.seek(getOpponentGoalPosition()); |
14 |
|
15 |
} else { |
16 |
// My team has the puck, but a teammate has it. Let's just follow him
|
17 |
// to give some support during the attack.
|
18 |
mBoid.steering = mBoid.steering + mBoid.followLeader(aPuckOwner.boid); |
19 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
20 |
}
|
21 |
} else { |
22 |
// The opponent has the puck! Stop the attack
|
23 |
// and try to steal it.
|
24 |
mBrain.popState(); |
25 |
mBrain.pushState(stealPuck); |
26 |
}
|
27 |
} else { |
28 |
// Puck has no owner, so there is no point to keep
|
29 |
// attacking. It's time to re-organize and start pursuing the puck.
|
30 |
mBrain.popState(); |
31 |
mBrain.pushState(pursuePuck); |
32 |
}
|
33 |
}
|
34 |
}
|
إذا قام amIThePuckOwner
() بإرجاع true
(السطر 10) ، فإن اللاعب الذي يقوم بتشغيل الكود يكون ذا puck. في هذه الحالة ، سوف يبحث فقط عن موقف هدف الخصم. هذا إلى حد كبير نفس المنطق المستخدم في متابعة عفريت في حالة pursPuck
.
إذا ظهر amIThePuckOwner
() خطأ ، فإن اللاعب
لا يمتلك القطة ، لذا يجب عليه مساعدة القائد. مساعدة القائد مهمة معقدة ، لذلك سنقوم بتبسيطها. سوف يساعد الرياضي القائد فقط من خلال السعي للحصول على منصب متقدم أمامه:



كما يتحرك الزعيم، أنه سوف تكون محاطة بزملائه
كما أنها تتبع هذه النقطة إلى الأمام. هذا يعطي الزعيم بعض الخيارات لتمرير عفريت إذا كان هناك أي مشكلة. كما هو الحال في لعبة حقيقية ، يجب على زملائه في الفريق البقاء بعيداً عن طريقة القائد.
يمكن تحقيق نمط المساعدة هذا بإضافة نسخة معدلة قليلاً من سلوك القائد التالي (السطر 18). والفرق الوحيد هو أن الرياضيين سيتبعون نقطة متقدمة على القائد ، بدلاً من واحد خلفه كما تم تنفيذه أصلاً في هذا السلوك.
يجب أيضًا على الرياضيين الذين يساعدون القائد أن يبقوا مسافة بين بعضهم البعض. يتم تنفيذ ذلك عن طريق إضافة قوة فصل (السطر 19).
والنتيجة هي أن الفريق قادر على التحرك نحو هدف المنافس ، من دون التزاحم أو أثناء محاكاة حركة الهجوم المدعومة:
تحسين دعم الهجوم
إن التنفيذ الحالي لدولة الهجوم جيد بما فيه الكفاية لبعض
المواقف ، لكنه يعاني من خلل. عندما يمسك شخص بعصا ، يصبح القائد ويتبعه فورا زملاؤه.
ماذا يحدث إذا كان القائد يتحرك نحو هدفه الخاص عندما يمسك بالطرد؟ ألق نظرة فاحصة على العرض التوضيحي أعلاه ولاحظ النمط غير الطبيعي عندما يبدأ أعضاء الفريق في متابعة القائد.
عندما يمسك القائد بالطرد ، فإن سلوك البحث يستغرق بعض الوقت لتصحيح مسار القائد ويجعله يتحرك بفاعلية
نحو هدف الخصم. حتى عندما يقوم الزعيم "بالمناورة" ، سيحاول زملاؤه السعي وراء نقطة التقدم ، مما يعني أنهم سوف يتحركون نحو هدفهم الخاص (أو المكان الذي يحدق فيه القائد).
عندما يكون القائد أخيرا في موقعه ومستعد للتحرك نحو هدف الخصم ، فإن أعضاء الفريق سوف "يناورون" لمتابعة القائد. ثم ينتقل القائد دون دعم زميل طالما أن الآخرين يقومون بتعديل مساراتهم.
هذا الخلل يمكن أن تكون ثابتة عن طريق التحقق من سواء زميله قبل الزعيم عندما يسترد الفريق عفريت. وهنا، شرط "الأمام" يعني "أقرب إلى مرمى المنافس":
1 |
class Athlete { |
2 |
// (...)
|
3 |
private function isAheadOfMe(theBoid :Boid) :Boolean { |
4 |
var aTargetDistance :Number = distance(getOpponentGoalPosition(), theBoid); |
5 |
var aMyDistance :Number = distance(getOpponentGoalPosition(), mBoid.position); |
6 |
|
7 |
return aTargetDistance <= aMyDistance; |
8 |
}
|
9 |
|
10 |
private function attack() :void { |
11 |
var aPuckOwner :Athlete = getPuckOwner(); |
12 |
|
13 |
// Does the puck have an owner?
|
14 |
if (aPuckOwner != null) { |
15 |
// Yeah, it has. Let's find out if the owner belongs to the opponents team.
|
16 |
if (doesMyTeamHaveThePuck()) { |
17 |
if (amIThePuckOwner()) { |
18 |
// My team has the puck and I am the one who has it! Let's move
|
19 |
// towards the opponent's goal.
|
20 |
mBoid.steering = mBoid.steering + mBoid.seek(getOpponentGoalPosition()); |
21 |
|
22 |
} else { |
23 |
// My team has the puck, but a teammate has it. Is he ahead of me?
|
24 |
if (isAheadOfMe(aPuckOwner.boid)) { |
25 |
// Yeah, he is ahead of me. Let's just follow him to give some support
|
26 |
// during the attack.
|
27 |
mBoid.steering = mBoid.steering + mBoid.followLeader(aPuckOwner.boid); |
28 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
29 |
} else { |
30 |
// Nope, the teammate with the puck is behind me. In that case
|
31 |
// let's hold our current position with some separation from the
|
32 |
// other, so we prevent crowding.
|
33 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
34 |
}
|
35 |
}
|
36 |
} else { |
37 |
// The opponent has the puck! Stop the attack
|
38 |
// and try to steal it.
|
39 |
mBrain.popState(); |
40 |
mBrain.pushState(stealPuck); |
41 |
}
|
42 |
} else { |
43 |
// Puck has no owner, so there is no point to keep
|
44 |
// attacking. It's time to re-organize and start pursuing the puck.
|
45 |
mBrain.popState(); |
46 |
mBrain.pushState(pursuePuck); |
47 |
}
|
48 |
}
|
49 |
}
|
إذا كان القائد (صاحب البطة) متقدما على اللاعب الذي يدير الكود ، فيجب على اللاعب أن يتبع القائد مثلما كان يفعل من قبل (السطور 27 و 28). إذا كان القائد وراءه ، ينبغي للرياضي أن يمسك بموقفه الحالي ، مع الحفاظ على الحد الأدنى للمسافة بين الآخرين (السطر 33).
والنتيجة هي أكثر إقناعا من تنفيذ
الهجوم الأولي:
تلميح: من التغيير والتبديل بعد العمليات الحسابية والمقارنات في الأسلوب isAheadOfMe
()، فمن الممكن تعديل طريقة الرياضيين عقد مواقفها الحالية.
سرقة عفريت
الحالة النهائية في عملية الهجوم هي stealPuck
، والتي تصبح نشطة عندما يكون الفريق المنافس لديه. الغرض الرئيسي من حالة stealPuck
هو سرقة العفريت من الخصم الذي يحملها ، بحيث يمكن للفريق البدء في الهجوم مرة أخرى:



نظراً لأن الفكرة من وراء هذه الدولة لسرقة عفريت من الخصم، إذا كان يتم استرداد عفريت فريق أو أن يصبح
حراً (أي له لا المالك)، ستيلبوك سوف البوب نفسها من الدماغ ودفع الدولة الصحيح للتعامل مع الوضع الجديد :
1 |
class Athlete { |
2 |
// (...)
|
3 |
private function stealPuck() :void { |
4 |
// Does the puck have any owner?
|
5 |
if (getPuckOwner() != null) { |
6 |
// Yeah, it has, but who has it?
|
7 |
if (doesMyTeamHaveThePuck()) { |
8 |
// My team has the puck, so it's time to stop trying to steal
|
9 |
// the puck and start attacking.
|
10 |
mBrain.popState(); |
11 |
mBrain.pushState(attack); |
12 |
} else { |
13 |
// An opponent has the puck.
|
14 |
var aOpponentLeader :Athlete = getPuckOwner(); |
15 |
|
16 |
// Let's pursue him while mantaining a certain separation from
|
17 |
// the others to avoid that everybody will ocuppy the same
|
18 |
// position in the pursuit.
|
19 |
mBoid.steering = mBoid.steering + mBoid.pursuit(aOpponentLeader.boid); |
20 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
21 |
}
|
22 |
} else { |
23 |
// The puck has no owner, it is probably running freely in the rink.
|
24 |
// There is no point to keep trying to steal it, so let's finish the 'stealPuck' state
|
25 |
// and switch to 'pursuePuck'.
|
26 |
mBrain.popState(); |
27 |
mBrain.pushState(pursuePuck); |
28 |
}
|
29 |
}
|
30 |
}
|
إذا كان لعرب البطة مالك وهو ينتمي إلى فريق الخصم ، يجب على اللاعب متابعة القائد المنافس ومحاولة سرقة العفريت. من أجل متابعة زعيم الخصم ، يجب على الرياضي التنبؤ بمكانه في المستقبل القريب ، بحيث يمكن اعتراضه في مساره. هذا يختلف عن مجرد البحث عن زعيم المعارضة.
لحسن الحظ ، يمكن تحقيق ذلك بسهولة مع سلوك المتابعة (السطر 19). باستخدام قوة السعي في حالة stealPuck
، سيحاول الرياضيون اعتراض قائد الخصم ، بدلاً من مجرد متابعته:
منع حركة سرقة مزدحمة
يعمل التنفيذ الحالي ستيلبوك، ولكن في رياضيين
واحد فقط أو اثنين لعبة حقيقية نهج زعيم الخصم لسرقة عفريت. وتظل بقية الفريق في المناطق المحيطة بها في محاولة لمساعدة، مما يمنع نمط سرقة مزدحمة.
أنها يمكن أن تكون ثابتة عن طريق إضافة تدقيق مسافة (البند 17) قبل اتباع الزعيم المنافس:
1 |
class Athlete { |
2 |
// (...)
|
3 |
private function stealPuck() :void { |
4 |
// Does the puck have any owner?
|
5 |
if (getPuckOwner() != null) { |
6 |
// Yeah, it has, but who has it?
|
7 |
if (doesMyTeamHaveThePuck()) { |
8 |
// My team has the puck, so it's time to stop trying to steal
|
9 |
// the puck and start attacking.
|
10 |
mBrain.popState(); |
11 |
mBrain.pushState(attack); |
12 |
} else { |
13 |
// An opponent has the puck.
|
14 |
var aOpponentLeader :Athlete = getPuckOwner(); |
15 |
|
16 |
// Is the opponent with the puck close to me?
|
17 |
if (distance(aOpponentLeader, this) < 150) { |
18 |
// Yeah, he is close! Let's pursue him while mantaining a certain
|
19 |
// separation from the others to avoid that everybody will ocuppy the same
|
20 |
// position in the pursuit.
|
21 |
mBoid.steering = mBoid.steering.add(mBoid.pursuit(aOpponentLeader.boid)); |
22 |
mBoid.steering = mBoid.steering.add(mBoid.separation(50)); |
23 |
|
24 |
} else { |
25 |
// No, he is too far away. In the future, we will switch
|
26 |
// to 'defend' and hope someone closer to the puck can
|
27 |
// steal it for us.
|
28 |
// TODO: mBrain.popState();
|
29 |
// TODO: mBrain.pushState(defend);
|
30 |
}
|
31 |
}
|
32 |
} else { |
33 |
// The puck has no owner, it is probably running freely in the rink.
|
34 |
// There is no point to keep trying to steal it, so let's finish the 'stealPuck' state
|
35 |
// and switch to 'pursuePuck'.
|
36 |
mBrain.popState(); |
37 |
mBrain.pushState(pursuePuck); |
38 |
}
|
39 |
}
|
40 |
}
|
بدلاً من اتباع الزعيم المنافس عمياء، رياضي وسوف تحقق ما إذا كانت المسافة بينه وبين زعيم الخصم أقل من، أقول، 150
. إذا كان هذا صحيحاً، السعي إلى يحدث عادة، ولكن إذا كانت المسافة أكبر من 150
، فهذا يعني الرياضي بعيدة جداً
عن زعيم الخصم.
إذا حدث ذلك ، فلا فائدة من الاستمرار في محاولة سرقة العصا ، لأنها بعيدة جدا ، وربما يكون هناك زملاؤان بالفعل في مكان يحاولون فعل الشيء نفسه. أفضل خيار هو أن تفلح في السرقة
من الدماغ ودفع الدولة الدفاعية (التي سيتم شرحها في البرنامج التعليمي
التالي). في الوقت الحالي ، سيحمل أحد الرياضيين منصبه الحالي فقط إذا كان قائد الخصم بعيدًا جدًا.
والنتيجة هي نمط سرقة أكثر إقناعا وطبيعية (بدون ازدحام):
تجنب المعارضين في حين مهاجمة
هناك خدعة أخيرة يجب على الرياضيين تعلمها من أجل الهجوم بفعالية. في الوقت الحالي ، يتحركون نحو هدف المنافس دون النظر إلى الخصوم على طول الطريق. يجب أن ينظر إلى الخصم على أنه تهديد ، ويجب تجنبه.
باستخدام سلوك تجنب الاصطدام ، يمكن للرياضيين تفادي المعارضين أثناء حركتهم:



سوف ينظر إلى المعارضين على أنها عقبات دائرية. نتيجة للطبيعة الديناميكية لسلوكيات القيادة ، والتي يتم تحديثها في كل حلقة لعبة ، فإن نمط التجنب سوف يعمل بأمان وسلاسة لتحريك العوائق (وهو ما يحدث هنا).
من أجل جعل الرياضيين يتجنبون المعارضين (العوائق) ، يجب إضافة سطر واحد إلى حالة الهجوم (السطر 14):
1 |
class Athlete { |
2 |
// (...)
|
3 |
private function attack() :void { |
4 |
var aPuckOwner :Athlete = getPuckOwner(); |
5 |
|
6 |
// Does the puck have an owner?
|
7 |
if (aPuckOwner != null) { |
8 |
// Yeah, it has. Let's find out if the owner belongs to the opponents team.
|
9 |
if (doesMyTeamHaveThePuck()) { |
10 |
if (amIThePuckOwner()) { |
11 |
// My team has the puck and I am the one who has it! Let's move
|
12 |
// towards the opponent's goal, avoding any opponents along the way.
|
13 |
mBoid.steering = mBoid.steering + mBoid.seek(getOpponentGoalPosition()); |
14 |
mBoid.steering = mBoid.steering + mBoid.collisionAvoidance(getOpponentTeam().members); |
15 |
|
16 |
} else { |
17 |
// My team has the puck, but a teammate has it. Is he ahead of me?
|
18 |
if (isAheadOfMe(aPuckOwner.boid)) { |
19 |
// Yeah, he is ahead of me. Let's just follow him to give some support
|
20 |
// during the attack.
|
21 |
mBoid.steering = mBoid.steering + mBoid.followLeader(aPuckOwner.boid); |
22 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
23 |
} else { |
24 |
// Nope, the teammate with the puck is behind me. In that case
|
25 |
// let's hold our current position with some separation from the
|
26 |
// other, so we prevent crowding.
|
27 |
mBoid.steering = mBoid.steering + mBoid.separation(); |
28 |
}
|
29 |
}
|
30 |
} else { |
31 |
// The opponent has the puck! Stop the attack
|
32 |
// and try to steal it.
|
33 |
mBrain.popState(); |
34 |
mBrain.pushState(stealPuck); |
35 |
}
|
36 |
} else { |
37 |
// Puck has no owner, so there is no point to keep
|
38 |
// attacking. It's time to re-organize and start pursuing the puck.
|
39 |
mBrain.popState(); |
40 |
mBrain.pushState(pursuePuck); |
41 |
}
|
42 |
}
|
43 |
}
|
سيضيف هذا الخط قوة تجنب الاصطدام للرياضي ، الذي سيتم دمجه مع القوى الموجودة بالفعل. نتيجة لذلك ، سوف يتجنب اللاعب العقبات في نفس الوقت الذي يسعى فيه للحصول على هدف الخصم.
أدناه هو مظاهرة للرياضي يدير حالة الهجوم. المعارضين غير قادرين على تسليط
الضوء على سلوك تجنب الاصطدام:
استنتاج
وأوضح هذا البرنامج التعليمي تنفيذ نمط الهجوم الذي يستخدمه الرياضيون لسرقة وحمل العجلة نحو هدف الخصم. باستخدام مزيج من سلوكيات القيادة ، أصبح الرياضيون الآن قادرين على القيام بأنماط حركة معقدة ، مثل اتباع قائد أو متابعة الخصم مع عفريت.
كما نوقش سابقا، أهداف تنفيذ الهجوم على محاكاة ما تفعله البشر، حيث تكون النتيجة تقريبي للعبة حقيقية. باللف فرادى الدول التي يؤلف الهجوم، يمكنك إنتاج محاكاة أفضل، أو واحد يلائم احتياجاتك.
في البرنامج التعليمي التالي، وسوف تتعلم كيفية جعل الدفاع عن الرياضيين. منظمة العفو الدولية سوف تصبح ميزة كاملة، وقادرة على الهجوم والدفاع، أدى مباراة مع 100 ٪ تسيطر عليها منظمة العفو الدولية فرق اللعب ضد بعضها البعض.