NPC для RPG

Форум для всего, что связано с ИИ.

NPC для RPG

Сообщение breNn0r- 15 апр 2015, 04:10

Вот сегодня делать было нечего, решил по писать скрипты. Не долго думая решил написать самое легкое. Бестолкового НПЦ и ИИ.
Это первая версия. Далее буду обновлять, мне хотелось бы чтобы вы отписывались, что лучше и как подправить, буду благодарен.
Скрипты сейчас рабочие. Можете использовать.
В данный момент не реализовано:
Отнимание маны, ярости, энергии при атаке
Восстановление маны, ярости, энергии когда НПЦ не в бою
Изменение скорости НПЦ, в зависимости от ситуации.

и еще чего то)

в NPC_BASE я думаю убрать структуру _stats. Но возможно я реализую влияние этих характеристик на НПЦ. Просто эти параметры для Игрока,
и меня просто понесло как всегда :) так бывает, пишешь НПЦ и приписываешь ему плюшки от игрока :)

Скрипт игрока сделан чисто для проверки работоспособности, поэтому он такой убогий.

Вот скрипты.

BASE_STATS
Синтаксис:
Используется csharp
//PLAYER SCRIPT
//RPG TEST NPC BASE CLASS
//Это базовый класс. Сделан для того чтобы из него читались все дефолтные значения для НПЦ 1 уровня.
//В принципе переписать для разных уровней не составляет труда, но увы я уже устал... да и печатная машинка сломалась
//Тут я думаю ничего комментировать не нужно, у кого с английским плохо, как у меня: Google Translate Welcome

using UnityEngine;

public class NPC_BASE
{
        [System.Serializable]
        public struct _main
        {
                const float _HEALTHMAX = 60f;
                const float _MANAMAX = 40f;
                const float _RAGEMAX = 100f;
                const float _ENERGYMAX = 100f;
               
                public float HealthMax { get { return _HEALTHMAX; }}
                public float ManaMax { get { return _MANAMAX; }}
                public float RageMax { get { return _RAGEMAX; }}
                public float EnergyMax { get { return _ENERGYMAX; }}
        }
        [System.Serializable]
        public struct _stats
        {
                const byte _STAMINA = 2;
                const byte _INTELLECT = 3;
                const byte _AGILITY = 3;
                const byte _STRENGH = 4;
                const byte _SPIRIT = 2;
               
                public byte Stamina { get { return _STAMINA; }}
                public byte Intellect { get { return _INTELLECT; }}
                public byte Agility { get { return _AGILITY; }}
                public byte Strengh { get { return _STRENGH; }}
                public byte Spirit { get { return _SPIRIT; }}
        }
        [System.Serializable]
        public struct _protection
        {
                const float _ARMOR = 0f;
                const float _RANGE_ARMOR_PERCENT = 0f;
                const float _MAGIC_ARMOR_PERCENT = 0f;
               
                public float Armor { get { return _ARMOR; } }
                public float RangeArmorPercent { get { return _RANGE_ARMOR_PERCENT; }}
                public float MagicArmorPercent { get { return _MAGIC_ARMOR_PERCENT; }}
        }
        [System.Serializable]
        public struct _level
        {
                const byte _LEVEL = 1;
                public byte Level { get { return _LEVEL; }}
        }
        [System.Serializable]
        public struct _speed
        {
                const float _WALK_SPEED = 1.5f;
                const float _FASTWALK_SPEED = 2.5f;
                const float _RUN_SPEED = 3.5f;
                const float _SWIM_SPEED = 3f;
                const float _FLY_SPEED = 7.5f;

                public float WalkSpeed { get { return _WALK_SPEED; }}
                public float FastWalkSpeed { get { return _FASTWALK_SPEED; }}
                public float RunSpeed { get { return _RUN_SPEED; }}
                public float SwimSpeed { get { return _SWIM_SPEED; }}
                public float FlySpeed { get { return _FLY_SPEED; }}
        }
        [System.Serializable]
        public struct _attack
        {
                const float _ATTACK_SPEED = 1.7f;
                const float _ATTACK_CRITICAL_CHANCE = 4.5f;
                const float _MIN_DAMAGE = 1f;
                const float _MAX_DAMAGE = 4f;
                const float _ATTACK_DISTANCE = 2.5f;
                const float _VISIBLE_DISTANCE = 10f;

