Exemplo n.º 1
0
    //============================= Funciones Unity ===========================================

    protected override void Awake()
    {
        base.Awake();

        ChargeEmission         = OnChargeParticle.emission;
        ChargeEmission.enabled = false;
        SmashEmission          = OnSmashParticle.emission;

        //Si interrumpo el ataque... voy a stunned.
        //FRitmo.OnComboSuccesfullyStart += () => { };
        FRitmo.OnComboCompleted += () => { sm.Feed(BossStates.Smashed); };
        FRitmo.TimeEnded        += () => { sm.Feed(BossStates.think); };
        FRitmo.OnComboFailed    += () => { sm.Feed(BossStates.think); };

        Tuple <int, Inputs>[] data = new Tuple <int, Inputs> [3];
        data[0] = Tuple.Create(1, Inputs.light);
        data[1] = Tuple.Create(3, Inputs.light);
        data[2] = Tuple.Create(8, Inputs.strong);

        FRitmo.AddVulnerability(0, data);

        #region State Machine

        idle = new State <BossStates>("Idle");
        var think      = new State <BossStates>("Thinking");
        var pursue     = new State <BossStates>("Pursue");
        var reposition = new State <BossStates>("Reposition");
        var charge     = new State <BossStates>("Charge");
        var BasicCombo = new State <BossStates>("Attack_BasicCombo");
        var JumpAttack = new State <BossStates>("Attack_SimpleJump");
        var Smashed    = new State <BossStates>("VulnerableToAttacks");
        var KillerJump = new State <BossStates>("Attack_KillerJump");
        var dead       = new State <BossStates>("Dead");

        /*
         * .OnEnter += (previousState) => { };
         * .OnUpdate += () => { };
         * .OnExit += (nextState) => { };
         */

        #region Estados

        #region Idle State

        idle.OnEnter  += (x) => { anims.SetFloat("Movement", 0f); };
        idle.OnUpdate += () =>
        {
            //print("Enemy is OnIdle");
            var toDamage = sight.target.GetComponent <IKilleable>();
            if (!toDamage.IsAlive)
            {
                return;
            }

            if (sight.IsInSight() || sight.distanceToTarget < minDetectionRange)
            {
                _targetDetected = true;
            }

            //transitions
            if (_targetDetected)
            {
                if (sight.distanceToTarget > HighRange)
                {
                    sm.Feed(BossStates.charge);
                }
                else
                {
                    sm.Feed(BossStates.pursue);
                }
            }
        };
        idle.OnExit += (x) => { anims.SetFloat("Movement", 0f); };

        #endregion

        #region ThinkState

        think.OnEnter += (previousState) =>
        {
            print("Thinking...");
            anims.SetFloat("Movement", 0f);
            agent.isStopped   = true;
            LookTowardsPlayer = true;
        };
        think.OnUpdate += () =>
        {
            if (thinkTime > 0)
            {
                thinkTime -= Time.deltaTime;
            }
            else
            {
                thinkTime = 0f;
                //Chequeo si el enemigo esta vivo.
                var toDamage = sight.target.GetComponent <IKilleable>();
                if (!toDamage.IsAlive)
                {
                    sm.Feed(BossStates.idle);                        // Si el enemigo no esta Vivo, vuelvo a idle
                }
                else                                                 // Si esta vivo...
                {
                    if (sight.angleToTarget > 45f)
                    {
                        sm.Feed(BossStates.reposition);
                    }
                    else
                    {
                        if (sight.distanceToTarget <= AttackRange)
                        {
                            if (canPerformSimpleJump)
                            {
                                //Genero los pesos relevantes.

                                float[] posibilities = new float[2]
                                {
                                    (AttackWeight * ((AttackRange - (AttackRange - sight.distanceToTarget)) / AttackRange)),
                                    (JumpWeight * ((AttackRange - sight.distanceToTarget) / AttackRange))
                                };

                                if (RoulleteSelection.Roll(posibilities) == 1)
                                {
                                    print("Decidí hacer un salto corto.");
                                    sm.Feed(BossStates.closeJump);
                                    return;
                                }
                            }

                            print("Decidí Atacar.");
                            sm.Feed(BossStates.basicCombo);
                        }
                        if (sight.distanceToTarget > AttackRange)
                        {
                            //Si la distancia es mayor al Highrange.
                            if (sight.distanceToTarget > HighRange)
                            {
                                print("Decidí hacer un Charge.");
                                sm.Feed(BossStates.charge);
                                return;
                            }

                            float[] posibilities = new float[]
                            {
                                (KillerJumpWeight * ((HighRange - sight.distanceToTarget) / HighRange)),
                                PursueWeight
                            };

                            //Si la distancia es mayor al mediumRange.
                            if (RoulleteSelection.Roll(posibilities) == 0)
                            {
                                print("Decidí hacer un Killer Jump.");
                                sm.Feed(BossStates.killerJump);
                            }

                            // si esta visible pero fuera del rango de ataque...
                            print("Decidí perseguir al enemigo.");
                            sm.Feed(BossStates.pursue);                  // paso a pursue.
                        }
                    }
                }
            }
        };
        think.OnExit += (nextState) =>
        {
            agent.isStopped   = false;
            LookTowardsPlayer = false;
        };

        #endregion

        #region SimpleJumpAttack

        JumpAttack.OnEnter += (previousState) =>
        {
            //Animación we.
            anims.SetInteger("Attack", 5);
            anims.SetFloat("Movement", 0f);
            LookTowardsPlayer = false;
        };
        //JumpAttack.OnUpdate += () => { };
        JumpAttack.OnExit += (nextState) =>
        {
            anims.SetInteger("Attack", 0);
            LookTowardsPlayer = false;
        };

        #endregion

        #region KillerJumpState

        KillerJump.OnEnter += (previousState) =>
        {
            //Animación we.
            anims.SetInteger("Attack", 4);
            anims.SetFloat("Movement", 0f);
            LookTowardsPlayer = false;
        };
        KillerJump.OnExit += (nextState) => { LookTowardsPlayer = true; };

        #endregion

        #region Pursue State
        pursue.OnEnter += (x) =>
        {
            //print("Chasing After Player...");
            anims.SetFloat("Movement", 1f);
            LookTowardsPlayer = true;
        };
        pursue.OnUpdate += () =>
        {
            //transitions
            if (!IsAlive)
            {
                sm.Feed(BossStates.dead);
            }

            if (sight.distanceToTarget < AttackRange) //Si entra dentro del rango de ataque.
            {
                sm.Feed(BossStates.basicCombo);
            }

            //if (sight.distanceToTarget > sight.range) //Si el objetivo se va afuera del rango de visión.
            //    sm.Feed(BossStates.idle);

            //Actions.
            agent.Move(sight.dirToTarget * MovementSpeed * Time.deltaTime);
        };

        #endregion

        #region Charge
        charge.OnEnter += (previousState) =>
        {
            charging = true;
            //Primero me quedo quieto.
            agent.isStopped = true;
            //Empiezo la animación del rugido.
            anims.SetBool("Roar", true);
        };
        charge.OnUpdate += () =>
        {
            float distance = Vector3.Distance(transform.position, _initialChargePosition);

            if (charging && distance < maxChargeDistance)
            {
                //Me muevo primero
                agent.Move(_chargeDir * chargeSpeed * Time.deltaTime);

                //Sino...
                //Voy calculando la distancia en la que me estoy moviendo
                distance = Vector3.Distance(transform.position, _initialChargePosition);
                //print("Charging distance" + distance);
            }
            //Si la distancia es mayor al máximo
            else if (distance > maxChargeDistance)
            {
                sm.Feed(BossStates.think); //Me detengo.
            }
        };
        charge.OnExit += (nextState) =>
        {
            attackDamage           = BasicAttackDamages[0];
            ChargeEmission.enabled = false;
            charging          = false;
            LookTowardsPlayer = true;
        };
        #endregion

        #region Attack State

        BasicCombo.OnEnter += (x) =>
        {
            print("Enemy started AttackMode");
            SetAttackState(1);
            agent.isStopped   = true;
            LookTowardsPlayer = false;

            FRitmo.ShowVulnerability();
        };
        BasicCombo.OnUpdate += () =>
        {
            //Seteo el primer ataque.
            if (AttackPhase == 0)
            {
                thinkTime = 2f;
                sm.Feed(BossStates.think);
            }
        };
        BasicCombo.OnExit += (x) =>
        {
            agent.isStopped   = false;
            LookTowardsPlayer = true;
        };

        #endregion

        #region Reposition State

        reposition.OnEnter += (previousState) =>
        {
            print("Reposicionando we");
            _rotationLerpSpeed = RepositionLerpSpeed;
            LookTowardsPlayer  = true;
        };
        reposition.OnUpdate += () =>
        {
            if (sight.angleToTarget < 5f)
            {
                sm.Feed(BossStates.think);
            }
            else
            {
                agent.isStopped = true;
            }
        };
        reposition.OnExit += (NextState) =>
        {
            _rotationLerpSpeed = NormalRotationLerpSeed;
        };

        #endregion

        #region Vulnerable State

        Smashed.OnEnter += (previousState) =>
        {
            _Smashed            = true;
            _remainingSmashTime = SmashDuration;
            anims.SetBool("Smashed", true);
        };
        Smashed.OnUpdate += () => { };
        Smashed.OnExit   += (nextState) =>
        {
            _Smashed = false;
        };

        #endregion


        #region Dead State
        dead.OnEnter += (x) =>
        {
            print("Enemy is dead");
            anims.SetTrigger("Dead");
            StopAllCoroutines();
            Die();
            // Posible Spawneo de cosas.
        };
        #endregion

        #endregion

        #region Transiciones
        //Transiciones posibles.
        idle.AddTransition(BossStates.pursue, pursue)
        .AddTransition(BossStates.charge, charge)
        .AddTransition(BossStates.dead, dead);

        pursue.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.basicCombo, BasicCombo)
        .AddTransition(BossStates.think, think);

        charge.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.think, think);

        think.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.pursue, pursue)
        .AddTransition(BossStates.reposition, reposition)
        .AddTransition(BossStates.charge, charge)
        .AddTransition(BossStates.closeJump, JumpAttack)
        .AddTransition(BossStates.killerJump, KillerJump)
        .AddTransition(BossStates.Smashed, Smashed)
        .AddTransition(BossStates.basicCombo, BasicCombo);

        JumpAttack.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.Smashed, Smashed)
        .AddTransition(BossStates.think, think);

        KillerJump.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.think, think)
        .AddTransition(BossStates.Smashed, Smashed);


        reposition.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.think, think);

        BasicCombo.AddTransition(BossStates.dead, dead)
        .AddTransition(BossStates.Smashed, Smashed)
        .AddTransition(BossStates.think, think);

        Smashed.AddTransition(BossStates.think, think)
        .AddTransition(BossStates.dead, dead);

        #endregion

        sm = new GenericFSM <BossStates>(idle);
        #endregion
    }
