Menu
CoddyTech

عقول الأعداء

جزء من قسم هندسة الألعاب في Unity في رحلة Unity على Coddy. الدرس 39 من 64.

دماغ العدو عبارة عن آلة حالات لها حالة واحدة لكل سلوك. يتحرك هذا الهيكل العظمي بين مواقعه، ويطارد اللاعب إذا اقترب إلى مسافة 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 على transform، وهذا يجعل الهيكل العظمي يمشي عبر الجدران. لتحريك الهيكل العظمي باستخدام Rigidbody بدلًا من ذلك، تضبط WalkTo سرعة الجسم، ويحدّث الهيكل العظمي الآلة في FixedUpdate، حيث تمثل Time.deltaTime خطوة الفيزياء. لا تتغير الحالات الثلاث: فهي تحدد إلى أين يذهب، أما الكائن المالك فيحدد كيف.

challenge icon

التحدي

متوسط

أضف إلى الفارس PatrolState وChaseState في KnightStates.cs، ومرّر الفارس إلى كل منهما.

  1. الدورية: تحرّك إلى waypoints[next]؛ وعندما تصبح المسافة أقل من 0.3، انتقل إلى next التالي بالتتابع، مع الاحتفاظ بقيمته بين الزيارات. إذا أصبحت المسافة أقل من sightRange، فابدأ المطاردة.
  2. المطاردة: تحرّك نحو اللاعب؛ وإذا تجاوزت المسافة loseRange، فابدأ الدورية.
  3. في Knight، أنشئ آلة الحالات والحالات في Awake، وابدأ بالدورية في Start، ونفّذ التحديث في FixedUpdate.

اضغط «العب»، ثم اقترب واركض. ثم اضغط «تحقق».

جرّب بنفسك

using UnityEngine;

// PatrolState و ChaseState

جميع دروس هندسة الألعاب في Unity

تدرّب بنفسك: Playground لـ Unity