                public float AttackSpeed { get { return _ATTACK_SPEED;}}
                public float AttackCriticalChance { get { return _ATTACK_CRITICAL_CHANCE; }}
                public float MinDamage { get { return _MIN_DAMAGE; }}
                public float MaxDamage { get { return _MAX_DAMAGE; }}
                public float AttackDistance { get { return _ATTACK_DISTANCE; }}
                public float VisibleDistance { get { return _VISIBLE_DISTANCE; }}
        }
        [System.Serializable]
        public struct _walking
        {
                const float _MAX_DISTANCE = 5f;
                const float _MAX_FOLLOWING_DISTANCE = 20f;
                const float _STAY_ON_NEW_POSITION = 15f;

                public float MaxWalkingDistance { get { return _MAX_DISTANCE; }}
                public float MaxFollowingDistance { get { return _MAX_FOLLOWING_DISTANCE; }}
                public float StayOnPosition { get { return _STAY_ON_NEW_POSITION; }}
        }
        [System.Serializable]
        public struct _chance
        {
                const float _MISS_CHANCE = 4f;
                const float _BLOCK_CHANCE = 2.5f;
                const float _PARRY_CHANCE = 1.75f;

                public float MissChance { get { return _MISS_CHANCE; }}
                public float BlockChance { get { return _BLOCK_CHANCE; }}
                public float ParryChance { get { return _PARRY_CHANCE; }}
        }

        public static _main Main;
        public static _stats Stats;
        public static _speed Speed;
        public static _attack Attack;
        public static _chance Chance;
        public static _protection Protection;
        public static _level level;
        public static _walking Walking;
}


NPC
Синтаксис:
Используется csharp
//Это мозг всего НПЦ! он и будет отвечать у вас за бла бла бла.. Ну вы поняли)) за ничего он будет отвечать.))
//Этот скрипт хранит в себе все текущие переменные типа статистик и прочего от вашего НПЦ.

using UnityEngine;

[System.Serializable]
public class NPC_STATS : NPC_BASE       //Наследуем от Базы.
{
        [HideInInspector] // Скрывает Public переменные в инспекторе
        public float currentHealth;
        [HideInInspector]
        public float currentMana;
        [HideInInspector]
        public float currentEnergy;
        [HideInInspector]
        public float currentRage;

        //MAIN
        [Tooltip("Default: 60")] // Показывает подсказки в инспекторе при наведение на название функции
        public float HealthMax;
        [Tooltip("Default: 40")]
        public float ManaMax;
        [Tooltip("Default: 100")]
        public float EnergyMax;
        [Tooltip("Default: 100")]
        public float RageMax;

        //STATS
        [Space(10)]
        [Tooltip("Default: 2")]
        public byte Stamina;
        [Tooltip("Default: 3")]
        public byte Intellect;
        [Tooltip("Default: 3")]
        public byte Agility;
        [Tooltip("Default: 4")]
        public byte Strengh;
        [Tooltip("Default: 2")]
        public byte Spirit;

        //###PROTECTION
        [Space(10)]
        [Tooltip("Default: 0")]
        public float Armor;
        [Tooltip("Default: 0")]
        public float RangeArmorPercent;
        [Tooltip("Default: 0")]
        public float MagicArmorPercent;

        //###LEVEL
        [Space(10)]
        [Tooltip("Default: 1")]
        public byte Level;

        //###SPEED
        [Space(10)]
        [Tooltip("Default: 1.7")]
        public float AttackSpeed;
        [Tooltip("Default: 1.5")]
        public float WalkSpeed;
        [Tooltip("Default: 2.5")]
        public float FastWalkSpeed;
        [Tooltip("Default: 3.5")]
        public float RunSpeed;
        [Tooltip("Default: 3")]
        public float SwimSpeed;
        [Tooltip("Default: 7.5")]
        public float FlySpeed;

        //###ATTACK
        [Space(10)]
        [Tooltip("Default: 1")]
        public float MinDamage;
        [Tooltip("Default: 4")]
        public float MaxDamage;
        [Tooltip("Default: 4.5")]
        public float AttackCriticalChance;
        [Tooltip("Default: 2.5")]
        public float AttackDistance;
        [Tooltip("Default: 7.5")]
        public float VisibleDistance;

