Главная » Статьи » Программирование » Unity

Пошаговая система боя для Unity
Как мы все знаем во многих играх разработчикам выбирают для проведения боя пошаговую систему. Такая система пользуется популярностью в пошаговых играх, например, серия игр герои. Я разумеется не буду рассматривать весь механизм работы пошаговой игры, такие моменты как переход от “обычного” режима в режим боя. Все это реализовать будете вы, так как в каждой игре это все выглядит по своему и написать один уникальный скрипт не получится. Хотя, если там будет куча странного кода, то получится, но производительности это не добавит.
Я буду рассматривать пошаговый бой как команда на команду. Одной командой управляет игрок, другой командой компьютер. Не забываем, что все приходит во время игры и игрок не должен понимать, что ему противостоит компьютер, поэтому обязательно добавляйте в код какие-нибудь паузы которые задержат решение компьютера о том как проводить атаку и все такое. На моей карте боя будут две команды по два бойца. Процесс боя будет заключаться в следующем: Атакует одна из команд, какая именно будет зависит от входных параметров. У меня их нет потому, что у меня одна сцена. В вашем случае должен будет быть входной параметр, который будет определять, кто первым атакует. Когда все члены атакующей команды завершат свои атаки, передаем управление второй команде.
В моем случае управляющий скрипт выглядит так:

public class BattleSystemScript : MonoBehaviour
{

    /// <summary>
    /// Сейчас ходит команда слева?
    /// </summary>
    public bool hodLeft = true;
    /// <summary>
    /// Массив мобов игрока
    /// </summary>
    public BSMobScript[] playerGroup;
    /// <summary>
    /// Массив мобов противника
    /// </summary>
    public BSMobScript[] enemyGroup;
    /// <summary>
    /// Указатель на скрипт управления игрока
    /// </summary>
    public BattleSystemPlayerControlScript playerScript;
    /// <summary>
    /// Указатель на спрайты выделения объектов
    /// </summary>
    public Transform selectObj, targetObj;
    /// <summary>
    /// Указатели на выделенные объекты
    /// </summary>
    public BSMobScript selectMob, targetMob;
    /// <summary>
    /// Указатель на скрипт управления компьютера
    /// </summary>
    public ComputerControlScript compScript;

    /// <summary>
    /// При старте сцены расчитываем границы камеры, потом устаналиваем поцизиции мобов, чтобы они не
    /// выходили за пределы сцены.
    /// </summary>
    void Start()
    {
        playerScript.enabled = hodLeft;
        compScript.enabled = !hodLeft;
        float dist = Vector3.Distance(transform.position, Camera.main.transform.position);
        float left = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, dist)).x;
        float right = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
        float bottom = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, dist)).y;
        float top = Camera.main.ViewportToWorldPoint(new Vector3(0, 1, dist)).y;
        Vector2[] position = new Vector2[8];
        position[0] = new Vector2(left / 4, top / 4 + top / 2);
        position[1] = new Vector2(left / 4, top / 4);
        position[2] = new Vector2(left / 4, bottom / 4);
        position[3] = new Vector2(left / 4, bottom / 4 + bottom / 2);
        position[4] = new Vector2(left / 2 + left / 4, top / 4 + top / 2);
        position[5] = new Vector2(left / 2 + left / 4, top / 4);
        position[6] = new Vector2(left / 2 + left / 4, bottom / 4);
        position[7] = new Vector2(left / 2 + left / 4, bottom / 4 + bottom / 2);
        for (int i = 0; i < playerGroup.Length; i++)
        {
            playerGroup[i].transform.position = new Vector3(position[i].x, position[i].y, 1);
            playerGroup[i].battleScript = this;
        }
        for (int i = 0; i < 4; i++)
            position[i].x = right / 4;
        for (int i = 4; i < 8; i++)
            position[i].x = right / 2 + right / 4;
        for (int i = 0; i < enemyGroup.Length; i++)
        {
            enemyGroup[i].transform.position = new Vector3(position[i].x, position[i].y, 1);
            enemyGroup[i].battleScript = this;
        }
        foreach (var item in hodLeft ? playerGroup : enemyGroup)
            item.hodFinish = false;
    }

    void Update()
    {
        /*Проверяем все ли мобы текущей команды завершили свои ходы, если да передаем ход
         другой команде*/
        if ((hodLeft ? playerGroup : enemyGroup).All(x => x.hodFinish || x.IsDead()))
        {
            hodLeft = !hodLeft;
            Debug.Log("Ход передан " + (hodLeft ? "игроку" : "компьютеру"));
            playerScript.enabled = hodLeft;
            compScript.enabled = !hodLeft;
            selectObj.position = new Vector3(0, 0, 1000);
            targetObj.position = new Vector3(0, 0, 1000);
            selectMob = null;
            targetMob = null;
            foreach (var item in playerGroup)
                item.hodFinish = false;
            foreach (var item in enemyGroup)
                item.hodFinish = false;
        }

        /*Ниже идут две проверки, которые проверяет факт победы той или другой стороны. Проверяя все ли мобы
         одной из команд мертвы*/
        
        if (playerGroup.All(x => x.IsDead()))
        {
            Debug.Log("Компьютер победил");
            this.enabled = false;
            playerScript.enabled = false;
            compScript.enabled = false;
        }
        if (enemyGroup.All(x => x.IsDead()))
        {
            Debug.Log("Игрок победил");
            this.enabled = false;
            playerScript.enabled = false;
            compScript.enabled = false;
        }
    }
}