Exemplo n.º 2
0
    //=========================================================================================

    protected override void Awake()
    {
        base.Awake();

        //Vulnerabilidad.
        //Si interrumpo el ataque... voy a stunned.
        FRitmo.OnComboSuccesfullyStart += () => _sm.Feed(ShieldEnemyStates.stunned);
        FRitmo.OnComboCompleted        += () => { Health -= ComboCompleteDamage; };
        FRitmo.TimeEnded     += () => { _sm.Feed(ShieldEnemyStates.think); };
        FRitmo.OnComboFailed += () => { _sm.Feed(ShieldEnemyStates.think); };

        //Primera vulnerabilidad.
        Tuple <int, Inputs>[] data = new Tuple <int, Inputs> [3];
        data[0] = Tuple.Create(1, Inputs.light);
        data[1] = Tuple.Create(3, Inputs.light);
        data[2] = Tuple.Create(7, Inputs.light);

        FRitmo.AddVulnerability(0, data);

        //Segunda vulnerabilidad
        Tuple <int, Inputs>[] data2 = new Tuple <int, Inputs> [3];
        data2[0] = Tuple.Create(2, Inputs.strong);
        data2[1] = Tuple.Create(5, Inputs.light);
        data[2]  = Tuple.Create(9, Inputs.light);

        FRitmo.AddVulnerability(1, data2);

        #region State Machine.

        var idle       = new State <ShieldEnemyStates>("Idle");
        var alerted    = new State <ShieldEnemyStates>("Alerted");
        var pursue     = new State <ShieldEnemyStates>("pursue");
        var blocking   = new State <ShieldEnemyStates>("Bloquing");
        var vulnerable = new State <ShieldEnemyStates>("Vulnerable");
        var reposition = new State <ShieldEnemyStates>("Repositioning");
        var parry      = new State <ShieldEnemyStates>("Parrying");
        var attack     = new State <ShieldEnemyStates>("Attacking");
        var think      = new State <ShieldEnemyStates>("Thinking");
        var dead       = new State <ShieldEnemyStates>("Dead");

        /*
         *  anims.SetBool("Dead", true);
         *  anims.SetTrigger("getDamage");
         *  anims.SetFloat("Moving", 1f);
         *  anims.SetInteger("Attack", 1);
         *  anims.SetBool("Blocking", true);
         *  anims.SetTrigger("BlockBreak");
         *  anims.SetBool("Parrying");
         */
        #region Transitions

        idle.AddTransition(ShieldEnemyStates.alerted, alerted)
        .AddTransition(ShieldEnemyStates.dead, dead);

        alerted.AddTransition(ShieldEnemyStates.think, think)
        .AddTransition(ShieldEnemyStates.dead, dead);

        pursue.AddTransition(ShieldEnemyStates.pursue, pursue)
        .AddTransition(ShieldEnemyStates.think, think)
        .AddTransition(ShieldEnemyStates.dead, dead);

        reposition.AddTransition(ShieldEnemyStates.dead, dead)
        .AddTransition(ShieldEnemyStates.think, think);

        blocking.AddTransition(ShieldEnemyStates.parry, parry)
        .AddTransition(ShieldEnemyStates.attack, attack)
        .AddTransition(ShieldEnemyStates.vulnerable, vulnerable)
        .AddTransition(ShieldEnemyStates.think, think)
        .AddTransition(ShieldEnemyStates.dead, dead);

        vulnerable.AddTransition(ShieldEnemyStates.think, think)
        .AddTransition(ShieldEnemyStates.dead, dead);

        parry.AddTransition(ShieldEnemyStates.think, think)
        .AddTransition(ShieldEnemyStates.dead, dead);

        attack.AddTransition(ShieldEnemyStates.dead, dead)
        .AddTransition(ShieldEnemyStates.think, think);

        think.AddTransition(ShieldEnemyStates.dead, dead)
        .AddTransition(ShieldEnemyStates.block, blocking)
        .AddTransition(ShieldEnemyStates.parry, parry)
        .AddTransition(ShieldEnemyStates.vulnerable, vulnerable)
        .AddTransition(ShieldEnemyStates.reposition, reposition)
        .AddTransition(ShieldEnemyStates.attack, attack)
        .AddTransition(ShieldEnemyStates.pursue, pursue)
        .AddTransition(ShieldEnemyStates.idle, idle);

        #endregion

        #region Estados

        idle.OnEnter += (previousState) =>
        {
            anims.SetFloat("Moving", 0f);
        };
        idle.OnUpdate += () =>
        {
            var toDamage = sight.target.GetComponent <IKilleable>();
            if (!toDamage.IsAlive)
            {
                return;
            }

            if (sight.IsInSight() || sight.distanceToTarget < minDetectionRange)
            {
                _targetDetected = true;
            }

            if (_targetDetected)
            {
                _sm.Feed(ShieldEnemyStates.alerted);
            }
        };

        alerted.OnEnter += (previousState) =>
        {
            _alertedTimeRemaining = AlertedTime;
        };
        alerted.OnUpdate += () =>
        {
            if (_alertedTimeRemaining >= 0)
            {
                _alertedTimeRemaining -= Time.deltaTime;
                transform.forward      = Vector3.Slerp(transform.forward, sight.dirToTarget, _rotationLerpSpeed);
            }
            else
            {
                _sm.Feed(ShieldEnemyStates.think);
            }
        };
        alerted.OnExit += (nextState) => { };


        blocking.OnEnter += (previousState) =>
        {
            anims.SetBool("Blocking", true);
            anims.SetFloat("Moving", 0f);
            LookTowardsPlayer     = true;
            _originalRotLerpSpeed = _rotationLerpSpeed;
            _rotationLerpSpeed    = BlockLerpSpeed;
            _blocking             = true;
            //recievedHits = 0;

            FRitmo.SetCurrentVulnerabilityCombo(1);
            FRitmo.ShowVulnerability();
        };
        blocking.OnUpdate += () =>
        {
            if (sight.distanceToTarget > BlockRange)
            {
                _sm.Feed(ShieldEnemyStates.think);
            }
        };
        blocking.OnExit += (nextState) =>
        {
            if (nextState == ShieldEnemyStates.vulnerable)
            {
                Debug.LogWarning("Voy a vulnerable");
            }

            anims.SetBool("Blocking", false);
            _rotationLerpSpeed = _originalRotLerpSpeed;
            _blocking          = false;
        };

        vulnerable.OnEnter += (previousState) =>
        {
            _blocking            = false;
            Disarmed             = true;
            _currentDisarmedTime = DisarmedTime;
            //SetVulnerabity(true, 1);

            anims.SetBool("Disarmed", true);
        };
        //vulnerable.OnUpdate += () => { };
        vulnerable.OnExit += (nextState) =>
        {
            anims.SetBool("Disarmed", false);
        };


        parry.OnEnter += (previousState) =>
        {
            //_parrying = true;
            //recievedHits = 0;

            anims.SetTrigger("Parrying");
            ShieldSparks.Play();
        };
        //parry.OnExit += (nextState) => { _parrying = false; };

        pursue.OnEnter += (previousState) =>
        {
            anims.SetFloat("Moving", 1f);
            //recievedHits = 0;
        };
        pursue.OnUpdate += () =>
        {
            //Correr como si no hubiera un mañana (?
            transform.forward = Vector3.Slerp(transform.forward, sight.dirToTarget, _rotationLerpSpeed);

            if (sight.distanceToTarget > AttackRange)
            {
                agent.Move(sight.dirToTarget * MovementSpeed * Time.deltaTime);
            }

            if (sight.distanceToTarget <= AttackRange)
            {
                _sm.Feed(ShieldEnemyStates.think);
            }
        };
        //pursue.OnExit += (nextState) => { };

        reposition.OnEnter += (previousState) =>
        {
            LookTowardsPlayer     = true;
            _originalRotLerpSpeed = _rotationLerpSpeed;
            _rotationLerpSpeed    = repositionLerpSpeed;
        };
        reposition.OnUpdate += () =>
        {
            if (sight.angleToTarget < 45)
            {
                _sm.Feed(ShieldEnemyStates.think);
            }
        };
        reposition.OnExit += (nextState) =>
        {
            LookTowardsPlayer  = false;
            _rotationLerpSpeed = _originalRotLerpSpeed;
        };

        attack.OnEnter += (previousState) =>
        {
            //_attacking = true;
            agent.isStopped = true;
            rb.velocity     = Vector3.zero;
            anims.SetInteger("Attack", 1);
            //recievedHits = 0;
        };
        //attack.OnUpdate += () => { };
        attack.OnExit += (nextState) =>
        {
            //_attacking = false;
            agent.isStopped = false;
        };

        think.OnEnter += (previousState) =>
        {
            if (ThinkTime > 0)
            {
                remainingThinkTime = ThinkTime;
            }
        };
        think.OnUpdate += () =>
        {
            if (remainingThinkTime > 0)
            {
                remainingThinkTime -= Time.deltaTime;
            }
            else
            {
                remainingThinkTime = 0;
                IKilleable target = Target.GetComponent <IKilleable>();

                if (sight.angleToTarget > 45f)
                {
                    _sm.Feed(ShieldEnemyStates.reposition);
                    return;
                }

                if (target.IsAlive)
                {
                    //Tomo una desición.
                    float blockImportance  = (1 - (Health / MaxHP)) * 10;
                    float pursueImportance = ((Health / MaxHP) * 10);
                    float AttackImportance = 10f - (blockImportance * 0.8f);

                    //print(string.Format("BlockImp: {0} / PursueImp: {1} / AttackImportance: {2}",
                    //    blockImportance, pursueImportance, AttackImportance));

                    //Desiciones posibles:
                    //Perseguir --> se realiza siempre que el enemigo esté más lejos de cierta distancia.
                    //Atacar --> Su importancia se mantiene.
                    //Bloquear --> Cuando la vida se va reduciendo su imporancia es mayor.

                    if (sight.distanceToTarget > AttackRange)
                    {
                        int decition = RoulleteSelection.Roll(new float[2] {
                            pursueImportance, blockImportance
                        });
                        //print("Distance is bigger than the AttackRange.\nDecition was: " + (decition == 0 ? "pursue" : "block"));

                        if (decition == 0)
                        {
                            _sm.Feed(ShieldEnemyStates.pursue);
                        }
                        if (decition == 1)
                        {
                            _sm.Feed(ShieldEnemyStates.block);
                        }
                    }

                    if (sight.distanceToTarget < AttackRange)
                    {
                        int decition = RoulleteSelection.Roll(new float[2] {
                            AttackImportance, blockImportance
                        });
                        //print("Distance is smaller than the AttackRange.\nDecition was: " + (decition == 0 ? "Attack" : "block"));

                        if (decition == 0)
                        {
                            _sm.Feed(ShieldEnemyStates.attack);
                        }
                        if (decition == 1)
                        {
                            _sm.Feed(ShieldEnemyStates.block);
                        }
                    }
                }
            }
        };
        think.OnExit += (nextState) =>
        {
            //print(string.Format("Exiting from Thinking, next State will be {0}", nextState.ToString()));
        };

        dead.OnEnter += (previousState) =>
        {
            anims.SetBool("Dead", true);
            Die();
        };

        #endregion

        _sm = new GenericFSM <ShieldEnemyStates>(idle);
        #endregion
    }