        //###CHANCE
        [Space(10)]
        [Tooltip("Default: 4")]
        public float MissChance;
        [Tooltip("Default: 2.5")]
        public float BlockChance;
        [Tooltip("Default: 1.75")]
        public float ParryChance;

        //###WALKING
        [Space(10)]
        public bool isWalking;
        [Tooltip("Default: 5")]
        public float MaxWalkingDistance;
        [Tooltip("Default: 20")]
        public float MaxFollowingDistance;
        [Tooltip("Default: 15")]
        public float StayOnPosition;

        //###ENUMS
        public enum _class { Melee, Range, Magic };
        public enum _friction { Neutral, Agressive, Friend };
        public _class Class;
        public _friction Friction;

        //###OTHER
        [HideInInspector]
        public bool isDead;
        [HideInInspector]
        public bool inCombat;
        [HideInInspector]
        public GameObject target;

        #region CheckOnEmptyAttributes
        //Метод сравнивающий значения, и если из тела метода он находит переменную которая равна нулю, заменяет это значение
        //на значение из NPC_BASE
        public void CheckOnEmptyAttributes ()
        {
                //###MAIN
                switch (Class)
                {
                case _class.Melee:
                        if (HealthMax == 0)
                                HealthMax = NPC_STATS.Main.HealthMax;
                        if (RageMax == 0)
                                RageMax = NPC_STATS.Main.RageMax;
                        break;

                case _class.Range:
                        if (HealthMax == 0)
                                HealthMax = NPC_STATS.Main.HealthMax;
                        if (EnergyMax == 0)
                                EnergyMax = NPC_STATS.Main.EnergyMax;
                        break;

                case _class.Magic:
                        if (HealthMax == 0)
                                HealthMax = NPC_STATS.Main.HealthMax;
                        if (ManaMax == 0)
                                ManaMax = NPC_STATS.Main.ManaMax;
                        break;
                }

                //###STATS
                if (Stamina == 0)
                        Stamina = NPC_STATS.Stats.Stamina;
                if (Intellect == 0)
                        Intellect = NPC_STATS.Stats.Intellect;
                if (Agility == 0)
                        Agility = NPC_STATS.Stats.Agility;
                if (Strengh == 0)
                        Strengh = NPC_STATS.Stats.Strengh;
                if (Spirit == 0)
                        Spirit = NPC_STATS.Stats.Spirit;

                //###PROTECTION
                //Все статы брони равны нулю в NPC_BASE скрипте, по этому тут нет присвоения значений
                //Если в NPC_BASE вы ввели значения не равные нулю, то расскоментируйте ниже написаное.
                /*
                if (Armor == 0)
                        Armor = NPC_STATS.Protection.Armor;
                if (RangeArmorPercent == 0)
                        RangeArmorPercent = NPC_STATS.Protection.RangeArmorPercent;
                if (MagicArmorPercent == 0)
                        MagicArmorPercent = NPC_STATS.Protection.MagicArmorPercent;
                 */


                //###LEVEL
                if (Level == 0)
                        Level = NPC_STATS.level.Level;

                //###SPEED
                if (WalkSpeed == 0)
                        WalkSpeed = NPC_STATS.Speed.WalkSpeed;
                if (FastWalkSpeed == 0)
                        FastWalkSpeed = NPC_STATS.Speed.FastWalkSpeed;
                if (RunSpeed == 0)
                        RunSpeed = NPC_STATS.Speed.RunSpeed;
                if (SwimSpeed == 0)
                        SwimSpeed = NPC_STATS.Speed.SwimSpeed;
                if (FlySpeed == 0)
                        FlySpeed = NPC_STATS.Speed.FlySpeed;


                //###ATTACK
                if (AttackSpeed == 0)
                        AttackSpeed = NPC_STATS.Attack.AttackSpeed;
                if (MinDamage == 0)
                        MinDamage = NPC_STATS.Attack.MinDamage;
                if (MaxDamage == 0)
                        MaxDamage = NPC_STATS.Attack.MaxDamage;
                if (AttackCriticalChance == 0)
                        AttackCriticalChance = NPC_STATS.Attack.AttackCriticalChance;
                if (AttackDistance == 0)
                        AttackDistance = NPC_STATS.Attack.AttackDistance;
                if (VisibleDistance == 0)
                        VisibleDistance = NPC_STATS.Attack.VisibleDistance;

                //###CHANCE
                if (MissChance == 0)
                        MissChance = NPC_STATS.Chance.MissChance;
                if (BlockChance == 0)
                        BlockChance = NPC_STATS.Chance.BlockChance;
                if (ParryChance == 0)
                        ParryChance = NPC_STATS.Chance.ParryChance;

                //###WALKING
                if (isWalking)
                {
                        if (MaxWalkingDistance == 0)
                                MaxWalkingDistance = NPC_STATS.Walking.MaxWalkingDistance;
                        if (MaxFollowingDistance == 0)
                                MaxFollowingDistance = NPC_STATS.Walking.MaxFollowingDistance;
                        if (StayOnPosition == 0)
                                StayOnPosition = NPC_STATS.Walking.StayOnPosition;
                }

                AssignCurrentVariables();
        }

        private void AssignCurrentVariables ()
        {
                currentHealth = HealthMax;
                currentMana = ManaMax;
                currentEnergy = EnergyMax;
                currentRage = RageMax;
        }
        #endregion
}
[RequireComponent(typeof(NPC_AI))]
public class NPC : MonoBehaviour
{
        public NPC_STATS Stats;

        void Start ()
        {      
                Stats.CheckOnEmptyAttributes ();
        }
}


NPC_AI
Синтаксис:
Используется csharp
//А Это наше ТЕЛО! тут вся логика не до логика)).
//В комментариях приведенны строки, они могут не совпадать, т.к возможно я что то до писал и строка сбилась.
//Делать Было нечего вот и решил навалять вот это..
using UnityEngine;

public class NPC_AI : MonoBehaviour
{
        private NPC NPC;                                                //Наш МОЗГ!!!
        private NavMeshAgent NAV_MESH_AGENT;    //Агент 007

        private Vector3 PLAYER_POSITION;                //Позиция игрока
        private Vector3 START_POSITION;                 //Стартовая позиция

        private float _distanceToPlayer;                //Дистанция до игрока 40строка
        private float _distanceToStartPosition; //Дистанция до позиции старта 42строка

        private PLAYER PLAYER_SCRIPT;                   //Ссылка на скрипт игрока.

        private int _frictionNumber;                    //Локальная переменная под фракцию НПЦ при старте.
        private float _attackSpeed;                             //Локальная переменная под счетчик задержки между ударами.
        private float _stayOnPosition;                  //Локальная переменная под счетчик ожидания на новой позиции.
        private float _timeOutBattle;                   //Название может и не правильное. Это для выхода из боя
        private float _visibleDistance;                 //Нужна для исправления бага.
       
        void Start ()
        {
                PLAYER_SCRIPT = GameObject.FindGameObjectWithTag("Player").GetComponent<PLAYER>();
                NPC = this.GetComponent<NPC>(); //Добавляем скрипт с этого же объекта

                //Если на объекте нет Агента 007 -> Добавляем его и сразу забиваем на него ссылку
                if (gameObject.GetComponent<NavMeshAgent>() == null)
                        NAV_MESH_AGENT = gameObject.AddComponent<NavMeshAgent>();
                //Или же просто забиваем его в ссылку
                else
                        NAV_MESH_AGENT = gameObject.GetComponent<NavMeshAgent>();

                //Указываем что останавливатся нужно на дистанции атаки, а не нюхать модельку игрока
                NAV_MESH_AGENT.stoppingDistance = NPC.Stats.AttackDistance;

                //Забиваем число его фракции. (Нужен для того чтобы вернуть его из агрессивного в нейтральный)
                _frictionNumber = (int)NPC.Stats.Friction; //0-neutral, 1-agressive, 2-friend : friction enum

                //Ну и стартовая позиция записывается при спавне
                START_POSITION = this.transform.position;

                //Сохранение начального значение переменной
                _visibleDistance = NPC.Stats.VisibleDistance;
        }
        void Update ()
        {
                //Записываем в переменную позицию игрока
                PLAYER_POSITION = GameObject.FindGameObjectWithTag("Player").transform.position;

                //Записываем в переменную дистанцию до игрока.
                _distanceToPlayer = Vector3.Distance(this.transform.position, PLAYER_POSITION);

                //Записываем в переменную дистанцию до спавна.
                _distanceToStartPosition = Vector3.Distance(this.transform.position, START_POSITION);

                //Если НПЦ НЕ МЕРТВ!
                if (!NPC.Stats.isDead)
                {
                        //Проверяем статус нпц ( Фракцией называется потому что изначально у него были фракции в дальнейшем убрал)
                        if (NPC.Stats.Friction == NPC_STATS._friction.Agressive && !PLAYER_SCRIPT.isDead)
                        {
                                //Если игрок в зони видимости нпц -> Преследуем игрока
                                if (_distanceToPlayer <= NPC.Stats.VisibleDistance)
                                        FollowingPlayer();
                        }

                        #region ПРОВЕРКИ НА БОЙ
                        //Если НПЦ не в бою
                        if (!NPC.Stats.inCombat)
                        {
                                //Если здоровья меньше максимального -> Восстанавливаем здоровье игроку.
                                if (NPC.Stats.currentHealth < NPC.Stats.HealthMax)
                                        NPC.Stats.currentHealth += (5 * NPC.Stats.Level) * Time.deltaTime;
                        }
                        //Если НПЦ в бою
                        if (NPC.Stats.inCombat)
                        {
                                //Если дистанция до игрока больше видимой, или игрок умер
                                if (_distanceToPlayer > NPC.Stats.VisibleDistance || PLAYER_SCRIPT.isDead)
                                {
                                        //Считаем время до выхода из боя
                                        if (_timeOutBattle > 0)
                                                _timeOutBattle -= Time.deltaTime;
                                        else
                                                //Выходим из боя
                                                NPC.Stats.inCombat = false;
                                }
                        }
                        #endregion
                        #region ПРОВЕРКИ НА ХП
                        //Если здоровье вышло за границы -> Выравниваем его по максимальному
                        if(NPC.Stats.currentHealth > NPC.Stats.HealthMax)
                                NPC.Stats.currentHealth = NPC.Stats.HealthMax;
                       
                        //Если здоровье ушло. -> Включаем смерть
                        if (NPC.Stats.currentHealth <= 0f)
                        {
                                NPC.Stats.isDead = true;
                                NPC.Stats.currentHealth = 0f;
                        }
                        #endregion
                }

                //Если НПЦ может ходить и не в бою и не мертв.
                if (NPC.Stats.isWalking && !NPC.Stats.inCombat && !NPC.Stats.isDead)
                {
                        //Вызываем метод.
                        Walking ();
                }

                //Возвращение обзора для НПЦ, когда он почти добежал до точки старта
                Debug.Log(Vector3.Distance(transform.position, START_POSITION));
                if (Vector3.Distance(transform.position, START_POSITION) < 5f)
                        NPC.Stats.VisibleDistance = _visibleDistance;
        }

        //А вот и он.
        private void Walking ()
        {
                //Если таймер простоя на точке больше 0 -> Считаем таймер
                if (_stayOnPosition > 0)
                        _stayOnPosition -= Time.deltaTime;
                //Или же Выдаем ему новую позицию, отправляем его туда и обнуляем таймер.
                else
                {
                        /*
                        ЭТОТ ВАРИАНТ Я ИСПОЛЬЗОВАЛ РАНЬШЕ.

                        float x = Random.Range(0f, NPC.Stats.MaxWalkingDistance);
                        float z = Random.Range(0f, NPC.Stats.MaxWalkingDistance);
                        Vector3 newPosition = new Vector3(x, transform.position.y, z);
                        NAV_MESH_AGENT.SetDestination(newPosition);
                        */


                        //СЕЙЧАС РЕШИЛ ПОПРОБОВАТЬ ВОТ ТАК.
                        //+ Тут его мы не отпускаем от стартовой позиции. иначе он бы ушел через 30 минут в африку пешком)))
                        NAV_MESH_AGENT.SetDestination(new Vector3(
                                                                  START_POSITION.x + Random.Range(0f, NPC.Stats.MaxWalkingDistance),    //x
                                                                  transform.position.y,                                                         //y
                                                                  START_POSITION.z + Random.Range(0f, NPC.Stats.MaxWalkingDistance)));  //z

                        //Обнуление таймера
                        _stayOnPosition = NPC.Stats.StayOnPosition;
                }
        }