Скрипт который висит на мобе:

public class BSMobScript : MonoBehaviour
{
    /// <summary>
    /// Указатель на управляющий скрипт
    /// </summary>
    public BattleSystemScript battleScript;
    /// <summary>
    /// Завершен ли ход данным мобов
    /// </summary>
    public bool hodFinish = false;
    /// <summary>
    /// Уровень здоровья
    /// </summary>
    public float health = 100;
    /// <summary>
    /// Уровень дамага
    /// </summary>
    public float damage = 40;

    /// <summary>
    /// Моб мертв?
    /// </summary>
    public bool IsDead()
    {
        return health <= 0;
    }

    /// <summary>
    /// Если моб мертв выключаем его и убираем выледение с данного моба.
    /// </summary>
    void Update()
    {
        if (health <= 0)
        {
            if (battleScript.targetObj.position.x == transform.position.x && battleScript.targetObj.position.y == transform.position.y)
                battleScript.targetObj.position = new Vector3(0, 0, 1000);
            gameObject.SetActive(false);
        }
    }

    /// <summary>
    /// Данный метод используется игроком. Проверяет какой команде принадлежит моб и выделяет его соответствующим
    /// выделением.
    /// </summary>
    private void OnMouseDown()
    {
        foreach (var item in battleScript.hodLeft ? battleScript.playerGroup : battleScript.enemyGroup)
        {
            if (this == item)
            {
                if (hodFinish == false)
                {
                    battleScript.selectObj.position = new Vector3(transform.position.x, transform.position.y, 2);
                    battleScript.selectMob = this;
                }
                return;
            }
        }
        foreach (var item in battleScript.hodLeft ? battleScript.enemyGroup : battleScript.playerGroup)
        {
            if (this == item)
            {
                battleScript.targetObj.position = new Vector3(transform.position.x, transform.position.y, 2);
                battleScript.targetMob = this;
                return;
            }
        }
    }
}

Скрипт игрока:

public class BattleSystemPlayerControlScript : MonoBehaviour {

    public BattleSystemScript battleScript;

    /// <summary>
    /// Атаковать выделенного противника
    /// </summary>
    public void ClickAttack()
    {
        if (battleScript.selectMob != null && battleScript.targetMob != null && battleScript.hodLeft)
        {
            battleScript.targetMob.health -= battleScript.selectMob.damage;
            battleScript.selectMob.hodFinish = true;
            battleScript.selectMob = null;
            battleScript.selectObj.position = new Vector3(0, 0, 1000);
        }
    }
}

Скрипт компьютера:

public class ComputerControlScript : MonoBehaviour {

    /// <summary>
    /// Указатель на управляющий скрипт
    /// </summary>
    public BattleSystemScript battleScript;
    /// <summary>
    /// Время последней атаки
    /// </summary>
    float time1 = float.PositiveInfinity;
    /// <summary>
    /// Устанавливаем текущее игровое время
    /// </summary>
    void Start()
    {
        time1 = UnityEngine.Time.time;
    }

    /// <summary>
    /// Флаги проведения определенных операций
    /// </summary>
    public bool attack, select, target;

    void Update()
    {
        //Если прошло более 3 секунд
        if (Time.time - time1 > 3f)
        {
            //Если мы еще не выделили своего бойца и прошло более 4 секунд
            if (select == false && Time.time - time1 > 4f)
            {
                //Находим бойца, который еще не производил атаку
                var list = battleScript.enemyGroup.Where(x => x.health > 0 && !x.hodFinish);
                if (list.Any())
                {
                    var item = list.ElementAt(Random.Range(0, list.Count()));
                    battleScript.selectObj.position = new Vector3(item.transform.position.x, item.transform.position.y, 2);
                    battleScript.selectMob = item;
                    select = true;
                }
            }
            //Если еще не выбрана цель
            else if (target == false && select && Time.time - time1 > 5f)
            {
                //Находим живую цель и выбираем ее
                var list = battleScript.playerGroup.Where(x => x.health > 0);
                if (list.Any())
                {
                    var item = list.ElementAt(Random.Range(0, list.Count()));
                    battleScript.targetObj.position = new Vector3(item.transform.position.x, item.transform.position.y, 2);
                    battleScript.targetMob = item;
                    target = true;
                }
            }
            //Если еще не произведена атака, то производим
            else if (attack == false && target && Time.time - time1 > 6f)
            {
                battleScript.targetMob.health -= battleScript.selectMob.damage;
                battleScript.selectMob.hodFinish = true;
                battleScript.selectMob = null;
                battleScript.selectObj.position = new Vector3(0, 0, 1000);
                attack = true;
            }
            //Сбрасываем флаги и время последней атаки
            else if (select && target && attack)
            {
                select = false;
                target = false;
                attack = false;
                time1 = UnityEngine.Time.time;
            }
        }
    }
}

Чтобы наглядно увидеть принцип работы можете посмотреть видео.
Категория: Unity | Добавил: Алексей (02.05.2017) | Автор: Фролов Алексей Алексеевич E
Просмотров: 1613 | Теги: Unity, C# | Рейтинг: 0.0/0
Всего комментариев: 0
avatar