//============================= 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 }
//========================================================================================= 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 }