// Use this for initialization void Start() { sp = gameObject.GetComponent <SpriteRenderer>(); anm = gameObject.GetComponent <Animator>(); patrolA = transform.position; eCurState = BossActions.Patrolling; }
//Moves from left to right, can do whatever when it reaches a point of its patrol. void Patrol() { anm.SetBool("isMoving", true); if (patrolA.x == transform.position.x) { moveUp = true; if (Flips) { direction = -direction; Flip(); eCurState = BossActions.Idle; } } if (patrolB.x == transform.position.x) { moveUp = false; if (Flips) { direction = -direction; Flip(); eCurState = BossActions.Idle; } } if (moveUp) { transform.position = Vector2.MoveTowards(transform.position, patrolB, speed); } else { transform.position = Vector2.MoveTowards(transform.position, patrolA, speed); } }
//And drops on them, dealing damage. void Fall() { anm.Play("Fall"); Vector3 fallPos = new Vector3(transform.position.x, -2); transform.position = Vector2.MoveTowards(transform.position, fallPos, jumpSpeed * 0.5f); if (transform.position.y <= -2) { anm.Play("FallRecover"); eCurState = BossActions.Idle; } }
// 행동이 종료될 때까지 기다림 private IEnumerator WaitForNextAction(BossActions _waitingAction, BossActions _nextAction, bool _conditionOfNextAction) { if (_waitingAction != BossActions.Finish) { while (action[_waitingAction].enabled) { yield return(new WaitForFixedUpdate()); } } if (!_nextAction.Equals(BossActions.Finish)) { ActionControl(_nextAction, true); action[_nextAction].Action(_conditionOfNextAction); StartCoroutine(WaitForNextAction(_nextAction, BossActions.Finish, false)); } else { // 모든 행동이 끝나면 최초 조건으로 돌아감 Action(); } }
// Used with action que. //private bool Aggresive(Vector3 a_directionToMove) //{ // // Am I in range to launch attack or am I already // // attacking? // if (CalculateDistance(a_directionToMove) || m_continue) // { // // So you're at the correct distance // // Stop your movement. // m_animator.SetFloat("Movement/Z", 0); // m_animator.SetFloat("Movement/X", 0); // // So launch attack if facing correct direction // if (!m_continue && CorrectFacing(a_directionToMove)) // { // m_animator.SetInteger("Ai/Action", m_actionQue[0].GetAnimNum); // m_continue = true; // return false; // } // else // { // // If attack variants would need to check what ones to go down somewhere // // Or do internal logic on state machine. // if (!m_animator.GetBool("Ai/PlayingAction") && m_continue) // { // ResetState(); // return true; // } // else // { // return false; // } // } // } // else // { // AddSeek(a_directionToMove); // return false; // } //} // Move towards target by default. // Needs to evaluate an animation if special // ability time limit is triggered. private bool Seek(Vector3 a_directionToMove) { // Check if within range or currently playing action if (CalculateDistance(a_directionToMove) && !m_animator.GetBool("Ai/PlayingAction")) { return(true); } // Needs to check if it's time to do an action like dodge forward ect if (m_timeOutSeek < Time.time) { // Set time for next evaluation m_timeOutSeek = Time.time + m_bossPhaseList[m_currentPhase].m_timeBetweenMovementAction; // Evaluate action BossActions t = m_bossPhaseList[m_currentPhase].EvaluateSeek(GetDistanceToPlayer(a_directionToMove), m_desiredRange); // If action is returned instantly do action if (t != null) { // Do the action straight away m_animator.SetInteger("Ai/Action", t.GetAnimNum); // Should set plaing action to true. return(false); } // Else seek towards player still. } // If not time boss will walk towards target. a_directionToMove = this.transform.worldToLocalMatrix * a_directionToMove.normalized; m_animator.SetFloat("Movement/Z", a_directionToMove.z); m_animator.SetFloat("Movement/X", a_directionToMove.x); m_animator.SetInteger("Ai/Action", 0); // Should always get the animation speed multiplyer //m_animator.SetFloat("AnimationSpeedMultiplyer", 2); // Has not reached target. return(false); }
// Seeks to desired range. Since seeks don't require a range this probably works. //public BossActions EvaluateSeek(float a_distanceToTarget, float m_desiredRange) //{ // BossActions output = null; // foreach (BossActions t in m_bossActions) // { // if(t.GetBehaviourType == SteeringBehaviours.SEEK_BEHAVIOUR) // { // // from 10 to 0 Range from 020 0 or neg * 5 // // optimal distance, coolness factor, timeused // // Weighing happens here // // If desirability is greater than previous replace. // // bossaction // // Only here to test // output = t; // break; // } // // Adjust the weighing values of the list // } // return output; //} // Seek to desired target. // Think point to point movement with animations public BossActions EvaluateSeek(float a_distanceToTarget, float m_desiredRange) { // Chosen action BossActions output = null; // If it's good it goes here to be chosen later. List <BossActions> desired = new List <BossActions>(); // If the move does not benefit but can be done. List <BossActions> neutral = new List <BossActions>(); foreach (BossActions t in m_bossActions) { // List to be populated. // Behaviors are seeks therefore we should be checking // if they put the boss in a good position. if (t.GetBehaviourType == SteeringBehaviours.SEEK) { // If the distance covered is beneficial or negligalble // does the movement get the boss closer or further? float distanceCovered = t.GetDestinationDistance; // Need to check if the distance covered is beneficial float resultingDistanceWhenMoveUsed = a_distanceToTarget - distanceCovered; // Should be passed in by action. float rangeThreshold = 2; // Check if the moves result is neutral. if (resultingDistanceWhenMoveUsed == a_distanceToTarget) { if (m_lastSeekAction != null) { if (m_lastSeekAction == t) { continue; } } // No benefit but do able. //Debug.Log(t.GetAnimNum + " is neutral"); neutral.Add(t); } // Check if the move gets boss closer to optimal position without passing // through player. // If 0 or neg // Need to check if the resulting distance when used is closer to the target. // If not it is not good and should be skipped. else if ((resultingDistanceWhenMoveUsed > 0 && resultingDistanceWhenMoveUsed > m_desiredRange)) { if (m_lastSeekAction != null) { if (m_lastSeekAction == t) { continue; } } // A good choice //Debug.Log(t.GetAnimNum + " is desired"); desired.Add(t); } // Otherwise move is skipped. } } int reroll = 0; if (desired.Count > 0) { while (reroll < 1) { BossActions t = ChooseSeekAction(desired); // Reset the last actions used if (m_lastSeekAction != null) { if (t == m_lastSeekAction) { t = ChooseSeekAction(desired); } } else { output = t; m_lastNeutralAction = m_timeBetweenNeutral + Time.time; reroll = 2; } reroll++; } } // Same loop as above but with neutral evalations else if (neutral.Count > 0 && m_lastNeutralAction < Time.time) { while (reroll < 1) { BossActions t = ChooseSeekAction(neutral); // Reset the last actions used if (m_lastSeekAction != null) { if (t == m_lastSeekAction) { t = ChooseSeekAction(neutral); } } else { output = t; m_lastNeutralAction = m_timeBetweenNeutral + Time.time; reroll = 2; } reroll++; } } else { output = null; } // Used to stop moves occuring twice in a row. if (output != null) { m_lastSeekAction = output; } else { m_lastSeekAction = null; } return(output); }
// Boss checks to see what it's stamina is at. // DEFUNCT //public BossActions EvaluateSelf(float a_distanceToTarget, Vector3 m_targetDirection) //{ // // Might need to be removed // // Boss should only need to act agressivly // // Did i do a dodge last action? // // if yes choose attack and seek towards player // // if no choose either an attack or dodge. // //float currentStam = EntityStats.Instance.GetStaminaOfEntity("Boss"); // //if (currentStam > m_aggresiveStaminaThreshold) // //{ // return EvaluateAggresiveAction(a_distanceToTarget); // //} // //else if(currentStam > m_defensiveStaminaThreshold) // //{ // // return EvaluateDefensiveAction(m_targetDirection); // //} // //else // //{ // // return EvaluatePassiveAction(); // //} //} // Before attack needs to evaluate if it should evaluate // whether it should do an action that would result in no change // and launch the attack straight after. // Needs to know if the attack is launchable. // Attacks that need to be launched from a larger // distance will be weighted public BossActions EvaluateAggresiveAction(float a_distanceToTarget) { //sets multiplyWeight multiplyWeight = multiplyWeightWeight / m_bossActions.Count; BossActions output = null; List <EvaluatedAction> actions = new List <EvaluatedAction>(); foreach (BossActions t in m_bossActions) { if (t.GetBehaviourType == SteeringBehaviours.AGGRESSIVE) { EvaluatedAction z = new EvaluatedAction(); // Need to give values for evaluations z.m_action = t; float range = t.AttackRange; // Higher value closter to the target range //float distanceCovered = t.GetDestinationDistance; // How far the move travels. float weight = t.AttackWeight; // Flat value if (weight < 10f) { weight = 10f; } //sets weight to 10 if input as less than 10 float timesUnused = t.TimesUnused; // Subtraction based on times used float rangeThreshold = 2; // needs to be passed in by attack // Did we want a random number added? // Weight z.m_score = weight; // Time used z.m_score += timesUnused * multiplyWeight; z.m_score *= RangeMultiply(a_distanceToTarget, 0.3f, range, rangeThreshold); actions.Add(z); // Distance to player, damage, coolness factor(set by designer) timeused // more ways to be effect weighing. // Weighing happens here // If desirability is greater than previous replace. // bossaction // Only here to test //output = t; //break; } // Add all the evaluated values together } // Add up all scores float total = 0; foreach (EvaluatedAction t in actions) { total += t.m_score; } float norm = 1 / total; float assaignedSoFar = 0; // Apply normal value to every attack // and set min and max roll foreach (EvaluatedAction t in actions) { float normalizedScore = norm * t.m_score; t.m_rollMin = assaignedSoFar; t.m_rollMax = assaignedSoFar + normalizedScore + 0.01f; // Will this be okay? I dunno lol assaignedSoFar = t.m_rollMax - 0.01f; } // roll and convert to 0 - 1. // pass out action within range. float number = UnityEngine.Random.Range(0, assaignedSoFar); //1 to 100 //number = number / assaignedSoFar; foreach (EvaluatedAction t in actions) { if (number >= t.m_rollMin && number <= t.m_rollMax) { output = t.m_action; t.m_action.TimesUnused = 0; } else { t.m_action.TimesUnused++; } } //Debug.Log(number); // Adjust the weighing values of the list // Needs an output other than null otherwise breakage. if (output == null) { Debug.Log("F**k ai is busted."); Debug.Log("Random number was: " + number + " and " + assaignedSoFar + " was the total assaigned values."); } return(output); }
// 행동 관련 public void ActionControl(BossActions _action, bool _enable) { action[_action].enabled = _enable; }
//************************************************************ //Action moves //************************************************************ //************************************************************ // Evaluation of moves on the action list. //************************************************************ // Evaluate best attack // Try to launch attack // If can't add a seek above this action // If can decide if another action should occur // before and/or after the attack action. // Once complete or player is dead or knockeddown // remove from list. // Used with base actions. // Currently bugged and ai will forget his action sometimes // while curretnly doing it. private bool Aggresive(Vector3 a_target) { if (!m_continue) { // If aggresive but no action get action and time out // Or launching attack (in case time breaks it. if (m_currentAttack != null && m_timeOutAttack > Time.time) { // If in action skip entirely if (!m_animator.GetBool("Ai/PlayingAction")) { // Check if you're at the correct distance or should continue if (m_launchAttack || CalculateDistance(a_target) && CorrectFacing(a_target)) { // Stop your movement. Mostly a safety. m_animator.SetFloat("Movement/Z", 0); m_animator.SetFloat("Movement/X", 0); int random = Random.Range(0, 100); //Debug.Log(random); // Should I do an action before I attack or currenlty not in action if (random < m_chanceOfPreMove && !m_launchAttack) { BossActions t = m_bossPhaseList[m_currentPhase].EvaluateSeek(GetDistanceToPlayer(a_target), m_desiredRange); if (t != null) { m_chanceOfPreMove = m_PreMoveFloor; m_animator.SetInteger("Ai/Action", t.GetAnimNum); m_launchAttack = true; return(false); } } else { // Increase chance boss will do something before // a move. m_chanceOfPreMove += m_increasePerPremove; if (m_chanceOfPreMove > 100) { m_chanceOfPreMove = 100; } } // if it is not launching an extra animation it should get here. m_animator.SetInteger("Ai/Action", m_currentAttack.GetAnimNum); m_continue = true; } else { // If null or not within distance launch attack launch attack AddSeek(); } } } else { // A safety in case no action chosen. BossActions t = m_bossPhaseList[m_currentPhase].EvaluateAggresiveAction(GetDistanceToPlayer(a_target)); if (t != null) { m_currentAttack = t; m_desiredRange = t.AttackRange; m_launchAttack = false; m_continue = false; //Debug.Log("New attack"); // Will try to launch attack for x time m_timeOutAttack = Time.time + 10; // Sometimes enters here unintentionally } } return(false); } // logic is wrong. Enters here when it shouldn't else if (!m_animator.GetBool("Ai/PlayingAction") && m_animator.GetInteger("Ai/Action") == 0) { Debug.Log("I am forget"); // Should be hitting this whenever attacks are finished. m_continue = false; m_launchAttack = false; // Null so new attack needed. V dangerous. m_currentAttack = null; //Debug.Log("Attack Complete"); return(true); } else { return(false); } }
//If it has finished whatever it was doing, it rerolls and does something else. void Idle() { eCurState = (BossActions)Random.Range(0, 3); }
IEnumerator wait(float secs) { yield return(new WaitForSeconds(secs)); eCurState = BossActions.Idle; }
//Gets close to the x value of the player void MoveAround() { transform.position = new Vector2(player.position.x + Random.Range(0, 2f), transform.position.y); eCurState = BossActions.Falling; }
public void changeState(BossActions action) { currentAction = action; }