This commit is contained in:
CortexCore
2023-10-04 16:50:27 +08:00
parent 947e52e748
commit 5cd094ed9a
263 changed files with 144068 additions and 66 deletions

View File

@@ -0,0 +1,109 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Lightbug.CharacterControllerPro.Implementation;
using Lightbug.Utilities;
using Lightbug.CharacterControllerPro.Core;
namespace Lightbug.CharacterControllerPro.Demo
{
[AddComponentMenu("Character Controller Pro/Demo/Character/AI/Follow Behaviour")]
public class AIFollowBehaviour : CharacterAIBehaviour
{
[Tooltip("The target transform used by the follow behaviour.")]
[SerializeField]
Transform followTarget = null;
[Tooltip("Desired distance to the target. if the distance to the target is less than this value the character will not move.")]
[SerializeField]
float reachDistance = 3f;
[Tooltip("The wait time between actions updates.")]
[Min(0f)]
[SerializeField]
float refreshTime = 0.65f;
float timer = 0f;
NavMeshPath navMeshPath = null;
protected CharacterStateController stateController = null;
protected override void Awake()
{
base.Awake();
stateController = this.GetComponentInBranch<CharacterActor, CharacterStateController>();
stateController.MovementReferenceMode = MovementReferenceParameters.MovementReferenceMode.World;
}
void OnEnable()
{
navMeshPath = new NavMeshPath();
;
}
public override void EnterBehaviour(float dt)
{
timer = refreshTime;
}
public override void UpdateBehaviour(float dt)
{
if (timer >= refreshTime)
{
timer = 0f;
UpdateFollowTargetBehaviour(dt);
}
else
{
timer += dt;
}
}
// Follow Behaviour --------------------------------------------------------------------------------------------------
/// <summary>
/// Sets the target to follow (only for the follow behaviour).
/// </summary>
public void SetFollowTarget(Transform followTarget, bool forceUpdate = true)
{
this.followTarget = followTarget;
if (forceUpdate)
timer = refreshTime + Mathf.Epsilon;
}
void UpdateFollowTargetBehaviour(float dt)
{
if (followTarget == null)
return;
characterActions.Reset();
NavMesh.CalculatePath(transform.position, followTarget.position, NavMesh.AllAreas, navMeshPath);
if (navMeshPath.corners.Length < 2)
return;
Vector3 path = navMeshPath.corners[1] - navMeshPath.corners[0];
bool isDirectPath = navMeshPath.corners.Length == 2;
if (isDirectPath && path.magnitude <= reachDistance)
return;
if (navMeshPath.corners.Length > 1)
SetMovementAction(path);
}
}
}

View File

@@ -0,0 +1,114 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Lightbug.CharacterControllerPro.Core;
using Lightbug.CharacterControllerPro.Implementation;
using Lightbug.Utilities;
namespace Lightbug.CharacterControllerPro.Demo
{
[AddComponentMenu("Character Controller Pro/Demo/Character/AI/Sequence Behaviour")]
public class AISequenceBehaviour : CharacterAIBehaviour
{
const float DefaultDelayTime = 0.5f;
[SerializeField]
List<CharacterAIAction> actionSequence = new List<CharacterAIAction>();
// float timer = 0f;
float durationWaitTime = 0f;
float wallHitWaitTime = 0f;
int currentActionIndex = 0;
void OnEnable()
{
CharacterActor.OnWallHit += OnWallHit;
}
void OnDisable()
{
CharacterActor.OnWallHit -= OnWallHit;
}
public override void EnterBehaviour(float dt)
{
currentActionIndex = 0;
characterActions = actionSequence[currentActionIndex].action;
if (actionSequence[currentActionIndex].sequenceType == SequenceType.Duration)
{
durationWaitTime = actionSequence[currentActionIndex].duration;
}
}
public override void UpdateBehaviour(float dt)
{
// Process the timers
if (wallHitWaitTime > 0)
wallHitWaitTime = Mathf.Max(0f, wallHitWaitTime - dt);
if (durationWaitTime > 0)
durationWaitTime = Mathf.Max(0f, durationWaitTime - dt);
switch (actionSequence[currentActionIndex].sequenceType)
{
case SequenceType.Duration:
if (durationWaitTime == 0f)
SelectNextSequenceElement();
break;
case SequenceType.OnWallHit:
break;
}
}
void SelectNextSequenceElement()
{
if (currentActionIndex == (actionSequence.Count - 1))
currentActionIndex = 0;
else
currentActionIndex++;
characterActions = actionSequence[currentActionIndex].action;
durationWaitTime = actionSequence[currentActionIndex].duration;
}
void OnWallHit(Contact contact)
{
if (actionSequence[currentActionIndex].sequenceType != SequenceType.OnWallHit)
return;
if (wallHitWaitTime > 0f)
return;
bool characterCollision = contact.gameObject.GetComponent<CharacterActor>() != null;
if (characterCollision)
return;
SelectNextSequenceElement();
wallHitWaitTime = DefaultDelayTime;
}
}
}

View File

