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,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: