1
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user