@@ -0,0 +1,114 @@
using UnityEngine;
using Lightbug.Utilities;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
namespace Lightbug.CharacterControllerPro.Demo
{
[CustomEditor(typeof(AISequenceBehaviour)), CanEditMultipleObjects]
public class AISequenceBehaviourEditor : Editor
{
ReorderableList reorderableList = null;
SerializedProperty actionSequence = null;
void OnEnable()
{
actionSequence = serializedObject.FindProperty("actionSequence");
reorderableList = new ReorderableList(
serializedObject,
actionSequence,
true,
true,
true,
true
);
reorderableList.elementHeight = 2 * EditorGUIUtility.singleLineHeight;
reorderableList.drawElementCallback += OnDrawElement;
reorderableList.drawHeaderCallback += OnDrawHeader;
reorderableList.elementHeightCallback += OnElementHeight;
}
void OnDisable()
{
reorderableList.drawElementCallback -= OnDrawElement;
reorderableList.drawHeaderCallback -= OnDrawHeader;
reorderableList.elementHeightCallback -= OnElementHeight;
}
void OnDrawHeader(Rect rect)
{
GUI.Label(rect, "Sequence");
}
void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty element = actionSequence.GetArrayElementAtIndex(index);
SerializedProperty sequenceType = element.FindPropertyRelative("sequenceType");
SerializedProperty duration = element.FindPropertyRelative("duration");
SerializedProperty action = element.FindPropertyRelative("action");
GUI.Box(rect, "", EditorStyles.helpBox);
Rect fieldRect = rect;
fieldRect.height = EditorGUIUtility.singleLineHeight;
fieldRect.x += 20;
fieldRect.width -= 30;
fieldRect.y += 0.5f * fieldRect.height;
EditorGUI.PropertyField(fieldRect, sequenceType);
fieldRect.y += 2 * fieldRect.height;
if (sequenceType.enumValueIndex == (int)SequenceType.Duration)
EditorGUI.PropertyField(fieldRect, duration);
fieldRect.y += 2 * fieldRect.height;
EditorGUI.PropertyField(fieldRect, action, true);
fieldRect.y += fieldRect.height;
}
float OnElementHeight(int index)
{
SerializedProperty element = actionSequence.GetArrayElementAtIndex(index);
SerializedProperty action = element.FindPropertyRelative("action");
float actionHeight = action.isExpanded ? EditorGUI.GetPropertyHeight(action) : EditorGUIUtility.singleLineHeight;
return 5 * EditorGUIUtility.singleLineHeight + actionHeight;
}
public override void OnInspectorGUI()
{
CustomUtilities.DrawMonoBehaviourField<AISequenceBehaviour>((AISequenceBehaviour)target);
serializedObject.Update();
GUILayout.Space(10);
reorderableList.DoLayoutList();
GUILayout.Space(10);
serializedObject.ApplyModifiedProperties();
}
}
}
#endif

View File

@@ -0,0 +1,101 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Lightbug.CharacterControllerPro.Core;
using Lightbug.CharacterControllerPro.Implementation;
using Lightbug.Utilities;
namespace Lightbug.CharacterControllerPro.Demo
{
[AddComponentMenu("Character Controller Pro/Demo/Character/AI/Wander Behaviour")]
public class AIWanderBehaviour : CharacterAIBehaviour
{
[Min(0f)]
[SerializeField]
float minRandomMagnitude = 1f;
[Min(0f)]
[SerializeField]
float maxRandomMagnitude = 1f;
[Min(0f)]
[SerializeField]
float minRandomYawAngle = 100f;
[Min(0f)]
[SerializeField]
float maxRandomYawAngle = 280f;
[Min(0f)]
[SerializeField]
float waitSeconds = 3f;
float timer = 0f;
Vector3 initialPosition = default(Vector3);
Vector3 target = default(Vector3);
void OnValidate()
{
if (minRandomMagnitude > maxRandomMagnitude)
minRandomMagnitude = maxRandomMagnitude;
if (maxRandomMagnitude < minRandomMagnitude)
maxRandomMagnitude = minRandomMagnitude;
if (minRandomYawAngle > maxRandomYawAngle)
minRandomYawAngle = maxRandomYawAngle;
if (maxRandomYawAngle < minRandomYawAngle)
maxRandomYawAngle = minRandomYawAngle;
}
public override void EnterBehaviour(float dt)
{
initialPosition = transform.position;
target = initialPosition + transform.forward * Random.Range(minRandomMagnitude, maxRandomMagnitude);
timer = 0f;
}
public override void UpdateBehaviour(float dt)
{
if (timer >= waitSeconds)
{
timer = 0f;
SetTarget();
}
else
{
timer += dt;
}
float distanceToTarget = (target - CharacterActor.Position).magnitude;
if (distanceToTarget > 0.5f)
SetMovementAction(target - CharacterActor.Position);
else
characterActions.Reset();
}
void SetTarget()
{
Vector3 centerToTargetDir = target - initialPosition;
centerToTargetDir.Normalize();
centerToTargetDir = Quaternion.Euler(0, Random.Range(minRandomYawAngle, maxRandomYawAngle), 0f) * centerToTargetDir;
target = initialPosition + centerToTargetDir * Random.Range(minRandomMagnitude, maxRandomMagnitude);
}
}
}