        //Преследование игрока
        private void FollowingPlayer ()
        {
                //Включаем режим боя.
                NPC.Stats.inCombat = true;
                //Если наш моб убежал за максимальную дистанцию преследования -> заставляем его бежать на респаун, и отключаем режим боя.
                if (_distanceToStartPosition > NPC.Stats.MaxFollowingDistance && NPC.Stats.inCombat)
                {
                        //Вот как раз тут и пригодится нам переменная _frictionNumber
                        //Здесь НПЦ изначально был нейтральным, но когда мы его ударили(стр.205) и он стал агрессивным, и тут мы все возвращаем назад
                        if (NPC.Stats.Friction == NPC_STATS._friction.Agressive && _frictionNumber == 0)
                                NPC.Stats.Friction = (NPC_STATS._friction)_frictionNumber;

                        NAV_MESH_AGENT.SetDestination(START_POSITION);
                        NPC.Stats.inCombat = false;
                        //Обнуляем ему таймер на тупняк.
                        _stayOnPosition = 0f;
                        //Ограничиваем обзор для того чтобы небыло бага, когда моб дрыгается из стороны в сторону, так как SetDestanation пересекаются
                        NPC.Stats.VisibleDistance = 2f;
                }
                //Или же бежим за игроком
                else
                        NAV_MESH_AGENT.SetDestination(PLAYER_POSITION);

                //Если дистанция до игрока уже позволяет бить игрока -> Бъем игрока
                if (_distanceToPlayer <= NPC.Stats.AttackDistance)
                        Attack ();
        }


        //Бъем игрока здесь
        private void Attack ()
        {
                //Если есть задержка перед ударом -> считаем ее.
                if (_attackSpeed > 0)
                        _attackSpeed -= Time.deltaTime;
                //Или же наносим игрока повреждения
                else
                {
                        //Кидаем монетку от 0 до 100 на промах
                        float randChance = Random.Range (0f, 100f);
                        //Если выпало больше, то бьем (если <= то промах)
                        if (randChance > NPC.Stats.MissChance)
                        {
                                //Снова выбрасываем монетку, только теперь на крит.
                                randChance = Random.Range (0f, 100f);
                                //Если выпало больше, то бьем обычный удар
                                if (randChance > NPC.Stats.AttackCriticalChance)
                                        //атака во врага обычная
                                        PLAYER_SCRIPT.TAKE_DAMAGE (Random.Range(NPC.Stats.MinDamage, NPC.Stats.MaxDamage));
                                else
                                        //атака во врага критом
                                        PLAYER_SCRIPT.TAKE_DAMAGE (Random.Range(NPC.Stats.MinDamage * 2, NPC.Stats.MaxDamage * 2));
                        }
                        else
                                //Можно вывести на экран, что атака не удалась
                                Debug.Log("NPC MISS");

                        //Обнуляем задержку между атаками.
                        _attackSpeed = NPC.Stats.AttackSpeed;
                }
        }

        //Публичная переменная т.к получаем урон из другого скрипта.
        public void TakeDamage (float Damage)
        {
                //Если наш тупоголовый нейтрал, и мы его ударяем то он становиться агрессивным
                //151-152 строка возвращает положение на изначальное.
                if (NPC.Stats.Friction == NPC_STATS._friction.Neutral)
                        NPC.Stats.Friction = NPC_STATS._friction.Agressive;

                //Вводим НПЦ в режим боя
                NPC.Stats.inCombat = true;
                //Устанавливаем Таймер для выхода из боя, реализация его здесь нужна для постоянного обновления при получении урона
                _timeOutBattle = 5f;

                //И снова тут мы будем бросать монетку на паррирование удара.
                float randChance = Random.Range (0f, 100f);
                //Если монетка выкинула больше, то нпц не с паррировал удар
                if (randChance > NPC.Stats.ParryChance)
                {
                        //Снова монетка, теперь уже на блокировку удара
                        randChance = Random.Range (0f, 100f);
                        //Если больше то не блокировали...
                        if (randChance > NPC.Stats.BlockChance)
                        {
                                //Заводим локальную переменную под логику деления урона на броню
                                //пример: 44 урон, 100 брони.
                                //damageDivider = ((100 брони + 100 = 200) / 100) = 2
                                //HP -= 44 / (2) = 22.
                               
                                //пример: 44 урон, 75 брони.
                                //Делитель = ((75 брони + 100 = 200) / 100) = 1.75
                                //ХП -= 44 / (1.75) = 25
                               
                                //ХП ИГРОКА                                      УРОН                                       ДЕЛИТЕЛЬ
                                NPC.Stats.currentHealth -=      Damage  /       ((NPC.Stats.Armor + 100) / 100);
                        }
                        else
                                Debug.Log("Attack Blocked");
                }
                else
                        Debug.Log("Parry Attack");
        }

