/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { foreach (IPolicy policy in policies) { policy.ChooseAction(controller); } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (shootTarget == null) { // Find a new target to attack. Controller closestOpponent = null; float closestDistance = Mathf.Infinity; foreach (Controller opponent in GameManager.instance.AllPlayers) { if (opponent != controller && opponent.LifeComponent.Health > 0) { float opponentDistance = Vector3.Distance(opponent.transform.position, controller.transform.position); if (opponentDistance < closestDistance) { closestDistance = opponentDistance; closestOpponent = opponent; } } } foreach (GameObject token in TokenSpawner.instance.Tokens) { // See if any tokens are close enough to bother with. if (token.activeSelf) { float tokenDistance = Vector3.Distance(token.transform.position, controller.transform.position); if (tokenDistance < closestDistance) { closestDistance = tokenDistance; targetToken = token; rushPolicy.target = token.gameObject; rushPolicy.targetDistance = 0; } } } if (targetToken == null && closestOpponent != null) { rushPolicy.target = closestOpponent.gameObject; rushPolicy.targetDistance = TARGETDISTANCE; } shootPolicy.target = closestOpponent; } rushPolicy.ChooseAction(controller); shootPolicy.ChooseAction(controller); if (!controller.aiming || (shootTarget != null && shootTarget.LifeComponent.Health <= 0)) { shootTarget = null; } if (targetToken != null && !targetToken.activeSelf) { targetToken = null; if (shootPolicy.target != null) { rushPolicy.target = shootPolicy.target.gameObject; } else { shootTarget = null; } } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (controller.opponent.LifeComponent.Health <= 0) { controller.aiming = false; return; } // Calculate the needed angle and strength to hit the opponent. Vector3 distance = controller.opponent.transform.position - controller.transform.position; float x = distance.x; float y = distance.y; float g = Physics.gravity.y; // Minimum speed at 45 degrees. float v = Mathf.Sqrt(x * x * g / (x + 2 * y * ROOT22 * ROOT22)); //Debug.Log(v); float minStrength = (-4 + Mathf.Sqrt(8 + 160 * v)) / 80; minStrength = Mathf.Sqrt(4 * minStrength * minStrength - minStrength * ROOT2 / 5 + 0.01f) / 2; float strength = Mathf.Max(power, minStrength); float angle = Mathf.PI / 4; // Change angle if the required power is greater than the minimum. /* if (strength != minStrength) { v = strength * 40 * (strength + 0.1f); float v2 = v * v; float discriminant = v2 * v2 - g * (g * x * x - 2 * y * v2); if (discriminant < 0) { discriminant = 0; } discriminant = 0; angle = Mathf.Atan((v2 - Mathf.Sqrt(discriminant)) / (g * x)); Debug.Log(angle + " " + v + " " + x + " " + y); }*/ controller.aim = Quaternion.Euler(0, 0, angle * 180 / Mathf.PI) * Vector3.up; //controller.aim = Vector3.Normalize(controller.opponent.transform.position - controller.transform.position); if (controller.aiming) { if (controller.ArcheryComponent.StrengthPercentage > strength - Time.deltaTime / 2f) { Debug.Log(v); controller.aiming = false; } } else { if (fireCooldown++ > cooldownTime) { controller.aiming = true; fireCooldown = 0; } } }
public void ChooseAction(AIController controller) { Vector3 opponentDistance = controller.GetOpponentDistance(); if (Mathf.Abs(opponentDistance.x) > targetDistance) { controller.SetRunInDirection(opponentDistance.x); // Don't chase an opponent off the map. RaycastHit hit = new RaycastHit(); Vector3 offsetPosition = controller.transform.position; offsetPosition.x += controller.runSpeed; if (!Physics.Raycast(offsetPosition, Vector3.down, out hit, 100, 1 << 13)) { controller.runSpeed = 0; } } else { controller.runSpeed = 0; } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { float totalOppDistance = Vector3.Distance(controller.opponent.transform.position, controller.transform.position); int shotChanceModifier = 0; // Experiment with 3 distance groups (determines if the bot shoots) //Choose the distance modifier if (totalOppDistance < 5) { shotChanceModifier = spacingReinforcement[0]; } else if (totalOppDistance < 9) { shotChanceModifier = spacingReinforcement[1]; } else { shotChanceModifier = spacingReinforcement[2]; } //Check to see if CPU will shoot given the modifier int randShotChance = random.Next(200000); if (shotChanceModifier * chanceToShoot < randShotChance) { shotInst.ChooseAction(controller); } //Experiment with 3 distance groups (determines favorite distance) int randMoveChance = random.Next(100); if (randMoveChance < distanceReinforcement[0]) { rushInst.targetDistance = 3; } else if (randMoveChance < (distanceReinforcement[0] + distanceReinforcement[1])) { rushInst.targetDistance = 4; } else if (randMoveChance < (distanceReinforcement[0] + distanceReinforcement[1] + distanceReinforcement[2])) { rushInst.targetDistance = 5; } rushInst.ChooseAction(controller); }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { Vector3 opponentDistance = controller.GetOpponentDistance(); float horizontalDistance = Mathf.Abs(opponentDistance.x); if (horizontalDistance > targetDistance) { controller.SetRunInDirection(opponentDistance.x); } else if (horizontalDistance < targetDistance - 1f) { controller.SetRunInDirection(-opponentDistance.x); } else { controller.runSpeed = 0; } if (controller.runSpeed != 0) { // Don't chase an opponent off the map. RaycastHit hit = new RaycastHit(); Vector3 offsetPosition = controller.transform.position; offsetPosition.x += controller.runSpeed; offsetPosition.y += 0.5f; if (!Physics.Raycast(offsetPosition, Vector3.down, out hit, 100, 1 << 13)) { if (controller.ParkourComponent.Sliding) { controller.SetRunInDirection(-opponentDistance.x); } else { controller.runSpeed = 0; } controller.slide = false; } else { controller.slide = horizontalDistance > targetDistance * 2; } } }
public void ChooseAction(AIController controller) { if (controller.opponent.LifeComponent.Health <= 0) { controller.aiming = false; return; } controller.aim = Vector3.Normalize(controller.opponent.transform.position - controller.transform.position); if (controller.aiming) { if (controller.ArcheryComponent.StrengthPercentage > power) { controller.aiming = false; } } else { if (fireCooldown++ > cooldownTime) { controller.aiming = true; fireCooldown = 0; } } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (target == null || target.LifeComponent.Health <= 0) { controller.aiming = true; return; } Vector3 positionOffset = target.transform.position - controller.transform.position; positionOffset.y += 0.25f; bool left = false; if (positionOffset.x < 0) { left = true; positionOffset.x = -positionOffset.x; } float angle = Vector3.Angle(Vector3.right, positionOffset) * Mathf.PI / 180; angle = Mathf.Max(angle, minAngle); float x = positionOffset.x; float y = positionOffset.y; float g = -Physics.gravity.y; // Find the minimum power for the required angle. float v = Mathf.Sqrt((x * x * g) / (x * Mathf.Sin(2 * angle) - 2 * y * Mathf.Pow(Mathf.Cos(angle), 2))); float power = (Mathf.Sqrt(10 * v + 1) - 1) / 20; // If the minimum power is less than the required power, find the needed angle for the power. if (power < minPower) { power = minPower; v = 40 * power * (power + 0.1f); float root = Mathf.Sqrt(Mathf.Pow(v, 4) - g * (g * x * x + 2 * y * v * v)); angle = Mathf.Atan((v * v - root) / (g * x)); if (angle < minAngle) { angle = Mathf.Atan((v * v + root) / (g * x)); } } power = Mathf.Min(power, 0.95f); if (left) { angle = Mathf.PI - angle; } controller.aim = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0); // Keep the bow drawn without shooting if there isn't a clear shot. if (left) { positionOffset.x = -positionOffset.x; } RaycastHit hit; if (!controller.HasClearShot(positionOffset, out hit)) { controller.aiming = true; return; } if (controller.aiming) { if (controller.ArcheryComponent.StrengthPercentage >= power) { controller.ArcheryComponent.StrengthPercentage = power; controller.aiming = false; } } else { fireCooldown += Time.deltaTime; if (fireCooldown > cooldownTime) { controller.aiming = true; fireCooldown = 0; } } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (DEBUGLINES) { foreach (LedgeNode ledge in ledges) { foreach (LedgeNode adjacent in ledge.adjacentEdges) { Debug.DrawLine(ledge.transform.position, adjacent.transform.position, Color.yellow); } } } if (target == null) { controller.SetRunInDirection(0); return; } Controller targetController = target.GetComponent<Controller>(); if (targetController != null && targetController.LifeComponent.Health <= 0) { controller.SetRunInDirection(0); return; } lastSpeed = controller.runSpeed; controller.jump = false; float currentTargetDistance = targetDistance; Vector3 opponentOffset = target.transform.position - controller.transform.position; Vector3 targetOffset = opponentOffset; float distanceTolerance = targetDistance - 1; Vector3 playerCenter = controller.transform.position + Vector3.up * 0.75f; RaycastHit under; Physics.Raycast(playerCenter, Vector3.down, out under, 30, AIController.LAYERMASK); // Check if there is a platform in the way of shooting. RaycastHit hit; // Find a ledge path to get to the target. replanTimer -= Time.deltaTime; if (pathTarget != target && currentNode == null) { replanTimer = 0; } if (OnSamePlatform(controller.transform, target.transform)) { if (Mathf.Abs(opponentOffset.y) < 5 && (Mathf.Abs(opponentOffset.y) < 0.1f || under.distance < 1)) { currentNode = null; } } else if (replanTimer <= 0) { replanTimer = REPLANTIME; pathTarget = target; currentPath = GetPath(GetClosestLedgeNode(controller.transform), GetClosestLedgeNode(target.transform)); if (currentPath.Count > 0) { currentPath.Last.Value.YOffset = target.transform.position.y - currentPath.Last.Value.transform.position.y; LoadNextLedge(); currentNode.YOffset = currentNode.transform.position.y - controller.transform.position.y; if (currentPath.Count > 0) { if (GetLedgePlatform(currentPath.First.Value) == GetPlatformUnderneath(controller.transform)) { LoadNextLedge(); } else if (currentPath.Count == 1 && Mathf.Abs(currentNode.transform.position.y - currentPath.First.Value.transform.position.y) < 0.2f) { currentNode = null; } } } else { currentNode = null; } } if (currentNode != null) { // Move towards the nearest ledge, jumping if needed. Vector3 currentOffset = currentNode.transform.position - controller.transform.position; // Go to the next ledge node if possible. if (currentNode.YOffset >= 0 && Math.Abs(currentOffset.x) <= LEDGEGRABDISTANCE / 3 && currentOffset.y <= 0 || currentNode.YOffset < 0 && currentOffset.y >= 1.5f) { LoadNextLedge(); } if (currentNode != null) { currentOffset = currentNode.transform.position - controller.transform.position; currentTargetDistance = 0; distanceTolerance = 0.1f; if (Mathf.Abs(currentOffset.y) < distanceTolerance) { currentOffset.y = 0; } Vector3 platformOffset = currentNode.transform.position - GetLedgePlatform(currentNode).transform.position; float ledgeOffset = -0.1f; if (currentNode.YOffset > 0 && currentOffset.y > -0.5f || currentNode.YOffset < 0) { ledgeOffset = LEDGEGRABDISTANCE; } if (platformOffset.x > 0) { currentOffset.x += ledgeOffset; } else { currentOffset.x -= ledgeOffset; } controller.jump = currentNode.YOffset >= 0 && Math.Abs(currentOffset.x) <= LEDGEGRABDISTANCE / 2; targetOffset = currentOffset; } } if (DEBUGLINES) { Debug.DrawRay(controller.transform.position, targetOffset, Color.red); if (currentNode != null) { LedgeNode c = currentNode; foreach (LedgeNode l in currentPath) { Debug.DrawLine(c.transform.position, l.transform.position, Color.red); c = l; } Debug.DrawLine(c.transform.position, target.transform.position, Color.red); } } LedgeNode closestLedge = null; // Check if the AI is falling to its death. if (under.collider == null) { // Find the closest ledge to go to. float closestLedgeDistance = Mathf.Infinity; foreach (LedgeNode ledge in ledges) { if (ledge != null) { float currentDistance = Mathf.Abs(ledge.transform.position.x - controller.transform.position.x); if (currentDistance < closestLedgeDistance && ledge.transform.position.y < controller.transform.position.y + 1) { closestLedge = ledge; closestLedgeDistance = currentDistance; } } } bool awayFromLedge = false; if (closestLedge == null) { controller.SetRunInDirection(-controller.transform.position.x); } else { float ledgeOffsetX = closestLedge.transform.position.x - controller.transform.position.x; if (Mathf.Abs(ledgeOffsetX) > LEDGEGRABDISTANCE) { controller.SetRunInDirection(ledgeOffsetX); awayFromLedge = true; } } controller.jump = true; if (awayFromLedge) { return; } } // Move towards the opponent. float horizontalDistance = Mathf.Abs(targetOffset.x); if (horizontalDistance > currentTargetDistance) { controller.SetRunInDirection(targetOffset.x); } else if (horizontalDistance < currentTargetDistance - distanceTolerance) { controller.SetRunInDirection(-targetOffset.x); } else if (opponentOffset == targetOffset && under.collider != null && (controller.ParkourComponent.FacingRight ^ opponentOffset.x > 0)) { controller.ParkourComponent.FacingRight = opponentOffset.x > 0; } else { controller.runSpeed = 0; } if (controller.runSpeed != 0) { // Don't chase an opponent off the map. Vector3 offsetPosition = controller.transform.position; offsetPosition.x += controller.runSpeed / 3; offsetPosition.y += 0.5f; Vector3 offsetPosition3 = offsetPosition; offsetPosition3.x += controller.runSpeed; if (!Physics.Raycast(offsetPosition, Vector3.down, out hit, 30, AIController.LAYERMASK) && !Physics.Raycast(offsetPosition3, Vector3.down, out hit, 30, AIController.LAYERMASK)) { if (controller.ParkourComponent.Sliding) { controller.SetRunInDirection(-opponentOffset.x); } else if (closestLedge == null) { controller.runSpeed = 0; } controller.slide = false; } else { // Slide if the opponent is far enough away for sliding to be useful. controller.ParkourComponent.FacingRight = opponentOffset.x > 0; controller.slide = horizontalDistance > 10 && currentNode == null && OnSamePlatform(controller.transform, target.transform); } } if (controller.runSpeed == 0 && Mathf.Abs(opponentOffset.x) < 1 && opponentOffset.y < 0 && target.GetComponent<Controller>() && controller.GetComponent<Rigidbody>().velocity.y <= Mathf.Epsilon) { // Don't sit on top of the opponent. controller.SetRunInDirection(-controller.transform.position.x); } if (controller.runSpeed > 0 && lastSpeed < 0 || controller.runSpeed < 0 && lastSpeed > 0) { // Check if the AI turned very recently to avoid thrashing. turnTimer -= Time.deltaTime; if (turnTimer <= 0) { turnTimer = TURNCOOLDOWN; } else { controller.runSpeed = 0; } } // Jump to reach some tokens. if (targetDistance == 0 && controller.runSpeed == 0 && target.GetComponent<ArrowToken>() && opponentOffset == targetOffset) { controller.jump = true; } controller.fastFall = targetOffset.y < -1f; }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (DEBUGLINES) { foreach (LedgeNode ledge in ledges) { foreach (LedgeNode adjacent in ledge.adjacentEdges) { Debug.DrawLine(ledge.transform.position, adjacent.transform.position, Color.yellow); } } } if (target == null) { controller.SetRunInDirection(0); return; } Controller targetController = target.GetComponent <Controller>(); if (targetController != null && targetController.LifeComponent.Health <= 0) { controller.SetRunInDirection(0); return; } lastSpeed = controller.runSpeed; controller.jump = false; float currentTargetDistance = targetDistance; Vector3 opponentOffset = target.transform.position - controller.transform.position; Vector3 targetOffset = opponentOffset; float distanceTolerance = targetDistance - 1; Vector3 playerCenter = controller.transform.position + Vector3.up * 0.75f; RaycastHit under; Physics.Raycast(playerCenter, Vector3.down, out under, 30, AIController.LAYERMASK); // Check if there is a platform in the way of shooting. RaycastHit hit; // Find a ledge path to get to the target. replanTimer -= Time.deltaTime; if (pathTarget != target && currentNode == null) { replanTimer = 0; } if (OnSamePlatform(controller.transform, target.transform)) { if (Mathf.Abs(opponentOffset.y) < 5 && (Mathf.Abs(opponentOffset.y) < 0.1f || under.distance < 1)) { currentNode = null; } } else if (replanTimer <= 0) { replanTimer = REPLANTIME; pathTarget = target; currentPath = GetPath(GetClosestLedgeNode(controller.transform), GetClosestLedgeNode(target.transform)); if (currentPath.Count > 0) { currentPath.Last.Value.YOffset = target.transform.position.y - currentPath.Last.Value.transform.position.y; LoadNextLedge(); currentNode.YOffset = currentNode.transform.position.y - controller.transform.position.y; if (currentPath.Count > 0) { if (GetLedgePlatform(currentPath.First.Value) == GetPlatformUnderneath(controller.transform)) { LoadNextLedge(); } else if (currentPath.Count == 1 && Mathf.Abs(currentNode.transform.position.y - currentPath.First.Value.transform.position.y) < 0.2f) { currentNode = null; } } } else { currentNode = null; } } if (currentNode != null) { // Move towards the nearest ledge, jumping if needed. Vector3 currentOffset = currentNode.transform.position - controller.transform.position; // Go to the next ledge node if possible. if (currentNode.YOffset >= 0 && Math.Abs(currentOffset.x) <= LEDGEGRABDISTANCE / 3 && currentOffset.y <= 0 || currentNode.YOffset < 0 && currentOffset.y >= 1.5f) { LoadNextLedge(); } if (currentNode != null) { currentOffset = currentNode.transform.position - controller.transform.position; currentTargetDistance = 0; distanceTolerance = 0.1f; if (Mathf.Abs(currentOffset.y) < distanceTolerance) { currentOffset.y = 0; } Vector3 platformOffset = currentNode.transform.position - GetLedgePlatform(currentNode).transform.position; float ledgeOffset = -0.1f; if (currentNode.YOffset > 0 && currentOffset.y > -0.5f || currentNode.YOffset < 0) { ledgeOffset = LEDGEGRABDISTANCE; } if (platformOffset.x > 0) { currentOffset.x += ledgeOffset; } else { currentOffset.x -= ledgeOffset; } controller.jump = currentNode.YOffset >= 0 && Math.Abs(currentOffset.x) <= LEDGEGRABDISTANCE / 2; targetOffset = currentOffset; } } if (DEBUGLINES) { Debug.DrawRay(controller.transform.position, targetOffset, Color.red); if (currentNode != null) { LedgeNode c = currentNode; foreach (LedgeNode l in currentPath) { Debug.DrawLine(c.transform.position, l.transform.position, Color.red); c = l; } Debug.DrawLine(c.transform.position, target.transform.position, Color.red); } } LedgeNode closestLedge = null; // Check if the AI is falling to its death. if (under.collider == null) { // Find the closest ledge to go to. float closestLedgeDistance = Mathf.Infinity; foreach (LedgeNode ledge in ledges) { if (ledge != null) { float currentDistance = Mathf.Abs(ledge.transform.position.x - controller.transform.position.x); if (currentDistance < closestLedgeDistance && ledge.transform.position.y < controller.transform.position.y + 1) { closestLedge = ledge; closestLedgeDistance = currentDistance; } } } bool awayFromLedge = false; if (closestLedge == null) { controller.SetRunInDirection(-controller.transform.position.x); } else { float ledgeOffsetX = closestLedge.transform.position.x - controller.transform.position.x; if (Mathf.Abs(ledgeOffsetX) > LEDGEGRABDISTANCE) { controller.SetRunInDirection(ledgeOffsetX); awayFromLedge = true; } } controller.jump = true; if (awayFromLedge) { return; } } // Move towards the opponent. float horizontalDistance = Mathf.Abs(targetOffset.x); if (horizontalDistance > currentTargetDistance) { controller.SetRunInDirection(targetOffset.x); } else if (horizontalDistance < currentTargetDistance - distanceTolerance) { controller.SetRunInDirection(-targetOffset.x); } else if (opponentOffset == targetOffset && under.collider != null && (controller.ParkourComponent.FacingRight ^ opponentOffset.x > 0)) { controller.ParkourComponent.FacingRight = opponentOffset.x > 0; } else { controller.runSpeed = 0; } if (controller.runSpeed != 0) { // Don't chase an opponent off the map. Vector3 offsetPosition = controller.transform.position; offsetPosition.x += controller.runSpeed / 3; offsetPosition.y += 0.5f; Vector3 offsetPosition3 = offsetPosition; offsetPosition3.x += controller.runSpeed; if (!Physics.Raycast(offsetPosition, Vector3.down, out hit, 30, AIController.LAYERMASK) && !Physics.Raycast(offsetPosition3, Vector3.down, out hit, 30, AIController.LAYERMASK)) { if (controller.ParkourComponent.Sliding) { controller.SetRunInDirection(-opponentOffset.x); } else if (closestLedge == null) { controller.runSpeed = 0; } controller.slide = false; } else { // Slide if the opponent is far enough away for sliding to be useful. controller.ParkourComponent.FacingRight = opponentOffset.x > 0; controller.slide = horizontalDistance > 10 && currentNode == null && OnSamePlatform(controller.transform, target.transform); } } if (controller.runSpeed == 0 && Mathf.Abs(opponentOffset.x) < 1 && opponentOffset.y < 0 && target.GetComponent <Controller>() && controller.GetComponent <Rigidbody>().velocity.y <= Mathf.Epsilon) { // Don't sit on top of the opponent. controller.SetRunInDirection(-controller.transform.position.x); } if (controller.runSpeed > 0 && lastSpeed < 0 || controller.runSpeed < 0 && lastSpeed > 0) { // Check if the AI turned very recently to avoid thrashing. turnTimer -= Time.deltaTime; if (turnTimer <= 0) { turnTimer = TURNCOOLDOWN; } else { controller.runSpeed = 0; } } // Jump to reach some tokens. if (targetDistance == 0 && controller.runSpeed == 0 && target.GetComponent <ArrowToken>() && opponentOffset == targetOffset) { controller.jump = true; } controller.fastFall = targetOffset.y < -1f; }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { fireCooldown += Time.deltaTime; if (target == null || target.LifeComponent.Health <= 0) { controller.aiming = false; return; } Vector3 positionOffset = target.transform.position - controller.transform.position; positionOffset.y += 0.25f; bool left = false; if (positionOffset.x < 0) { left = true; positionOffset.x = -positionOffset.x; } float angle = Vector3.Angle(Vector3.right, positionOffset) * Mathf.PI / 180; angle = Mathf.Max(angle, minAngle); float x = positionOffset.x; float y = positionOffset.y; float g = -Physics.gravity.y; // Find the minimum power for the required angle. float v = Mathf.Sqrt((x * x * g) / (x * Mathf.Sin(2 * angle) - 2 * y * Mathf.Pow(Mathf.Cos(angle), 2))); float power = (Mathf.Sqrt(10 * v + 1) - 1) / 20; // If the minimum power is less than the required power, find the needed angle for the power. if (power < minPower) { power = minPower; v = 40 * power * (power + 0.1f); float root = Mathf.Sqrt(Mathf.Pow(v, 4) - g * (g * x * x + 2 * y * v * v)); angle = Mathf.Atan((v * v - root) / (g * x)); if (angle < minAngle) { angle = Mathf.Atan((v * v + root) / (g * x)); } } power = Mathf.Min(power, 0.95f); if (left) { angle = Mathf.PI - angle; } controller.aim = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0); // Keep the bow drawn without shooting if there isn't a clear shot. if (left) { positionOffset.x = -positionOffset.x; } RaycastHit hit; if (!controller.HasClearShot(positionOffset, out hit)) { controller.aiming = false; return; } if (controller.aiming) { if (controller.ArcheryComponent.StrengthPercentage >= power && Mathf.Abs(Vector3.Angle(controller.aim, Vector3.right) - controller.ArcheryComponent.GetAimAngle()) < 7) { controller.ArcheryComponent.StrengthPercentage = power; controller.aiming = false; } } else { if (fireCooldown > cooldownTime) { controller.aiming = true; fireCooldown = 0; } } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (controller.opponent.LifeComponent.Health <= 0) { controller.aiming = false; return; } // Calculate the needed angle and strength to hit the opponent. Vector3 distance = controller.opponent.transform.position - controller.transform.position; float x = distance.x; float y = distance.y; float g = Physics.gravity.y; // Minimum speed at 45 degrees. float v = Mathf.Sqrt(x * x * g / (x + 2 * y * ROOT22 * ROOT22)); //Debug.Log(v); float minStrength = (-4 + Mathf.Sqrt(8 + 160 * v)) / 80; minStrength = Mathf.Sqrt(4 * minStrength * minStrength - minStrength * ROOT2 / 5 + 0.01f) / 2; float strength = Mathf.Max(power, minStrength); float angle = Mathf.PI / 4; // Change angle if the required power is greater than the minimum. /* * if (strength != minStrength) * { * v = strength * 40 * (strength + 0.1f); * float v2 = v * v; * * float discriminant = v2 * v2 - g * (g * x * x - 2 * y * v2); * if (discriminant < 0) * { * discriminant = 0; * } * discriminant = 0; * angle = Mathf.Atan((v2 - Mathf.Sqrt(discriminant)) / (g * x)); * Debug.Log(angle + " " + v + " " + x + " " + y); * }*/ controller.aim = Quaternion.Euler(0, 0, angle * 180 / Mathf.PI) * Vector3.up; //controller.aim = Vector3.Normalize(controller.opponent.transform.position - controller.transform.position); if (controller.aiming) { if (controller.ArcheryComponent.StrengthPercentage > strength - Time.deltaTime / 2f) { Debug.Log(v); controller.aiming = false; } } else { if (fireCooldown++ > cooldownTime) { controller.aiming = true; fireCooldown = 0; } } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (shootTarget == null) { // Find a new target to attack. Controller closestOpponent = null; float closestDistance = Mathf.Infinity; foreach (Controller opponent in GameManager.instance.AllPlayers) { if (opponent != controller && opponent.LifeComponent.Health > 0) { float opponentDistance = Vector3.Distance(opponent.transform.position, controller.transform.position); if (opponentDistance < closestDistance) { closestDistance = opponentDistance; closestOpponent = opponent; } } } if (controller.ArcheryComponent.CanCollectToken()) { foreach (GameObject token in TokenSpawner.instance.Tokens) { // See if any tokens are close enough to bother with. if (token.activeSelf) { float tokenDistance = Vector3.Distance(token.transform.position, controller.transform.position); if (tokenDistance < closestDistance && !Util.Bitwise.IsBitOn(controller.ArcheryComponent.ArrowTypes, (int)token.GetComponent <ArrowToken>().Type)) { closestDistance = tokenDistance; targetToken = token; rushPolicy.target = token.gameObject; rushPolicy.targetDistance = 0; } } } } if (targetToken == null && closestOpponent != null) { rushPolicy.target = closestOpponent.gameObject; rushPolicy.targetDistance = TARGETDISTANCE; } shootPolicy.target = closestOpponent; shootTarget = closestOpponent; } rushPolicy.ChooseAction(controller); shootPolicy.ChooseAction(controller); if (!controller.aiming || (shootTarget != null && shootTarget.LifeComponent.Health <= 0)) { shootTarget = null; } if (targetToken != null && !targetToken.activeSelf) { targetToken = null; if (shootPolicy.target != null) { rushPolicy.target = shootPolicy.target.gameObject; } } }
/// <summary> /// Picks an action for the character to do every tick. /// </summary> /// <param name="controller">The controller for the character.</param> public void ChooseAction(AIController controller) { if (target == null) { return; } lastSpeed = controller.runSpeed; controller.jump = false; float currentTargetDistance = targetDistance; Vector3 opponentOffset = target.transform.position - controller.transform.position; Vector3 targetOffset = opponentOffset; float distanceTolerance = 1f; // Check if there is a platform in the way of shooting. RaycastHit hit; controller.HasClearShot(opponentOffset, out hit); Transform blockingLedge = null; if (hit.collider != null) { // If an obstacle is in the way, move around it. float closestDistance = Mathf.Infinity; // Find ledges on the obstructing platform. BoxCollider[] children = hit.collider.GetComponentsInChildren<BoxCollider>(); bool foundBetween = false; foreach (BoxCollider child in children) { if (child.tag == "Ledge") { Vector3 ledgeOffset = child.transform.position - controller.transform.position; if (Mathf.Sign(ledgeOffset.y) == Mathf.Sign(targetOffset.y)) { // Look for the closest ledge to grab or fall from. float currentDistance = Mathf.Abs(child.transform.position.x - controller.transform.position.x); bool between = BetweenX(child.transform, controller.transform, target.transform); if (!(foundBetween && !between) && currentDistance < closestDistance || !foundBetween && between) { foundBetween = foundBetween || between; // Make sure the edge isn't off the side of the map. float edgeMultiplier = 3; if (Physics.Raycast(child.transform.position + Vector3.down * 0.5f + Vector3.left * edgeMultiplier, Vector3.down, 30, AIController.LAYERMASK) || Physics.Raycast(child.transform.position + Vector3.down * 0.5f + Vector3.right * edgeMultiplier, Vector3.down, 30, AIController.LAYERMASK)) { // Don't target ledges that have already been jumped over. if (currentDistance > LEDGEGRABDISTANCE || child.transform.position.y >= controller.transform.position.y) { blockingLedge = child.transform; closestDistance = currentDistance; } } } } } } } Transform gapLedge = null; RaycastHit under; Physics.Raycast(controller.transform.position + Vector3.up * 0.5f, Vector3.down, out under, 30, AIController.LAYERMASK); if (hit.collider == null) { // If the ranger and its target are not on the same platform, go to a nearby ledge. RaycastHit underTarget; Physics.Raycast(target.transform.position + Vector3.up * 0.5f, Vector3.down, out underTarget, 30, AIController.LAYERMASK); if (under.collider != null && underTarget.collider != null && under.collider.gameObject != underTarget.collider.gameObject) { float closestLedgeDistance = Mathf.Infinity; foreach (GameObject ledge in ledges) { float currentDistance = Mathf.Abs(ledge.transform.position.x - controller.transform.position.x); if (currentDistance < closestLedgeDistance && ledge.transform.position.y > controller.transform.position.y + 1 && BetweenX(ledge.transform, controller.transform, target.transform)) { gapLedge = ledge.transform; closestLedgeDistance = currentDistance; } } } } Transform closestLedge; if (blockingLedge == null) { closestLedge = gapLedge; } else if (gapLedge == null) { closestLedge = blockingLedge; } else { if (Vector3.Distance(blockingLedge.position, controller.transform.position) <= Vector3.Distance(gapLedge.position, controller.transform.position)) { closestLedge = blockingLedge; } else { closestLedge = gapLedge; } } if (closestLedge != null) { // Move towards the nearest ledge, jumping if needed. Vector3 closestVector = closestLedge.position - controller.transform.position; if (closestLedge.position.x - closestLedge.parent.position.x > 0) { closestVector.x += LEDGEGRABDISTANCE; } else { closestVector.x -= LEDGEGRABDISTANCE; } if (Math.Abs(closestVector.x) < 1f) { controller.jump = opponentOffset.y > 0 || gapLedge != null; } else { controller.jump = false; } currentTargetDistance = 0; distanceTolerance = 0.1f; targetOffset = closestVector; } Debug.DrawRay(controller.transform.position, targetOffset); // Check if the AI is falling to its death. if (under.collider == null) { // Find the closest ledge to go to. closestLedge = null; float closestLedgeDistance = Mathf.Infinity; foreach (GameObject ledge in ledges) { float currentDistance = Mathf.Abs(ledge.transform.position.x - controller.transform.position.x); if (currentDistance < closestLedgeDistance && ledge.transform.position.y < controller.transform.position.y + 1) { closestLedge = ledge.transform; closestLedgeDistance = currentDistance; } } bool awayFromLedge = false; if (closestLedge == null) { controller.SetRunInDirection(-controller.transform.position.x); } else { float ledgeOffsetX = closestLedge.position.x - controller.transform.position.x; if (Mathf.Abs(ledgeOffsetX) > LEDGEGRABDISTANCE) { controller.SetRunInDirection(ledgeOffsetX); awayFromLedge = true; } } controller.jump = true; if (awayFromLedge) { return; } } if (currentTargetDistance > 0 && targetOffset.y < -1 && (closestLedge != null || Mathf.Abs(opponentOffset.x) > 1)) { // Move onto a platform if a ledge was just negotiated. currentTargetDistance = 0; } // Move towards the opponent. float horizontalDistance = Mathf.Abs(targetOffset.x); if (horizontalDistance > currentTargetDistance) { controller.SetRunInDirection(targetOffset.x); } else if (horizontalDistance < currentTargetDistance - distanceTolerance) { controller.SetRunInDirection(-targetOffset.x); } else { controller.runSpeed = 0; } if (controller.runSpeed != 0) { // Don't chase an opponent off the map. Vector3 offsetPosition = controller.transform.position; offsetPosition.x += controller.runSpeed; offsetPosition.y += 0.5f; Vector3 offsetPosition3 = offsetPosition; offsetPosition3.x += controller.runSpeed * 2; if (!Physics.Raycast(offsetPosition, Vector3.down, out hit, 30, AIController.LAYERMASK) && !Physics.Raycast(offsetPosition3, Vector3.down, out hit, 30, AIController.LAYERMASK)) { if (controller.ParkourComponent.Sliding) { controller.SetRunInDirection(-opponentOffset.x); } else if (closestLedge == null) { controller.runSpeed = 0; } controller.slide = false; } else { // Slide if the opponent is far enough away for sliding to be useful. controller.slide = horizontalDistance > targetDistance * 2; } } if (controller.runSpeed == 0 && Mathf.Abs(opponentOffset.x) < 1 && opponentOffset.y < 0 && target.GetComponent<Controller>() && controller.GetComponent<Rigidbody>().velocity.y <= Mathf.Epsilon) { // Don't sit on top of the opponent. controller.SetRunInDirection(-controller.transform.position.x); } if (controller.runSpeed > 0 && lastSpeed < 0 || controller.runSpeed < 0 && lastSpeed > 0) { // Check if the AI turned very recently to avoid thrashing. if (turnTimer-- <= 0) { turnTimer = TURNCOOLDOWN; } else { controller.runSpeed = 0; } } // Jump to reach some tokens. if (targetDistance == 0 && controller.runSpeed == 0) { controller.jump = true; } }