protected override void SetupStateMachine() { stateMachine = new StateMachine(); var pickAPointAtASafeDistance = new MaintainRangeFromTransform(this, player, playerProximity); var walkToTarget = new MoveToTarget(this, agent, MoveState.Walking); var startRunningAway = new StartRunningAway(this, agent, playerProximity, maxRangeToPlayer + 2.0f); var runAwayABit = new RunAwayFromTransform(this, agent, playerProximity, MoveState.Running); var stareAtPlayer = new StareAtTransform(this, agent, player); var chargeAtPlayer = new ChargeAtPlayer(this, agent, player, rescueAttack); var toggleAttack = new ToggleAttack(rescueAttack); var aggressivelyWait = new StandAndWait(); //////---- // Set Transitions // // Passives // pickAPointAtASafeDistance stateMachine.AddTransition(pickAPointAtASafeDistance, walkToTarget, () => true); // walkToTarget stateMachine.AddTransition(walkToTarget, pickAPointAtASafeDistance, ReachedGoalPosition()); stateMachine.AddTransition(walkToTarget, startRunningAway, TooCloseToPlayer()); stateMachine.AddTransition(walkToTarget, pickAPointAtASafeDistance, GotStuck()); // startRunningAway stateMachine.AddTransition(startRunningAway, runAwayABit, () => true); // runAwayABit stateMachine.AddTransition(runAwayABit, pickAPointAtASafeDistance, EscapedFromPlayer()); // Aggressive State Transitions // stareAtPlayer stateMachine.AddTransition(stareAtPlayer, toggleAttack, WaitFor(stareAtPlayer, chargeUpTime)); // toggleAttack stateMachine.AddTransition(toggleAttack, chargeAtPlayer, () => true); // chargeAtPlayer stateMachine.AddTransition(chargeAtPlayer, aggressivelyWait, ChargeAttackEnded()); // aggressivelyWait stateMachine.AddTransition(aggressivelyWait, pickAPointAtASafeDistance, WaitFor(aggressivelyWait, pauseAfterAttack)); //////---- // Universal Transitions // // One-frame States stateMachine.AddUniversalTransition(stareAtPlayer, GoingAggressive()); // Conditions Func <bool> ReachedGoalPosition() => () => Vector3.Distance(transform.position, goalPosition) <= agent.stoppingDistance + 0.2f; Func <bool> GotStuck() => () => timeSinceLastProgress > StuckTime; Func <bool> TooCloseToPlayer() => () => playerProximity.IsInRange; Func <bool> EscapedFromPlayer() => () => !playerProximity.IsInRange; Func <bool> WaitFor(IWaitableState state, float seconds) => () => state.GetTimeElapsed() > seconds; Func <bool> ChargeAttackEnded() => () => !rescueAttack.IsEnabled(); Func <bool> GoingAggressive() => () => { if (attackCooldown < 0.0f) { StartAttackCooldown(); return(true); } return(false); }; // Set initial state stateMachine.SetState(pickAPointAtASafeDistance); }
protected override void SetupStateMachine() { stateMachine = new StateMachine(); /* @NOTE: * Current intent is to not actually implement the AI as described. We don't have the API to make * assertions about whether or not a child has received the balloon they wanted. We can only check * unicorns for captured states. As a result, I see fit to only implement guarding *unicorns* * and not children from being delivered. We can revisit this later if there is time. */ var findGuardTarget = new FindGuardTarget(this); var betweenTargetAndGuard = new TargetPositionBetween(this, player); var moveToGuardPoint = new MoveToTarget(this, agent, MoveState.Walking); var passivelyWait = new StandAndWait(); var stareAtPlayer = new StareAtTransform(this, agent, player); var chargeAtPlayer = new ChargeAtPlayer(this, agent, player, chargingAttack); var chargeAttackRecoil = new ChargeAttackRecoil(agent, player, chargingAttack); var aggressivelyWait = new StandAndWait(); #region Transitions // Passive State Transitions // findGuardTarget stateMachine.AddTransition(findGuardTarget, betweenTargetAndGuard, HasGuardTarget()); stateMachine.AddTransition(betweenTargetAndGuard, moveToGuardPoint, () => true); // moveToGuardPoint stateMachine.AddTransition(moveToGuardPoint, findGuardTarget, GotStuck()); stateMachine.AddTransition(moveToGuardPoint, findGuardTarget, WaitFor(moveToGuardPoint, 1.0f)); stateMachine.AddTransition(moveToGuardPoint, passivelyWait, ReachedGoalPosition()); // standAndWait stateMachine.AddTransition(passivelyWait, findGuardTarget, WaitFor(passivelyWait, 1.0f)); // Aggressive State Transitions // stareAtPlayer stateMachine.AddTransition(stareAtPlayer, chargeAtPlayer, WaitFor(stareAtPlayer, chargeUpTime)); stateMachine.AddTransition(stareAtPlayer, findGuardTarget, PlayerNotInRange()); // chargeAtPlayer stateMachine.AddTransition(chargeAtPlayer, findGuardTarget, PlayerNotInRange()); stateMachine.AddTransition(chargeAtPlayer, aggressivelyWait, PlayerNotInFront()); stateMachine.AddTransition(chargeAtPlayer, chargeAttackRecoil, ChargeAttackSuccessful()); //chargeAttackRecoil stateMachine.AddTransition(chargeAttackRecoil, stareAtPlayer, WaitFor(chargeAttackRecoil, chargingAttack.ministunDuration * 2.0f)); // aggressivelyWait stateMachine.AddTransition(aggressivelyWait, stareAtPlayer, WaitFor(aggressivelyWait, unsuccessfulWaitTime)); // Universal State Transitions stateMachine.AddUniversalTransition(stareAtPlayer, GoAggressive()); stateMachine.AddUniversalTransition(findGuardTarget, GoPassive()); #endregion // Conditions Func <bool> HasGuardTarget() => () => guardTarget != null; Func <bool> GotStuck() => () => timeSinceLastProgress > StuckTime; Func <bool> ReachedGoalPosition() => () => Vector3.Distance(transform.position, goalPosition) <= agent.stoppingDistance + 0.2f; Func <bool> WaitFor(IWaitableState state, float seconds) => () => state.GetTimeElapsed() > seconds; Func <bool> GoAggressive() => () => { if (isAggressive || !playerProximity.IsInRange) { return(false); } isAggressive = true; return(true); }; Func <bool> GoPassive() => () => { if (!isAggressive || playerProximity.IsInRange) { return(false); } isAggressive = false; return(true); }; Func <bool> PlayerNotInRange() => () => !playerProximity.IsInRange; Func <bool> PlayerNotInFront() => () => Vector3.Dot(transform.forward, player.position - transform.position) < 0.3f; Func <bool> ChargeAttackSuccessful() => () => !chargingAttack.IsEnabled(); // Set initial state stateMachine.SetState(findGuardTarget); }