        //Можете сами заменить на что хотите, мне подходит и такой вариант.
        //Если у нас мышка находится на объекте и мы нажали ЛКМ. Добавляем в таргет этот объект
        //Если нажимаем ПКМ то добавляем в таргет объект и включаем атаку.
        void OnMouseOver ()
        {
                if (Input.GetButtonDown("Fire1"))
                        PLAYER_SCRIPT.Target = this.gameObject;
                if (Input.GetButtonDown("Fire2"))
                {
                        PLAYER_SCRIPT.blaAttack = true;
                        PLAYER_SCRIPT.Target = this.gameObject;
                }
        }
}
 


TEST PLAYER
Синтаксис:
Используется csharp
//Простенький скрипт игрока. Чисто для проверок
using UnityEngine;

public class PLAYER : MonoBehaviour
{
        private float _currentHealth;           //Текущее ХП
        public float MaxHealth;                         //Максимальное ХП

        public bool isDead;                                     //Мертв или нет
        public bool blaAttack;                          //Атакуем ли врага (bla ничего не значит, просто чтобы не совподало с методом название)

        public GameObject Target;                       //Цель наша

        float _attackDelay;                                     //Задержка при атаки

        void Awake ()   //http://unity3d.com/learn/tutorials/modules/beginner/scripting/awake-and-start
        {
                tag = "Player"; name = "Player";        //Присваение имени и тэга
                _currentHealth = MaxHealth;                     //Присвоение текущему ХП максимального ХП
        }

        void Update ()  //http://unity3d.com/learn/tutorials/modules/beginner/scripting/update-and-fixedupdate
        {
                if (!isDead)
                {
                       
                        if (Target && Input.GetButtonDown("Cancel"))    //Проверка на наличие цели и нажатие на ESCAPE (Esc)
                        {
                                Target = null;          //Убираем таргет
                                blaAttack = false;      //Убираем атаку
                                _attackDelay = 2f;      //Обнуляем таймер
                        }
                        if (Target && blaAttack && !Target.GetComponent<NPC>().Stats.isDead)                    //Если есть цель, если атакуем, если НПЦ не мертв
                                Attack();                       //Вызов метода атаки (почему то всегда когда я пишу метод, мне на голову приходит "МЕТАДОН!!!" (Я НЕ НАРКОМАН!!!) <img src="./images/smilies/4.gif" alt=":D" title="Гы" />

                }
        }

        //Метод получения урона ничего сложного. Есть входящая переменная типа Float которая записывает в себя урон
        public void TAKE_DAMAGE (float Damage)
        {
                _currentHealth -= Damage;
        }

        //Атака
        private void Attack ()
        {
                if (Target.GetComponent<NPC>().Stats.Friction != NPC_STATS._friction.Friend)
                {
                        //Таймер
                        if (_attackDelay > 0)
                                _attackDelay -= Time.deltaTime;
                        else
                        {
                                Target.GetComponent<NPC_AI>().TakeDamage (Random.Range(1f, 10f)); //Наносим урон от 1 до 10
                                _attackDelay = 2f; //Обнуляем таймер
                        }
                }
                else
                {
                        //Так как Атака у нас включается из НПЦ, придется добавить отключение атаки.
                        //Или там сделать проверку, но мне и так норм <img src="./images/smilies/4.gif" alt=":D" title="Гы" />
                        Debug.Log("Извини, я немогу бить друзей!");
                        blaAttack = false;
                }
        }
}
 
breNn0r-
UNITрон
 
Сообщения: 150
Зарегистрирован: 22 май 2013, 15:05

Вернуться в Искуственный Интеллект

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1