Update
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Demo.AI
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Character.Abilities.AI;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities.Items;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Moves the agent to random positions within a circle. No pathfinding is done.
|
||||
/// </summary>
|
||||
public class AgentMovement : PathfindingMovement
|
||||
{
|
||||
[Tooltip("The radius of the position that the character can traverse to.")]
|
||||
[SerializeField] protected float m_Radius = 3;
|
||||
[Tooltip("The agent has arrived at the destination when the distance is less than the stopping distance.")]
|
||||
[SerializeField] protected float m_StoppingDistance = 0.1f;
|
||||
[Tooltip("The amount of time that the agent should wait at each destination.")]
|
||||
[SerializeField] protected MinMaxFloat m_RandomWait = new MinMaxFloat(0.2f, 0.7f);
|
||||
|
||||
private Use m_UseAbility;
|
||||
private Vector3 m_Center;
|
||||
private Vector3 m_Destination;
|
||||
private Vector2 m_InputVector;
|
||||
private float m_WaitTime;
|
||||
private float m_ArriveTime;
|
||||
|
||||
public override Vector2 InputVector { get { return m_InputVector; } }
|
||||
public override Vector3 DeltaRotation { get { return Vector3.zero; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
m_UseAbility = m_CharacterLocomotion.GetAbility<Use>();
|
||||
m_Center = m_Transform.position;
|
||||
DetermineDestination();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a new destination to move towards.
|
||||
/// </summary>
|
||||
private void DetermineDestination()
|
||||
{
|
||||
var randomPosition = Random.insideUnitSphere * m_Radius;
|
||||
randomPosition.y = 0;
|
||||
SetDestination(m_Center + randomPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the destination of the pathfinding agent.
|
||||
/// </summary>
|
||||
/// <param name="destination">The position to move towards.</param>
|
||||
/// <returns>True if the destination was set.</returns>
|
||||
public override bool SetDestination(Vector3 destination)
|
||||
{
|
||||
m_Destination = destination;
|
||||
m_ArriveTime = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the ability.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
// The Use ability uses root motion to control the movement so the pathfinding movement should not override that.
|
||||
if (m_UseAbility.IsActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose a new destination after the agent has arrived at the current destination.
|
||||
if (IsAtDestination()) {
|
||||
if (m_ArriveTime == -1) {
|
||||
m_WaitTime = m_RandomWait.RandomValue;
|
||||
m_ArriveTime = Time.time;
|
||||
}
|
||||
if (m_ArriveTime + m_WaitTime < Time.time) {
|
||||
DetermineDestination();
|
||||
}
|
||||
}
|
||||
|
||||
// The normalized velocity should be relative to the target rotation.
|
||||
var desiredMovement = (m_Destination - m_Transform.position).normalized;
|
||||
var velocity = Quaternion.Inverse(m_Transform.rotation) * desiredMovement;
|
||||
m_InputVector.x = velocity.x;
|
||||
m_InputVector.y = velocity.z;
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the move direction is valid.
|
||||
/// </summary>
|
||||
public override void ApplyPosition()
|
||||
{
|
||||
if (IsAtDestination()) {
|
||||
// Prevent the character from jittering back and forth to land precisely on the target.
|
||||
var direction = m_Transform.InverseTransformPoint(m_Destination);
|
||||
var moveDirection = m_Transform.InverseTransformDirection(m_CharacterLocomotion.MoveDirection);
|
||||
if (Mathf.Abs(moveDirection.x) > Mathf.Abs(direction.x)) {
|
||||
moveDirection.x = direction.x;
|
||||
}
|
||||
if (Mathf.Abs(moveDirection.z) > Mathf.Abs(direction.z)) {
|
||||
moveDirection.z = direction.z;
|
||||
}
|
||||
m_CharacterLocomotion.MoveDirection = m_Transform.TransformDirection(moveDirection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the agent at the destination?
|
||||
/// </summary>
|
||||
/// <returns>True if the agent is at the destination.</returns>
|
||||
private bool IsAtDestination()
|
||||
{
|
||||
return (m_Destination - m_Transform.position).sqrMagnitude < m_StoppingDistance * m_StoppingDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db4525879aef5de42ae6a444f0f07efa
|
||||
timeCreated: 1546852169
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Demo.AI
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Character;
|
||||
using Opsive.UltimateCharacterController.Game;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Enables the attack on the agent when the character enters the trigger.
|
||||
/// </summary>
|
||||
public class AttackTrigger : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The agent doing the attacking.")]
|
||||
[SerializeField] protected MeleeAgent m_Agent;
|
||||
|
||||
/// <summary>
|
||||
/// An object has exited the trigger.
|
||||
/// </summary>
|
||||
/// <param name="other">The collider that exited the trigger.</param>
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// A main character collider is required.
|
||||
if (!MathUtility.InLayerMask(other.gameObject.layer, 1 << LayerManager.Character)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The object must be a character.
|
||||
var characterLocomotion = other.GetComponentInParent<UltimateCharacterLocomotion>();
|
||||
if (characterLocomotion == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Agent.Attack();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An object has entered the trigger.
|
||||
/// </summary>
|
||||
/// <param name="other">The object that entered the trigger.</param>
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
// A main character collider is required.
|
||||
if (!MathUtility.InLayerMask(other.gameObject.layer, 1 << LayerManager.Character)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The object must be a character.
|
||||
var characterLocomotion = other.GetComponentInParent<UltimateCharacterLocomotion>();
|
||||
if (characterLocomotion == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Agent.CancelAttack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c376161036d18244ace898a0f6e31c1
|
||||
timeCreated: 1546375022
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Demo.AI
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Character.MovementTypes;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// The Idle Movement Type will ensure the AI character doesn't move. A regular Movement Type could have also been used but there isn't a common Movement Type included
|
||||
/// with all of the different packages so it's easier to use the same Movement Type for demo purposes.
|
||||
/// </summary>
|
||||
public class Idle : MovementType
|
||||
{
|
||||
public override bool FirstPersonPerspective { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the delta yaw rotation of the character.
|
||||
/// </summary>
|
||||
/// <param name="characterHorizontalMovement">The character's horizontal movement.</param>
|
||||
/// <param name="characterForwardMovement">The character's forward movement.</param>
|
||||
/// <param name="cameraHorizontalMovement">The camera's horizontal movement.</param>
|
||||
/// <param name="cameraVerticalMovement">The camera's vertical movement.</param>
|
||||
/// <returns>The delta yaw rotation of the character.</returns>
|
||||
public override float GetDeltaYawRotation(float characterHorizontalMovement, float characterForwardMovement, float cameraHorizontalMovement, float cameraVerticalMovement)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controller's input vector relative to the movement type.
|
||||
/// </summary>
|
||||
/// <param name="inputVector">The current input vector.</param>
|
||||
/// <returns>The updated input vector.</returns>
|
||||
public override Vector2 GetInputVector(Vector2 inputVector)
|
||||
{
|
||||
return inputVector;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fe9d6d438edc4e48b091a0a8ac9a331
|
||||
timeCreated: 1546054356
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Demo.AI
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Character;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities.Items;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// An extremely simple AI agent that will attack at a fixed interval.
|
||||
/// </summary>
|
||||
public class MeleeAgent : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The interval that the agent should attack.")]
|
||||
[SerializeField] protected MinMaxFloat m_AttackInterval = new MinMaxFloat(2, 4);
|
||||
[Tooltip("The target must be within the specified distance before the agent can attack.")]
|
||||
[SerializeField] protected float m_TargetDistance = 3;
|
||||
[Tooltip("Attack immediately if the character is within the close distance.")]
|
||||
[SerializeField] protected float m_ImmediateAttackDistance = 1.5f;
|
||||
[Tooltip("The delay between immediate attacks to prevent the agent from attacking too often.")]
|
||||
[SerializeField] protected float m_ImmediateAttackDelay = 0.75f;
|
||||
|
||||
private Transform m_Transform;
|
||||
private UltimateCharacterLocomotion m_CharacterLocomotion;
|
||||
private AgentMovement m_AgentMovement;
|
||||
private Use m_UseAbility;
|
||||
private LocalLookSource m_LocalLookSource;
|
||||
private float m_AttackTime;
|
||||
private float m_NextAttackTime;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the default values.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
m_Transform = transform;
|
||||
m_CharacterLocomotion = GetComponent<UltimateCharacterLocomotion>();
|
||||
m_AgentMovement = m_CharacterLocomotion.GetAbility<AgentMovement>();
|
||||
m_UseAbility = m_CharacterLocomotion.GetAbility<Use>();
|
||||
m_LocalLookSource = GetComponent<LocalLookSource>();
|
||||
|
||||
m_AgentMovement.Enabled = false;
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attacks the target when within distance.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
var attack = false;
|
||||
var distance = (m_LocalLookSource.Target.position - m_Transform.position).sqrMagnitude;
|
||||
if (m_AttackTime + m_ImmediateAttackDelay < Time.time && distance < m_ImmediateAttackDistance * m_ImmediateAttackDistance) {
|
||||
attack = true;
|
||||
} else if (m_NextAttackTime < Time.time && distance < m_TargetDistance * m_TargetDistance) {
|
||||
attack = true;
|
||||
}
|
||||
|
||||
if (attack) {
|
||||
m_CharacterLocomotion.TryStartAbility(m_UseAbility);
|
||||
m_AttackTime = Time.time;
|
||||
m_NextAttackTime = Time.time + m_AttackInterval.RandomValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the attack.
|
||||
/// </summary>
|
||||
public void Attack()
|
||||
{
|
||||
if (m_AgentMovement == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The agent should be able to move while attacking.
|
||||
if (!m_AgentMovement.Enabled) {
|
||||
m_AgentMovement.Enabled = true;
|
||||
}
|
||||
enabled = true;
|
||||
m_NextAttackTime = Time.time + m_AttackInterval.RandomValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the attack.
|
||||
/// </summary>
|
||||
public void CancelAttack()
|
||||
{
|
||||
m_AgentMovement.Enabled = false;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8459a721f04ca1449a588756a99b2e4d
|
||||
timeCreated: 1546052871
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user