This commit is contained in:
2026-06-09 09:18:17 +07:00
parent 3578a2750c
commit 71a096556a
5777 changed files with 6675 additions and 13 deletions

View File

@@ -1,128 +0,0 @@
/// ---------------------------------------------
/// 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;
}
}
}

View File

@@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: db4525879aef5de42ae6a444f0f07efa
timeCreated: 1546852169
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,62 +0,0 @@
/// ---------------------------------------------
/// 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();
}
}
}

View File

@@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 1c376161036d18244ace898a0f6e31c1
timeCreated: 1546375022
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,43 +0,0 @@
/// ---------------------------------------------
/// 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;
}
}
}

View File

@@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 7fe9d6d438edc4e48b091a0a8ac9a331
timeCreated: 1546054356
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,97 +0,0 @@
/// ---------------------------------------------
/// 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;
}
}
}

View File

@@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 8459a721f04ca1449a588756a99b2e4d
timeCreated: 1546052871
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: