عقول الأعداء
جزء من قسم هندسة الألعاب في Unity في رحلة Unity على Coddy. الدرس 31 من 50.
عقل العدو عبارة عن آلة حالات، بحالة واحدة لكل سلوك. هذا الهيكل العظمي يتنقّل بين نقاط حراسته، ويطارد اللاعب إذا اقترب لمسافة تقل عن 6 وحدات، ويضرب بسيفه مرة كل ثانية عندما يكون قريبًا. تحدد كل حالة بنفسها كيفية الانتقال منها، ويحتفظ الهيكل العظمي بالمساعدات المشتركة بين الحالات:
public class Skeleton : MonoBehaviour
{
public Transform player;
public Transform[] posts;
public float speed = 2f;
public StateMachine machine = new StateMachine();
public IState patrol, chase, attack;
void Start()
{
patrol = new PatrolState(this);
chase = new ChaseState(this);
attack = new AttackState(this);
machine.ChangeState(patrol);
}
void Update() { machine.Tick(); }
public float PlayerDistance() { return Vector3.Distance(transform.position, player.position); }
public void WalkTo(Vector3 target)
{
target.y = transform.position.y;
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
public class PatrolState : IState
{
private Skeleton skel;
private int next;
public PatrolState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() < 6f) { skel.machine.ChangeState(skel.chase); return; }
Vector3 post = skel.posts[next].position;
post.y = skel.transform.position.y;
skel.WalkTo(post);
if (skel.transform.position == post) next = (next + 1) % skel.posts.Length;
}
}
public class ChaseState : IState
{
private Skeleton skel;
public ChaseState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
float d = skel.PlayerDistance();
if (d > 6f) { skel.machine.ChangeState(skel.patrol); return; }
if (d < 1.5f) { skel.machine.ChangeState(skel.attack); return; }
skel.WalkTo(skel.player.position);
}
}
public class AttackState : IState
{
private Skeleton skel;
private float cooldown;
public AttackState(Skeleton skel) { this.skel = skel; }
public void Enter() { cooldown = 0f; }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() > 1.5f) { skel.machine.ChangeState(skel.chase); return; }
cooldown -= Time.deltaTime;
if (cooldown <= 0f) { Debug.Log("Swing!"); cooldown = 1f; }
}
}تُنشأ الحالات مرة واحدة وتظل موجودة طوال عمر الهيكل العظمي، لذلك تبقى حقولها محفوظة بين مرات الدخول إليها. تظل قيمة next في PatrolState كما هي بعد المطاردة، ويواصل الهيكل العظمي المشي نحو نقطة الحراسة التي كان متجهًا إليها. تعيد Enter ضبط ما ينبغي أن يبدأ من جديد فقط: تضبط cooldown في AttackState إلى 0، لذا تحدث الضربة الأولى فور وصول الهيكل العظمي إلى اللاعب.
تتم كل الحركة عبر WalkTo، وهي هنا تستخدم MoveTowards على المحوّل، ما يجعل الهيكل العظمي يمشي عبر الجدران. لتحريك الهيكل العظمي باستخدام Rigidbody بدلًا من ذلك، تضبط WalkTo سرعة الجسم، ويحدّث الهيكل العظمي آلة الحالات في FixedUpdate، حيث تمثل Time.deltaTime خطوة الفيزياء. لا تتغير الحالات الثلاث: فهي تحدد إلى أين يذهب، أما الكائن المالك فيحدد كيفية ذلك.
التحدي
متوسطيحرس فارس ساحة القلعة. تتبع الأسهم اللاعب، الذي لديه Health وتملك TakeDamage(int amount) (كلاهما جاهز). يحتوي Knight.cs على الحقول التي يملؤها Inspector (waypoints من W0 إلى W2، وplayer، وplayerHealth، وspeed بقيمة 3، والنطاقات أدناه)، وعلى دوال مساعدة تستخدم Rigidbody الخاص به: تقيس DistanceTo(point) وPlayerDistance() المسافة عبر الأرض، وتتحرك WalkTo(target) نحو نقطة، بينما تجعل Stop() الفارس ثابتًا. امنحه عقلًا:
- في
Knight.cs، أنشئ الآلة والحالات الثلاث (الحقولpatrolوchaseوattack)، وابدأ بالدورية، ونفّذ Tick للآلة في FixedUpdate. - في
KnightStates.cs، اكتبPatrolStateوChaseStateوAttackState، على أن يأخذ كل منها الفارس في المُنشئ. - الدورية: تحرّك إلى
waypoints[next]؛ وعندما تصبح المسافة إليها أقل من 0.3، اتجه إلى النقطة التالية، وبعد الأخيرة عُد إلى الأولى. إذا كان اللاعب أقرب منsightRange(5)، فطارده. بعد المطاردة، تابِع الدورية إلى نقطة الطريق التي كان الفارس متجهًا إليها. - المطاردة: تحرّك نحو اللاعب. إذا أصبحت المسافة أقل من
attackRange(1.5)، فهاجم. وإذا أصبحت أكبر منloseRange(8)، فعُد إلى الدورية. تستمر المطاردة بين 5 و8، كيلا يتنقل الفارس بين الحالتين عند حافة مجال رؤيته. - الهجوم: ابقَ ثابتًا واضرب اللاعب مرة واحدة فورًا، ثم كل ثانية، ما دام ضمن
attackRange؛ وعندما يبتعد أكثر، عاود مطاردته.
اضغط Play، ودع الفارس يراك، وتلقَّ ضربة، ثم اهرب، وبعدها اضغط Check.
جرّب بنفسك
using UnityEngine;
// PatrolState وChaseState وAttackState
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس هندسة الألعاب في Unity
1المكوّنات لا التسلسلات الهرمية
مكوّنات صغيرةسكربت أساسي للعدومكوّنات مجرّدةتعمل المكوّنات معًامراجعة: برج دفاعي معياري4البيانات المتسلسلة
الأصناف القابلة للتسلسلالقوائم في Inspectorالتعدادات في Inspectorمراجعة: جدول الغنائم10مشروع: الناجي في الساحة
البطلأنواع الأعداء