This commit is contained in:
2026-05-30 09:16:35 +07:00
parent 2f87ce19a7
commit 1c0ee6efb7
4001 changed files with 3363438 additions and 1738 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 6d5a0f09d1aa7594198de26184d0e3f3
folderAsset: yes
timeCreated: 1456602722
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,246 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
namespace Invector.vCharacterController.AI
{
public class vCreateEnemyEditor : EditorWindow
{
GUISkin skin;
GameObject charObj;
Animator charAnimator;
RuntimeAnimatorController controller;
Vector2 rect = new Vector2(500, 680);
Vector2 scrool;
UnityEditor.Editor humanoidpreview;
Texture2D m_Logo;
public enum CharacterType
{
EnemyAI,
CompanionAI
}
public CharacterType charType = CharacterType.EnemyAI;
/// <summary>
/// 3rdPersonController Menu
/// </summary>
[MenuItem("Invector/Melee Combat/Create Simple Melee AI")]
public static void CreateNewCharacter()
{
GetWindow<vCreateEnemyEditor>();
}
bool isHuman, isValidAvatar, charExist;
void OnGUI()
{
if (!skin)
{
skin = Resources.Load("vSkin") as GUISkin;
}
GUI.skin = skin;
m_Logo = Resources.Load("icon_v2") as Texture2D;
this.minSize = rect;
this.titleContent = new GUIContent("Character", null, "Simple Melee AI Character Creator");
GUILayout.BeginVertical("SIMPLE MELEE AI CHARACTER CREATOR WINDOW", "window");
GUILayout.Label(m_Logo, GUILayout.MaxHeight(25));
GUILayout.Space(5);
GUILayout.BeginVertical("box");
EditorGUILayout.HelpBox("This is a Simple Melee AI solution that comes with the Melee Combat Package, if you're looking for a more advanced and customizable AI check our AI Template in the AssetStore, it has Shooter Behavior, Waypoint System, NodeEditor for custom behaviours and more...", MessageType.Info);
charType = (CharacterType)EditorGUILayout.EnumPopup("Character Type", charType);
if (!charObj)
{
EditorGUILayout.HelpBox("Make sure to select the FBX model and not a Prefab already with components attached!", MessageType.Info);
}
else if (!charExist)
{
EditorGUILayout.HelpBox("Missing a Animator Component", MessageType.Error);
}
else if (!isHuman)
{
EditorGUILayout.HelpBox("This is not a Humanoid", MessageType.Error);
}
else if (!isValidAvatar)
{
EditorGUILayout.HelpBox(charObj.name + " is a invalid Humanoid", MessageType.Info);
}
charObj = EditorGUILayout.ObjectField("FBX Model", charObj, typeof(GameObject), true, GUILayout.ExpandWidth(true)) as GameObject;
if (GUI.changed && charObj != null && charObj.GetComponent<vSimpleMeleeAI_Controller>() == null)
{
humanoidpreview = Editor.CreateEditor(charObj);
}
if (charObj != null && charObj.GetComponent<vSimpleMeleeAI_Controller>() != null)
{
EditorGUILayout.HelpBox("This gameObject already contains the component v_AIController", MessageType.Warning);
}
controller = EditorGUILayout.ObjectField("Animator Controller: ", controller, typeof(RuntimeAnimatorController), false) as RuntimeAnimatorController;
GUILayout.EndVertical();
GUILayout.BeginHorizontal("box");
EditorGUILayout.LabelField("Need to know how it works?");
if (GUILayout.Button("Video Tutorial"))
{
Application.OpenURL("https://www.youtube.com/watch?v=tuwg-H8vjqY&list=PLvgXGzhT_qehtuCYl2oyL-LrWoT7fhg9d&index=3");
}
GUILayout.EndHorizontal();
if (charObj)
{
charAnimator = charObj.GetComponent<Animator>();
}
charExist = charAnimator != null;
isHuman = charExist ? charAnimator.isHuman : false;
isValidAvatar = charExist ? charAnimator.avatar.isValid : false;
if (CanCreate())
{
DrawHumanoidPreview();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (controller != null)
{
if (GUILayout.Button("Create"))
{
Create();
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
bool CanCreate()
{
return isValidAvatar && isHuman && charObj != null && charObj.GetComponent<vSimpleMeleeAI_Controller>() == null; ;
}
/// <summary>
/// Draw the Preview window
/// </summary>
void DrawHumanoidPreview()
{
GUILayout.FlexibleSpace();
if (humanoidpreview != null)
{
humanoidpreview.OnInteractivePreviewGUI(GUILayoutUtility.GetRect(100, 400), "window");
}
}
private GameObject InstantiateNewCharacter(GameObject selected)
{
if (selected == null)
{
return selected;
}
if (selected.scene.IsValid())
{
return selected;
}
return PrefabUtility.InstantiatePrefab(selected) as GameObject;
}
/// <summary>
/// Created the Third Person Controller
/// </summary>
public virtual void Create()
{
// base for the character
GameObject newCharacter = InstantiateNewCharacter(charObj);
if (!newCharacter)
{
return;
}
if (charType == CharacterType.CompanionAI)
{
newCharacter.tag = "CompanionAI";
newCharacter.name = "vCompanionAI";
newCharacter.AddComponent<vSimpleMeleeAI_Companion>();
var p_layer = LayerMask.NameToLayer("CompanionAI");
newCharacter.layer = p_layer;
foreach (Transform t in newCharacter.transform.GetComponentsInChildren<Transform>())
{
t.gameObject.layer = p_layer;
}
}
else
{
newCharacter.name = "vEnemyAI";
newCharacter.tag = "Enemy";
newCharacter.AddComponent<vSimpleMeleeAI_Controller>();
var p_layer = LayerMask.NameToLayer("Enemy");
newCharacter.layer = p_layer;
foreach (Transform t in newCharacter.transform.GetComponentsInChildren<Transform>())
{
t.gameObject.layer = p_layer;
}
}
// rigidbody settings
var rigidbody = newCharacter.AddComponent<Rigidbody>();
rigidbody.useGravity = true;
rigidbody.constraints = RigidbodyConstraints.FreezeRotation;
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
rigidbody.mass = 50;
// capsule collider settings
var collider = newCharacter.AddComponent<CapsuleCollider>();
collider.height = ColliderHeight(newCharacter.GetComponent<Animator>());
collider.center = new Vector3(0, (float)System.Math.Round(collider.height * 0.5f, 2), 0);
collider.radius = (float)System.Math.Round(collider.height * 0.15f, 2);
// navmesh settings
var navMesh = newCharacter.AddComponent<NavMeshAgent>();
navMesh.radius = 0.4f;
navMesh.height = 1.8f;
navMesh.speed = 1f;
navMesh.angularSpeed = 300f;
navMesh.acceleration = 8f;
navMesh.stoppingDistance = 2f;
navMesh.autoBraking = false;
if (controller)
{
newCharacter.GetComponent<Animator>().runtimeAnimatorController = controller;
}
this.Close();
}
/// <summary>
/// Capsule Collider height based on the Character height
/// </summary>
/// <param name="animator">animator humanoid</param>
/// <returns></returns>
float ColliderHeight(Animator animator)
{
var foot = animator.GetBoneTransform(HumanBodyBones.LeftFoot);
var hips = animator.GetBoneTransform(HumanBodyBones.Hips);
return (float)System.Math.Round(Vector3.Distance(foot.position, hips.position) * 2f, 2);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ce527d42ae211d84282aeb892f847e20
timeCreated: 1467828935
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,176 @@
using UnityEditor;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
[CanEditMultipleObjects]
[CustomEditor(typeof(vSimpleMeleeAI_Motor), true)]
public class vSimpleMeleeAI_Editor : vEditorBase
{
protected override void OnEnable()
{
base.OnEnable();
vSimpleMeleeAI_Motor motor = (vSimpleMeleeAI_Motor)target;
if (motor.gameObject.layer == LayerMask.NameToLayer("Default"))
{
PopUpLayerInfoEditor window = ScriptableObject.CreateInstance<PopUpLayerInfoEditor>();
window.position = new Rect(Screen.width, Screen.height / 2, 360, 100);
window.ShowPopup();
}
}
public void OnSceneGUI()
{
if (Selection.activeTransform == null || !Selection.activeGameObject.activeSelf)
{
return;
}
vSimpleMeleeAI_Motor motor = (vSimpleMeleeAI_Motor)target;
if (!motor)
{
return;
}
if (!motor.displayGizmos)
{
return;
}
Handles.color = new Color(0, 0, 0, 0.5f);
Handles.DrawSolidDisc(motor.transform.position, Vector3.up, motor.lostTargetDistance);
Handles.color = new Color(1, 1, 0, 0.2f);
Handles.DrawSolidArc(motor.transform.position, Vector3.up, motor.transform.forward, motor.fieldOfView * 0.5f, motor.maxDetectDistance);
Handles.DrawSolidArc(motor.transform.position, Vector3.up, motor.transform.forward, -motor.fieldOfView * 0.5f, motor.maxDetectDistance);
Handles.color = new Color(1, 1, 1, 0.5f);
Handles.DrawWireDisc(motor.transform.position, Vector3.up, motor.maxDetectDistance);
Handles.color = new Color(0, 1, 0, 0.1f);
Handles.DrawSolidDisc(motor.transform.position, Vector3.up, motor.strafeDistance);
Handles.color = new Color(1, 0, 0, 0.2f);
Handles.DrawSolidDisc(motor.transform.position, Vector3.up, motor.minDetectDistance);
Handles.color = new Color(0, 0, 1, 0.2f);
Handles.DrawSolidDisc(motor.transform.position, Vector3.up, motor.distanceToAttack);
}
void CreateSensor(vSimpleMeleeAI_Motor motor)
{
if (Selection.activeTransform == null || !Selection.activeGameObject.activeSelf)
{
return;
}
motor.sphereSensor = motor.GetComponentInChildren<vSimpleMeleeAI_SphereSensor>();
if (motor.sphereSensor != null)
{
return;
}
var sensor = new GameObject("SphereSensor", typeof(SphereCollider));
var layer = LayerMask.NameToLayer("Triggers");
sensor.layer = layer;
sensor.tag = "Weapon";
motor.sphereSensor = sensor.AddComponent<vSimpleMeleeAI_SphereSensor>();
sensor.transform.position = motor.transform.position;
sensor.transform.parent = motor.transform;
motor.sphereSensor.GetComponent<SphereCollider>().isTrigger = true;
EditorUtility.SetDirty(motor);
}
public override void OnInspectorGUI()
{
vSimpleMeleeAI_Motor motor = (vSimpleMeleeAI_Motor)target;
serializedObject.Update();
if (!motor)
{
return;
}
if (motor.sphereSensor == null)
{
CreateSensor(motor);
}
else
{
motor.sphereSensor.SetColliderRadius(motor.maxDetectDistance);
}
if (motor.gameObject.layer == LayerMask.NameToLayer("Default"))
{
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.HelpBox("Please assign the Layer of the Character to 'Enemy'", MessageType.Warning);
}
if (motor.groundLayer == 0)
{
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.HelpBox("Please assign the Ground Layer to 'Default' ", MessageType.Warning);
}
if (Application.isPlaying)
{
GUILayout.Box("Current Health: " + motor.currentHealth.ToString());
}
base.OnInspectorGUI();
serializedObject.ApplyModifiedProperties();
}
//**********************************************************************************//
// DEBUG RAYCASTS //
// draw the casts of the controller on play mode //
//**********************************************************************************//
[DrawGizmo(GizmoType.Selected)]
private static void CustomDrawGizmos(Transform aTarget, GizmoType aGizmoType)
{
#if UNITY_EDITOR
if (Application.isPlaying)
{
vSimpleMeleeAI_Motor motor = aTarget.GetComponent<vSimpleMeleeAI_Motor>();
if (!motor || !motor.enabled)
{
return;
}
if (Selection.activeTransform == null || !Selection.activeGameObject.activeSelf)
{
return;
}
// debug auto crouch
Vector3 posHead = motor.transform.position + Vector3.up * ((motor._capsuleCollider.height * 0.5f) - motor._capsuleCollider.radius);
Ray ray1 = new Ray(posHead, Vector3.up);
Gizmos.DrawWireSphere(ray1.GetPoint((motor.headDetect - (motor._capsuleCollider.radius * 0.1f))), motor._capsuleCollider.radius * 0.9f);
Handles.Label(ray1.GetPoint((motor.headDetect + (motor._capsuleCollider.radius))), "Head Detection");
}
#endif
}
}
public class PopUpLayerInfoEditor : EditorWindow
{
GUISkin skin;
Vector2 rect = new Vector2(360, 100);
void OnGUI()
{
this.titleContent = new GUIContent("Warning!");
this.minSize = rect;
EditorGUILayout.HelpBox("Please assign your EnemyAI to the Layer 'Enemy'.", MessageType.Warning);
EditorGUILayout.Space();
EditorGUILayout.Space();
if (GUILayout.Button("OK", GUILayout.Width(80), GUILayout.Height(20)))
{
this.Close();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 29c5953a8ccf7dd41a954286bb5af48f
timeCreated: 1456602756
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,271 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
using vItemManager;
[CustomEditor(typeof(vSimpleMeleeAI_WeaponsControl))]
public class vSimpleMeleeAI_WeaponsControl_Editor : UnityEditor.Editor
{
GUISkin skin;
vSimpleMeleeAI_WeaponsControl weaponCtrl;
bool editLeftCustomPoint, isOpenL;
bool editRightCustomPoint, isOpenR;
Animator animator;
Transform leftHand;
Transform rightHand;
string customPointName;
int selectedItemL, selectedItemR;
int[] ids;
void OnEnable()
{
weaponCtrl = (vSimpleMeleeAI_WeaponsControl)target;
weaponCtrl.itemCollection = weaponCtrl.GetComponentInChildren<vItemCollection>(true);
skin = Resources.Load("vSkin") as GUISkin;
animator = weaponCtrl.GetComponent<Animator>();
if (animator)
{
leftHand = animator.GetBoneTransform(HumanBodyBones.LeftHand);
rightHand = animator.GetBoneTransform(HumanBodyBones.RightHand);
if (leftHand && weaponCtrl.defaultEquipPointL == null)
{
var customPoint = new GameObject("defaultEquipPoint");
customPoint.transform.parent = leftHand;
customPoint.transform.localPosition = Vector3.zero;
customPoint.transform.forward = weaponCtrl.transform.forward;
weaponCtrl.defaultEquipPointL = customPoint.transform;
EditorUtility.SetDirty(weaponCtrl);
}
if (rightHand && weaponCtrl.defaultEquipPointR == null)
{
var child = rightHand.Find("defaultEquipPoint");
GameObject customPoint;
if (child)
{
customPoint = child.gameObject;
}
else
{
customPoint = new GameObject("defaultEquipPoint");
}
customPoint.transform.parent = leftHand;
customPoint.transform.localPosition = Vector3.zero;
customPoint.transform.forward = weaponCtrl.transform.forward;
weaponCtrl.defaultEquipPointR = customPoint.transform;
EditorUtility.SetDirty(weaponCtrl);
}
}
}
string[] GetItems()
{
if (weaponCtrl.itemCollection && weaponCtrl.itemCollection.items != null && weaponCtrl.itemCollection.itemListData && weaponCtrl.itemCollection.itemListData.items != null)
{
var items = weaponCtrl.itemCollection.items.FindAll(_item => weaponCtrl.itemCollection.itemListData.items.Find(_item2 => _item2.id == _item.id && _item2.type != vItemType.Consumable));
string[] names = new string[items.Count];
ids = new int[items.Count];
for (int i = 0; i < names.Length; i++)
{
var item = weaponCtrl.itemCollection.itemListData.items.Find(_item => _item.id == items[i].id);
if (item != null)
{
names[i] = item.name;
ids[i] = item.id;
}
}
return names;
}
ids = new int[0];
return new string[0];
}
public override void OnInspectorGUI()
{
if (skin)
{
GUI.skin = skin;
}
serializedObject.Update();
GUILayout.BeginVertical("AI Weapons Control", "window");
GUILayout.Space(30);
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Script"));
weaponCtrl.itemCollection = (vItemCollection)EditorGUILayout.ObjectField("Item Collection", weaponCtrl.itemCollection, typeof(vItemCollection), true);
if (weaponCtrl.itemCollection)
{
var names = GetItems();
GUILayout.BeginVertical("box");
GUILayout.Box("Left Weapon");
weaponCtrl.useLeftWeapon = EditorGUILayout.Toggle("Use Left Weapon", weaponCtrl.useLeftWeapon);
if (weaponCtrl.useLeftWeapon)
{
weaponCtrl.randomLeftWeapon = EditorGUILayout.Toggle("Random Weapon", weaponCtrl.randomLeftWeapon);
if (!weaponCtrl.randomLeftWeapon)
{
if (names.Length > 0)
{
if (!Array.Exists<int>(ids, num => num == weaponCtrl.leftWeaponID))
{
weaponCtrl.leftWeaponID = ids[0];
}
var indexOf = Array.IndexOf(ids, weaponCtrl.leftWeaponID);
indexOf = EditorGUILayout.Popup("Weapon", indexOf, names);
weaponCtrl.leftWeaponID = ids[indexOf];
}
else
{
EditorGUILayout.HelpBox("ItemCollect dosent have Weapon Items", MessageType.Warning);
}
}
weaponCtrl.defaultEquipPointL = (Transform)EditorGUILayout.ObjectField("LeftDefaultPoint", weaponCtrl.defaultEquipPointL, typeof(Transform), true);
DrawCustomEquipPoint(ref weaponCtrl.customEquipPointL, ref isOpenL, ref editLeftCustomPoint, "Left Custom Equip Points");
}
GUILayout.EndVertical();
GUILayout.BeginVertical("box");
GUILayout.Box("Right Weapon");
weaponCtrl.useRightWeapon = EditorGUILayout.Toggle("Use Right Weapon", weaponCtrl.useRightWeapon);
if (weaponCtrl.useRightWeapon)
{
weaponCtrl.randomRightWeapon = EditorGUILayout.Toggle("Random Weapon", weaponCtrl.randomRightWeapon);
if (!weaponCtrl.randomRightWeapon)
{
if (names.Length > 0)
{
if (!Array.Exists<int>(ids, num => num == weaponCtrl.rightWeaponID))
{
weaponCtrl.rightWeaponID = ids[0];
}
var indexOf = Array.IndexOf(ids, weaponCtrl.rightWeaponID);
indexOf = EditorGUILayout.Popup("Weapon", indexOf, names);
weaponCtrl.rightWeaponID = ids[indexOf];
}
else
{
EditorGUILayout.HelpBox("ItemCollect dosent have Weapon Items", MessageType.Warning);
}
}
weaponCtrl.defaultEquipPointR = (Transform)EditorGUILayout.ObjectField("RightDefaultPoint", weaponCtrl.defaultEquipPointR, typeof(Transform), true);
DrawCustomEquipPoint(ref weaponCtrl.customEquipPointR, ref isOpenR, ref editRightCustomPoint, "Right Custom Equip Points");
}
GUILayout.EndVertical();
}
else
{
EditorGUILayout.HelpBox("Please Create a item Collection inside Character to use", MessageType.Warning);
}
GUILayout.EndVertical();
if (GUI.changed)
{
EditorUtility.SetDirty(weaponCtrl);
serializedObject.ApplyModifiedProperties();
}
}
public void DrawCustomEquipPoint(ref List<Transform> list, ref bool isOpen, ref bool inEdition, string name)
{
if (list == null)
{
list = new List<Transform>();
}
GUILayout.BeginVertical("box");
isOpen = GUILayout.Toggle(isOpen, name, EditorStyles.miniButton);
if (isOpen)
{
if (!inEdition && GUILayout.Button("New Custom Point", EditorStyles.miniButton))
{
inEdition = true;
}
if (inEdition)
{
Transform parentBone;
if (name.Contains("Left") || name.Contains("left"))
{
leftHand = (Transform)EditorGUILayout.ObjectField("Parent Bone", leftHand, typeof(Transform), true);
parentBone = leftHand;
}
else
{
rightHand = (Transform)EditorGUILayout.ObjectField("Parent Bone", rightHand, typeof(Transform), true);
parentBone = rightHand;
}
customPointName = EditorGUILayout.TextField("Custom Point Name", customPointName);
bool valid = true;
if (string.IsNullOrEmpty(customPointName))
{
valid = false;
EditorGUILayout.HelpBox("Custom Point Name is empty", MessageType.Error);
}
if (list.Find(t => t.gameObject.name.Equals(customPointName)) != null)
{
valid = false;
EditorGUILayout.HelpBox("Custom Point Name already exist", MessageType.Error);
}
GUILayout.BeginHorizontal();
if (GUILayout.Button("Cancel", EditorStyles.miniButton))
{
inEdition = false;
}
GUI.enabled = parentBone && valid;
if (GUILayout.Button("Create", EditorStyles.miniButton))
{
var customPoint = new GameObject(customPointName);
customPoint.transform.parent = parentBone;
customPoint.transform.localPosition = Vector3.zero;
customPoint.transform.forward = weaponCtrl.transform.forward;
list.Add(customPoint.transform);
EditorUtility.SetDirty(weaponCtrl);
inEdition = false;
}
GUI.enabled = true;
GUILayout.EndHorizontal();
}
for (int i = 0; i < list.Count; i++)
{
bool remove = false;
GUILayout.BeginHorizontal();
list[i] = (Transform)EditorGUILayout.ObjectField(list[i], typeof(Transform), true);
if (GUILayout.Button("X", EditorStyles.miniButton, GUILayout.Width(20)))
{
remove = true;
}
GUILayout.EndHorizontal();
if (remove)
{
if (list[i] != null)
{
DestroyImmediate(list[i].gameObject);
}
list.RemoveAt(i);
EditorUtility.SetDirty(weaponCtrl);
serializedObject.ApplyModifiedProperties();
break;
}
}
}
GUILayout.EndVertical();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c1e83b0485525f1418768f711221c54a
timeCreated: 1476291778
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,382 @@
using UnityEngine;
namespace Invector.vCharacterController.AI
{
public class vSimpleMeleeAI_Animator : vSimpleMeleeAI_Motor
{
#region AI Variables
private bool triggerDieBehaviour;
private bool resetState;
private float strafeInput;
// get Layers from the Animator Controller
public AnimatorStateInfo baseLayerInfo, rightArmInfo, leftArmInfo, fullBodyInfo, upperBodyInfo, underBodyInfo;
int baseLayer { get { return animator.GetLayerIndex("Base Layer"); } }
int underBodyLayer { get { return animator.GetLayerIndex("UnderBody"); } }
int rightArmLayer { get { return animator.GetLayerIndex("RightArm"); } }
int leftArmLayer { get { return animator.GetLayerIndex("LeftArm"); } }
int upperBodyLayer { get { return animator.GetLayerIndex("UpperBody"); } }
int fullbodyLayer { get { return animator.GetLayerIndex("FullBody"); } }
#endregion
public void UpdateAnimator(float _speed, float _direction)
{
if (animator == null || !animator.enabled)
{
return;
}
LayerControl();
LocomotionAnimation(_speed, _direction);
RollAnimation();
CrouchAnimation();
ResetAndLockAgent();
MoveSetIDControl();
MeleeATK_Animation();
DEF_Animation();
DeadAnimation();
}
void LayerControl()
{
baseLayerInfo = animator.GetCurrentAnimatorStateInfo(baseLayer);
underBodyInfo = animator.GetCurrentAnimatorStateInfo(underBodyLayer);
rightArmInfo = animator.GetCurrentAnimatorStateInfo(rightArmLayer);
leftArmInfo = animator.GetCurrentAnimatorStateInfo(leftArmLayer);
upperBodyInfo = animator.GetCurrentAnimatorStateInfo(upperBodyLayer);
fullBodyInfo = animator.GetCurrentAnimatorStateInfo(fullbodyLayer);
}
void OnAnimatorMove()
{
actions = baseLayerInfo.IsTag("CustomAction") || lockMovement;
if (Time.timeScale == 0 || agent == null)
{
return;
}
if (agent.enabled && !agent.isOnOffMeshLink && agent.updatePosition)
{
Vector3 velocity = animator.deltaPosition / Time.deltaTime;
if (!velocity.IsVectorNaN())
{
agent.velocity = velocity;
}
else
{
agent.velocity = Vector3.zero;
}
}
if (!_rigidbody.useGravity && !actions && !agent.isOnOffMeshLink)
{
_rigidbody.linearVelocity = animator.deltaPosition;
}
if (!agent.updatePosition && !actions)
{
var point = agent.enabled ? agent.nextPosition : destination;
if (Vector3.Distance(transform.position, point) > 0.5f)
{
desiredRotation = Quaternion.LookRotation(point - transform.position);
var rot = Quaternion.Euler(transform.eulerAngles.x, desiredRotation.eulerAngles.y, transform.eulerAngles.z);
transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, agent.angularSpeed * Time.deltaTime);
}
transform.position = animator.rootPosition;
return;
}
// Strafe Movement
if (OnStrafeArea && !actions && currentTarget.transform != null && canSeeTarget && currentHealth > 0f)
{
Vector3 targetDir = currentTarget.transform.position - transform.position;
float step = (meleeManager != null && isAttacking) ? attackRotationSpeed * Time.deltaTime : (strafeRotationSpeed * Time.deltaTime);
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0F);
var rot = Quaternion.LookRotation(newDir);
transform.eulerAngles = new Vector3(transform.eulerAngles.x, rot.eulerAngles.y, transform.eulerAngles.z);
}
// Rotate the Character to the OffMeshLink End
else if (agent.isOnOffMeshLink && !actions)
{
var pos = agent.nextOffMeshLinkData.endPos;
targetPos = pos;
UnityEngine.AI.OffMeshLinkData data = agent.currentOffMeshLinkData;
desiredRotation = Quaternion.LookRotation(new Vector3(data.endPos.x, transform.position.y, data.endPos.z) - transform.position);
transform.rotation = Quaternion.RotateTowards(transform.rotation, desiredRotation, (agent.angularSpeed * 2f) * Time.deltaTime);
}
// Free Movement
else if (agent.desiredVelocity.magnitude > 0.1f && !actions && agent.enabled && currentHealth > 0f)
{
if (meleeManager != null && isAttacking)
{
desiredRotation = Quaternion.LookRotation(agent.desiredVelocity);
transform.rotation = Quaternion.RotateTowards(transform.rotation, desiredRotation, agent.angularSpeed * attackRotationSpeed * Time.deltaTime);
}
else
{
desiredRotation = Quaternion.LookRotation(agent.desiredVelocity);
transform.rotation = Quaternion.RotateTowards(transform.rotation, desiredRotation, agent.angularSpeed * Time.deltaTime);
}
}
// Use the Animator rotation while doing an Action
else if (actions || currentHealth <= 0f || isAttacking)
{
if (isRolling)
{
desiredRotation = Quaternion.LookRotation(rollDirection, Vector3.up);
transform.rotation = desiredRotation;
}
else
{
transform.rotation = animator.rootRotation;
}
// Use the Animator position while doing an Action
if (!agent.enabled)
{
destination = transform.position;
transform.position = animator.rootPosition;
}
}
}
#region AI Locomotion Animations
/// <summary>
/// Control the Locomotion behaviour of the AI
/// </summary>
/// <param name="_speed"></param>
/// <param name="_direction"></param>
void LocomotionAnimation(float _speed, float _direction)
{
isGrounded = agent.enabled ? agent.isOnNavMesh : isRolling ? true : groundDistance <= groundCheckDistance;
animator.SetBool("IsGrounded", isGrounded);
_speed = Mathf.Clamp(_speed, -maxSpeed, maxSpeed);
if (OnStrafeArea)
{
_direction = Mathf.Clamp(_direction, -strafeSpeed, strafeSpeed);
}
var animSpeed = Mathf.Abs(_speed) > 0.1f ? _speed : 0;
var animDirection = Mathf.Abs(_direction) > 0.1f ? _direction : 0;
var newInput = new Vector2(animSpeed, animDirection);
strafeInput = Mathf.Clamp(newInput.magnitude, 0, 1.5f);
animator.SetFloat("InputMagnitude", strafeInput, .2f, Time.deltaTime);
animator.SetFloat("InputVertical", actions ? 0 : (_speed != 0) ? _speed : 0, 0.2f, Time.fixedDeltaTime);
animator.SetFloat("InputHorizontal", _direction, 0.2f, Time.fixedDeltaTime);
animator.SetBool("IsStrafing", OnStrafeArea);
animator.SetBool("isDead", isDead);
}
protected virtual float maxSpeed
{
get
{
return (OnStrafeArea ? strafeSpeed : chaseSpeed);
}
}
/// <summary>
/// Trigger a Death by Animation, Animation with Ragdoll or just turn the Ragdoll On
/// </summary>
void DeadAnimation()
{
if (!isDead)
{
return;
}
if (!triggerDieBehaviour)
{
triggerDieBehaviour = true;
DeathBehaviour();
}
// death by animation
if (deathBy == DeathBy.Animation)
{
if (fullBodyInfo.IsName("Dead"))
{
if (fullBodyInfo.normalizedTime >= 0.99f && groundDistance <= 0.15f)
{
RemoveComponents();
}
}
}
// death by animation & ragdoll after a time
else if (deathBy == DeathBy.AnimationWithRagdoll)
{
if (fullBodyInfo.IsName("Dead"))
{
// activate the ragdoll after the animation finish played
if (fullBodyInfo.normalizedTime >= 0.8f)
{
onActiveRagdoll.Invoke(null);
RemoveComponents();
}
}
}
// death by ragdoll
else if (deathBy == DeathBy.Ragdoll)
{
onActiveRagdoll.Invoke(null);
RemoveComponents();
}
}
private void DeathBehaviour()
{
// change the culling mode to render the animation until finish
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
// trigger die animation
if (deathBy == DeathBy.Animation || deathBy == DeathBy.AnimationWithRagdoll)
{
animator.SetBool("isDead", isDead);
}
}
void CrouchAnimation()
{
animator.SetBool("IsCrouching", isCrouched);
if (animator != null && animator.enabled)
{
CheckAutoCrouch();
}
}
protected void RollAnimation()
{
if (animator == null || animator.enabled == false)
{
return;
}
isRolling = baseLayerInfo.IsName("Roll");
if (isRolling)
{
_rigidbody.constraints = RigidbodyConstraints.None | RigidbodyConstraints.FreezeRotation;
_rigidbody.useGravity = true;
agent.enabled = false;
agent.updatePosition = false;
}
}
void ResetAIRotation()
{
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
}
#endregion
#region AI Melee Combat Animations
/// <summary>
/// MOVE SET ID - check the Animator to see what MoveSet the character will move, also check your weapon to see if the moveset matches
/// ps* Move Set is the way your character will move, ATK_ID is the way your character will attack. You can have different locomotion animations and attacks.
/// </summary>
void MoveSetIDControl()
{
if (meleeManager == null)
{
return;
}
animator.SetFloat("MoveSet_ID", meleeManager.GetMoveSetID());
}
/// <summary>
/// Control Attack Behaviour
/// </summary>
void MeleeATK_Animation()
{
if (meleeManager == null)
{
return;
}
if (actions)
{
attackCount = 0;
}
animator.SetInteger("AttackID", meleeManager.GetAttackID());
}
/// <summary>
/// ATTACK MELEE ANIMATION - it's activate by the AttackInput() method at the TPController by a trigger
/// </summary>
void DEF_Animation()
{
if (meleeManager == null)
{
return;
}
if (isBlocking)
{
animator.SetInteger("DefenseID", meleeManager.GetDefenseID());
}
animator.SetBool("IsBlocking", isBlocking);
}
/// <summary>
/// Trigger the Attack animation
/// </summary>
public void MeleeAttack()
{
if (animator != null && animator.enabled && !actions)
{
animator.SetTrigger("WeakAttack");
}
}
/// <summary>
/// Check if is in LockMovement to reset attack and disable agent
/// </summary>
void ResetAndLockAgent()
{
lockMovement = fullBodyInfo.IsTag("LockMovement") || upperBodyInfo.IsTag("ResetState");
if (lockMovement)
{
if (attackCount > 0)
{
canAttack = false;
attackCount = 0;
}
if (baseLayerInfo.normalizedTime > 0.1f)
{
animator.ResetTrigger("ResetState");
_rigidbody.constraints = RigidbodyConstraints.None | RigidbodyConstraints.FreezeRotation;
_rigidbody.useGravity = true;
agent.enabled = false;
agent.updatePosition = false;
}
}
}
/// <summary>
/// Trigger Recoil Animation - It's Called at the MeleeWeapon
/// </summary>
public void TriggerRecoil(int recoil_id)
{
if (animator != null && animator.enabled && !isRolling)
{
animator.SetInteger("RecoilID", recoil_id);
animator.SetTrigger("TriggerRecoil");
animator.SetTrigger("ResetState");
}
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8d7f27e9e1a4aa34cab4f80a72164fa7
timeCreated: 1452173169
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,306 @@
using System.Collections;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
public class vSimpleMeleeAI_Companion : vSimpleMeleeAI_Controller
{
[vEditorToolbar("Companion")]
[SerializeField] protected string _companionTag = "Player";
public virtual string companionTag { get { return _companionTag; } set { _companionTag = value; } }
public virtual float companionMaxDistance { get { return _companionMaxDistance; } set { _companionMaxDistance = value; } }
[SerializeField] protected float _companionMaxDistance = 10f;
[Range(0f, 1.5f)]
[SerializeField] protected float _followSpeed = 1f;
public virtual float followSpeed { get { return _followSpeed; } set { _followSpeed = value; } }
[SerializeField] protected float _followStopDistance = 2f;
public virtual float followStopDistance { get { return _followStopDistance; } set { _followStopDistance = value; } }
[Range(0f, 1.5f)]
[SerializeField] protected float _moveToStopDistance = 0.5f;
public virtual float moveToStopDistance { get { return _moveToStopDistance; } set { _moveToStopDistance = value; } }
[SerializeField] protected Transform _moveToTarget;
public virtual Transform moveToTarget { get { return _moveToTarget; } set { _moveToTarget = value; } }
[SerializeField] protected CompanionState _companionState = CompanionState.Follow;
public virtual CompanionState companionState { get { return _companionState; } set { _companionState = value; } }
[SerializeField] protected Transform _companion;
public virtual Transform companion { get { return _companion; } set { _companion = value; } }
public bool debug = true;
public UnityEngine.UI.Text debugUIText;
public enum CompanionState
{
None, // this state works with AiController normal routine
Follow,
MoveTo,
Stay
}
protected void LateUpdate()
{
CompanionInputs();
}
protected virtual void CompanionInputs()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
companionState = CompanionState.Stay;
agressiveAtFirstSight = false;
}
if (Input.GetKeyDown(KeyCode.Alpha2))
{
companionState = CompanionState.Follow;
agressiveAtFirstSight = false;
}
if (Input.GetKeyDown(KeyCode.Alpha3))
{
agressiveAtFirstSight = !agressiveAtFirstSight;
}
if (Input.GetKeyDown(KeyCode.Alpha4) && moveToTarget != null)
{
SetMoveTo(moveToTarget);
companionState = CompanionState.MoveTo;
agressiveAtFirstSight = false;
}
}
/// <summary>
/// Gets the companion distance.
/// </summary>
/// <value>The companion distance.</value>
protected virtual float companionDistance
{
get { return companion != null ? Vector3.Distance(transform.position, companion.transform.position) : 0f; }
}
/// <summary>
/// Gets a value indicating whether this <see cref="vSimpleMeleeAI_Companion"/> is near of companion. Relative to <see cref="companionMaxDistance"/>
/// </summary>
/// <value><c>true</c> if near of companion; otherwise, <c>false</c>.</value>
protected virtual bool nearOfCompanion
{
get
{
var value = ((companion != null && companion.gameObject.activeSelf && companionDistance < companionMaxDistance) || (companion == null || !companion.gameObject.activeSelf));
return value;
}
}
/// <summary>
/// Sets the target Move to.
/// </summary>
/// <param name="_target">Target.</param>
public virtual void SetMoveTo(Transform _target)
{
companionState = CompanionState.MoveTo;
moveToTarget = _target;
}
#region Override Ai Controller rotine
protected override void Start()
{
try
{
var comp = GameObject.FindGameObjectWithTag(companionTag);
if (comp != null)
{
companion = comp.transform;
}
else
{
companionState = CompanionState.None;
Debug.LogWarning("Cant find the " + companionTag);
}
}
catch (UnityException e)
{
companionState = CompanionState.None;
Debug.LogWarning("AICompanion Cant find the " + companionTag);
Debug.LogWarning("AICompanion " + e.Message);
}
Init();
agent.enabled = true;
StartCoroutine(CompanionStateRoutine());
StartCoroutine(FindTarget());
StartCoroutine(DestinationBehaviour());
}
/// <summary>
/// override <see cref="vSimpleMeleeAI_Companion.StateRoutine()"/>
/// ps: this rotine work with internal while loop
/// </summary>
/// <returns></returns>
protected IEnumerator CompanionStateRoutine()
{
while (this.enabled)
{
yield return new WaitForEndOfFrame();
System.Text.StringBuilder debugString = new System.Text.StringBuilder();
debugString.AppendLine("----DEBUG----");
debugString.AppendLine("Agressive : " + agressiveAtFirstSight);
CheckIsOnNavMesh();
CheckAutoCrouch();
SetTarget();
//Companion Behavior (override Aicontroller Behavior)
switch (companionState)
{
#region Companion rotine
case CompanionState.Follow:
if (canSeeTarget && nearOfCompanion)
{
yield return StartCoroutine(base.Chase());
}
else
{
yield return StartCoroutine(FollowCompanion());
}
debugString.AppendLine(canSeeTarget && nearOfCompanion ? "Chase/Follow" : "Follow");
break;
case CompanionState.MoveTo:
if (canSeeTarget)
{
yield return StartCoroutine(base.Chase());
}
else
{
yield return StartCoroutine(MoveTo());
}
debugString.AppendLine(canSeeTarget ? "Chase/MoveTo" : "MoveTo");
break;
case CompanionState.Stay:
if (canSeeTarget)
{
yield return StartCoroutine(base.Chase());
}
else
{
yield return StartCoroutine(Stay());
}
debugString.AppendLine(canSeeTarget ? "Chase/Stay" : "Stay");
break;
#endregion
case CompanionState.None:
//Aicontroller Behavior
#region Ai controller Normal Rotine
debugString.AppendLine("None : using normal AI routine");
switch (currentState)
{
case AIStates.Idle:
debugString.AppendLine("idle");
yield return StartCoroutine(base.Idle());
break;
case AIStates.Chase:
yield return StartCoroutine(base.Chase());
break;
case AIStates.Wander:
yield return StartCoroutine(base.Wander());
break;
}
break;
#endregion
}
if (debugUIText != null && debug)
{
debugUIText.text = debugString.ToString();
}
}
}
/// <summary>
/// override <see cref="vSimpleMeleeAI_Companion.Idle()"/>
/// </summary>
/// <returns></returns>
protected IEnumerator Stay()
{
if (companion != null)
{
agent.speed = Mathf.Lerp(agent.speed, 0, 2f * Time.deltaTime);
}
else
{
yield return StartCoroutine(Idle());
}
}
protected override void SetAggressive(bool value)
{
if (companionState != CompanionState.Follow)
{
base.SetAggressive(value);
}
}
#endregion
#region Companion rotine
/// <summary>
/// Follows the companion.
/// </summary>
/// <returns>The companion.</returns>
protected virtual IEnumerator FollowCompanion()
{
while (!agent.enabled || currentHealth <= 0)
{
yield return null;
}
// check if companion exist in Scene to work follow rotine
if (companion != null && companion.gameObject.activeSelf)
{
agent.speed = Mathf.Lerp(agent.speed, followSpeed, 10f * Time.deltaTime);
agent.stoppingDistance = followStopDistance;
UpdateDestination(companion.position);
}
else // go to start position case companion dont exist
{
agent.speed = Mathf.Lerp(agent.speed, moveToSpeed, 10f * Time.deltaTime);
agent.stoppingDistance = moveToStopDistance;
UpdateDestination(startPosition);
}
}
/// <summary>
/// Moves to target applied from <see cref="SetMoveTo"/>
/// </summary>
/// <returns>The to.</returns>
protected virtual IEnumerator MoveTo()
{
while (!agent.enabled || currentHealth <= 0)
{
yield return null;
}
agent.speed = Mathf.Lerp(agent.speed, moveToSpeed, 2f * Time.deltaTime);
agent.stoppingDistance = moveToStopDistance;
// update destination to moveTo target position
UpdateDestination(moveToTarget.position);
// check if can see some target (included from SetUpTarget method)
if (canSeeTarget && nearOfCompanion)
{
currentState = AIStates.Chase;
}
}
protected override float maxSpeed
{
get
{
if (companionState != CompanionState.None)
{
return companionState == CompanionState.Follow ? followSpeed : companionState == CompanionState.MoveTo ? moveToSpeed : 0;
}
return base.maxSpeed;
}
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2df1049f14967d64791ed2a2dbbdccc1
timeCreated: 1460650630
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,731 @@
using Invector.vEventSystems;
using System.Collections;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
[vClassHeader("Simple Melee AI", "This is a Simple Melee AI that comes with the MeleeCombat package as a bonus, if you want a more advanced AI check our AI Template")]
public class vSimpleMeleeAI_Controller : vSimpleMeleeAI_Animator, vIMeleeFighter
{
[vEditorToolbar("Iterations")]
public float stateRoutineIteration = 0.15f;
public float destinationRoutineIteration = 0.25f;
public float findTargetIteration = 0.25f;
public float smoothSpeed = 5f;
[vEditorToolbar("Events")]
[Header("--- On Change State Events ---")]
public UnityEngine.Events.UnityEvent onIdle;
public UnityEngine.Events.UnityEvent onChase;
public UnityEngine.Events.UnityEvent onPatrol;
protected AIStates oldState;
protected float ignorePatrolTimer;
protected float _moveToSpeed;
protected virtual float moveToSpeed { get { return _moveToSpeed; } set { _moveToSpeed = value; } }
protected Vector3 _moveToDestination;
protected virtual Vector3 moveToDestination { get { return _moveToDestination; } set { _moveToDestination = value; } }
protected override void Start()
{
base.Start();
ignorePatrolTimer = -1f;
moveToDestination = transform.position;
Init();
StartCoroutine(StateRoutine());
StartCoroutine(FindTarget());
StartCoroutine(DestinationBehaviour());
}
protected void FixedUpdate()
{
ControlLocomotion();
}
#region AI Target
public virtual void SetCurrentTarget(Transform target)
{
if (target != currentTarget.transform)
{
currentTarget.transform = target;
currentTarget.colliderTarget = target.GetComponent<Collider>();
currentTarget.character = target.GetComponent<vIHealthController>();
}
AddTagsToDetect(target.gameObject.tag);
sphereSensor.AddTarget(target);
}
public virtual void RemoveCurrentTarget()
{
if (currentTarget.transform)
{
currentTarget.transform = null;
currentTarget.colliderTarget = null;
currentTarget.character = null;
}
}
public virtual void AddTagsToDetect(string tag)
{
if (!tagsToDetect.Contains(tag))
{
tagsToDetect.Add(tag);
}
}
public virtual void RemoveTagToDetect(string tag)
{
if (tagsToDetect.Contains(tag))
{
tagsToDetect.Remove(tag);
}
}
protected void SetTarget()
{
if (currentHealth > 0 && sphereSensor != null)
{
if (currentTarget.transform == null || (sortTargetFromDistance))
{
sphereSensor.CheckTargetsAround(fieldOfView, minDetectDistance, maxDetectDistance, tagsToDetect, layersToDetect, sortTargetFromDistance);
var vChar = sphereSensor.GetTargetvCharacter();
if (vChar != null && vChar.currentHealth > 0)
{
currentTarget.transform = vChar.transform;
currentTarget.character = vChar;
}
}
if (!CheckTargetIsAlive() || TargetDistance > lostTargetDistance)
{
currentTarget.transform = null;
}
}
else if (currentHealth <= 0f)
{
destination = transform.position;
currentTarget.transform = null;
}
}
bool CheckTargetIsAlive()
{
if (currentTarget.transform == null || currentTarget.character == null)
{
return false;
}
if (currentTarget.character.currentHealth > 0)
{
return true;
}
return false;
}
protected IEnumerator FindTarget()
{
while (true)
{
yield return new WaitForSeconds(findTargetIteration);
if (currentHealth > 0)
{
SetTarget();
CheckTarget();
}
}
}
#endregion
#region AI Locomotion
void ControlLocomotion()
{
if (AgentDone() && agent.updatePosition || lockMovement)
{
agent.speed = 0f;
combatMovement = Vector3.zero;
}
if (agent.isOnOffMeshLink)
{
float speed = agent.desiredVelocity.magnitude;
UpdateAnimator(AgentDone() ? 0f : speed, direction);
}
else
{
var desiredVelocity = agent.enabled ? agent.updatePosition ? agent.desiredVelocity : (agent.nextPosition - transform.position) : (destination - transform.position);
if (OnStrafeArea)
{
var destin = transform.InverseTransformDirection(desiredVelocity).normalized;
combatMovement = Vector3.Lerp(combatMovement, destin, 2f * Time.deltaTime);
UpdateAnimator(AgentDone() ? 0f : combatMovement.z, combatMovement.x);
}
else
{
float speed = desiredVelocity.magnitude;
combatMovement = Vector3.zero;
UpdateAnimator(AgentDone() ? 0f : speed, 0f);
}
}
}
Vector3 AgentDirection()
{
var forward = AgentDone() ? (currentTarget.transform != null && OnStrafeArea && canSeeTarget ?
(new Vector3(destination.x, transform.position.y, destination.z) - transform.position) :
transform.forward) : agent.desiredVelocity;
fwd = Vector3.Lerp(fwd, forward, 20 * Time.deltaTime);
return fwd;
}
protected virtual IEnumerator DestinationBehaviour()
{
while (true)
{
yield return new WaitForSeconds(destinationRoutineIteration);
CheckGroundDistance();
if (agent.updatePosition)
{
UpdateDestination(destination);
}
}
}
protected virtual void UpdateDestination(Vector3 position)
{
if (agent.isOnNavMesh)
{
agent.SetDestination(position);
}
#region debug Path
if (agent.enabled && agent.hasPath)
{
if (drawAgentPath)
{
Debug.DrawLine(transform.position, position, Color.red, 0.5f);
var oldPos = transform.position;
for (int i = 0; i < agent.path.corners.Length; i++)
{
var pos = agent.path.corners[i];
Debug.DrawLine(oldPos, pos, Color.green, 0.5f);
oldPos = pos;
}
}
}
#endregion
}
protected void CheckIsOnNavMesh()
{
// check if the AI is on a valid Navmesh, if not he dies
if (!agent.isOnNavMesh && agent.enabled && !ragdolled)
{
Debug.LogWarning("Missing NavMesh Bake, character will die - Please Bake your navmesh again!");
currentHealth = 0;
}
}
#endregion
#region AI States
protected IEnumerator StateRoutine()
{
while (this.enabled)
{
CheckIsOnNavMesh();
CheckAutoCrouch();
yield return new WaitForSeconds(stateRoutineIteration);
if (!lockMovement)
{
switch (currentState)
{
case AIStates.Idle:
if (currentState != oldState) { onIdle.Invoke(); oldState = currentState; }
yield return StartCoroutine(Idle());
break;
case AIStates.Chase:
if (currentState != oldState) { onChase.Invoke(); oldState = currentState; }
yield return StartCoroutine(Chase());
break;
case AIStates.PatrolSubPoints:
if (currentState != oldState) { onPatrol.Invoke(); oldState = currentState; }
yield return StartCoroutine(PatrolSubPoints());
break;
case AIStates.PatrolWaypoints:
if (currentState != oldState) { onPatrol.Invoke(); oldState = currentState; }
yield return StartCoroutine(PatrolWaypoints());
break;
case AIStates.Wander:
if (currentState != oldState) { onPatrol.Invoke(); oldState = currentState; }
yield return StartCoroutine(Wander());
break;
}
}
}
}
protected IEnumerator Idle()
{
while (currentHealth <= 0)
{
yield return null;
}
if (canSeeTarget)
{
currentState = AIStates.Chase;
}
else
{
agent.speed = Mathf.Lerp(agent.speed, 0f, smoothSpeed * Time.deltaTime);
}
}
protected IEnumerator Chase()
{
while (currentHealth <= 0)
{
yield return null;
}
agent.speed = Mathf.Lerp(agent.speed, chaseSpeed, smoothSpeed * Time.deltaTime);
agent.stoppingDistance = chaseStopDistance;
if (!isBlocking && !tryingBlock)
{
StartCoroutine(CheckChanceToBlock(chanceToBlockInStrafe, lowerShield));
}
if (currentTarget.transform == null || !agressiveAtFirstSight)
{
currentState = AIStates.PatrolWaypoints;
}
// begin the Attack Routine when close to the Target
if (TargetDistance <= distanceToAttack && meleeManager != null && canAttack && !actions)
{
canAttack = false;
yield return StartCoroutine(MeleeAttackRotine());
}
if (attackCount <= 0 && !inResetAttack && !isAttacking)
{
StartCoroutine(ResetAttackCount());
yield return null;
}
// strafing while close to the Target
if (OnStrafeArea && strafeSideways)
{
//Debug.DrawRay(transform.position, dir * 2, Color.red, 0.2f);
if (strafeSwapeFrequency <= 0)
{
sideMovement = GetRandonSide();
strafeSwapeFrequency = UnityEngine.Random.Range(minStrafeSwape, maxStrafeSwape);
}
else
{
strafeSwapeFrequency -= Time.deltaTime;
}
fwdMovement = (TargetDistance < distanceToAttack) ? (strafeBackward ? -1 : 0) : TargetDistance > distanceToAttack ? 1 : 0;
var dir = ((transform.right * sideMovement) + (transform.forward * fwdMovement));
Ray ray = new Ray(new Vector3(transform.position.x, currentTarget.transform != null ? currentTarget.transform.position.y : transform.position.y, transform.position.z), dir);
if (TargetDistance < strafeDistance - 0.5f)
{
destination = OnStrafeArea ? ray.GetPoint(agent.stoppingDistance + 0.5f) : currentTarget.transform.position;
}
else if (currentTarget.transform != null)
{
destination = currentTarget.transform.position;
}
}
// chase Target
else
{
if (!OnStrafeArea && currentTarget.transform != null)
{
destination = currentTarget.transform.position;
}
else
{
fwdMovement = (TargetDistance < distanceToAttack) ? (strafeBackward ? -1 : 0) : TargetDistance > distanceToAttack ? 1 : 0;
Ray ray = new Ray(transform.position, transform.forward * fwdMovement);
if (TargetDistance < strafeDistance - 0.5f)
{
destination = (fwdMovement != 0) ? ray.GetPoint(agent.stoppingDistance + ((fwdMovement > 0) ? TargetDistance : 1f)) : transform.position;
}
else if (currentTarget.transform != null)
{
destination = currentTarget.transform.position;
}
}
}
}
protected IEnumerator PatrolSubPoints()
{
while (!agent.enabled)
{
yield return null;
}
if (targetWaypoint)
{
if (targetPatrolPoint == null || !targetPatrolPoint.isValid)
{
targetPatrolPoint = GetPatrolPoint(targetWaypoint);
}
else
{
agent.speed = Mathf.Lerp(agent.speed, (agent.hasPath && targetPatrolPoint.isValid) ? patrolSpeed : 0, smoothSpeed * Time.deltaTime);
agent.stoppingDistance = patrollingStopDistance;
destination = targetPatrolPoint.isValid ? targetPatrolPoint.position : transform.position;
if (Vector3.Distance(transform.position, destination) < targetPatrolPoint.areaRadius && targetPatrolPoint.CanEnter(transform) && !targetPatrolPoint.IsOnWay(transform))
{
targetPatrolPoint.Enter(transform);
wait = Time.time + targetPatrolPoint.timeToStay;
visitedPatrolPoint.Add(targetPatrolPoint);
}
else if (Vector3.Distance(transform.position, destination) < targetPatrolPoint.areaRadius && (!targetPatrolPoint.CanEnter(transform) || !targetPatrolPoint.isValid))
{
targetPatrolPoint = GetPatrolPoint(targetWaypoint);
}
if (targetPatrolPoint != null && (targetPatrolPoint.IsOnWay(transform) && Vector3.Distance(transform.position, destination) < distanceToChangeWaypoint))
{
if (wait < Time.time || !targetPatrolPoint.isValid)
{
wait = 0;
if (visitedPatrolPoint.Count == pathArea.GetValidSubPoints(targetWaypoint).Count)
{
currentState = AIStates.PatrolWaypoints;
targetWaypoint.Exit(transform);
targetPatrolPoint.Exit(transform);
targetWaypoint = null;
targetPatrolPoint = null;
visitedPatrolPoint.Clear();
}
else
{
targetPatrolPoint.Exit(transform);
targetPatrolPoint = GetPatrolPoint(targetWaypoint);
}
}
}
}
}
if (canSeeTarget)
{
currentState = AIStates.Chase;
}
}
protected IEnumerator PatrolWaypoints()
{
while (!agent.enabled)
{
yield return null;
}
if (pathArea != null && pathArea.waypoints.Count > 0)
{
if (targetWaypoint == null || !targetWaypoint.isValid)
{
targetWaypoint = GetWaypoint();
}
else
{
agent.speed = Mathf.Lerp(agent.speed, (agent.hasPath && targetWaypoint.isValid) ? patrolSpeed : 0, smoothSpeed * Time.deltaTime);
agent.stoppingDistance = patrollingStopDistance;
destination = targetWaypoint.position;
if (Vector3.Distance(transform.position, destination) < targetWaypoint.areaRadius && targetWaypoint.CanEnter(transform) && !targetWaypoint.IsOnWay(transform))
{
targetWaypoint.Enter(transform);
wait = Time.time + targetWaypoint.timeToStay;
}
else if (Vector3.Distance(transform.position, destination) < targetWaypoint.areaRadius && (!targetWaypoint.CanEnter(transform) || !targetWaypoint.isValid))
{
targetWaypoint = GetWaypoint();
}
if (targetWaypoint != null && targetWaypoint.IsOnWay(transform) && Vector3.Distance(transform.position, destination) < distanceToChangeWaypoint)
{
if (wait < Time.time || !targetWaypoint.isValid)
{
wait = 0;
if (targetWaypoint.subPoints.Count > 0)
{
currentState = AIStates.PatrolSubPoints;
}
else
{
targetWaypoint.Exit(transform);
visitedPatrolPoint.Clear();
targetWaypoint = GetWaypoint();
}
}
}
}
}
else if (ignorePatrolTimer < Time.time)
{
switch (patrolWithoutAreaStyle)
{
case AIPatrolWithOutAreaStyle.GoToStartPoint:
yield return StartCoroutine(GoToStartingPoint());
break;
case AIPatrolWithOutAreaStyle.Idle:
currentState = AIStates.Idle;
break;
case AIPatrolWithOutAreaStyle.Wander:
currentState = AIStates.Wander;
break;
}
}
else if (ignorePatrolTimer > Time.time)
{
yield return StartCoroutine(GoToDestionation());
}
if (canSeeTarget)
{
currentState = AIStates.Chase;
}
}
protected virtual IEnumerator GoToDestionation()
{
yield return null;
agent.speed = Mathf.Lerp(agent.speed, moveToSpeed, smoothSpeed * Time.deltaTime);
agent.stoppingDistance = patrollingStopDistance;
destination = moveToDestination;
}
protected virtual IEnumerator GoToStartingPoint()
{
yield return null;
agent.speed = Mathf.Lerp(agent.speed, patrolSpeed, smoothSpeed * Time.deltaTime);
agent.stoppingDistance = patrollingStopDistance;
destination = startPosition;
}
protected virtual IEnumerator Wander()
{
agent.speed = Mathf.Lerp(agent.speed, wanderSpeed, smoothSpeed * Time.deltaTime);
do
{
yield return null;
destination = transform.position + (Quaternion.AngleAxis(UnityEngine.Random.Range(-120, 120), transform.up) * transform.forward) * (patrollingStopDistance + 4);
} while (agent.enabled && agent.isOnNavMesh && agent.remainingDistance <= patrollingStopDistance);
if (canSeeTarget)
{
currentState = AIStates.Chase;
}
}
#endregion
#region AI Waypoint & PatrolPoint
vWaypoint GetWaypoint()
{
var waypoints = pathArea.GetValidPoints();
if (randomWaypoints)
{
currentWaypoint = randomWaypoint.Next(waypoints.Count);
}
else
{
currentWaypoint++;
}
if (currentWaypoint >= waypoints.Count)
{
currentWaypoint = 0;
}
if (waypoints.Count == 0)
{
agent.isStopped = true;
return null;
}
if (visitedWaypoint.Count == waypoints.Count)
{
visitedWaypoint.Clear();
}
if (visitedWaypoint.Contains(waypoints[currentWaypoint]))
{
return null;
}
agent.isStopped = false;
return waypoints[currentWaypoint];
}
vPoint GetPatrolPoint(vWaypoint waypoint)
{
var subPoints = pathArea.GetValidSubPoints(waypoint);
if (waypoint.randomPatrolPoint)
{
currentPatrolPoint = randomPatrolPoint.Next(subPoints.Count);
}
else
{
currentPatrolPoint++;
}
if (currentPatrolPoint >= subPoints.Count)
{
currentPatrolPoint = 0;
}
if (subPoints.Count == 0)
{
agent.isStopped = true;
return null;
}
if (visitedPatrolPoint.Contains(subPoints[currentPatrolPoint]))
{
return null;
}
agent.isStopped = false;
return subPoints[currentPatrolPoint];
}
#endregion
#region AI Melee Combat
protected IEnumerator MeleeAttackRotine()
{
if (!isAttacking && !actions && attackCount > 0 && !lockMovement && !isRolling)
{
sideMovement = GetRandonSide();
agent.stoppingDistance = distanceToAttack;
attackCount--;
MeleeAttack();
yield return null;
}
//else if (!actions && attackCount > 0) canAttack = true;
}
public void FinishAttack()
{
// if(attackCount > 0)
canAttack = true;
}
IEnumerator ResetAttackCount()
{
inResetAttack = true;
canAttack = false;
var value = 0f;
if (firstAttack)
{
firstAttack = false;
value = firstAttackDelay;
}
else
{
value = UnityEngine.Random.Range(minTimeToAttack, maxTimeToAttack);
}
yield return new WaitForSeconds(value);
attackCount = randomAttackCount ? UnityEngine.Random.Range(1, maxAttackCount + 1) : maxAttackCount;
canAttack = true;
inResetAttack = false;
}
public void OnEnableAttack()
{
isAttacking = true;
}
public void OnDisableAttack()
{
isAttacking = false;
canAttack = true;
}
public void ResetAttackTriggers()
{
animator.ResetTrigger("WeakAttack");
}
public void BreakAttack(int breakAtkID)
{
ResetAttackCount();
ResetAttackTriggers();
OnRecoil(breakAtkID);
}
public void OnRecoil(int recoilID)
{
TriggerRecoil(recoilID);
}
public void OnReceiveAttack(vDamage damage, vIMeleeFighter attacker)
{
StartCoroutine(CheckChanceToBlock(chanceToBlockAttack, 0));
var attackPos = (attacker != null && attacker.character != null) ? attacker.character.transform.position : damage.hitPosition;
if (!damage.ignoreDefense && isBlocking && meleeManager != null && meleeManager.CanBlockAttack(attackPos))
{
var damageReduction = meleeManager != null ? meleeManager.GetDefenseRate() : 0;
if (damageReduction > 0)
{
damage.ReduceDamage(damageReduction);
}
if (attacker != null && meleeManager != null && meleeManager.CanBreakAttack())
{
attacker.OnRecoil(meleeManager.GetDefenseRecoilID());
}
meleeManager.OnDefense();
}
// apply tag from the character that hit you and start chase
if (!passiveToDamage && damage.sender != null)
{
SetCurrentTarget(damage.sender);
currentState = AIStates.Chase;
}
damage.hitReaction = !isBlocking;
if (!passiveToDamage)
{
SetAggressive(true);
}
TakeDamage(damage);
}
public virtual void MoveTo(Vector3 position, float moveToSpeed = 1f, float ignorePatrolTimer = 2f)
{
moveToDestination = position;
currentState = AIStates.PatrolWaypoints;
this.moveToSpeed = moveToSpeed;
this.ignorePatrolTimer = Time.time + ignorePatrolTimer;
}
public vICharacter character
{
get { return this; }
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7fce10a830503234d997fb0f9d1d94b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,730 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
using UnityEngine.AI;
using vMelee;
public class vSimpleMeleeAI_Motor : vCharacter
{
#region Variables
#region Layers
[vEditorToolbar("Layers")]
[Tooltip("Layers that the character can walk on")]
public LayerMask groundLayer = 1 << 0;
[Tooltip("Distance to became not grounded")]
[SerializeField]
protected float groundCheckDistance = 0.5f;
[Tooltip("What objects can make the character auto crouch")]
public LayerMask autoCrouchLayer = 1 << 0;
[Tooltip("[SPHERECAST] ADJUST IN PLAY MODE - White Spherecast put just above the head, this will make the character Auto-Crouch if something hit the sphere.")]
public float headDetect = 0.95f;
#endregion
#region AI variables
[vEditorToolbar("Locomotion")]
[Tooltip("Use to limit your locomotion animation, if you want to patrol walking set this value to 0.5f")]
[Range(0f, 1.5f)]
public float patrolSpeed = 0.5f;
[Tooltip("Use to limit your locomotion animation, if you want to wander walking set this value to 0.5f")]
[Range(0f, 1.5f)]
public float wanderSpeed = 0.5f;
[Tooltip("Use to limit your locomotion animation, if you want to chase the target walking set this value to 0.5f")]
[Range(0f, 1.5f)]
public float chaseSpeed = 1f;
[Tooltip("Use to limit your locomotion animation, if you want to strafe the target walking set this value to 0.5f")]
[Range(0f, 1.5f)]
public float strafeSpeed = 1f;
[Header("--- Strafe ---")]
[Tooltip("Strafe around the target")]
public bool strafeSideways = true;
[Tooltip("Strafe a few steps backwards")]
public bool strafeBackward = true;
[Tooltip("Distance to switch to the strafe locomotion, leave with 0 if you don't want your character to strafe")]
public float strafeDistance = 3f;
[Tooltip("Min time to change the strafe direction")]
public float minStrafeSwape = 2f;
[Tooltip("Max time to change the strafe direction")]
public float maxStrafeSwape = 5f;
[Tooltip("Velocity to rotate the character while strafing")]
public float strafeRotationSpeed = 5f;
[vEditorToolbar("Detection")]
protected AIStates _currentState = AIStates.PatrolWaypoints;
public virtual AIStates currentState { get { return _currentState; } set { _currentState = value; } }
[Header("Who is your Target?")]
public vSimpleMeleeAI_SphereSensor sphereSensor;
public vTagMask tagsToDetect = new vTagMask { "Player" };
public LayerMask layersToDetect = 1 << 0;
public LayerMask obstaclesLayer;
public bool sortTargetFromDistance = false;
[Range(0f, 360f)]
public float fieldOfView = 95f;
[Tooltip("Max Distance to detect the Target with FOV")]
public float maxDetectDistance = 5f;
[Tooltip("Min Distance to noticed the Target without FOV")]
public float minDetectDistance = 2f;
[Tooltip("Distance to lost the Target")]
public float distanceToLostTarget = 5f;
public float lostTargetDistance { get { return maxDetectDistance + distanceToLostTarget; } }
[Tooltip("Distance to stop when chasing the Player")]
public float chaseStopDistance = 1f;
public bool drawAgentPath = false;
public bool displayGizmos;
[vEditorToolbar("Combat")]
[Tooltip("Check if you want the Enemy to be passive even if you attack him")]
public bool passiveToDamage = false;
[Tooltip("Check if you want the Enemy to chase the Target at first sight")]
public bool agressiveAtFirstSight = true;
[Tooltip("Velocity to rotate the character while attacking")]
public float attackRotationSpeed = 0.5f;
[Tooltip("Delay to trigger the first attack when close to the target")]
public float firstAttackDelay = 0f;
[Tooltip("Min frequency to attack")]
public float minTimeToAttack = 4f;
[Tooltip("Max frequency to attack")]
public float maxTimeToAttack = 6f;
[Tooltip("How many attacks the AI will make on a combo")]
public int maxAttackCount = 3;
[Tooltip("Randomly attacks based on the maxAttackCount")]
public bool randomAttackCount = true;
[Range(0f, 1f)]
public float chanceToRoll = .1f;
[Range(0f, 1f)]
public float chanceToBlockInStrafe = .1f;
[Range(0f, 1f)]
public float chanceToBlockAttack = 0f;
[Tooltip("How much time the character will stand up the shield")]
public float raiseShield = 4f;
[Tooltip("How much time the character will lower the shield")]
public float lowerShield = 2f;
[vEditorToolbar("Waypoint")]
[Tooltip("Max Distance to change waypoint")]
[Range(0.5f, 100f)]
public float distanceToChangeWaypoint = 1f;
[Tooltip("Min Distance to stop when Patrolling through waypoints")]
[Range(0.5f, 100f)]
public float patrollingStopDistance = 0.5f;
public AIPatrolWithOutAreaStyle patrolWithoutAreaStyle = AIPatrolWithOutAreaStyle.GoToStartPoint;
public vWaypointArea pathArea;
public bool randomWaypoints;
public vFisherYatesRandom randomWaypoint = new vFisherYatesRandom();
public vFisherYatesRandom randomPatrolPoint = new vFisherYatesRandom();
[HideInInspector]
public CapsuleCollider _capsuleCollider;
// there is a prefab of health hud example that you can drag and drop into the head bone of your character
[HideInInspector]
public v_SpriteHealth healthSlider;
// attach a meleeManager component to create new hitboxs and set up different weapons
[HideInInspector]
public vMeleeManager meleeManager;
// check your MeleeWeapon Inspector, each weapon can set up different distances to attack
public OnSetAgressiveEvent onSetAgressive = new OnSetAgressiveEvent();
public class OnSetAgressiveEvent : UnityEngine.Events.UnityEvent<bool> { }
[HideInInspector]
public bool lockMovement;
public enum AIPatrolWithOutAreaStyle
{
GoToStartPoint,
Wander,
Idle
}
public enum AIStates
{
Idle,
PatrolSubPoints,
PatrolWaypoints,
Wander,
Chase
}
#endregion
#region Protected Variables
public CharacterTarget currentTarget;// { protected set; get; }
[System.Serializable]
public struct CharacterTarget
{
public Transform transform;
public vIHealthController character;
public Collider colliderTarget;
}
protected Vector3 _targetPos;
protected virtual Vector3 targetPos { get { return _targetPos; } set { _targetPos = value; } }
[SerializeField, vReadOnly]
protected bool _canSeeTarget;
protected virtual bool canSeeTarget { get { return _canSeeTarget; } set { _canSeeTarget = value; } }
protected Vector3 _destination;
protected virtual Vector3 destination { get { return _destination; } set { _destination = value; } }
protected Vector3 _fwd;
protected virtual Vector3 fwd { get { return _fwd; } set { _fwd = value; } }
protected virtual bool _isGrounded { get; set; }
protected virtual bool isGrounded { get { return _isGrounded; } set { _isGrounded = value; } }
protected bool _isStrafing;
protected virtual bool isStrafing { get { return _isStrafing; } set { _isStrafing = value; } }
protected virtual bool inResetAttack { get; set; }
protected virtual bool firstAttack { get; set; }
protected virtual int attackCount { get; set; }
protected virtual int _currentWaypoint { get; set; }
protected virtual int currentWaypoint { get { return _currentWaypoint; } set { _currentWaypoint = value; } }
protected virtual int _currentPatrolPoint { get; set; }
protected virtual int currentPatrolPoint { get { return _currentPatrolPoint; } set { _currentPatrolPoint = value; } }
protected float _direction;
protected virtual float direction { get { return _direction; } set { _direction = value; } }
protected float _timer;
protected virtual float timer { get { return _timer; } set { _timer = value; } }
protected float _wait;
protected virtual float wait { get { return _wait; } set { _wait = value; } }
protected float _fovAngle;
protected virtual float fovAngle { get { return _fovAngle; } set { _fovAngle = value; } }
protected virtual float sideMovement { get; set; }
protected virtual float fwdMovement { get; set; }
protected virtual float strafeSwapeFrequency { get; set; }
protected virtual float groundDistance { get; set; }
protected Vector3 _startPosition;
protected virtual Vector3 startPosition { get { return _startPosition; } set { _startPosition = value; } }
protected RaycastHit groundHit;
protected NavMeshAgent agent;
protected NavMeshPath agentPath;
protected Quaternion freeRotation;
protected Quaternion desiredRotation;
protected Vector3 oldPosition;
protected Vector3 combatMovement;
protected Vector3 rollDirection;
protected Rigidbody _rigidbody;
protected PhysicsMaterial frictionPhysics;
protected Transform head;
protected Collider colliderTarget;
protected vWaypoint targetWaypoint;
protected vPoint targetPatrolPoint;
protected List<vPoint> visitedPatrolPoint = new List<vPoint>();
protected List<vWaypoint> visitedWaypoint = new List<vWaypoint>();
#endregion
#region Actions
protected bool _isCrouched;
protected bool _canAttack;
protected bool _tryingBlock;
protected bool _isRolling;
protected virtual bool isCrouched { get { return _isCrouched; } set { _isCrouched = value; } }
protected virtual bool canAttack { get { return _canAttack; } set { _canAttack = value; } }
protected virtual bool tryingBlock { get { return _tryingBlock; } set { _tryingBlock = value; } }
protected virtual bool isRolling { get { return _isRolling; } set { _isRolling = value; } }
protected bool _isBlocking;
public virtual bool isBlocking { get { return _isBlocking; } protected set { _isBlocking = value; } }
protected bool _isAttacking;
public bool isAttacking { get { return _isAttacking; } protected set { _isAttacking = value; } }
public bool isArmed { get { return meleeManager != null && (meleeManager.rightWeapon != null || (meleeManager.leftWeapon != null && meleeManager.leftWeapon.meleeType != vMeleeType.OnlyDefense)); } }
protected bool _actions;
public virtual bool actions { get { return _actions; } set { _actions = value; } }
#endregion
#endregion
public override void Init()
{
base.Init();
fwd = transform.forward;
destination = transform.position;
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
agentPath = new UnityEngine.AI.NavMeshPath();
sphereSensor = GetComponentInChildren<vSimpleMeleeAI_SphereSensor>();
if (sphereSensor)
{
sphereSensor.root = transform;
}
meleeManager = GetComponent<vMeleeManager>();
canAttack = true;
attackCount = 0;
sideMovement = GetRandonSide();
destination = transform.position;
_rigidbody = GetComponent<Rigidbody>();
_rigidbody.useGravity = true;
_rigidbody.constraints = RigidbodyConstraints.None | RigidbodyConstraints.FreezeRotation;
agent.updatePosition = false;
agent.updateRotation = false;
agent.enabled = false;
_capsuleCollider = GetComponent<CapsuleCollider>();
// avoid collision detection with inside colliders
Collider[] AllColliders = this.GetComponentsInChildren<Collider>();
Collider thisCollider = GetComponent<Collider>();
for (int i = 0; i < AllColliders.Length; i++)
{
Physics.IgnoreCollision(thisCollider, AllColliders[i]);
}
healthSlider = GetComponentInChildren<v_SpriteHealth>();
head = animator.GetBoneTransform(HumanBodyBones.Head);
oldPosition = transform.position;
if (!fillHealthOnStart) currentHealth = _currentHealth;
startPosition = transform.position;
}
#region AI Locomotion
public float distanceToAttack
{
get { if (meleeManager) { return meleeManager.GetAttackDistance(); } return 1f; }
}
public bool OnCombatArea
{
get
{
if (currentTarget.transform == null)
{
return false;
}
var inFloor = Vector3.Distance(new Vector3(0, transform.position.y, 0), new Vector3(0, currentTarget.transform.position.y, 0)) < distanceToAttack;
return (inFloor && agressiveAtFirstSight && TargetDistance <= strafeDistance && !agent.isOnOffMeshLink);
}
}
public bool OnStrafeArea
{
get
{
if (!canSeeTarget)
{
isStrafing = false;
return false;
}
if (currentTarget.transform == null || !agressiveAtFirstSight)
{
return false;
}
var inFloor = Vector3.Distance(new Vector3(0, transform.position.y, 0), new Vector3(0, currentTarget.transform.position.y, 0)) < 1.5f;
// exit strafe
if (isStrafing)
{
isStrafing = TargetDistance < (strafeDistance + 2f);
}
// enter strafe
else
{
isStrafing = OnCombatArea;
}
return inFloor ? isStrafing : false;
}
}
public bool AgentDone()
{
if (!agent.enabled && agent.updatePosition)
{
return true;
}
return !agent.pathPending && AgentStopping() && agent.updatePosition;
}
public bool AgentStopping()
{
if (!agent.enabled || !agent.isOnNavMesh)
{
return true;
}
return agent.remainingDistance <= agent.stoppingDistance;
}
public int GetRandonSide()
{
var side = UnityEngine.Random.Range(-1, 1);
if (side < 0)
{
side = -1;
}
else
{
side = 1;
}
return side;
}
protected void CheckGroundDistance()
{
if (_capsuleCollider != null)
{
var dist = 10f;
Ray ray1 = new Ray(transform.position + new Vector3(0, _capsuleCollider.height / 2, 0), Vector3.down);
if (Physics.Raycast(ray1, out groundHit, _capsuleCollider.height * 0.75f, groundLayer))
{
dist = transform.position.y - groundHit.point.y;
}
groundDistance = dist;
if (!actions && !isRolling && groundDistance < 0.3f)
{
if (currentHealth > 0)
{
UnityEngine.AI.NavMeshHit navHit;
if (UnityEngine.AI.NavMesh.SamplePosition(transform.position, out navHit, 5f, UnityEngine.AI.NavMesh.AllAreas))
{
_rigidbody.constraints = RigidbodyConstraints.None | RigidbodyConstraints.FreezeRotation | RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ;
_rigidbody.useGravity = false;
agent.updatePosition = false;
agent.enabled = true;
}
}
if (agent.enabled && agent.isOnNavMesh && Vector3.Distance(agent.nextPosition, transform.position) <= 0.1f)
{
agent.updatePosition = true;
}
else if (agent.enabled && agent.isOnNavMesh && !agent.updatePosition)
{
agent.nextPosition = transform.position;
}
}
if (!agent.isOnNavMesh && groundDistance > 0.3f && !ragdolled)
{
_rigidbody.useGravity = true;
_rigidbody.constraints = RigidbodyConstraints.None | RigidbodyConstraints.FreezeRotation;
agent.enabled = false;
agent.updatePosition = false;
}
}
}
public void CheckAutoCrouch()
{
// radius of SphereCast
float radius = _capsuleCollider.radius * 0.9f;
// Position of SphereCast origin stating in base of capsule
Vector3 pos = transform.position + Vector3.up * ((_capsuleCollider.height * 0.5f) - _capsuleCollider.radius);
// ray for SphereCast
Ray ray2 = new Ray(pos, Vector3.up);
RaycastHit groundHit;
// sphere cast around the base of capsule for check ground distance
//if (Physics.SphereCast(ray2, radius, out groundHit, _capsuleCollider.bounds.max.y - (_capsuleCollider.radius * 0.1f), groundLayer))
if (Physics.SphereCast(ray2, radius, out groundHit, headDetect - (_capsuleCollider.radius * 0.1f), autoCrouchLayer))
{
isCrouched = true;
}
else
{
isCrouched = false;
}
}
#endregion
#region Check Target
/// <summary>
/// Calculate Fov Angle
/// </summary>
/// <returns></returns>
public bool onFovAngle()
{
if (currentTarget.transform == null)
{
return false;
}
var freeRotation = Quaternion.LookRotation(currentTarget.transform.position - transform.position, Vector3.up);
var newAngle = freeRotation.eulerAngles - transform.eulerAngles;
fovAngle = newAngle.NormalizeAngle().y;
if (fovAngle < fieldOfView && fovAngle > -fieldOfView)
{
return true;
}
return false;
}
int canSeeTargetIteration;
/// <summary>
/// Target Detection
/// </summary>
/// <param name="_target"></param>
/// <returns></returns>
public void CheckTarget()
{
if (currentTarget.transform == null || !agressiveAtFirstSight)
{
canSeeTarget = false;
canSeeTargetIteration = 0;
return;
}
if (TargetDistance > maxDetectDistance)
{
canSeeTarget = false;
canSeeTargetIteration = 0;
return;
}
if (currentTarget.colliderTarget == null || currentTarget.colliderTarget.transform != currentTarget.transform)
{
currentTarget.colliderTarget = currentTarget.transform.GetComponent<Collider>();
}
if (currentTarget.colliderTarget == null)
{
canSeeTarget = false;
canSeeTargetIteration = 0;
return;
}
var top = new Vector3(currentTarget.colliderTarget.bounds.center.x, currentTarget.colliderTarget.bounds.max.y, currentTarget.colliderTarget.bounds.center.z);
var bottom = new Vector3(currentTarget.colliderTarget.bounds.center.x, currentTarget.colliderTarget.bounds.min.y, currentTarget.colliderTarget.bounds.center.z);
var offset = Vector3.Distance(top, bottom) * 0.15f;
top.y -= offset;
bottom.y += offset;
if (!onFovAngle() && TargetDistance > minDetectDistance)
{
canSeeTarget = false;
canSeeTargetIteration = 0;
return;
}
RaycastHit hit;
if (canSeeTargetIteration == 0 && !Physics.Linecast(head.position, top, out hit, obstaclesLayer))
{
canSeeTarget = true;
canSeeTargetIteration = 0;
return;
}
else if (canSeeTargetIteration == 1 && !Physics.Linecast(head.position, bottom, out hit, obstaclesLayer))
{
canSeeTarget = true;
canSeeTargetIteration = 0;
return;
}
else if (canSeeTargetIteration == 2 && !Physics.Linecast(head.position, currentTarget.colliderTarget.bounds.center, out hit, obstaclesLayer))
{
canSeeTarget = true;
canSeeTargetIteration = 0;
return;
}
else
{
canSeeTargetIteration++;
}
if (canSeeTargetIteration > 1)
{
canSeeTargetIteration = 0;
}
canSeeTarget = false;
}
public float TargetDistance
{
get
{
if (currentTarget.transform != null)
{
return currentTarget.colliderTarget ? Vector3.Distance(_capsuleCollider.bounds.center, currentTarget.colliderTarget.bounds.center) :
Vector3.Distance(transform.position, currentTarget.transform.position);
}
return maxDetectDistance + 1f;
}
}
public Transform headTarget
{
get
{
if (currentTarget.transform != null && currentTarget.transform.GetComponent<Animator>() != null)
{
return currentTarget.transform.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Head);
}
else
{
return null;
}
}
}
#endregion
#region AI Health
protected void RemoveComponents()
{
if (!removeComponentsAfterDie)
{
return;
}
if (_capsuleCollider != null)
{
Destroy(_capsuleCollider);
}
if (_rigidbody != null)
{
Destroy(_rigidbody);
}
if (animator != null)
{
Destroy(animator);
}
if (agent != null)
{
Destroy(agent);
}
var comps = GetComponents<MonoBehaviour>();
for (int i = 0; i < comps.Length; i++)
{
Destroy(comps[i]);
}
}
/// <summary>
/// TAKE DAMAGE - you can override the take damage method from the vCharacter and add your own calls
/// </summary>
/// <param name="damage"> damage to apply </param>
public override void TakeDamage(vDamage damage)
{
// ignore damage if the character is rolling, dead or the animator is disable
if (isRolling || currentHealth <= 0 || !animator.enabled)
{
return;
}
if (!damage.ignoreDefense && !actions && CheckChanceToRoll())
{
return;
}
base.TakeDamage(damage);
}
protected override void TriggerDamageReaction(vDamage damage)
{
if (!isRolling)
{
base.TriggerDamageReaction(damage);
}
}
#endregion
#region AI Melee Combat Methods
protected bool CheckChanceToRoll()
{
if (isAttacking || actions)
{
return false;
}
float randomRoll = UnityEngine.Random.value;
if (randomRoll < chanceToRoll && randomRoll > 0 && currentTarget.transform != null)
{
animator.SetTrigger("ResetState");
sideMovement = GetRandonSide();
Ray ray = new Ray(currentTarget.transform.position, currentTarget.transform.right * sideMovement);
rollDirection = ray.direction;
animator.CrossFadeInFixedTime("Roll", 0.1f);
return true;
}
return false;
}
protected IEnumerator CheckChanceToBlock(float chance, float timeToEnter)
{
tryingBlock = true;
float randomBlock = UnityEngine.Random.value;
if (randomBlock < chance && randomBlock > 0 && !isBlocking)
{
if (timeToEnter > 0)
{
yield return new WaitForSeconds(timeToEnter);
}
isBlocking = currentTarget.transform == null || (actions) || isAttacking ? false : true;
StartCoroutine(ResetBlock());
tryingBlock = false;
}
else
{
tryingBlock = false;
}
}
protected IEnumerator ResetBlock()
{
yield return new WaitForSeconds(currentTarget.transform == null ? 0 : raiseShield);
isBlocking = false;
}
protected virtual void SetAggressive(bool value)
{
agressiveAtFirstSight = value;//
onSetAgressive.Invoke(value);
}
int GetDamageResult(int damage, float defenseRate)
{
int result = (int)(damage - ((damage * defenseRate) / 100));
return result;
}
#endregion
#region Ragdoll
public override void ResetRagdoll()
{
oldPosition = transform.position;
ragdolled = false;
_capsuleCollider.isTrigger = false;
_rigidbody.isKinematic = false;
agent.updatePosition = false;
agent.enabled = true;
}
public override void EnableRagdoll()
{
agent.enabled = false;
agent.updatePosition = false;
ragdolled = true;
_rigidbody.isKinematic = true;
_capsuleCollider.isTrigger = true;
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0e30d18d6120c6c41bfcf6015dabc83b
timeCreated: 1452892889
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,155 @@
using System.Collections.Generic;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
public class vSimpleMeleeAI_SphereSensor : MonoBehaviour
{
public Transform root;
public List<Transform> targetsInArea;
protected bool getFromDistance;
protected float lastDetectionDistance;
protected virtual void Start()
{
targetsInArea = new List<Transform>();
}
public virtual void AddTarget(Transform _transform)
{
if (!targetsInArea.Contains(_transform))
{
targetsInArea.Add(_transform);
}
}
public virtual void SetColliderRadius(float radius)
{
var collider = GetComponent<SphereCollider>();
if (collider)
{
collider.radius = radius;
}
}
public virtual Transform GetTargetTransform()
{
if (targetsInArea.Count > 0)
{
SortTargets();
if (targetsInArea.Count > 0)
{
return targetsInArea[0].transform;
}
}
return null;
}
public virtual vIHealthController GetTargetvCharacter()
{
if (targetsInArea.Count > 0)
{
SortCharacters();
if (targetsInArea.Count > 0)
{
var vChar = targetsInArea[0].GetComponent<vIHealthController>();
if (vChar != null && vChar.currentHealth > 0)
{
return vChar;
}
}
}
return null;
}
protected virtual void SortCharacters()
{
for (int i = targetsInArea.Count - 1; i >= 0; i--)
{
var t = targetsInArea[i];
var dist = Vector3.Distance(transform.position, targetsInArea[i].transform.position);
if (t == null || dist > lastDetectionDistance || t.GetComponent<vIHealthController>() == null)
{
targetsInArea.RemoveAt(i);
}
}
if (getFromDistance)
{
targetsInArea.Sort(delegate (Transform c1, Transform c2)
{
return Vector3.Distance(this.transform.position, c1 != null ? c1.transform.position : Vector3.one * Mathf.Infinity).CompareTo
((Vector3.Distance(this.transform.position, c2 != null ? c2.transform.position : Vector3.one * Mathf.Infinity)));
});
}
}
protected virtual void SortTargets()
{
for (int i = targetsInArea.Count - 1; i >= 0; i--)
{
var t = targetsInArea[i];
var dist = Vector3.Distance(transform.position, targetsInArea[i].transform.position);
if (t == null || dist > lastDetectionDistance)
{
targetsInArea.RemoveAt(i);
}
}
if (getFromDistance)
{
targetsInArea.Sort(delegate (Transform c1, Transform c2)
{
return Vector3.Distance(this.transform.position, c1 != null ? c1.transform.position : Vector3.one * Mathf.Infinity).CompareTo
((Vector3.Distance(this.transform.position, c2 != null ? c2.transform.position : Vector3.one * Mathf.Infinity)));
});
}
}
public virtual void CheckTargetsAround(float FOV, float minDistance, float maxDistance, vTagMask detectTags, LayerMask detectMask, bool getTargetFromDistance = false)
{
this.getFromDistance = getTargetFromDistance;
lastDetectionDistance = maxDistance;
var targetsAround = Physics.OverlapSphere(transform.position, maxDistance, detectMask);
targetsAround = System.Array.FindAll(targetsAround, t =>
(root && root != t.transform)
&& (detectTags != null && detectTags.Count > 0 && detectTags.Contains(t.gameObject.tag))
&& InFovAngle(t.transform, minDistance, FOV));
targetsInArea = System.Array.ConvertAll(targetsAround, c => c.transform).vToList();
}
protected virtual bool InFovAngle(Transform target, float minDistance, float FOV)
{
var dist = Vector3.Distance(transform.position, target.position);
if (dist < minDistance)
{
return true;
}
var rot = Quaternion.LookRotation(target.position - transform.position, Vector3.up);
var detectionAngle = transform.eulerAngles;
var newAngle = rot.eulerAngles - detectionAngle;
var fovAngleY = newAngle.NormalizeAngle().y;
var fovAngleX = newAngle.NormalizeAngle().x;
if (fovAngleY <= (FOV * 0.5f) && fovAngleY >= -(FOV * 0.5f) && fovAngleX <= (FOV * 0.5f) && fovAngleX >= -(FOV * 0.5f))
{
return true;
}
return false;
}
//void OnTriggerEnter(Collider other)
//{
// if (tagsToDetect.Contains(other.gameObject.tag) && !targetsInArea.Contains(other.transform))
// targetsInArea.Add(other);
//}
//void OnTriggerExit(Collider other)
//{
// if (tagsToDetect.Contains(other.gameObject.tag) && targetsInArea.Contains(other.transform))
// targetsInArea.Remove(other);
//}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43ce90429324b114c883f287bad02448
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,368 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
using vItemManager;
using vMelee;
public class vSimpleMeleeAI_WeaponsControl : MonoBehaviour
{
#region Variables
[Header("---- Right Weapon Settings----")]
public bool useRightWeapon = true;
public int rightWeaponID;
public bool randomRightWeapon = true;
[Header("Right Equip Point")]
public Transform defaultEquipPointR;
public List<Transform> customEquipPointR;
[Header("---- Left Weapon Settings----")]
public bool useLeftWeapon = true;
public int leftWeaponID;
public bool randomLeftWeapon = true;
[Header("Left Equip Point")]
public Transform defaultEquipPointL;
public List<Transform> customEquipPointL;
public vItemCollection itemCollection;
protected vSimpleMeleeAI_Controller ai;
protected vMeleeManager manager;
protected vItem leftWeaponItem, rightWeaponItem;
protected GameObject leftWeapon, rightWeapon;
protected List<vItem> weaponItems = new List<vItem>();
protected Transform leftArm;
protected Transform rightArm;
protected bool inEquip;
protected bool inUnequip;
protected float equipTimer, unequipTimer;
protected float timeToStart;
protected int equipCalls;
protected bool changeLeft, changeRight;
#if !UNITY_5_4_OR_NEWER
protected System.Random random;
#endif
#endregion
IEnumerator Start()
{
itemCollection = GetComponentInChildren<vItemCollection>(true);
ai = GetComponent<vSimpleMeleeAI_Controller>();
manager = GetComponent<vMeleeManager>();
yield return new WaitForEndOfFrame();
if (itemCollection && ai && manager)
{
ai.onSetAgressive.AddListener(OnSetAgressive);
leftArm = ai.animator.GetBoneTransform(HumanBodyBones.LeftLowerArm);
rightArm = ai.animator.GetBoneTransform(HumanBodyBones.RightLowerArm);
for (int i = 0; i < itemCollection.items.Count; i++)
{
if (itemCollection.items[i].amount > 0)
{
var item = itemCollection.itemListData.items.Find(_item => _item.id == itemCollection.items[i].id && _item.type == vItemType.MeleeWeapon);
if (item != null)
{
AddItem(itemCollection.items[i].id, itemCollection.items[i].amount);
}
}
}
if (useRightWeapon)
{
if (randomRightWeapon)
{
GetRandomWeapon(ref rightWeaponItem, vMeleeType.OnlyAttack);
}
else
{
GetItemWeapon(rightWeaponID, ref rightWeaponItem, vMeleeType.OnlyAttack);
}
}
if (useLeftWeapon)
{
if (randomLeftWeapon)
{
GetRandomWeapon(ref leftWeaponItem, vMeleeType.OnlyDefense);
}
else
{
GetItemWeapon(leftWeaponID, ref leftWeaponItem, vMeleeType.OnlyDefense);
}
}
if (rightArm && rightWeaponItem)
{
Transform equipPoint = null;
if (customEquipPointR.Count > 0)
{
equipPoint = customEquipPointR.Find(t => t.name == rightWeaponItem.customHandler);
}
if (equipPoint == null)
{
equipPoint = defaultEquipPointR;
}
if (equipPoint)
{
rightWeapon = Instantiate(rightWeaponItem.originalObject);
rightWeapon.transform.parent = equipPoint;
rightWeapon.transform.localPosition = Vector3.zero;
rightWeapon.transform.localEulerAngles = Vector3.zero;
manager.SetRightWeapon(rightWeapon);
rightWeapon.SetActive(false);
if (ai.agressiveAtFirstSight)
{
StartCoroutine(EquipItemRoutine(false, rightWeaponItem, rightWeapon));
}
}
}
if (leftArm && leftWeaponItem)
{
Transform equipPoint = null;
if (customEquipPointL.Count > 0)
{
equipPoint = customEquipPointL.Find(t => t.name == leftWeaponItem.customHandler);
}
if (equipPoint == null)
{
equipPoint = defaultEquipPointL;
}
if (equipPoint)
{
leftWeapon = Instantiate(leftWeaponItem.originalObject);
leftWeapon.transform.parent = equipPoint;
leftWeapon.transform.localPosition = Vector3.zero;
leftWeapon.transform.localEulerAngles = Vector3.zero;
var scale = leftWeapon.transform.localScale;
scale.x *= -1;
leftWeapon.transform.localScale = scale;
manager.SetLeftWeapon(leftWeapon);
leftWeapon.SetActive(false);
if (ai.agressiveAtFirstSight)
{
StartCoroutine(EquipItemRoutine(true, leftWeaponItem, leftWeapon));
}
}
}
}
}
public void OnSetAgressive(bool value)
{
timeToStart = 2f;
if (value)
{
if (rightWeapon && !rightWeapon.activeSelf && !changeRight)
{
changeRight = true;
StartCoroutine(EquipItemRoutine(false, rightWeaponItem, rightWeapon));
}
if (leftWeapon && !leftWeapon.activeSelf && !changeLeft)
{
changeLeft = true;
StartCoroutine(EquipItemRoutine(true, leftWeaponItem, leftWeapon));
}
}
else
{
if (rightWeapon && rightWeapon.activeSelf)
{
StartCoroutine(UnequipItemRoutine(false, rightWeaponItem, rightWeapon));
}
if (leftWeapon && leftWeapon.activeSelf)
{
StartCoroutine(UnequipItemRoutine(true, leftWeaponItem, leftWeapon));
}
}
}
void GetItemWeapon(int id, ref vItem weaponItem, vMeleeType type)
{
if (weaponItems.Count > 0)
{
weaponItem = weaponItems.Find(_item => _item.id == id &&
_item.originalObject != null && _item.originalObject.GetComponent<vMeleeWeapon>() != null &&
(_item.originalObject.GetComponent<vMeleeWeapon>().meleeType == vMeleeType.AttackAndDefense || _item.originalObject.GetComponent<vMeleeWeapon>().meleeType == type));
weaponItems.Remove(weaponItem);
}
}
void GetRandomWeapon(ref vItem weaponItem, vMeleeType type)
{
if (weaponItems.Count > 0)
{
var _weaponItems = weaponItems.FindAll(_item =>
_item.originalObject != null && _item.originalObject.GetComponent<vMeleeWeapon>() != null &&
(_item.originalObject.GetComponent<vMeleeWeapon>().meleeType == vMeleeType.AttackAndDefense || _item.originalObject.GetComponent<vMeleeWeapon>().meleeType == type));
var itemIndex = 0;
#if UNITY_5_4_OR_NEWER
Random.InitState(Random.Range(0, System.DateTime.Now.Millisecond));
itemIndex = Random.Range(0, _weaponItems.Count - 1);
#else
random = new System.Random(Random.Range(0, System.DateTime.Now.Millisecond));
itemIndex = random.Next(0, _weaponItems.Count - 1);
#endif
weaponItem = _weaponItems[itemIndex];
weaponItems.Remove(weaponItem);
}
}
IEnumerator EquipItemRoutine(bool flipEquip, vItem item, GameObject weapon)
{
equipCalls++;
while (inEquip || timeToStart > 0 || ai.ragdolled)
{
timeToStart -= Time.deltaTime;
yield return new WaitForEndOfFrame();
}
if (!inUnequip)
{
inEquip = true;
if (weapon != null && item != null)
{
equipTimer = item.enableDelayTime;
var type = item.type;
if (type != vItemType.Consumable && (!ai.isDead))
{
if (!string.IsNullOrEmpty(item.EnableAnim))
{
ai.animator.SetBool("FlipEquip", flipEquip);
ai.animator.CrossFade(item.EnableAnim, 0.25f);
}
}
if (weapon != null)
{
while (equipTimer > 0)
{
equipTimer -= Time.deltaTime;
yield return new WaitForEndOfFrame();
}
if (!ai.isDead)
{
weapon.SetActive(true);
}
}
}
inEquip = false;
equipCalls--;
if (equipCalls == 0)
{
ai.lockMovement = false;
}
if (flipEquip)
{
changeLeft = false;
}
else
{
changeRight = false;
}
}
}
IEnumerator UnequipItemRoutine(bool flipEquip, vItem item, GameObject weapon)
{
ai.lockMovement = true;
while (inUnequip || ai.actions || ai.isAttacking || ai.ragdolled)
{
yield return new WaitForEndOfFrame();
}
if (!inEquip)
{
yield return new WaitForSeconds(1);
inUnequip = true;
if (weapon != null && item != null)
{
unequipTimer = item.enableDelayTime;
var type = item.type;
if (type != vItemType.Consumable)
{
if (!string.IsNullOrEmpty(item.DisableAnim))
{
ai.animator.SetBool("FlipEquip", flipEquip);
ai.animator.CrossFade(item.DisableAnim, 0.25f);
}
}
if (weapon != null)
{
while (unequipTimer > 0)
{
unequipTimer -= Time.deltaTime;
yield return new WaitForEndOfFrame();
}
weapon.SetActive(false);
}
}
inUnequip = false;
}
ai.lockMovement = true;
}
/// <summary>
/// Add new Instance of Item to itemList
/// </summary>
/// <param name="item"></param>
public void AddItem(int itemID, int amount)
{
if (itemCollection.itemListData != null && itemCollection.itemListData.items.Count > 0)
{
var item = itemCollection.itemListData.items.Find(t => t.id.Equals(itemID));
if (item)
{
var sameItems = weaponItems.FindAll(i => i.stackable && i.id == item.id && i.amount < i.maxStack);
if (sameItems.Count == 0)
{
var _item = Instantiate(item);
_item.name = _item.name.Replace("(Clone)", string.Empty);
_item.amount = 0;
for (int i = 0; i < item.maxStack && _item.amount < _item.maxStack && amount > 0; i++)
{
_item.amount++;
amount--;
}
weaponItems.Add(_item);
if (amount > 0)
{
AddItem(item.id, amount);
}
}
else
{
var indexOffItem = weaponItems.IndexOf(sameItems[0]);
for (int i = 0; i < weaponItems[indexOffItem].maxStack && weaponItems[indexOffItem].amount < weaponItems[indexOffItem].maxStack && amount > 0; i++)
{
weaponItems[indexOffItem].amount++;
amount--;
}
if (amount > 0)
{
AddItem(item.id, amount);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c97737e385a96a741a0e10d55c34b9ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: