init 1.1.2
This commit is contained in:
426
Runtime/Scripts/Managers/CombatTextSystem.cs
Normal file
426
Runtime/Scripts/Managers/CombatTextSystem.cs
Normal file
@@ -0,0 +1,426 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace EmeraldAI
|
||||
{
|
||||
public class CombatTextSystem : MonoBehaviour
|
||||
{
|
||||
public static CombatTextSystem Instance;
|
||||
[HideInInspector]
|
||||
public GameObject CombatTextObject;
|
||||
public enum AnimationTypeEnum { Bounce, Upwards, OutwardsV1, OutwardsV2, Stationary };
|
||||
[HideInInspector]
|
||||
public AnimationTypeEnum AnimationType = AnimationTypeEnum.Bounce;
|
||||
[HideInInspector]
|
||||
public GameObject CombatTextCanvas;
|
||||
[HideInInspector]
|
||||
public EmeraldCombatTextData m_EmeraldAICombatTextData;
|
||||
Transform m_CombatTextParent;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
CombatTextObject = (GameObject)Resources.Load("Combat Text") as GameObject;
|
||||
m_CombatTextParent = CombatTextCanvas.transform.GetChild(0);
|
||||
m_EmeraldAICombatTextData = (EmeraldCombatTextData)Resources.Load("Combat Text Data") as EmeraldCombatTextData;
|
||||
}
|
||||
|
||||
public void CreateCombatText(int amount, Vector3 TextPosition, bool CriticalHit, bool HealingText, bool PlayerTakingDamage)
|
||||
{
|
||||
if (m_EmeraldAICombatTextData.CombatTextState == EmeraldCombatTextData.CombatTextStateEnum.Enabled)
|
||||
{
|
||||
if (m_EmeraldAICombatTextData.CombatTextTargets == EmeraldCombatTextData.CombatTextTargetEnum.AIOnly && PlayerTakingDamage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AnimationType = (AnimationTypeEnum)m_EmeraldAICombatTextData.AnimationType;
|
||||
var t = EmeraldAI.Utility.EmeraldObjectPool.Spawn(CombatTextObject, TextPosition + Vector3.up, Quaternion.identity);
|
||||
t.transform.SetParent(m_CombatTextParent);
|
||||
t.transform.position = Vector3.zero;
|
||||
|
||||
Text m_Text = t.GetComponent<Text>();
|
||||
m_Text.text = amount.ToString();
|
||||
m_Text.font = m_EmeraldAICombatTextData.TextFont;
|
||||
m_Text.fontSize = m_EmeraldAICombatTextData.FontSize;
|
||||
|
||||
Outline m_OutLine = t.GetComponent<Outline>();
|
||||
if (m_EmeraldAICombatTextData.OutlineEffect == EmeraldCombatTextData.OutlineEffectEnum.Enabled)
|
||||
{
|
||||
m_OutLine.enabled = true;
|
||||
}
|
||||
else if (m_EmeraldAICombatTextData.OutlineEffect == EmeraldCombatTextData.OutlineEffectEnum.Disabled)
|
||||
{
|
||||
m_OutLine.enabled = false;
|
||||
}
|
||||
|
||||
if (AnimationType == AnimationTypeEnum.Bounce)
|
||||
{
|
||||
StartCoroutine(AnimateBounceText(m_Text, m_EmeraldAICombatTextData.PlayerTextColor, m_EmeraldAICombatTextData.PlayerCritTextColor,TextPosition, CriticalHit, HealingText, PlayerTakingDamage));
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.Upwards)
|
||||
{
|
||||
StartCoroutine(AnimateUpwardsText(m_Text, m_EmeraldAICombatTextData.PlayerTextColor, m_EmeraldAICombatTextData.PlayerCritTextColor, TextPosition, CriticalHit, HealingText, PlayerTakingDamage));
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.OutwardsV1 || AnimationType == AnimationTypeEnum.OutwardsV2)
|
||||
{
|
||||
StartCoroutine(AnimateOutwardsText(m_Text, m_EmeraldAICombatTextData.PlayerTextColor, m_EmeraldAICombatTextData.PlayerCritTextColor, TextPosition, CriticalHit, HealingText, PlayerTakingDamage));
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.Stationary)
|
||||
{
|
||||
StartCoroutine(AnimateStationaryText(m_Text, m_EmeraldAICombatTextData.PlayerTextColor, m_EmeraldAICombatTextData.PlayerCritTextColor, TextPosition, CriticalHit, HealingText, PlayerTakingDamage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateCombatTextAI(int Amount, Vector3 TextPosition, bool CriticalHit, bool HealingText)
|
||||
{
|
||||
if (m_EmeraldAICombatTextData.CombatTextState == EmeraldCombatTextData.CombatTextStateEnum.Enabled)
|
||||
{
|
||||
AnimationType = (AnimationTypeEnum)m_EmeraldAICombatTextData.AnimationType;
|
||||
var t = EmeraldAI.Utility.EmeraldObjectPool.Spawn(CombatTextObject, TextPosition + Vector3.up, Quaternion.identity);
|
||||
t.transform.SetParent(m_CombatTextParent);
|
||||
t.transform.position = Vector3.zero;
|
||||
|
||||
Text m_Text = t.GetComponent<Text>();
|
||||
m_Text.text = Amount.ToString();
|
||||
m_Text.font = m_EmeraldAICombatTextData.TextFont;
|
||||
m_Text.fontSize = m_EmeraldAICombatTextData.FontSize;
|
||||
|
||||
Outline m_OutLine = t.GetComponent<Outline>();
|
||||
if (m_EmeraldAICombatTextData.OutlineEffect == EmeraldCombatTextData.OutlineEffectEnum.Enabled)
|
||||
{
|
||||
m_OutLine.enabled = true;
|
||||
}
|
||||
else if (m_EmeraldAICombatTextData.OutlineEffect == EmeraldCombatTextData.OutlineEffectEnum.Disabled)
|
||||
{
|
||||
m_OutLine.enabled = false;
|
||||
}
|
||||
|
||||
if (AnimationType == AnimationTypeEnum.Bounce)
|
||||
{
|
||||
StartCoroutine(AnimateBounceText(m_Text, m_EmeraldAICombatTextData.AITextColor, m_EmeraldAICombatTextData.AICritTextColor, TextPosition, CriticalHit, HealingText, false));
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.Upwards)
|
||||
{
|
||||
StartCoroutine(AnimateUpwardsText(m_Text, m_EmeraldAICombatTextData.AITextColor, m_EmeraldAICombatTextData.AICritTextColor, TextPosition, CriticalHit, HealingText, false));
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.OutwardsV1 || AnimationType == AnimationTypeEnum.OutwardsV2)
|
||||
{
|
||||
StartCoroutine(AnimateOutwardsText(m_Text, m_EmeraldAICombatTextData.AITextColor, m_EmeraldAICombatTextData.AICritTextColor, TextPosition, CriticalHit, HealingText, false));
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.Stationary)
|
||||
{
|
||||
StartCoroutine(AnimateStationaryText(m_Text, m_EmeraldAICombatTextData.AITextColor, m_EmeraldAICombatTextData.AICritTextColor, TextPosition, CriticalHit, HealingText, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator AnimateBounceText(Text m_Text, Color RegularTextColor, Color CritTextColor, Vector3 TargetPosition, bool CriticalHit, bool HealingText, bool PlayerTakingDamage)
|
||||
{
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
else if (!CriticalHit)
|
||||
{
|
||||
m_Text.color = RegularTextColor;
|
||||
}
|
||||
|
||||
if (HealingText)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.HealingTextColor;
|
||||
m_Text.text = "+" + m_Text.text;
|
||||
}
|
||||
|
||||
if (PlayerTakingDamage)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.PlayerTakeDamageTextColor;
|
||||
m_Text.text = "-" + m_Text.text;
|
||||
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
float t = 0;
|
||||
float m_TextFade = 0;
|
||||
float RandomXPosition = Random.Range(-0.8f, 0.6f);
|
||||
RandomXPosition = Mathf.Round(RandomXPosition * 10f) / 10;
|
||||
float AnimateSmaller = 0;
|
||||
float AnimateLarger = 0;
|
||||
|
||||
while ((t / 1.5f) < 1)
|
||||
{
|
||||
t += Time.deltaTime;
|
||||
float r = 1.0f - (t / 1);
|
||||
Vector3 m_TextPos = TargetPosition + new Vector3(r - RandomXPosition, Mathf.Sin(r * Mathf.PI), 0);
|
||||
m_TextPos = Camera.main.WorldToScreenPoint(m_TextPos - Vector3.right + Vector3.up * m_EmeraldAICombatTextData.DefaultHeight / 2);
|
||||
m_TextPos.z = 0.0f;
|
||||
|
||||
if (m_EmeraldAICombatTextData.UseAnimateFontSize == EmeraldCombatTextData.UseAnimateFontSizeEnum.Enabled)
|
||||
{
|
||||
if (t <= 0.15f)
|
||||
{
|
||||
AnimateLarger += Time.deltaTime * 8;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize, (float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, AnimateLarger);
|
||||
}
|
||||
else if (t > 0.15f && t <= 0.3f)
|
||||
{
|
||||
AnimateSmaller += Time.deltaTime * 6;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, (float)m_EmeraldAICombatTextData.FontSize, AnimateSmaller);
|
||||
}
|
||||
}
|
||||
|
||||
if (t > 0.5f)
|
||||
{
|
||||
m_TextFade += Time.deltaTime;
|
||||
m_Text.color = new Color(m_Text.color.r, m_Text.color.g, m_Text.color.b, 1 - (m_TextFade * 2));
|
||||
}
|
||||
|
||||
m_Text.transform.position = m_TextPos;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
EmeraldAI.Utility.EmeraldObjectPool.Despawn(m_Text.gameObject);
|
||||
}
|
||||
|
||||
IEnumerator AnimateUpwardsText(Text m_Text, Color RegularTextColor, Color CritTextColor, Vector3 TargetPosition, bool CriticalHit, bool HealingText, bool PlayerTakingDamage)
|
||||
{
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
else if (!CriticalHit)
|
||||
{
|
||||
m_Text.color = RegularTextColor;
|
||||
}
|
||||
|
||||
if (HealingText)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.HealingTextColor;
|
||||
m_Text.text = "+" + m_Text.text;
|
||||
}
|
||||
|
||||
if (PlayerTakingDamage)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.PlayerTakeDamageTextColor;
|
||||
m_Text.text = "-" + m_Text.text;
|
||||
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
float t = 0;
|
||||
float m_TextFade = 0;
|
||||
float RandomXPosition = Random.Range(-0.8f, 0.9f);
|
||||
RandomXPosition = Mathf.Round(RandomXPosition * 10f) / 10;
|
||||
float AnimateSmaller = 0;
|
||||
float AnimateLarger = 0;
|
||||
|
||||
while ((t / 1.5f) < 1)
|
||||
{
|
||||
t += Time.deltaTime*0.75f;
|
||||
float r = 1.0f - (t / 1);
|
||||
Vector3 m_TextPos = TargetPosition + new Vector3(RandomXPosition, -r * m_EmeraldAICombatTextData.DefaultHeight, 0);
|
||||
m_TextPos = Camera.main.WorldToScreenPoint(m_TextPos + Vector3.up * m_EmeraldAICombatTextData.DefaultHeight);
|
||||
m_TextPos.z = 0.0f;
|
||||
|
||||
if (m_EmeraldAICombatTextData.UseAnimateFontSize == EmeraldCombatTextData.UseAnimateFontSizeEnum.Enabled)
|
||||
{
|
||||
if (t <= 0.15f)
|
||||
{
|
||||
AnimateLarger += Time.deltaTime * 8;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize, (float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, AnimateLarger);
|
||||
}
|
||||
else if (t > 0.15f && t <= 0.3f)
|
||||
{
|
||||
AnimateSmaller += Time.deltaTime * 6;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, (float)m_EmeraldAICombatTextData.FontSize, AnimateSmaller);
|
||||
}
|
||||
}
|
||||
|
||||
if (t > 0.5f)
|
||||
{
|
||||
m_TextFade += Time.deltaTime;
|
||||
m_Text.color = new Color(m_Text.color.r, m_Text.color.g, m_Text.color.b, 1 - (m_TextFade * 1.5f));
|
||||
}
|
||||
|
||||
m_Text.transform.position = m_TextPos;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
EmeraldAI.Utility.EmeraldObjectPool.Despawn(m_Text.gameObject);
|
||||
}
|
||||
|
||||
IEnumerator AnimateOutwardsText(Text m_Text, Color RegularTextColor, Color CritTextColor, Vector3 TargetPosition, bool CriticalHit, bool HealingText, bool PlayerTakingDamage)
|
||||
{
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
else if (!CriticalHit)
|
||||
{
|
||||
m_Text.color = RegularTextColor;
|
||||
}
|
||||
|
||||
if (HealingText)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.HealingTextColor;
|
||||
m_Text.text = "+" + m_Text.text;
|
||||
}
|
||||
|
||||
if (PlayerTakingDamage)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.PlayerTakeDamageTextColor;
|
||||
m_Text.text = "-" + m_Text.text;
|
||||
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
float t = 0;
|
||||
float m_TextFade = 0;
|
||||
float RandomXPosition = Random.Range(-2f, 2.1f);
|
||||
RandomXPosition = Mathf.Round(RandomXPosition * 10f) / 10;
|
||||
float RandomYPosition = m_EmeraldAICombatTextData.DefaultHeight;
|
||||
RandomYPosition = Mathf.Round(RandomYPosition * 10f) / 10;
|
||||
Vector3 m_TextPos = Vector3.zero;
|
||||
float r = 1.0f;
|
||||
float AnimateSmaller = 0;
|
||||
float AnimateLarger = 0;
|
||||
|
||||
while ((t / 1) < 1)
|
||||
{
|
||||
t += Time.deltaTime/2;
|
||||
|
||||
if (r > 0.5f)
|
||||
{
|
||||
r = 1.0f - (t*4 / 1);
|
||||
}
|
||||
|
||||
if (AnimationType == AnimationTypeEnum.OutwardsV1)
|
||||
{
|
||||
m_TextPos = TargetPosition + new Vector3(Mathf.Lerp(0, RandomXPosition, t), -r * m_EmeraldAICombatTextData.DefaultHeight + Mathf.Sin(t * Mathf.PI), 0);
|
||||
}
|
||||
else if (AnimationType == AnimationTypeEnum.OutwardsV2)
|
||||
{
|
||||
m_TextPos = TargetPosition + new Vector3(Mathf.Lerp(0, RandomXPosition, 1 - r), -r * m_EmeraldAICombatTextData.DefaultHeight + Mathf.Sin(r + RandomYPosition * Mathf.PI), 0);
|
||||
}
|
||||
|
||||
m_TextPos = Camera.main.WorldToScreenPoint(m_TextPos+Vector3.up*RandomYPosition);
|
||||
m_TextPos.z = 0.0f;
|
||||
m_Text.transform.position = m_TextPos;
|
||||
|
||||
if (m_EmeraldAICombatTextData.UseAnimateFontSize == EmeraldCombatTextData.UseAnimateFontSizeEnum.Enabled)
|
||||
{
|
||||
if (t <= 0.15f)
|
||||
{
|
||||
AnimateLarger += Time.deltaTime * 8;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize, (float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, AnimateLarger);
|
||||
}
|
||||
else if (t > 0.15f && t <= 0.3f)
|
||||
{
|
||||
AnimateSmaller += Time.deltaTime * 6;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, (float)m_EmeraldAICombatTextData.FontSize, AnimateSmaller);
|
||||
}
|
||||
}
|
||||
|
||||
if (t > 0.5f)
|
||||
{
|
||||
m_TextFade += Time.deltaTime;
|
||||
m_Text.color = new Color(m_Text.color.r, m_Text.color.g, m_Text.color.b, 1 - (m_TextFade * 2));
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
EmeraldAI.Utility.EmeraldObjectPool.Despawn(m_Text.gameObject);
|
||||
}
|
||||
|
||||
IEnumerator AnimateStationaryText(Text m_Text, Color RegularTextColor, Color CritTextColor, Vector3 TargetPosition, bool CriticalHit, bool HealingText, bool PlayerTakingDamage)
|
||||
{
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
else if (!CriticalHit)
|
||||
{
|
||||
m_Text.color = RegularTextColor;
|
||||
}
|
||||
|
||||
if (HealingText)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.HealingTextColor;
|
||||
m_Text.text = "+" + m_Text.text;
|
||||
}
|
||||
|
||||
if (PlayerTakingDamage)
|
||||
{
|
||||
m_Text.color = m_EmeraldAICombatTextData.PlayerTakeDamageTextColor;
|
||||
m_Text.text = "-" + m_Text.text;
|
||||
|
||||
if (CriticalHit)
|
||||
{
|
||||
m_Text.color = CritTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
float t = 0;
|
||||
float m_TextFade = 0;
|
||||
float RandomXPosition = Random.Range(-1f, 1.1f);
|
||||
RandomXPosition = Mathf.Round(RandomXPosition * 10f) / 10;
|
||||
float RandomYPosition = m_EmeraldAICombatTextData.DefaultHeight;
|
||||
RandomYPosition = Mathf.Round(RandomYPosition * 10f) / 10;
|
||||
float AnimateSmaller = 0;
|
||||
float AnimateLarger = 0;
|
||||
|
||||
while ((t / 1.5f) < 1)
|
||||
{
|
||||
t += Time.deltaTime;
|
||||
Vector3 m_TextPos = TargetPosition + new Vector3(RandomXPosition, RandomYPosition, 0);
|
||||
m_TextPos = Camera.main.WorldToScreenPoint(m_TextPos - Vector3.up * 0.5f);
|
||||
m_TextPos.z = 0.0f;
|
||||
|
||||
if (m_EmeraldAICombatTextData.UseAnimateFontSize == EmeraldCombatTextData.UseAnimateFontSizeEnum.Enabled)
|
||||
{
|
||||
if (t <= 0.15f)
|
||||
{
|
||||
AnimateLarger += Time.deltaTime * 8;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize, (float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, AnimateLarger);
|
||||
}
|
||||
else if (t > 0.15f && t <= 0.3f)
|
||||
{
|
||||
AnimateSmaller += Time.deltaTime * 6;
|
||||
m_Text.fontSize = (int)Mathf.Lerp((float)m_EmeraldAICombatTextData.FontSize + m_EmeraldAICombatTextData.MaxFontSize, (float)m_EmeraldAICombatTextData.FontSize, AnimateSmaller);
|
||||
}
|
||||
}
|
||||
|
||||
if (t > 0.5f)
|
||||
{
|
||||
m_TextFade += Time.deltaTime;
|
||||
m_Text.color = new Color(m_Text.color.r, m_Text.color.g, m_Text.color.b, 1 - (m_TextFade * 2));
|
||||
}
|
||||
|
||||
m_Text.transform.position = m_TextPos;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
EmeraldAI.Utility.EmeraldObjectPool.Despawn(m_Text.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Runtime/Scripts/Managers/CombatTextSystem.cs.meta
Normal file
12
Runtime/Scripts/Managers/CombatTextSystem.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 484765c497187e848a5a48062f0c81ea
|
||||
timeCreated: 1572300755
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Scripts/Managers/Editor.meta
Normal file
8
Runtime/Scripts/Managers/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfc7a11bf676b924a8da3c174b4a4f34
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
951
Runtime/Scripts/Managers/Editor/AnimationViewerManager.cs
Normal file
951
Runtime/Scripts/Managers/Editor/AnimationViewerManager.cs
Normal file
@@ -0,0 +1,951 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class AnimationViewerManager : EditorWindow
|
||||
{
|
||||
public static AnimationViewerManager Instance;
|
||||
|
||||
Color TimelineOutlineColor = new Color(0.7f, 0.7f, 0.7f, 1f);
|
||||
|
||||
public bool UseRootMotion;
|
||||
public int CurrentPreviewAnimationIndex = 0;
|
||||
public int PresetAnimationEventIndex = 0;
|
||||
|
||||
float TimeScale = 1.0f;
|
||||
Vector2 WindowOffset;
|
||||
protected float time = 0.0f;
|
||||
Texture AnimationProfileEditorIcon;
|
||||
Texture PlayButtonIcon;
|
||||
Texture PauseButtonIcon;
|
||||
Texture AnimationEventIcon;
|
||||
public static Vector3 DefaultPosition;
|
||||
public static Vector3 DefaultEuler;
|
||||
bool AnimationIsPlaying;
|
||||
|
||||
bool RootMotionChanged;
|
||||
GameObject CurrentAnimationViewerAI = null;
|
||||
|
||||
AnimationClip PreviewClip = null;
|
||||
List<AnimationClip> PreviewClips = new List<AnimationClip>();
|
||||
List<string> AnimationNames = new List<string>();
|
||||
List<string> AnimationEventNames = new List<string>();
|
||||
List<EmeraldAnimationEventsClass> AnimationEventPresets = new List<EmeraldAnimationEventsClass>();
|
||||
Rect AnimationClipTimelineArea;
|
||||
Rect AnimationClipTimelinePoint;
|
||||
int AnimationEventIndex;
|
||||
int PreviousPreviewAnimationIndex;
|
||||
public AnimationEvent CurrentAnimationEvent;
|
||||
Rect CurrentEventArea;
|
||||
GameObject AnimationPreviewParent;
|
||||
Transform PreviousParent;
|
||||
Vector3 StartingPosition;
|
||||
Vector3 StartingEuler;
|
||||
bool InitializeTimelineMovement;
|
||||
bool InitializeAnimationEventMovement;
|
||||
bool EnableDebugging = false; //Internal Use Only
|
||||
|
||||
[SerializeField]
|
||||
public List<AnimationClip> DuplicateAnimationEvents = new List<AnimationClip>();
|
||||
[SerializeField]
|
||||
public List<AnimationEventElement> CurrentAnimationEvents = new List<AnimationEventElement>();
|
||||
[System.Serializable]
|
||||
public class AnimationEventElement
|
||||
{
|
||||
public AnimationClip Clip;
|
||||
public List<AnimationEvent> AnimationEvents = new List<AnimationEvent>();
|
||||
public bool Modified;
|
||||
|
||||
public AnimationEventElement (AnimationClip m_Clip, List<AnimationEvent> m_AnimationEvents)
|
||||
{
|
||||
Clip = m_Clip;
|
||||
AnimationEvents = new List<AnimationEvent>(m_AnimationEvents);
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (EditorApplication.isPlaying)
|
||||
return;
|
||||
|
||||
if (AnimationProfileEditorIcon == null) AnimationProfileEditorIcon = Resources.Load("Editor Icons/EmeraldDetection") as Texture;
|
||||
if (PlayButtonIcon == null) PlayButtonIcon = Resources.Load("Editor Icons/EmeraldPlayButton") as Texture;
|
||||
if (PauseButtonIcon == null) PauseButtonIcon = Resources.Load("Editor Icons/EmeraldPauseButton") as Texture;
|
||||
if (AnimationEventIcon == null) AnimationEventIcon = Resources.Load("Editor Icons/EmeraldAnimationEvent") as Texture;
|
||||
|
||||
this.minSize = new Vector2(Screen.currentResolution.width / 6f, Screen.currentResolution.height / 1.7f);
|
||||
this.maxSize = new Vector2(Screen.currentResolution.width / 4f, 1500);
|
||||
|
||||
Instance = this;
|
||||
SubscribeCallbacks(); //Subscribe callbacks
|
||||
InitiailizeList();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (EnableDebugging) Debug.Log("OnDisable");
|
||||
UnsubscribeCallbacks(); //Unsubscribe callbacks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe callbacks, which are used for handling the animation preview state when saving, scene changes, and recompiling.
|
||||
/// </summary>
|
||||
void SubscribeCallbacks ()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
EditorSceneManager.sceneSaved += OnSceneSaved;
|
||||
EditorSceneManager.sceneSaving += OnSceneSaving;
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe callbacks, which are used for handling the animation preview state when saving, scene changes, and recompiling.
|
||||
/// </summary>
|
||||
void UnsubscribeCallbacks()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
|
||||
EditorSceneManager.sceneSaved -= OnSceneSaved;
|
||||
EditorSceneManager.sceneSaving -= OnSceneSaving;
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Done through using a OnSceneGUI callback) Used for drawing a GUI at the base of the AI when an Animation Event is triggered.
|
||||
/// This allows the user to focus on the animation and what frame the event is triggerd, rather than on the editor window itself.
|
||||
/// The GUI is change color while the event is within the time frame of triggering.
|
||||
/// </summary>
|
||||
private void OnSceneGUI(SceneView obj)
|
||||
{
|
||||
if (PreviewClip != null)
|
||||
{
|
||||
Vector3 pos = CurrentAnimationViewerAI.transform.position;
|
||||
Color OutLineColor = new Color(0, 0, 0, 1);
|
||||
Color FaceColor = new Color(0.5f, 0.5f, 0.5f, 0.1f);
|
||||
|
||||
Vector3[] verts = new Vector3[]
|
||||
{
|
||||
new Vector3(pos.x - 0.5f, pos.y, pos.z - 0.5f),
|
||||
new Vector3(pos.x - 0.5f, pos.y, pos.z + 0.5f),
|
||||
new Vector3(pos.x + 0.5f, pos.y, pos.z + 0.5f),
|
||||
new Vector3(pos.x + 0.5f, pos.y, pos.z - 0.5f)
|
||||
};
|
||||
|
||||
//Calculate the time so that each Animation Event changes the GUI Handle color when the timeline point is within range.
|
||||
float MouseLerp = (AnimationClipTimelinePoint.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x - 2.5f, AnimationClipTimelineArea.min.x + 1f, MouseLerp);
|
||||
float ModifiedTime = ((AnimationClipTimelinePoint.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length);
|
||||
|
||||
for (int i = 0; i < CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Count; i++)
|
||||
{
|
||||
if (ModifiedTime >= (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time - 0.005f) && ModifiedTime <= (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time + 0.005f))
|
||||
{
|
||||
OutLineColor = new Color(0, 0, 0f, 1);
|
||||
FaceColor = new Color(0.5f, 1f, 0.5f, 0.15f);
|
||||
}
|
||||
|
||||
}
|
||||
Handles.DrawSolidRectangleWithOutline(verts, FaceColor, OutLineColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A callback for detecting before a scene has been saved. This is used to disable the animation preview state so it isn't included with the save.
|
||||
/// </summary>
|
||||
private void OnSceneSaving(UnityEngine.SceneManagement.Scene scene, string path)
|
||||
{
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
SetAnimatorStates(true);
|
||||
DeparentPreviewObject();
|
||||
if (EnableDebugging) Debug.Log("OnSceneSaving");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A callback for detecting when a scene has been saved. This is used to reapply the animation preview state so it wasn't included with the save.
|
||||
/// </summary>
|
||||
void OnSceneSaved(UnityEngine.SceneManagement.Scene scene)
|
||||
{
|
||||
SetAnimatorStates(false);
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
AnimationMode.StartAnimationMode();
|
||||
|
||||
if (GameObject.Find("Animation Viewer Parent") == null)
|
||||
{
|
||||
AnimationPreviewParent = new GameObject("Animation Viewer Parent");
|
||||
Selection.activeObject = AnimationPreviewParent;
|
||||
AnimationPreviewParent.transform.position = CurrentAnimationViewerAI.transform.position;
|
||||
AnimationPreviewParent.transform.eulerAngles = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
CurrentAnimationViewerAI.transform.SetParent(AnimationPreviewParent.transform);
|
||||
}
|
||||
|
||||
if (EnableDebugging) Debug.Log("OnSceneSaved");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display the ApplyChanges menu. If the user selects apply, write all changes to all modified animation clips. If the user reverts, discard all changes and close the editor window.
|
||||
/// </summary>
|
||||
void DisplayApplyChangesMenu (GameObject G = null)
|
||||
{
|
||||
if (G != CurrentAnimationViewerAI && CurrentAnimationEvents.Any(x => x.Modified == true))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Unapplied Changes Detected", "Changes have not been applied on: \n" + CurrentAnimationViewerAI.name + "\n\nWould you like to apply your changes?", "Apply", "Revert"))
|
||||
{
|
||||
ApplyChanges(false);
|
||||
if (EnableDebugging) Debug.Log("OnChangesAppliedMenuMessage");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EnableDebugging) Debug.Log("OnChangesRevertedMenuMessage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfirmDiscardingMessage ()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Discard Changes?", "Are you sure you would like to discard your changes, this process cannot be undone?", "Yes", "No"))
|
||||
{
|
||||
Initialize(CurrentAnimationViewerAI);
|
||||
CurrentAnimationEvent = null;
|
||||
if (EnableDebugging) Debug.Log("OnConfirmDiscardingMessageYes");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EnableDebugging) Debug.Log("OnConfirmDiscardingMessageNo");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable animation sampling before compiling to stop AI from getting stuck in the animation sampling state.
|
||||
/// </summary>
|
||||
public void OnBeforeAssemblyReload()
|
||||
{
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
|
||||
SetAnimatorStates(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-enable animation sampling after compiling to put an AI back in the animation sampling state.
|
||||
/// </summary>
|
||||
public void OnAfterAssemblyReload()
|
||||
{
|
||||
SetAnimatorStates(false);
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
AnimationMode.StartAnimationMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the editor window and animation sampling before entering play mode to stop AI from getting stuck in the animation sampling state.
|
||||
/// </summary>
|
||||
void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
this.Close();
|
||||
if (EnableDebugging) Debug.Log("OnPlayModeChanged");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Editor Window lists with their needed data.
|
||||
/// </summary>
|
||||
void InitiailizeList ()
|
||||
{
|
||||
//Get all of the commonly used Emerald AI Event so they can be automatically added through an enum.
|
||||
AnimationEventPresets = AnimationEventInitializer.GetEmeraldAnimationEvents();
|
||||
|
||||
//Add each animation event display name and add it to a list so it can be displayed through an enum.
|
||||
for (int i = 0; i < AnimationEventPresets.Count; i++)
|
||||
{
|
||||
AnimationEventNames.Add(AnimationEventPresets[i].eventDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Editor Window with the data from the Emerald AI Animation Editor and its Animation Profile.
|
||||
/// </summary>
|
||||
public void Initialize (GameObject G)
|
||||
{
|
||||
DisplayApplyChangesMenu(G); //Ask to save changes before initializing another AI.
|
||||
|
||||
SetAnimatorStates(false); //The Animator has to be set to Always Animate to avoid bug. Can be set back to default after closing.
|
||||
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
|
||||
DeparentPreviewObject(); //Deparent the previous AI, given that one exists, before initializing a new one.
|
||||
CurrentAnimationViewerAI = G; //Cache the current object the user is editing.
|
||||
|
||||
//Store the starting position and euler angles on initialization.
|
||||
StartingPosition = CurrentAnimationViewerAI.transform.position;
|
||||
StartingEuler = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
|
||||
ParentPreviewObject(); //Create a temporary parent object to house the AI in while the user is previewing its animations. This allows Root Motion animations to play at the starting position and not at the (0,0,0) position.
|
||||
|
||||
InitializeAnimationData(); //Initialize the animation enum with all animations form the current AI's Animation Profile.
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
AnimationMode.StartAnimationMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the animation enum with all the animations from its source.
|
||||
/// </summary>
|
||||
void InitializeAnimationData ()
|
||||
{
|
||||
PreviewClips.Clear();
|
||||
AnimationNames.Clear();
|
||||
CurrentAnimationEvents.Clear();
|
||||
var m_AnimationProfile = CurrentAnimationViewerAI.GetComponent<EmeraldAnimation>().m_AnimationProfile;
|
||||
|
||||
AssignAnimationNames(m_AnimationProfile.NonCombatAnimations, ""); //Non-Combat
|
||||
AssignAnimationNames(m_AnimationProfile.Type1Animations, "Type 1 -"); //Type 1
|
||||
AssignAnimationNames(m_AnimationProfile.Type2Animations, "Type 2 -"); //Type 2
|
||||
|
||||
//Cancel the Animation Viewer Manager if there are no detected animations.
|
||||
if (CurrentAnimationEvents.Count == 0)
|
||||
{
|
||||
Close();
|
||||
if (EditorUtility.DisplayDialog("No Animations", "The attached Animation Profile doesn't have any animations. Press the 'Edit Animation Profile' button to add some animations.", "Okay"))
|
||||
{
|
||||
Selection.activeGameObject = CurrentAnimationViewerAI;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign the names of each animation using reflection.
|
||||
/// </summary>
|
||||
void AssignAnimationNames (AnimationParentClass AnimationCategory, string AnimationCategoryName)
|
||||
{
|
||||
foreach (var field in AnimationCategory.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if (AnimationCategory.GetType().GetField(field.Name.ToString()).FieldType.ToString() == "System.Collections.Generic.List`1[EmeraldAI.AnimationClass]")
|
||||
{
|
||||
List<AnimationClass> m_AnimationClass = (List<AnimationClass>)AnimationCategory.GetType().GetField(field.Name.ToString()).GetValue(AnimationCategory);
|
||||
|
||||
for (int i = 0; i < m_AnimationClass.Count; i++)
|
||||
{
|
||||
AnimationClip m_AnimationClip = m_AnimationClass[i].AnimationClip;
|
||||
|
||||
if (m_AnimationClip != null) // && !PreviewClips.Contains(m_AnimationClip)
|
||||
{
|
||||
CurrentAnimationEvents.Add(new AnimationEventElement(m_AnimationClip, m_AnimationClip.events.ToList()));
|
||||
PreviewClips.Add(m_AnimationClip);
|
||||
|
||||
string TempName = field.Name.Replace("List", "");
|
||||
TempName = TempName.Replace("Animation", "");
|
||||
TempName = System.Text.RegularExpressions.Regex.Replace(TempName, "[A-Z]", " $0");
|
||||
TempName = "(" + AnimationCategoryName + TempName + " " + (i + 1) + ")";
|
||||
TempName = TempName.Replace("( ", "(");
|
||||
AnimationNames.Add(m_AnimationClip.name + " - " + TempName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AnimationCategory.GetType().GetField(field.Name.ToString()).FieldType.ToString() == "EmeraldAI.AnimationClass")
|
||||
{
|
||||
AnimationClass m_AnimationClass = (AnimationClass)AnimationCategory.GetType().GetField(field.Name.ToString()).GetValue(AnimationCategory);
|
||||
AnimationClip m_AnimationClip = m_AnimationClass.AnimationClip;
|
||||
|
||||
if (m_AnimationClip != null)
|
||||
{
|
||||
CurrentAnimationEvents.Add(new AnimationEventElement(m_AnimationClip, m_AnimationClip.events.ToList()));
|
||||
PreviewClips.Add(m_AnimationClip);
|
||||
|
||||
string TempName = field.Name.Replace("List", " ");
|
||||
TempName = TempName.Replace("Animation", "");
|
||||
TempName = System.Text.RegularExpressions.Regex.Replace(TempName, "[A-Z]", " $0");
|
||||
TempName = "(" + AnimationCategoryName + TempName + ")";
|
||||
TempName = TempName.Replace("( ", "(");
|
||||
AnimationNames.Add(m_AnimationClip.name + " - " + TempName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the changes to all modified animation clips.
|
||||
/// </summary>
|
||||
void ApplyChanges (bool AnimationModeEnabled)
|
||||
{
|
||||
List<string> PathList = new List<string>();
|
||||
|
||||
//Store all clip file paths that have been modified
|
||||
for (int i = 0; i < CurrentAnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvents[i].Modified)
|
||||
{
|
||||
if (!PathList.Contains(AssetDatabase.GetAssetPath(CurrentAnimationEvents[i].Clip)))
|
||||
{
|
||||
PathList.Add(AssetDatabase.GetAssetPath(CurrentAnimationEvents[i].Clip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < CurrentAnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvents[i].Modified && !DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip))
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(CurrentAnimationEvents[i].Clip);
|
||||
string PathType = AssetImporter.GetAtPath(path).ToString(); //Get the path type, this will help determine whether the clip is from an FBX or an individual animation clip.
|
||||
PathType = PathType.Replace(" ", "");
|
||||
PathType = PathType.Replace("(", "");
|
||||
PathType = PathType.Replace(")", "");
|
||||
|
||||
if (PathType == "UnityEngine.FBXImporter")
|
||||
{
|
||||
var modelImporter = (ModelImporter)AssetImporter.GetAtPath(path) as ModelImporter;
|
||||
SerializedObject so = new SerializedObject(modelImporter);
|
||||
SerializedProperty clips = so.FindProperty("m_ClipAnimations");
|
||||
|
||||
//Set the events equal to the events from the CurrentAnimationEvents for each clip, given that it has been modified.
|
||||
for (int m = 0; m < modelImporter.clipAnimations.Length; m++)
|
||||
{
|
||||
if (clips.GetArrayElementAtIndex(m).displayName == CurrentAnimationEvents[i].Clip.name)
|
||||
{
|
||||
Debug.Log(clips.GetArrayElementAtIndex(m).displayName + "'s Animation Events have been updated");
|
||||
SerializedProperty prop = clips.GetArrayElementAtIndex(m).FindPropertyRelative("events");
|
||||
if (CurrentAnimationEvents[i].AnimationEvents.Count == 0)
|
||||
{
|
||||
prop.ClearArray();
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEvents(clips.GetArrayElementAtIndex(m), CurrentAnimationEvents[i].AnimationEvents.ToArray(), CurrentAnimationEvents[i].Clip);
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentAnimationEvents[i].Modified = false;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Get the single animation clip's path
|
||||
AnimationClip animClip = (AnimationClip)AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip));
|
||||
SerializedObject so = new SerializedObject(animClip);
|
||||
|
||||
if (CurrentAnimationEvents[i].AnimationEvents.Count == 0)
|
||||
{
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log(animClip.name + "'s Animation Events have been updated");
|
||||
SetEventsOnClip(CurrentAnimationEvents[i].AnimationEvents.ToArray(), CurrentAnimationEvents[i].Clip);
|
||||
if (!DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip)) DuplicateAnimationEvents.Add(CurrentAnimationEvents[i].Clip);
|
||||
}
|
||||
|
||||
CurrentAnimationEvents[i].Modified = false;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
else if (DuplicateAnimationEvents.Contains(CurrentAnimationEvents[i].Clip))
|
||||
{
|
||||
CurrentAnimationEvents[i].Modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PathList.Count; i++)
|
||||
{
|
||||
string PathType = AssetImporter.GetAtPath(PathList[i]).ToString();
|
||||
PathType = PathType.Replace(" ", "");
|
||||
PathType = PathType.Replace("(", "");
|
||||
PathType = PathType.Replace(")", "");
|
||||
|
||||
if (PathType == "UnityEngine.FBXImporter")
|
||||
{
|
||||
var modelImporter = (ModelImporter)AssetImporter.GetAtPath(PathList[i]) as ModelImporter;
|
||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(modelImporter));
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
if (!AnimationMode.InAnimationMode() && AnimationModeEnabled)
|
||||
AnimationMode.StartAnimationMode();
|
||||
|
||||
//Remove the focus of the currently selected Animation Event
|
||||
CurrentAnimationEvent = null;
|
||||
CurrentEventArea = new Rect();
|
||||
GUI.FocusControl(null);
|
||||
Repaint();
|
||||
DuplicateAnimationEvents.Clear();
|
||||
}
|
||||
|
||||
// Main editor window
|
||||
public void OnGUI()
|
||||
{
|
||||
GUIStyle Style = EditorStyles.wordWrappedLabel;
|
||||
Style.fontStyle = FontStyle.Bold;
|
||||
Style.fontSize = 16;
|
||||
Style.padding.top = -11;
|
||||
Style.alignment = TextAnchor.UpperCenter;
|
||||
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.BeginVertical("Window");
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.LabelField(new GUIContent(AnimationProfileEditorIcon), Style, GUILayout.ExpandWidth(true), GUILayout.Height(32));
|
||||
EditorGUILayout.LabelField("Animation Viewer", Style, GUILayout.ExpandWidth(true));
|
||||
GUILayout.Space(4);
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window");
|
||||
GUILayout.Space(-18);
|
||||
|
||||
CustomEditorProperties.TextTitleWithDescription("Animation Clip Selection", "Select an animation clip (pulled from the AI's current Animation Profile) to preview the animation on the recntly selected AI right within the scene.", false);
|
||||
CustomEditorProperties.NoticeTextDescription("Note: Inverse Kinematics and animation blending are not used during this process. Animations will have better quality during runtime usage.", true);
|
||||
|
||||
//Populate an enum with the selected AI's current animations.
|
||||
CurrentPreviewAnimationIndex = EditorGUILayout.Popup("Current Animation", CurrentPreviewAnimationIndex, AnimationNames.ToArray());
|
||||
PreviewClip = CurrentAnimationEvents[CurrentPreviewAnimationIndex].Clip;
|
||||
|
||||
if (PreviousPreviewAnimationIndex != CurrentPreviewAnimationIndex)
|
||||
{
|
||||
CurrentAnimationEvent = null;
|
||||
PreviousPreviewAnimationIndex = CurrentPreviewAnimationIndex;
|
||||
}
|
||||
|
||||
GUILayout.Space(15);
|
||||
|
||||
CustomEditorProperties.CustomHelpLabelField("Makes the currently selected object the Current Animation Clip within the Project tab.", false);
|
||||
if (GUILayout.Button("View Current Clip in Project"))
|
||||
{
|
||||
Selection.activeObject = PreviewClip;
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
UseRootMotion = EditorGUILayout.Toggle("Use Root Motion", UseRootMotion);
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window");
|
||||
GUILayout.Space(-18);
|
||||
|
||||
if (PreviewClip != null)
|
||||
{
|
||||
CustomEditorProperties.TextTitleWithDescription("Animation Timeline", "You can select anywhere within the timeline area below to cycle through an animation clip. Pressing the play button will allow the animation to play in real-time, continiously, " +
|
||||
"until the pause button is pressed. You can add preset, and custom, Animation Events by using the Event Type popup. You can hover over the Event Type dropdown for a tooltip description of the currently selected event.", false);
|
||||
GUILayout.Space(2.5f);
|
||||
|
||||
GUIStyle BoldStyle = GUI.skin.button;
|
||||
BoldStyle.fontStyle = FontStyle.Bold;
|
||||
|
||||
if (AnimationIsPlaying)
|
||||
{
|
||||
GUI.backgroundColor = new Color(1.5f, 0.1f, 0f, 0.75f);
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button(new GUIContent(PlayButtonIcon, "Play and pause the Current Animation."), BoldStyle))
|
||||
{
|
||||
AnimationIsPlaying = !AnimationIsPlaying;
|
||||
if (AnimationIsPlaying)
|
||||
PlayButtonIcon = Resources.Load("Editor Icons/EmeraldPauseButton") as Texture;
|
||||
else
|
||||
PlayButtonIcon = Resources.Load("Editor Icons/EmeraldPlayButton") as Texture;
|
||||
}
|
||||
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
//Add Event Button
|
||||
if (GUILayout.Button(new GUIContent(AnimationEventIcon, "Adds the current Event Type at the current frame. This will also apply some needed parameters."), BoldStyle))
|
||||
{
|
||||
GUI.FocusControl(null); //Remove the focus of the currently selected Animation Event
|
||||
var m_event = new AnimationEvent();
|
||||
m_event.functionName = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.functionName;
|
||||
m_event.stringParameter = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.stringParameter;
|
||||
m_event.floatParameter = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.floatParameter;
|
||||
m_event.intParameter = AnimationEventPresets[PresetAnimationEventIndex].animationEvent.intParameter;
|
||||
m_event.time = time + Mathf.Lerp(0.009f, -0.0111f, time / PreviewClip.length);
|
||||
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Add(m_event); //Add the event to the AnimationEvents list
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].Modified = true;
|
||||
|
||||
UpdateIdenticalAnimationClips();
|
||||
|
||||
//Set the CurrentAnimationEvent and other info to the newly created event.
|
||||
CurrentAnimationEvent = m_event;
|
||||
AnimationEventIndex = CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.IndexOf(m_event);
|
||||
Repaint();
|
||||
}
|
||||
|
||||
Rect r2 = EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Space(20);
|
||||
PresetAnimationEventIndex = EditorGUILayout.Popup("", PresetAnimationEventIndex, AnimationEventNames.ToArray(), GUILayout.MinWidth(100));
|
||||
Rect LastRect = GUILayoutUtility.GetLastRect();
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Space(20);
|
||||
|
||||
TimeScale = EditorGUILayout.Slider("", TimeScale, 0.01f, 1f, GUILayout.MaxHeight(0), GUILayout.MinWidth(100));
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUI.LabelField(new Rect((r2.min.x + 45), r2.position.y - 2, (r2.width), 20), new GUIContent("Event Type"));
|
||||
EditorGUI.LabelField(new Rect((r2.min.x + 6), r2.position.y + 18, LastRect.width, 20), new GUIContent("", AnimationEventPresets[PresetAnimationEventIndex].eventDescription));
|
||||
EditorGUI.LabelField(new Rect((r2.max.x - 265) + r2.min.x, r2.position.y - 2, 100, 20), "Playback Speed");
|
||||
EditorGUI.LabelField(new Rect((r2.min.x + r2.width - 40), r2.position.y + 19, (r2.width), 20), System.Math.Round(TimeScale, 2) + "x");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.Space(10);
|
||||
|
||||
float stopTime = PreviewClip.length;
|
||||
Rect r = EditorGUILayout.BeginVertical();
|
||||
WindowOffset.x = 5;
|
||||
var EditedTime = time * PreviewClip.frameRate;
|
||||
var niceTime = Mathf.Floor(EditedTime / PreviewClip.frameRate).ToString("00") + ":" + Mathf.FloorToInt(EditedTime % PreviewClip.frameRate).ToString("00");
|
||||
var ClipPercentage = ((time / stopTime) * 100f).ToString("F2");
|
||||
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x - 3, r.position.y - 3f, r.width - (WindowOffset.x * 2) + 6, 105.5f), TimelineOutlineColor); //Draws the outline around the whole timeline area
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x, r.position.y + 0.25f, r.width - (WindowOffset.x * 2), 100f), new Color(0.3f, 0.3f, 0.3f, 1f)); //Draws the background over the whole timeline area
|
||||
|
||||
AnimationClipTimelineArea = new Rect(r.x + WindowOffset.x - 3, r.position.y - 0.75f, r.width - (WindowOffset.x * 2) + 3, 50f);
|
||||
float AdjustedTime = (time / stopTime);
|
||||
EditorGUI.DrawRect(new Rect((r.x + WindowOffset.x), r.position.y + 0.25f, (r.width - (WindowOffset.x * 2) - 2) * AdjustedTime, 50), new Color(0.1f, 0.25f, 0.5f, 1f)); //Draws the timeline progress
|
||||
|
||||
//---Draw the timeline info---
|
||||
GUIStyle LabelStyle = new GUIStyle();
|
||||
LabelStyle.alignment = TextAnchor.MiddleCenter;
|
||||
LabelStyle.fontStyle = FontStyle.Bold;
|
||||
LabelStyle.normal.textColor = Color.white;
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x , r.position.y + 50f, r.width - (WindowOffset.x * 2), 50f), new Color(0.1f, 0.1f, 0.1f, 1f)); //Draws the background of the timeline
|
||||
EditorGUI.LabelField(new Rect(r.x + WindowOffset.x, r.position.y + 50, r.width - (WindowOffset.x * 2), 50), niceTime + " (" + (ClipPercentage + "%") + ") Frame " + Mathf.Round(time * PreviewClip.frameRate).ToString(), LabelStyle); //Draw Animation Info on the Timeline
|
||||
//---Draw the timeline info---
|
||||
|
||||
//-------------ANIMATION EVENT TIMELINE----------------
|
||||
GUIStyle AnimationEventStyle = GUI.skin.box;
|
||||
|
||||
//Draws a line for each 10% within the Animation Event timeline.
|
||||
for (int i = 1; i <= 60; i++)
|
||||
{
|
||||
EditorGUI.DrawRect(new Rect(((r.x + WindowOffset.x) + (float)(i / 60f) * (r.width - (WindowOffset.x * 2) - (r.min.x * 2.5f))), r.position.y + 37.5f, 1f, 12f), new Color(0.6f, 0.6f, 0.6f, 1f)); //Draws the Animation Event timeline Dashes
|
||||
}
|
||||
|
||||
for (int i = 0; i < CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvent != null && CurrentAnimationEvent.time == CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time)
|
||||
GUI.backgroundColor = new Color(1, 1, 1, 2f);
|
||||
else
|
||||
GUI.backgroundColor = new Color(3, 3, 3, 3);
|
||||
|
||||
//Draw each Animation Event to the Animation Event timeline based on its time within the PreviewAnimation clip.
|
||||
Rect AnimationEventRect = new Rect((WindowOffset.x - AnimationClipTimelineArea.min.x / WindowOffset.x) + (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time / stopTime) * (r.width - (WindowOffset.x - (AnimationClipTimelineArea.min.x / WindowOffset.x) * 2) - (r.min.x)), r.position.y + 19.5f, 7.5f, 30f); //Was 41
|
||||
|
||||
if (CurrentAnimationEvent != null && CurrentAnimationEvent.time == CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i].time)
|
||||
EditorGUI.DrawRect(AnimationEventRect, new Color(1f, 0.25f, 0.25f, 1f));
|
||||
else
|
||||
EditorGUI.DrawRect(AnimationEventRect, Color.white);
|
||||
|
||||
if (AnimationEventRect.Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
|
||||
{
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].Modified = true;
|
||||
CurrentAnimationEvent = CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[i];
|
||||
UpdateIdenticalAnimationClips();
|
||||
AnimationEventIndex = i;
|
||||
CurrentEventArea = AnimationEventRect;
|
||||
InitializeAnimationEventMovement = true;
|
||||
Repaint();
|
||||
GUI.FocusControl(null); //Remove the focus of the currently selected Animation Event
|
||||
}
|
||||
}
|
||||
|
||||
//Draws the timeline point
|
||||
AnimationClipTimelinePoint = new Rect(((r.x + WindowOffset.x) + (time / stopTime) * (r.width - (WindowOffset.x * 2) - (r.min.x))), r.position.y, 3.5f, 50f);
|
||||
EditorGUI.DrawRect(AnimationClipTimelinePoint, new Color(0.8f, 0.8f, 0.8f, 1f));
|
||||
//Draws the timeline point
|
||||
|
||||
ChangeEventTime();
|
||||
|
||||
EditorGUI.DrawRect(new Rect(r.x + WindowOffset.x - 3, r.position.y + 50, r.width - (WindowOffset.x * 2) + 6, 2.5f), TimelineOutlineColor); //Draws the outline around the whole timeline area
|
||||
|
||||
GUI.backgroundColor = Color.white;
|
||||
//-------------ANIMATION EVENT TIMELINE----------------
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(120);
|
||||
|
||||
//-------------ANIMATION EVENT DATA--------------------
|
||||
if (CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.Count > 0 && CurrentAnimationEvent != null)
|
||||
{
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].functionName = EditorGUILayout.TextField("Function Name", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].functionName);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].floatParameter = EditorGUILayout.FloatField("Float", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].floatParameter);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].intParameter = EditorGUILayout.IntField("Int", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].intParameter);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].stringParameter = EditorGUILayout.TextField("String", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].stringParameter);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].objectReferenceParameter = EditorGUILayout.ObjectField("Object", CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].objectReferenceParameter, typeof(object), false);
|
||||
|
||||
GUILayout.Space(25);
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(CurrentAnimationEvents.Count > 0 && !CurrentAnimationEvents.Any(x => x.Modified == true));
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button("Apply Changes", GUILayout.MaxHeight(30)))
|
||||
{
|
||||
ApplyChanges(true);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Discard Changes", GUILayout.MaxHeight(30)))
|
||||
{
|
||||
ConfirmDiscardingMessage();
|
||||
}
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
//-------------ANIMATION EVENT DATA--------------------
|
||||
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update all of the passed AnimationClip's events to be equal to those of the CurrentAnimationEvents (based on its index).
|
||||
/// </summary>
|
||||
public void SetEvents(SerializedProperty sp, AnimationEvent[] newEvents, AnimationClip clip)
|
||||
{
|
||||
SerializedProperty serializedProperty = sp.FindPropertyRelative("events");
|
||||
if (serializedProperty != null && serializedProperty.isArray && newEvents != null && newEvents.Length > 0)
|
||||
{
|
||||
serializedProperty.ClearArray();
|
||||
for (int i = 0; i < newEvents.Length; i++)
|
||||
{
|
||||
AnimationEvent animationEvent = newEvents[i];
|
||||
serializedProperty.InsertArrayElementAtIndex(serializedProperty.arraySize);
|
||||
|
||||
SerializedProperty eventProperty = serializedProperty.GetArrayElementAtIndex(i);
|
||||
eventProperty.FindPropertyRelative("floatParameter").floatValue = animationEvent.floatParameter;
|
||||
eventProperty.FindPropertyRelative("functionName").stringValue = animationEvent.functionName;
|
||||
eventProperty.FindPropertyRelative("intParameter").intValue = animationEvent.intParameter;
|
||||
eventProperty.FindPropertyRelative("objectReferenceParameter").objectReferenceValue = animationEvent.objectReferenceParameter;
|
||||
eventProperty.FindPropertyRelative("data").stringValue = animationEvent.stringParameter;
|
||||
eventProperty.FindPropertyRelative("time").floatValue = animationEvent.time / clip.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a single passed AnimationClip's events to be equal to those of the CurrentAnimationEvents.
|
||||
/// </summary>
|
||||
public void SetEventsOnClip(AnimationEvent[] newEvents, AnimationClip clip)
|
||||
{
|
||||
if (newEvents != null && newEvents.Length > 0)
|
||||
{
|
||||
AnimationUtility.SetAnimationEvents(clip, newEvents);
|
||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(clip));
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Track the mouse input and position to modify animation events on the current animation clip.
|
||||
/// </summary>
|
||||
void ChangeEventTime ()
|
||||
{
|
||||
int controlId = GUIUtility.GetControlID(FocusType.Passive); //Allows Event.current to work outside of the Editor Window.
|
||||
|
||||
var current = Event.current;
|
||||
if (current != null)
|
||||
{
|
||||
switch (current.type)
|
||||
{
|
||||
case EventType.MouseDrag:
|
||||
if (CurrentEventArea != new Rect())
|
||||
{
|
||||
if (InitializeAnimationEventMovement)
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
float MouseLerp = (Event.current.mousePosition.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x - 1.75f, AnimationClipTimelineArea.min.x + 3f, MouseLerp);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents[AnimationEventIndex].time = Mathf.Clamp(((Event.current.mousePosition.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length), 0.015f, PreviewClip.length - 0.015f);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
else if (CurrentEventArea == new Rect() && InitializeTimelineMovement)
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
float MouseLerp = (Event.current.mousePosition.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x + 2.5f, AnimationClipTimelineArea.min.x - 2.5f, MouseLerp);
|
||||
time = Mathf.Clamp(((Event.current.mousePosition.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length), 0, PreviewClip.length);
|
||||
Repaint();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (EnableDebugging) Debug.Log("MouseUp");
|
||||
InitializeTimelineMovement = false;
|
||||
InitializeAnimationEventMovement = false;
|
||||
|
||||
if (CurrentEventArea != new Rect())
|
||||
{
|
||||
UpdateIdenticalAnimationClips();
|
||||
CurrentEventArea = new Rect();
|
||||
Repaint();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDown:
|
||||
if (EnableDebugging) Debug.Log("MouseDown");
|
||||
if (new Rect(AnimationClipTimelineArea.x + 3.5f, AnimationClipTimelineArea.position.y, AnimationClipTimelineArea.width - 6.5f, AnimationClipTimelineArea.height).Contains(current.mousePosition) && CurrentEventArea == new Rect())
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
float MouseLerp = (Event.current.mousePosition.x / AnimationClipTimelineArea.width);
|
||||
float MouseOffset = Mathf.LerpAngle(AnimationClipTimelineArea.min.x + 2.5f, AnimationClipTimelineArea.min.x - 2.5f, MouseLerp);
|
||||
time = ((Event.current.mousePosition.x - MouseOffset) / (AnimationClipTimelineArea.width)) * (PreviewClip.length);
|
||||
InitializeTimelineMovement = true; //The mouse has been detected and pressed within the timeline area. The timeline will continusouly detect movement until the mouse button has been lifted. This is to allow smooth movement outside of the window area.
|
||||
Repaint();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.current.isKey)
|
||||
{
|
||||
KeyCode key = Event.current.keyCode;
|
||||
|
||||
if (key == KeyCode.Delete && CurrentAnimationEvent != null)
|
||||
{
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents.RemoveAt(AnimationEventIndex);
|
||||
CurrentAnimationEvents[CurrentPreviewAnimationIndex].Modified = true;
|
||||
|
||||
UpdateIdenticalAnimationClips();
|
||||
|
||||
Repaint();
|
||||
CurrentAnimationEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update everything while animation sampling is enabled, given that the EditorApplication is not playing.
|
||||
/// </summary>
|
||||
void Update()
|
||||
{
|
||||
if (!EditorApplication.isPlaying && AnimationMode.InAnimationMode() && PreviewClip != null)
|
||||
{
|
||||
//Reset the AI back to the parent's root position when disabling Root Motion.
|
||||
if (UseRootMotion != RootMotionChanged)
|
||||
{
|
||||
CurrentAnimationViewerAI.transform.localPosition = Vector3.zero;
|
||||
CurrentAnimationViewerAI.transform.localEulerAngles = Vector3.zero;
|
||||
RootMotionChanged = UseRootMotion;
|
||||
}
|
||||
|
||||
//Update the timeline when an animation is playing.
|
||||
if (AnimationIsPlaying)
|
||||
{
|
||||
time += Time.deltaTime * TimeScale;
|
||||
if (time >= PreviewClip.length)
|
||||
time = 0;
|
||||
}
|
||||
|
||||
AnimationMode.BeginSampling();
|
||||
DefaultPosition = CurrentAnimationViewerAI.transform.position;
|
||||
DefaultEuler = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
AnimationMode.SampleAnimationClip(CurrentAnimationViewerAI, PreviewClip, time);
|
||||
|
||||
//Keep the AI in-placw when Root Motion is disabled.
|
||||
if (!UseRootMotion)
|
||||
{
|
||||
CurrentAnimationViewerAI.transform.position = DefaultPosition;
|
||||
CurrentAnimationViewerAI.transform.eulerAngles = DefaultEuler;
|
||||
}
|
||||
|
||||
AnimationMode.EndSampling();
|
||||
|
||||
//Always repaint when sampling an animation to have smooth UI
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all states back to their original values and positions when closing the editor window.
|
||||
/// </summary>
|
||||
void OnDestroy()
|
||||
{
|
||||
DisplayApplyChangesMenu();
|
||||
|
||||
if (AnimationMode.InAnimationMode())
|
||||
AnimationMode.StopAnimationMode();
|
||||
|
||||
SetAnimatorStates(true);
|
||||
DeparentPreviewObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update all identical animation clip and their modifications so they are present on all duplicate clips.
|
||||
/// </summary>
|
||||
void UpdateIdenticalAnimationClips ()
|
||||
{
|
||||
for (int i = 0; i < CurrentAnimationEvents.Count; i++)
|
||||
{
|
||||
if (CurrentAnimationEvents[i].Clip == CurrentAnimationEvents[CurrentPreviewAnimationIndex].Clip)
|
||||
{
|
||||
CurrentAnimationEvents[i].AnimationEvents = CurrentAnimationEvents[CurrentPreviewAnimationIndex].AnimationEvents;
|
||||
CurrentAnimationEvents[i].Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deparent the AI from the Parent Holder object. This is used to sample Root Motion animations while retaining the AI's starting position.
|
||||
/// </summary>
|
||||
public void DeparentPreviewObject()
|
||||
{
|
||||
if (CurrentAnimationViewerAI != null)
|
||||
{
|
||||
CurrentAnimationViewerAI.transform.SetParent(PreviousParent);
|
||||
CurrentAnimationViewerAI.transform.position = StartingPosition;
|
||||
CurrentAnimationViewerAI.transform.eulerAngles = StartingEuler;
|
||||
if (AnimationPreviewParent != null && AnimationPreviewParent.transform.childCount == 0)
|
||||
DestroyImmediate(AnimationPreviewParent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a temporary parent object to house the AI in while the user is previewing its animations. This allows Root Motion animations to play at the starting position and not at the (0,0,0) position.
|
||||
/// </summary>
|
||||
public void ParentPreviewObject ()
|
||||
{
|
||||
AnimationPreviewParent = new GameObject("Animation Viewer Parent");
|
||||
Selection.activeObject = AnimationPreviewParent;
|
||||
AnimationPreviewParent.transform.position = CurrentAnimationViewerAI.transform.position;
|
||||
AnimationPreviewParent.transform.eulerAngles = CurrentAnimationViewerAI.transform.eulerAngles;
|
||||
PreviousParent = CurrentAnimationViewerAI.transform.parent;
|
||||
CurrentAnimationViewerAI.transform.SetParent(AnimationPreviewParent.transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Due to a Unity bug, all Animator Controllers have to be disabled within the scene when testing animations using AnimationMode.
|
||||
/// Callbacks for saving, recompiling, and entering play mode are used to avoid unwanted modifications of Animator Controllers' enabled states.
|
||||
/// </summary>
|
||||
public void SetAnimatorStates (bool state)
|
||||
{
|
||||
var AnimatorsInScene = GameObject.FindObjectsOfType<Animator>();
|
||||
for (int i = 0; i < AnimatorsInScene.Length; i++)
|
||||
{
|
||||
AnimatorsInScene[i].enabled = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fe66a7f17c8ff243b2261df11af96d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1250
Runtime/Scripts/Managers/Editor/EmeraldAIDuplicatorManager.cs
Normal file
1250
Runtime/Scripts/Managers/Editor/EmeraldAIDuplicatorManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6080e363cf6770841b6e8b8abb36ce61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
244
Runtime/Scripts/Managers/Editor/EmeraldCombatTextManager.cs
Normal file
244
Runtime/Scripts/Managers/Editor/EmeraldCombatTextManager.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldCombatTextManager : EditorWindow
|
||||
{
|
||||
EmeraldCombatTextData m_EmeraldAICombatTextData;
|
||||
GUIStyle TitleStyle;
|
||||
Texture CombatTextIcon;
|
||||
int CurrentTab = 0;
|
||||
|
||||
SerializedObject serializedObject;
|
||||
SerializedProperty TextFont;
|
||||
SerializedProperty CombatTextState;
|
||||
SerializedProperty PlayerTextColor;
|
||||
SerializedProperty PlayerCritTextColor;
|
||||
SerializedProperty PlayerTakeDamageTextColor;
|
||||
SerializedProperty AITextColor;
|
||||
SerializedProperty AICritTextColor;
|
||||
SerializedProperty HealingTextColor;
|
||||
SerializedProperty FontSize;
|
||||
SerializedProperty MaxFontSize;
|
||||
SerializedProperty AnimationType;
|
||||
SerializedProperty CombatTextTargets;
|
||||
SerializedProperty OutlineEffect;
|
||||
SerializedProperty UseAnimateFontSize;
|
||||
SerializedProperty TextHeight;
|
||||
|
||||
[MenuItem("Window/Emerald AI/Combat Text Manager #%c", false, 200)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow APS = EditorWindow.GetWindow(typeof(EmeraldCombatTextManager), false, "Combat Text Manager");
|
||||
APS.minSize = new Vector2(600f, 650);
|
||||
}
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
m_EmeraldAICombatTextData = (EmeraldCombatTextData)Resources.Load("Combat Text Data") as EmeraldCombatTextData;
|
||||
if (CombatTextIcon == null) CombatTextIcon = Resources.Load("Editor Icons/EmeraldCTM") as Texture;
|
||||
|
||||
serializedObject = new SerializedObject(m_EmeraldAICombatTextData);
|
||||
TextFont = serializedObject.FindProperty("TextFont");
|
||||
CombatTextState = serializedObject.FindProperty("CombatTextState");
|
||||
PlayerTextColor = serializedObject.FindProperty("PlayerTextColor");
|
||||
PlayerCritTextColor = serializedObject.FindProperty("PlayerCritTextColor");
|
||||
PlayerTakeDamageTextColor = serializedObject.FindProperty("PlayerTakeDamageTextColor");
|
||||
AITextColor = serializedObject.FindProperty("AITextColor");
|
||||
AICritTextColor = serializedObject.FindProperty("AICritTextColor");
|
||||
HealingTextColor = serializedObject.FindProperty("HealingTextColor");
|
||||
FontSize = serializedObject.FindProperty("FontSize");
|
||||
MaxFontSize = serializedObject.FindProperty("MaxFontSize");
|
||||
AnimationType = serializedObject.FindProperty("AnimationType");
|
||||
CombatTextTargets = serializedObject.FindProperty("CombatTextTargets");
|
||||
OutlineEffect = serializedObject.FindProperty("OutlineEffect");
|
||||
UseAnimateFontSize = serializedObject.FindProperty("UseAnimateFontSize");
|
||||
TextHeight = serializedObject.FindProperty("DefaultHeight");
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Top Left Side Indent
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUI.backgroundColor = Color.white;
|
||||
TitleStyle = CustomEditorProperties.UpdateTitleStyle();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(32, 32));
|
||||
EditorGUILayout.LabelField(new GUIContent(" " + "Combat Text Manager", CombatTextIcon), TitleStyle);
|
||||
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Top Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Bottom Left Side Indent
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUILayout.Space(-18);
|
||||
CustomEditorProperties.TextTitleWithDescription("Combat Text Manager", "With the Combat Text Manager, you can globally handle all combat text settings such as text size, text font, text color, text animation, and more.", true);
|
||||
|
||||
|
||||
GUIContent[] CombatTextManagerButtons = new GUIContent[2] { new GUIContent("Combat Text Settings"), new GUIContent("Combat Text Colors") };
|
||||
CurrentTab = GUILayout.Toolbar(CurrentTab, CombatTextManagerButtons, EditorStyles.miniButton, GUILayout.Height(25));
|
||||
|
||||
CombatTextSettings();
|
||||
ColorSettings();
|
||||
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Bottom Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (!Application.isPlaying && GUI.changed)
|
||||
{
|
||||
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
||||
}
|
||||
}
|
||||
|
||||
void CombatTextSettings()
|
||||
{
|
||||
if (CurrentTab == 0)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.2f, 0.2f, 0.2f, 0.25f);
|
||||
EditorGUILayout.BeginVertical("Box");
|
||||
EditorGUILayout.LabelField("Combat Text Settings", EditorStyles.boldLabel);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndVertical();
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(CombatTextState, new GUIContent("Combat Text State"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the whether the Combat Text System is enabled or disabled.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(TextHeight, new GUIContent("Combat Text Height"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the overall height the Combat Text will spawn above targets.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(CombatTextTargets, new GUIContent("Combat Text Targets"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls the target types that will be able to have the Combat Text displayed.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(OutlineEffect, new GUIContent("Use Outline Effect"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls the whether the Combat Text System will use a text outline effect.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(AnimationType, new GUIContent("Animation Type"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls the Combat Text's Animation Type.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
CustomEditorProperties.CustomObjectField(new Rect(), new GUIContent(), TextFont, "Text Font", typeof(Font), true);
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's font.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
CustomEditorProperties.CustomIntSlider(new Rect(), new GUIContent(), FontSize, "Font Size", 10, 50);
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the size of the Combat Text's font.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(UseAnimateFontSize, new GUIContent("Use Animate Font"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.LabelField("Controls whether or not the font size is animated.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (m_EmeraldAICombatTextData.UseAnimateFontSize == EmeraldCombatTextData.UseAnimateFontSizeEnum.Enabled)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
CustomEditorProperties.CustomIntSlider(new Rect(), new GUIContent(), MaxFontSize, "Animated Font Size", 1, 30);
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("Controls the additional size of the Combat Text's font when Animate Size is enabled. After the text has been animated, it will return to the Font Size.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColorSettings ()
|
||||
{
|
||||
if (CurrentTab == 1)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.2f, 0.2f, 0.2f, 0.25f);
|
||||
EditorGUILayout.BeginVertical("Box");
|
||||
EditorGUILayout.LabelField("Combat Text Colors", EditorStyles.boldLabel);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndVertical();
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(PlayerTextColor, new GUIContent("Player Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's color when used by the player.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(PlayerTakeDamageTextColor, new GUIContent("Take Damage Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's color when a player takes damage.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(PlayerCritTextColor, new GUIContent("Player Crit Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's critical hit color when used by the player.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(AITextColor, new GUIContent("AI Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's AI color when used between other AI.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(AICritTextColor, new GUIContent("AI Crit Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's critical hit color when used between other AI.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(HealingTextColor, new GUIContent("Healing Color"));
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox("The Combat Text's color when used for healing.", MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de35206336289c2498f7bae2e0c416f3
|
||||
timeCreated: 1517957035
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
151
Runtime/Scripts/Managers/Editor/EmeraldFactionManager.cs
Normal file
151
Runtime/Scripts/Managers/Editor/EmeraldFactionManager.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldFactionManager : EditorWindow
|
||||
{
|
||||
GUIStyle TitleStyle;
|
||||
Texture FactionIcon;
|
||||
Vector2 scrollPos;
|
||||
SerializedObject serializedObject;
|
||||
ReorderableList FactionList;
|
||||
bool RefreshInspector;
|
||||
|
||||
[MenuItem("Window/Emerald AI/Faction Manager #%r", false, 200)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow APS = EditorWindow.GetWindow(typeof(EmeraldFactionManager), false, "Faction Manager");
|
||||
APS.minSize = new Vector2(300f, 300f);
|
||||
}
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (FactionIcon == null) FactionIcon = Resources.Load("FactionExtension") as Texture;
|
||||
LoadFactionData();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.update += OnEditorUpdate;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.update -= OnEditorUpdate;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is needed (using callback from EditorApplication.update) to reliably update the faction enums within an AI's editor, given factions are being changed while an AI is selected.
|
||||
/// </summary>
|
||||
protected virtual void OnEditorUpdate()
|
||||
{
|
||||
if (RefreshInspector)
|
||||
{
|
||||
UpdateStaticFactionData();
|
||||
RefreshInspector = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Top Left Side Indent
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUI.backgroundColor = Color.white;
|
||||
TitleStyle = CustomEditorProperties.UpdateTitleStyle();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(32, 32));
|
||||
EditorGUILayout.LabelField(new GUIContent(" " + "Faction Manager", FactionIcon), TitleStyle);
|
||||
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Top Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Bottom Left Side Indent
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUILayout.Space(-18);
|
||||
CustomEditorProperties.TextTitleWithDescription("Faction Manager", "With the Faction Manager, you can create factions that your AI will use to identify targets. " +
|
||||
"Factions created here will be globally available for all Emerald AI agents to use. You can assign factions through an AI's Detection component within the Faction Settings foldout.", true);
|
||||
|
||||
FactionList.DoLayoutList();
|
||||
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
GUILayout.Space(15); //Bottom Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void UpdateStaticFactionData ()
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(Resources.Load("Faction Data"));
|
||||
EmeraldFactionData FactionData = (EmeraldFactionData)AssetDatabase.LoadAssetAtPath(path, typeof(EmeraldFactionData));
|
||||
EmeraldDetection.StringFactionList = new List<string>(FactionData.FactionNameList);
|
||||
FactionExtension.StringFactionList = new List<string>(FactionData.FactionNameList);
|
||||
//Repaint();
|
||||
|
||||
EditorUtility.SetDirty(FactionData);
|
||||
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
||||
InternalEditorUtility.RepaintAllViews();
|
||||
SceneView.RepaintAll();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void LoadFactionData()
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(Resources.Load("Faction Data"));
|
||||
EmeraldFactionData FactionData = (EmeraldFactionData)AssetDatabase.LoadAssetAtPath(path, typeof(EmeraldFactionData));
|
||||
serializedObject = new SerializedObject(FactionData);
|
||||
|
||||
FactionList = new ReorderableList(serializedObject, serializedObject.FindProperty("FactionNameList"), false, true, true, true);
|
||||
FactionList.drawHeaderCallback = rect =>
|
||||
{
|
||||
EditorGUI.LabelField(rect, "Faction List", EditorStyles.boldLabel);
|
||||
};
|
||||
FactionList.drawElementCallback =
|
||||
(Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var element = FactionList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
FactionList.elementHeight = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.LabelField(new Rect(rect.x, rect.y + 3f, rect.width, EditorGUIUtility.singleLineHeight), "Faction " + (index + 1));
|
||||
EditorGUI.PropertyField(new Rect(rect.x + 75, rect.y + 3f, rect.width - 75, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
RefreshInspector = true;
|
||||
UpdateStaticFactionData();
|
||||
}
|
||||
};
|
||||
|
||||
FactionList.onChangedCallback = (FactionList) =>
|
||||
{
|
||||
UpdateStaticFactionData();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5254bc5a28e5a584c82d06e89faa02d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
465
Runtime/Scripts/Managers/Editor/EmeraldSetupManager.cs
Normal file
465
Runtime/Scripts/Managers/Editor/EmeraldSetupManager.cs
Normal file
@@ -0,0 +1,465 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldSetupManager : EditorWindow
|
||||
{
|
||||
public GameObject ObjectToSetup;
|
||||
public LayerMask AILayer = 4;
|
||||
public string AITag = "Untagged";
|
||||
public List<GameObject> ObjectsToSetup = new List<GameObject>();
|
||||
|
||||
//Required
|
||||
public bool m_EmeraldSystem = true;
|
||||
public bool m_EmeraldAnimation = true;
|
||||
public bool m_EmeraldCombat = true;
|
||||
public bool m_EmeraldSounds = true;
|
||||
public bool m_EmeraldMovement = true;
|
||||
public bool m_EmeraldHealth = true;
|
||||
public bool m_EmeraldBehaviors = true;
|
||||
public bool m_EmeraldDetection = true;
|
||||
|
||||
//Optional
|
||||
public bool m_EmeraldEvents = false;
|
||||
public bool m_EmeraldItems = false;
|
||||
public bool m_EmeraldInverseKinematics = false;
|
||||
public bool m_EmeraldOptimization = false;
|
||||
public bool m_EmeraldSoundDetector = false;
|
||||
public bool m_EmeraldUI = false;
|
||||
public bool m_EmeraldFootsteps = false;
|
||||
public bool m_EmeraldDebugger = false;
|
||||
public bool m_TargetPositionModifier = false;
|
||||
|
||||
public AnimationProfile m_AnimationProfile;
|
||||
public EmeraldSoundProfile m_SoundProfile;
|
||||
|
||||
public AnimatorTypeState AnimatorType = AnimatorTypeState.RootMotion;
|
||||
public enum AnimatorTypeState { RootMotion, NavMeshDriven }
|
||||
|
||||
GUIStyle TitleStyle;
|
||||
Vector2 scrollPos;
|
||||
Texture SettingsIcon;
|
||||
bool DisplayConfirmation = false;
|
||||
bool IsInScene;
|
||||
static bool DontShowDisplayConfirmation = false;
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Emerald AI/Setup Manager #%e", false, 200)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow APS = EditorWindow.GetWindow(typeof(EmeraldSetupManager), false, "Setup Manager");
|
||||
APS.minSize = new Vector2(300, 250f); //500
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (SettingsIcon == null) SettingsIcon = Resources.Load("Editor Icons/EmeraldSetupManager") as Texture;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (ObjectToSetup != null) IsInScene = ObjectToSetup.scene.IsValid();
|
||||
else IsInScene = true;
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUI.backgroundColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Top Left Side Indent
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUI.backgroundColor = Color.white;
|
||||
TitleStyle = CustomEditorProperties.UpdateTitleStyle();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(32, 32));
|
||||
EditorGUILayout.LabelField(new GUIContent(" " + "Setup Manager", SettingsIcon), TitleStyle);
|
||||
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(15); //Top Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(15); //Bottom Left Side Indent
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(45));
|
||||
GUILayout.Space(-18);
|
||||
CustomEditorProperties.TextTitleWithDescription("Setup Settings", "Set the settings this AI wil be setup with. If you do not assign an Animation Profile or Sound Profile here, you will need to do so through their editors after the setup process has been complete.", true);
|
||||
|
||||
if (ObjectToSetup == null)
|
||||
{
|
||||
GUI.backgroundColor = new Color(10f, 0.0f, 0.0f, 0.25f);
|
||||
EditorGUILayout.LabelField("This field cannot be left blank.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
ObjectToSetup = (GameObject)EditorGUILayout.ObjectField("Object to Setup", ObjectToSetup, typeof(GameObject), true);
|
||||
if (!IsInScene) CustomEditorProperties.DisplayImportantMessage("The Setup Manager will not work with objects from the Project Tab. Please ensure your object is within the Scene before assigning it to the Setup Manager.");
|
||||
DescriptionElement("The object that the Setup Manager will create an AI with.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
||||
AITag = EditorGUILayout.TagField("Tag for AI", AITag);
|
||||
DescriptionElement("The Unity Tag that will be applied to your AI. Note: Untagged cannot be used.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
AILayer = EditorGUILayout.LayerField("Layer for AI", AILayer);
|
||||
DescriptionElement("The Unity Layer that will be applied to your AI. Note: Default cannot be used.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
||||
m_AnimationProfile = (AnimationProfile)EditorGUILayout.ObjectField("Animation Profile", m_AnimationProfile, typeof(AnimationProfile), false);
|
||||
DescriptionElement("(Optional) You can assign an Animation Profile to this AI that will be applied during the setup process.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
m_SoundProfile = (EmeraldSoundProfile)EditorGUILayout.ObjectField("Sound Profile", m_SoundProfile, typeof(EmeraldSoundProfile), false);
|
||||
DescriptionElement("(Optional) You can assign an Sound Profile to this AI that will be applied during the setup process.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
|
||||
AnimatorType = (AnimatorTypeState)EditorGUILayout.EnumPopup("Animator Type", AnimatorType);
|
||||
DescriptionElement("Controls whether this AI will use Root Motion or NavMesh for its movement and speed.");
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
//Displays the toggle settings that control which components are added to an object.
|
||||
DisplayToggleOptions();
|
||||
|
||||
if (ObjectToSetup == null)
|
||||
{
|
||||
GUI.backgroundColor = new Color(10f, 0.0f, 0.0f, 0.25f);
|
||||
EditorGUILayout.LabelField("You must have an object applied to the AI Object slot before you can complete the setup process.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
if (!IsInScene)
|
||||
{
|
||||
GUI.backgroundColor = new Color(10f, 0.0f, 0.0f, 0.25f);
|
||||
EditorGUILayout.LabelField("The Setup Manager will not work with objects from the Project Tab. Please ensure your object is within the Scene before assigning it as the Object to Setup.", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(ObjectToSetup == null || !IsInScene);
|
||||
if (GUILayout.Button("Setup AI", GUILayout.Height(25)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Emerald Setup Manager", "Are you sure you'd like to setup an AI on this object?", "Setup", "Cancel"))
|
||||
{
|
||||
UnpackPrefab(ObjectToSetup);
|
||||
InitializeSetup();
|
||||
}
|
||||
}
|
||||
GUILayout.Space(25);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
GUILayout.Space(15); //Bottom Right Side Indent
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (DisplayConfirmation && !DontShowDisplayConfirmation)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Setup Manager", "Your AI has been successfully created. Several components still need to be configured, which is indicated by a warning message at the top some components. " +
|
||||
"These warning messages will tell you how to configure the components that need it.", "Okay", "Okay, Don't Show Again"))
|
||||
{
|
||||
DisplayConfirmation = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayConfirmation = false;
|
||||
DontShowDisplayConfirmation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpack the passed GameObject if it is a prefab.
|
||||
/// </summary>
|
||||
void UnpackPrefab(GameObject ObjectToUnpack)
|
||||
{
|
||||
PrefabAssetType m_AssetType = PrefabUtility.GetPrefabAssetType(ObjectToUnpack);
|
||||
|
||||
//Only unpack prefab if the ObjectToUnpack is a prefab.
|
||||
if (m_AssetType != PrefabAssetType.NotAPrefab)
|
||||
{
|
||||
PrefabUtility.UnpackPrefabInstance(ObjectToUnpack, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the toggle settings that control which components are added to an object.
|
||||
/// </summary>
|
||||
void DisplayToggleOptions()
|
||||
{
|
||||
if (position.width > 500)
|
||||
{
|
||||
//Required
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
RequiredSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
//Optional
|
||||
GUILayout.Space(10);
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
OptionalSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.Space(15);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
RequiredSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
GUI.backgroundColor = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
EditorGUILayout.BeginVertical("Window", GUILayout.Height(50));
|
||||
GUILayout.Space(-18);
|
||||
OptionalSettings();
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
|
||||
void RequiredSettings()
|
||||
{
|
||||
CustomEditorProperties.TextTitleWithDescription("(Required Components)", "The components that are required to make Emerald AI run. " +
|
||||
"Enabled components will automatically be added to the assigned object during the setting up process. Each component has its own editor with various settings related to its functionality. You can hover over the ? icon for a tooltip description of each component.", true);
|
||||
m_EmeraldSystem = ToggleComponentElement(true, "Emerald System", "The base Emerald AI script that controls the management and updating off all other required scripts.");
|
||||
m_EmeraldAnimation = ToggleComponentElement(true, "Emerald Animation", "Handles the management of an AI's animations through Animation Profiles.");
|
||||
m_EmeraldCombat = ToggleComponentElement(true, "Emerald Combat", "Gives AI the ability to engage in combat, setup an AI's abilities, and other combat related settings.");
|
||||
m_EmeraldSounds = ToggleComponentElement(true, "Emerald Sounds", "Handles the management of an AI's sounds through Sound Profiles.");
|
||||
m_EmeraldMovement = ToggleComponentElement(true, "Emerald Movement", "Controls various movement, turn, and alignement related settings.");
|
||||
m_EmeraldHealth = ToggleComponentElement(true, "Emerald Health", "Gives AI the ability to take damage. Users can access this component through the IDamageable interface component.");
|
||||
m_EmeraldBehaviors = ToggleComponentElement(true, "Emerald Behaviors", "Gives AI the ability to use preset behaviors. Users can create custom behavior script derived from the EmeraldBehavior class, if desired.");
|
||||
m_EmeraldDetection = ToggleComponentElement(true, "Emerald Detection", "Gives AI the ability to see and detect targets.");
|
||||
}
|
||||
|
||||
void OptionalSettings ()
|
||||
{
|
||||
CustomEditorProperties.TextTitleWithDescription("(Optional Components)", "The components that add optional features to Emerald AI. " +
|
||||
"Enabled components will automatically be added to the assigned object during the setting up process. Each component has its own editor with various settings related to its functionality. You can hover over the ? icon for a tooltip description of each component.", true);
|
||||
m_EmeraldEvents = ToggleComponentElement(m_EmeraldEvents, "Emerald Events", "Adds the ability for users to create custom events for most of an AI's actions.", false);
|
||||
m_EmeraldItems = ToggleComponentElement(m_EmeraldItems, "Emerald Items", "Adds the ability for users to setup droppable items, equippable items, and more on their AI.", false);
|
||||
m_EmeraldInverseKinematics = ToggleComponentElement(m_EmeraldInverseKinematics, "Emerald Inverse Kinematics", "Adds the ability for AI to use Inverse Kinematics using Unity's Animation Rigging system.", false);
|
||||
m_EmeraldOptimization = ToggleComponentElement(m_EmeraldOptimization, "Emerald Optimization", "Adds the ability for AI to be disabled when culled or not in view of the camera. This increases performance by disabling AI that are not currently seen.", false);
|
||||
m_EmeraldUI = ToggleComponentElement(m_EmeraldUI, "Emerald UI", "Adds the ability for AI to use Emerald AI's built-in UI system which can display health bars and an AI's name above their head.", false);
|
||||
m_EmeraldFootsteps = ToggleComponentElement(m_EmeraldFootsteps, "Emerald Footsteps", "Adds the ability to play footstep sounds and effects based on the detected surfaces. Surfaces can be customized through Footstep Surface Objects. " +
|
||||
"Both textures from Unity Terrains and tags from game objects can be used.\n\nNote: Footstep Animation Events are required for footsteps to trigger. Ensure that these have been set up on your AI's movement animations.", false);
|
||||
m_EmeraldSoundDetector = ToggleComponentElement(m_EmeraldSoundDetector, "Emerald Sound Detector", "Adds the ability for AI to hear player targets and other sounds made by external sources.", false);
|
||||
m_EmeraldDebugger = ToggleComponentElement(m_EmeraldDebugger, "Emerald Debugger", "Adds the ability for AI to use and display debugging information (debug logs, debug lines, pathfinding information, etc). This can help with development or identifying possible issues.", false);
|
||||
m_TargetPositionModifier = ToggleComponentElement(m_TargetPositionModifier, "Target Position Modifier", "Adds the ability to modify the target height of targets allowing AI agents to target the modified position, which can improve targeting accuracy.", false);
|
||||
}
|
||||
|
||||
void InitializeSetup()
|
||||
{
|
||||
if (ObjectToSetup != null && ObjectToSetup.GetComponent<EmeraldSystem>() == null && ObjectToSetup.GetComponent<Animator>() != null)
|
||||
{
|
||||
AssignComponents();
|
||||
SetupBoxCollider();
|
||||
SetupAudio();
|
||||
SetupTagsAndLayers();
|
||||
SetupMovement();
|
||||
AutoFindHeadTransform();
|
||||
|
||||
if (!DontShowDisplayConfirmation)
|
||||
{
|
||||
DisplayConfirmation = true;
|
||||
}
|
||||
|
||||
ObjectToSetup = null;
|
||||
}
|
||||
else if (ObjectToSetup == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Emerald Setup Manager - Oops!", "There is no object assigned to the AI Object slot. Please assign one and try again.", "Okay");
|
||||
return;
|
||||
}
|
||||
else if (ObjectToSetup.GetComponent<EmeraldSystem>() != null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Emerald Setup Manager - Oops!", "There is already an Emerald AI component on this object. Please choose another object to apply an Emerald AI component to.", "Okay");
|
||||
return;
|
||||
}
|
||||
else if (ObjectToSetup.GetComponent<Animator>() == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Emerald Setup Manager - Oops!", "There is no Animator component attached to your AI. Please assign one and try again.", "Okay");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns and order the assigned componenets.
|
||||
/// </summary>
|
||||
void AssignComponents()
|
||||
{
|
||||
ObjectToSetup.AddComponent<EmeraldSystem>();
|
||||
if (m_EmeraldEvents) ObjectToSetup.AddComponent<EmeraldEvents>();
|
||||
if (m_EmeraldItems) ObjectToSetup.AddComponent<EmeraldItems>();
|
||||
if (m_EmeraldInverseKinematics) ObjectToSetup.AddComponent<EmeraldInverseKinematics>();
|
||||
if (m_EmeraldOptimization) ObjectToSetup.AddComponent<EmeraldOptimization>();
|
||||
if (m_EmeraldUI) ObjectToSetup.AddComponent<EmeraldUI>();
|
||||
if (m_EmeraldFootsteps) ObjectToSetup.AddComponent<EmeraldFootsteps>();
|
||||
if (m_EmeraldSoundDetector) ObjectToSetup.AddComponent<SoundDetection.EmeraldSoundDetector>();
|
||||
if (m_EmeraldDebugger) ObjectToSetup.AddComponent<EmeraldDebugger>();
|
||||
if (m_TargetPositionModifier) ObjectToSetup.AddComponent<TargetPositionModifier>();
|
||||
|
||||
if (m_AnimationProfile != null) ObjectToSetup.GetComponent<EmeraldAnimation>().m_AnimationProfile = m_AnimationProfile;
|
||||
if (m_SoundProfile != null) ObjectToSetup.GetComponent<EmeraldSounds>().SoundProfile = m_SoundProfile;
|
||||
|
||||
MoveToBottom(ObjectToSetup.GetComponent<EmeraldSystem>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<Animator>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<BoxCollider>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<UnityEngine.AI.NavMeshAgent>());
|
||||
MoveToBottom(ObjectToSetup.GetComponent<AudioSource>());
|
||||
}
|
||||
|
||||
void MoveToBottom (Component ComponentToMove)
|
||||
{
|
||||
Component[] AllComponents = ObjectToSetup.GetComponents<Component>();
|
||||
|
||||
for (int i = 0; i < AllComponents.Length; i++)
|
||||
{
|
||||
UnityEditorInternal.ComponentUtility.MoveComponentDown(ComponentToMove);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's movement as some settings are dependent on Root Motion.
|
||||
/// </summary>
|
||||
void SetupMovement ()
|
||||
{
|
||||
EmeraldMovement EmeraldMovement = ObjectToSetup.GetComponent<EmeraldMovement>();
|
||||
EmeraldMovement.MovementType = (EmeraldMovement.MovementTypes)AnimatorType;
|
||||
if (EmeraldMovement.MovementType == EmeraldMovement.MovementTypes.RootMotion)
|
||||
{
|
||||
EmeraldMovement.StationaryTurningSpeedCombat = 30;
|
||||
EmeraldMovement.StationaryTurningSpeedNonCombat = 30;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's user set tags and layers.
|
||||
/// </summary>
|
||||
void SetupTagsAndLayers ()
|
||||
{
|
||||
ObjectToSetup.tag = AITag;
|
||||
ObjectToSetup.layer = AILayer;
|
||||
ObjectToSetup.GetComponent<EmeraldDetection>().DetectionLayerMask = (1 << LayerMask.NameToLayer("Water"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to automatically find the AI's head transform.
|
||||
/// </summary>
|
||||
void AutoFindHeadTransform ()
|
||||
{
|
||||
foreach (Transform root in ObjectToSetup.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (i < root.childCount && root.GetChild(i).name == "root" || i < root.childCount && root.GetChild(i).name == "Root" || i < root.childCount && root.GetChild(i).name == "ROOT") //Only look in the root transform - 3 child index in
|
||||
{
|
||||
foreach (Transform t in root.GetChild(i).GetComponentsInChildren<Transform>())
|
||||
{
|
||||
if (t.name.Contains("head") || t.name.Contains("Head") || t.name.Contains("HEAD")) //Look for the word head within all transforms within the root transform
|
||||
{
|
||||
ObjectToSetup.GetComponent<EmeraldDetection>().HeadTransform = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If no head transform was found, the model may not have a root named bone, try again with less conditions.
|
||||
if (ObjectToSetup.GetComponent<EmeraldDetection>().HeadTransform == null)
|
||||
{
|
||||
foreach (Transform t in ObjectToSetup.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
//Ignore transforms with a MultiAimConstraint as these can be for the Animation Rigging system/constraint.
|
||||
if (t.GetComponent<UnityEngine.Animations.Rigging.MultiAimConstraint>() == null)
|
||||
{
|
||||
if (t.name.Contains("head") || t.name.Contains("Head") || t.name.Contains("HEAD")) //Look for the word head within all transforms within all the transforms
|
||||
{
|
||||
ObjectToSetup.GetComponent<EmeraldDetection>().HeadTransform = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's audio and its defaul AudioSource settings.
|
||||
/// </summary>
|
||||
void SetupAudio ()
|
||||
{
|
||||
ObjectToSetup.GetComponent<AudioSource>().spatialBlend = 1;
|
||||
ObjectToSetup.GetComponent<AudioSource>().dopplerLevel = 0;
|
||||
ObjectToSetup.GetComponent<AudioSource>().rolloffMode = AudioRolloffMode.Linear;
|
||||
ObjectToSetup.GetComponent<AudioSource>().minDistance = 1;
|
||||
ObjectToSetup.GetComponent<AudioSource>().maxDistance = 50;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the AI's default box collider based on its main renderer's bounds.
|
||||
/// </summary>
|
||||
void SetupBoxCollider ()
|
||||
{
|
||||
List<SkinnedMeshRenderer> TempSkinnedMeshes = new List<SkinnedMeshRenderer>();
|
||||
List<float> TempSkinnedMeshBounds = new List<float>();
|
||||
|
||||
foreach (SkinnedMeshRenderer SMR in ObjectToSetup.GetComponentsInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
if (!TempSkinnedMeshes.Contains(SMR))
|
||||
{
|
||||
TempSkinnedMeshes.Add(SMR);
|
||||
TempSkinnedMeshBounds.Add(SMR.bounds.size.sqrMagnitude);
|
||||
}
|
||||
}
|
||||
|
||||
float m_LargestBounds = TempSkinnedMeshBounds.Max();
|
||||
var AIRenderer = TempSkinnedMeshes[TempSkinnedMeshBounds.IndexOf(m_LargestBounds)];
|
||||
|
||||
ObjectToSetup.GetComponent<BoxCollider>().size = new Vector3(AIRenderer.bounds.size.x / 3 / ObjectToSetup.transform.localScale.y, AIRenderer.bounds.size.y / ObjectToSetup.transform.localScale.y, AIRenderer.bounds.size.z / 3 / ObjectToSetup.transform.localScale.y);
|
||||
ObjectToSetup.GetComponent<BoxCollider>().center = new Vector3(ObjectToSetup.GetComponent<BoxCollider>().center.x, ObjectToSetup.GetComponent<BoxCollider>().size.y / 2, ObjectToSetup.GetComponent<BoxCollider>().center.z);
|
||||
}
|
||||
|
||||
bool ToggleComponentElement (bool Setting, string Name, string Description, bool RequiredComponent = true)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(RequiredComponent);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
var textDimensions = GUI.skin.label.CalcSize(new GUIContent(Name));
|
||||
Setting = EditorGUILayout.ToggleLeft(new GUIContent(Name), Setting, GUILayout.ExpandWidth(false), GUILayout.Width(textDimensions.x + 13.5f));
|
||||
EditorGUILayout.LabelField(new GUIContent(EditorGUIUtility.IconContent("_Help").image, Description), GUILayout.Width(25));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
GUILayout.Space(5);
|
||||
return Setting;
|
||||
}
|
||||
|
||||
void DescriptionElement (string DescriptionText)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.19f);
|
||||
EditorGUILayout.HelpBox(DescriptionText, MessageType.None, true);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Runtime/Scripts/Managers/Editor/EmeraldSetupManager.cs.meta
Normal file
12
Runtime/Scripts/Managers/Editor/EmeraldSetupManager.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01f95d3fc726cc24ab4a91e0176a9614
|
||||
timeCreated: 1507164265
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
191
Runtime/Scripts/Managers/Editor/EmeraldWikiManager.cs
Normal file
191
Runtime/Scripts/Managers/Editor/EmeraldWikiManager.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
public class EmeraldWikiManager : EditorWindow
|
||||
{
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Home", false, 250)]
|
||||
public static void Home()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Getting Started", false, 250)]
|
||||
public static void GettingStarted()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/getting-started/getting-started");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Upgrading to URP or HDRP", false, 251)]
|
||||
public static void UpgradingToURPAndHDRP()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/getting-started/upgrading-to-urp-and-hdrp");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Animation Component", false, 300)]
|
||||
public static void AnimationComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/animation-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Behaviors Component", false, 300)]
|
||||
public static void BehaviorsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/behaviors-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Combat Component", false, 300)]
|
||||
public static void CombatComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/combat-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Detection Component", false, 300)]
|
||||
public static void DetectionComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/detection-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Movement Component", false, 300)]
|
||||
public static void MovementComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/movement-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Health Component", false, 300)]
|
||||
public static void HealthComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/health-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Sounds Component", false, 300)]
|
||||
public static void SoundsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/sounds-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Debugger Component", false, 400)]
|
||||
public static void DebuggerComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/debugger-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Events Component", false, 400)]
|
||||
public static void EventsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/events-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Inverse Kinematics Component", false, 400)]
|
||||
public static void IKComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/inverse-kinematics-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Items Component", false, 400)]
|
||||
public static void ItemsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/items-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Location Based Damage Component", false, 400)]
|
||||
public static void LBDComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/location-based-damage-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Optimization Component", false, 400)]
|
||||
public static void OptimizationComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/optimization-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Sound Detector Component", false, 400)]
|
||||
public static void SoundDetectorComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/sound-detector-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Target Position Modifier Component", false, 400)]
|
||||
public static void TPMComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/target-position-modifier-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/UI Component", false, 400)]
|
||||
public static void UIComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-required/ui-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Offical Emerald AI Wiki/Weapon Collisions Component", false, 400)]
|
||||
public static void WeaponCollisionsComponent()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/emerald-components-optional/weapon-collisions-component");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/FPS Engine", false, 251)]
|
||||
public static void FPSEngineIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/fps-engine");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Invector", false, 252)]
|
||||
public static void InvectorIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/invector");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Final IK", false, 253)]
|
||||
public static void FinalIKIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/final-ik");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Dialogue System", false, 253)]
|
||||
public static void DialogueSystemIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/dialogue-system");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Quest Machine", false, 254)]
|
||||
public static void QuestMachineIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/quest-machine");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Integrations/Love Hate", false, 255)]
|
||||
public static void LoveHateIntegration()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/integrations/integrations/love-hate");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Support/AI Generated Solutions", false, 253)]
|
||||
public static void AIGeneratedSolutions()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/help/using-the-wiki-search-tool");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Support/Solutions to Possible Issues", false, 253)]
|
||||
public static void SolutionsToPossibleIssues()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/help/solutions-to-possible-issues");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Support/Contact", false, 253)]
|
||||
public static void ContactSupport()
|
||||
{
|
||||
Application.OpenURL("https://black-horizon-studios.gitbook.io/emerald-ai-wiki/help/support");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Emerald AI/Report a Bug", false, 300)]
|
||||
public static void ReportBug()
|
||||
{
|
||||
Application.OpenURL("https://github.com/Black-Horizon-Studios/Emerald-AI-2024/issues");
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Runtime/Scripts/Managers/Editor/EmeraldWikiManager.cs.meta
Normal file
12
Runtime/Scripts/Managers/Editor/EmeraldWikiManager.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea76c308fe3a2484cb2a751af4029708
|
||||
timeCreated: 1507164265
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
531
Runtime/Scripts/Managers/EmeraldCombatManager.cs
Normal file
531
Runtime/Scripts/Managers/EmeraldCombatManager.cs
Normal file
@@ -0,0 +1,531 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace EmeraldAI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles all Emerald AI combat related functionality.
|
||||
/// </summary>
|
||||
public static class EmeraldCombatManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the generation of all Emerald AI attacks.
|
||||
/// </summary>
|
||||
public static void GenerateAttack(EmeraldSystem EmeraldComponent, AttackClass SentAttackClass, bool OverridePickType = false)
|
||||
{
|
||||
if (SentAttackClass.AttackDataList.Count > 0)
|
||||
{
|
||||
List<AttackClass.AttackData> AvailableAttacks = new List<AttackClass.AttackData>();
|
||||
|
||||
//Go through the attack list and get all cooled down attacks. Note: This is ignored for the Ordered Pick Type.
|
||||
for (int i = 0; i < SentAttackClass.AttackDataList.Count; i++)
|
||||
{
|
||||
SentAttackClass.AttackDataList[i].CooldownIgnored = false; //Reset before checking cooldown again
|
||||
|
||||
float CooldownTime = 0;
|
||||
if (SentAttackClass.AttackDataList[i].AbilityObject != null) CooldownTime = (SentAttackClass.AttackDataList[i].CooldownTimeStamp + SentAttackClass.AttackDataList[i].AbilityObject.CooldownSettings.CooldownLength);
|
||||
|
||||
if (Time.time >= CooldownTime || SentAttackClass.AttackDataList[i].CooldownTimeStamp == 0 || SentAttackClass.AttackDataList[i].AbilityObject != null && !SentAttackClass.AttackDataList[i].AbilityObject.CooldownSettings.Enabled)
|
||||
{
|
||||
if (!AvailableAttacks.Contains(SentAttackClass.AttackDataList[i]))
|
||||
{
|
||||
AvailableAttacks.Add(SentAttackClass.AttackDataList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If there are no available attacks, ignore the cooldowns and pick a random ability to avoid an error.
|
||||
if (AvailableAttacks.Count == 0 && SentAttackClass.AttackPickType != AttackPickTypes.Order)
|
||||
{
|
||||
SetAttackValues(EmeraldComponent, SentAttackClass.AttackDataList[Random.Range(0, SentAttackClass.AttackDataList.Count)], true);
|
||||
Debug.Log("All cooldowns are busy with the: '" + EmeraldComponent.gameObject.name + "' AI so the a random attack within its Attack List was used. To avoid this, add more attacks or decrease some of this AI's cooldowns times within the Ability Objects.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SentAttackClass.AttackPickType == AttackPickTypes.Odds) //Pick an ability by using the odds for each available ability
|
||||
{
|
||||
List<float> OddsList = new List<float>();
|
||||
for (int i = 0; i < AvailableAttacks.Count; i++)
|
||||
{
|
||||
OddsList.Add(AvailableAttacks[i].AttackOdds);
|
||||
}
|
||||
int OddsIndex = (int)GenerateProbability(OddsList.ToArray());
|
||||
SetAttackValues(EmeraldComponent, AvailableAttacks[OddsIndex], false);
|
||||
}
|
||||
else if (SentAttackClass.AttackPickType == AttackPickTypes.Order) //Pick an ability by going through the list in order
|
||||
{
|
||||
SetAttackValues(EmeraldComponent, SentAttackClass.AttackDataList[SentAttackClass.AttackListIndex], true);
|
||||
|
||||
SentAttackClass.AttackListIndex++;
|
||||
if (SentAttackClass.AttackListIndex == SentAttackClass.AttackDataList.Count) SentAttackClass.AttackListIndex = 0;
|
||||
}
|
||||
else if (SentAttackClass.AttackPickType == AttackPickTypes.Random) //Pick a random ability from the list
|
||||
{
|
||||
int RandomIndex = Random.Range(0, AvailableAttacks.Count);
|
||||
SetAttackValues(EmeraldComponent, AvailableAttacks[RandomIndex], false);
|
||||
}
|
||||
|
||||
//Overrides the current pick type by picking the closest attack. So far, this is done to force a random attack from the AI's attack list.
|
||||
if (OverridePickType)
|
||||
{
|
||||
//TODO: This section will be improved with an update.
|
||||
/*
|
||||
float min = SentAttackClass.AttackDataList.Min(attack => attack.TooCloseDistance);
|
||||
var ClosestAttacks = SentAttackClass.AttackDataList.Where(attack => attack.TooCloseDistance == min).ToList();
|
||||
var RandomCloseAttack = ClosestAttacks[Random.Range(0, ClosestAttacks.Count)];
|
||||
Debug.Log(EmeraldComponent.name + " " + ClosestAttacks.Count);
|
||||
int AttackIndex = SentAttackClass.AttackDataList.FindIndex(attack => attack == RandomCloseAttack);
|
||||
SentAttackClass.AttackListIndex = AttackIndex;
|
||||
EmeraldComponent.CombatComponent.CurrentEmeraldAIAbility = SentAttackClass.AttackDataList[AttackIndex].AbilityObject;
|
||||
EmeraldComponent.CombatComponent.CurrentAnimationIndex = SentAttackClass.AttackDataList[AttackIndex].AttackAnimation;
|
||||
SetAttackValues(EmeraldComponent, AvailableAttacks[AttackIndex], true);
|
||||
*/
|
||||
|
||||
int RandomIndex = Random.Range(0, AvailableAttacks.Count);
|
||||
SetAttackValues(EmeraldComponent, AvailableAttacks[RandomIndex], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the data of the currently generated attack.
|
||||
/// </summary>
|
||||
static void SetAttackValues(EmeraldSystem EmeraldComponent, AttackClass.AttackData AttackData, bool CooldownIgnored)
|
||||
{
|
||||
EmeraldComponent.CombatComponent.CurrentAttackData = AttackData;
|
||||
|
||||
AttackData.CooldownIgnored = CooldownIgnored;
|
||||
|
||||
EmeraldComponent.CombatComponent.CurrentAnimationIndex = AttackData.AttackAnimation;
|
||||
EmeraldComponent.CombatComponent.CurrentEmeraldAIAbility = AttackData.AbilityObject;
|
||||
|
||||
EmeraldComponent.CombatComponent.AttackDistance = AttackData.AttackDistance;
|
||||
//Don't allow the attack distance from being higher than the detection distance.
|
||||
if (EmeraldComponent.CombatComponent.AttackDistance > EmeraldComponent.DetectionComponent.DetectionRadius) EmeraldComponent.CombatComponent.AttackDistance = EmeraldComponent.DetectionComponent.DetectionRadius;
|
||||
EmeraldComponent.CombatComponent.TooCloseDistance = AttackData.TooCloseDistance;
|
||||
if (EmeraldComponent.CombatComponent.CombatState) EmeraldComponent.m_NavMeshAgent.stoppingDistance = EmeraldComponent.CombatComponent.AttackDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the next attack using the current attack class and the AI's Pick Type.
|
||||
/// </summary>
|
||||
public static void GenerateNextAttack (EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type1)
|
||||
GenerateAttack(EmeraldComponent, EmeraldComponent.CombatComponent.Type1Attacks);
|
||||
else if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type2)
|
||||
GenerateAttack(EmeraldComponent, EmeraldComponent.CombatComponent.Type2Attacks);
|
||||
|
||||
EmeraldComponent.AnimationComponent.AttackingTracker = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the currently generated attack with the closest attack within an AI's Attack List.
|
||||
/// </summary>
|
||||
public static void GenerateClosestAttack(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
EmeraldComponent.AnimationComponent.AttackingTracker = false;
|
||||
|
||||
if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type1)
|
||||
GenerateAttack(EmeraldComponent, EmeraldComponent.CombatComponent.Type1Attacks, true);
|
||||
else if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type2)
|
||||
GenerateAttack(EmeraldComponent, EmeraldComponent.CombatComponent.Type2Attacks, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's current attack and weapon transforms based on the sent AttackTransformName from an EmeraldAttackEvent Animation Event.
|
||||
/// This is done by searching for a matching name within the Attack Transform list. If no match is found, the AI's head transfrom will be used instead.
|
||||
/// </summary>
|
||||
public static void UpdateAttackTransforms (EmeraldSystem EmeraldComponent, string AttackTransformName)
|
||||
{
|
||||
if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type1)
|
||||
{
|
||||
Transform AttackTransform = EmeraldComponent.CombatComponent.WeaponType1AttackTransforms.Find(x => x != null && x.name == AttackTransformName);
|
||||
if (AttackTransform != null)
|
||||
{
|
||||
EmeraldComponent.CombatComponent.CurrentAttackTransform = AttackTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
EmeraldComponent.CombatComponent.CurrentAttackTransform = EmeraldComponent.DetectionComponent.HeadTransform;
|
||||
}
|
||||
}
|
||||
else if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type2)
|
||||
{
|
||||
Transform AttackTransform = EmeraldComponent.CombatComponent.WeaponType2AttackTransforms.Find(x => x.name == AttackTransformName);
|
||||
if (AttackTransform != null)
|
||||
{
|
||||
EmeraldComponent.CombatComponent.CurrentAttackTransform = AttackTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
EmeraldComponent.CombatComponent.CurrentAttackTransform = EmeraldComponent.DetectionComponent.HeadTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the weapon transforms based on the sent AttackTransformName from an EmeraldChargeAttack Animation Event.
|
||||
/// This is done by searching for a matching name within the Attack Transform list. If no match is found, the EmeraldChargeAttack event will not fire.
|
||||
/// </summary>
|
||||
public static Transform GetAttackTransform(EmeraldSystem EmeraldComponent, string AttackTransformName)
|
||||
{
|
||||
Transform WeaponTransform = null;
|
||||
|
||||
if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type1)
|
||||
{
|
||||
WeaponTransform = EmeraldComponent.CombatComponent.WeaponType1AttackTransforms.Find(x => x != null && x.name == AttackTransformName);
|
||||
}
|
||||
else
|
||||
{
|
||||
WeaponTransform = EmeraldComponent.CombatComponent.WeaponType2AttackTransforms.Find(x => x.name == AttackTransformName);
|
||||
}
|
||||
|
||||
return WeaponTransform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the AI's weapon type swap time.
|
||||
/// </summary>
|
||||
public static void ResetWeaponSwapTime (EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
EmeraldComponent.CombatComponent.SwitchWeaponTime = Random.Range((float)EmeraldComponent.CombatComponent.SwitchWeaponTimeMin, (float)EmeraldComponent.CombatComponent.SwitchWeaponTimeMax + 1);
|
||||
EmeraldComponent.CombatComponent.SwitchWeaponTimer = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the AI's Combat State.
|
||||
/// </summary>
|
||||
public static void ActivateCombatState(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.CombatComponent.CombatState)
|
||||
return;
|
||||
|
||||
EmeraldComponent.AIAnimator.ResetTrigger("Hit");
|
||||
EmeraldComponent.CombatComponent.CombatState = true;
|
||||
EmeraldComponent.AIAnimator.SetBool("Idle Active", false);
|
||||
EmeraldComponent.AIAnimator.SetBool("Combat State Active", true);
|
||||
EmeraldComponent.MovementComponent.CurrentMovementState = EmeraldMovement.MovementStates.Run;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables an AI's components (called when an AI dies).
|
||||
/// </summary>
|
||||
public static void DisableComponents(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.GetComponent<SoundDetection.EmeraldSoundDetector>() != null)
|
||||
EmeraldComponent.GetComponent<SoundDetection.EmeraldSoundDetector>().enabled = false;
|
||||
|
||||
if (EmeraldComponent.OptimizationComponent != null && EmeraldComponent.OptimizationComponent.m_VisibilityCheck != null)
|
||||
{
|
||||
EmeraldComponent.OptimizationComponent.enabled = false;
|
||||
EmeraldComponent.OptimizationComponent.m_VisibilityCheck.enabled = false;
|
||||
}
|
||||
|
||||
EmeraldComponent.CombatComponent.ExitCombat();
|
||||
EmeraldComponent.AIBoxCollider.enabled = false;
|
||||
EmeraldComponent.DetectionComponent.enabled = false;
|
||||
EmeraldComponent.AnimationComponent.enabled = false;
|
||||
EmeraldComponent.CombatComponent.enabled = false;
|
||||
EmeraldComponent.MovementComponent.enabled = false;
|
||||
EmeraldComponent.BehaviorsComponent.enabled = false;
|
||||
EmeraldComponent.m_NavMeshAgent.ResetPath();
|
||||
EmeraldComponent.m_NavMeshAgent.enabled = false;
|
||||
EmeraldComponent.StartCoroutine(AlignAIOnDeath(EmeraldComponent)); //Align the AI on death, even if the alignment feature is disabled.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aligns the AI when the AI dies.
|
||||
/// </summary>
|
||||
static IEnumerator AlignAIOnDeath (EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type1 && EmeraldComponent.AnimationComponent.m_AnimationProfile.Type1Animations.DeathList.Count == 0 ||
|
||||
EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type2 && EmeraldComponent.AnimationComponent.m_AnimationProfile.Type2Animations.DeathList.Count == 0)
|
||||
yield break;
|
||||
|
||||
Vector3 SurfaceNormal = Vector3.zero;
|
||||
|
||||
while (EmeraldComponent.AIAnimator.enabled)
|
||||
{
|
||||
RaycastHit HitDown;
|
||||
if (Physics.Raycast(new Vector3(EmeraldComponent.transform.position.x, EmeraldComponent.transform.position.y + 0.25f, EmeraldComponent.transform.position.z), -Vector3.up, out HitDown, 2f, EmeraldComponent.MovementComponent.AlignmentLayerMask))
|
||||
{
|
||||
if (HitDown.transform != EmeraldComponent.transform)
|
||||
{
|
||||
float m_MaxNormalAngle = EmeraldComponent.MovementComponent.MaxNormalAngle * 0.01f;
|
||||
SurfaceNormal = HitDown.normal;
|
||||
SurfaceNormal.x = Mathf.Clamp(SurfaceNormal.x, -m_MaxNormalAngle, m_MaxNormalAngle);
|
||||
SurfaceNormal.z = Mathf.Clamp(SurfaceNormal.z, -m_MaxNormalAngle, m_MaxNormalAngle);
|
||||
}
|
||||
}
|
||||
|
||||
EmeraldComponent.transform.rotation = Quaternion.Slerp(EmeraldComponent.transform.rotation, Quaternion.FromToRotation(EmeraldComponent.transform.up, SurfaceNormal) * EmeraldComponent.transform.rotation, Time.deltaTime * 5);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enables an AI's components (called when an AI is reset).
|
||||
/// </summary>
|
||||
public static void EnableComponents(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.GetComponent<SoundDetection.EmeraldSoundDetector>() != null)
|
||||
EmeraldComponent.GetComponent<SoundDetection.EmeraldSoundDetector>().enabled = true;
|
||||
|
||||
if (EmeraldComponent.OptimizationComponent != null && EmeraldComponent.OptimizationComponent.m_VisibilityCheck != null)
|
||||
{
|
||||
EmeraldComponent.OptimizationComponent.enabled = false;
|
||||
EmeraldComponent.OptimizationComponent.m_VisibilityCheck.enabled = true;
|
||||
}
|
||||
|
||||
if (EmeraldComponent.GetComponent<EmeraldInverseKinematics>() != null) EmeraldComponent.GetComponent<EmeraldInverseKinematics>().EnableInverseKinematics();
|
||||
if (EmeraldComponent.LBDComponent != null) EmeraldComponent.LBDComponent.ResetLBDComponents();
|
||||
if (EmeraldComponent.ItemsComponent != null) EmeraldComponent.ItemsComponent.ResetSettings();
|
||||
EmeraldComponent.m_NavMeshAgent.enabled = true;
|
||||
EmeraldComponent.AIBoxCollider.enabled = true;
|
||||
EmeraldComponent.DetectionComponent.enabled = true;
|
||||
EmeraldComponent.AnimationComponent.enabled = true;
|
||||
EmeraldComponent.CombatComponent.enabled = true;
|
||||
EmeraldComponent.MovementComponent.enabled = true;
|
||||
EmeraldComponent.BehaviorsComponent.enabled = true;
|
||||
EmeraldComponent.AIAnimator.enabled = true;
|
||||
EmeraldComponent.AIAnimator.Rebind();
|
||||
EmeraldComponent.AnimationComponent.InitializeWeaponTypeAnimationAndSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable colliders and rigidbodies, given they're detected.
|
||||
/// </summary>
|
||||
public static void DisableRagdoll(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
//Return if a LocationBasedDamage component is detected as colliders need to stay active for this feature.
|
||||
if (EmeraldComponent.GetComponent<LocationBasedDamage>() != null)
|
||||
return;
|
||||
|
||||
foreach (Rigidbody R in EmeraldComponent.GetComponentsInChildren<Rigidbody>())
|
||||
{
|
||||
R.isKinematic = true;
|
||||
}
|
||||
|
||||
if (EmeraldComponent.LBDComponent != null)
|
||||
return;
|
||||
|
||||
foreach (Collider C in EmeraldComponent.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
C.enabled = false;
|
||||
}
|
||||
|
||||
EmeraldComponent.GetComponent<BoxCollider>().enabled = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enable colliders and rigidbodies inside the AI, given they're detected.
|
||||
/// </summary>
|
||||
public static void EnableRagdoll(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
//Only enable the ragdoll components if the current weapon type death animation lists don't have animations in them.
|
||||
if (EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type1 && EmeraldComponent.AnimationComponent.m_AnimationProfile.Type1Animations.DeathList.Count > 0 ||
|
||||
EmeraldComponent.CombatComponent.CurrentWeaponType == EmeraldCombat.WeaponTypes.Type2 && EmeraldComponent.AnimationComponent.m_AnimationProfile.Type2Animations.DeathList.Count > 0)
|
||||
return;
|
||||
|
||||
EmeraldComponent.AIAnimator.enabled = false;
|
||||
|
||||
if (EmeraldComponent.LBDComponent == null)
|
||||
{
|
||||
foreach (Collider C in EmeraldComponent.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
if (C.transform != EmeraldComponent.transform)
|
||||
{
|
||||
C.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Rigidbody R in EmeraldComponent.GetComponentsInChildren<Rigidbody>())
|
||||
{
|
||||
R.isKinematic = false;
|
||||
R.useGravity = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < EmeraldComponent.LBDComponent.ColliderList.Count; i++)
|
||||
{
|
||||
if (EmeraldComponent.LBDComponent.ColliderList[i].ColliderObject != null)
|
||||
EmeraldComponent.LBDComponent.ColliderList[i].ColliderObject.enabled = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < EmeraldComponent.LBDComponent.ColliderList.Count; i++)
|
||||
{
|
||||
Rigidbody ColliderRigidbody = EmeraldComponent.LBDComponent.ColliderList[i].ColliderObject.GetComponent<Rigidbody>();
|
||||
|
||||
if (ColliderRigidbody != null)
|
||||
{
|
||||
ColliderRigidbody.isKinematic = false;
|
||||
ColliderRigidbody.useGravity = true;
|
||||
}
|
||||
|
||||
EmeraldComponent.LBDComponent.ColliderList[i].ColliderObject.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
EmeraldComponent.StartCoroutine(AddRagdollForceInternal(EmeraldComponent)); //Add force to the ragdoll after enable its components
|
||||
}
|
||||
|
||||
static IEnumerator AddRagdollForceInternal(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
float t = 0;
|
||||
//Used to provide the force to the ragdoll in the opposite direction of the last target to hit the AI.
|
||||
Transform LastAttacker = EmeraldComponent.CombatComponent.LastAttacker;
|
||||
float Force = EmeraldComponent.CombatComponent.ReceivedRagdollForceAmount;
|
||||
Rigidbody m_Rigidbody = null;
|
||||
|
||||
//Use the HeadTransform for the default rigidbody force on death.
|
||||
if (EmeraldComponent.CombatComponent.RagdollTransform == null) m_Rigidbody = EmeraldComponent.DetectionComponent.HeadTransform.GetComponent<Rigidbody>();
|
||||
//If the RagdollTransform is not null, add the force for the ragdoll death to this as an AI was damaged through a LBD area.
|
||||
else m_Rigidbody = EmeraldComponent.CombatComponent.RagdollTransform.GetComponent<Rigidbody>();
|
||||
|
||||
if (m_Rigidbody != null && LastAttacker != null)
|
||||
{
|
||||
while (t < 0.2f)
|
||||
{
|
||||
t += Time.fixedDeltaTime;
|
||||
m_Rigidbody.AddForce((EmeraldComponent.transform.position - LastAttacker.position).normalized * Force + (Vector3.up * Force * 0.05f), ForceMode.Acceleration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the conditions are right to allow the AI to trigger an attack.
|
||||
/// </summary>
|
||||
public static bool AllowedToAttack(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.DetectionComponent.TargetObstructed || EmeraldComponent.CombatComponent.DeathDelayActive || EmeraldComponent.MovementComponent.DefaultMovementPaused || EmeraldComponent.AnimationComponent.InternalHit || EmeraldComponent.AnimationComponent.InternalDodge || EmeraldComponent.AnimationComponent.InternalBlock) return false;
|
||||
if (!WithinStoppingDistanceOfTarget(EmeraldComponent) || !WithinDistanceOfTarget(EmeraldComponent)) return false;
|
||||
if (EmeraldComponent.AIAnimator.GetBool("Hit") || EmeraldComponent.AIAnimator.GetBool("Strafe Active") || EmeraldComponent.AIAnimator.GetBool("Dodge Triggered") || EmeraldComponent.AIAnimator.GetBool("Blocking") || EmeraldComponent.AnimationComponent.IsSwitchingWeapons || EmeraldComponent.AnimationComponent.IsBackingUp || EmeraldComponent.AnimationComponent.IsBlocking || EmeraldComponent.AnimationComponent.IsAttacking || EmeraldComponent.AnimationComponent.IsRecoiling || EmeraldComponent.AnimationComponent.IsStrafing || EmeraldComponent.AnimationComponent.IsDodging || EmeraldComponent.AnimationComponent.IsGettingHit) return false;
|
||||
if (!EmeraldComponent.CombatComponent.TargetWithinAngleLimit() || EmeraldComponent.CurrentTargetInfo.CurrentIDamageable.Health <= 0) return false;
|
||||
//The AI has passed all checks and can trigger an attack.
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the AI is within stopping distance of its current target (using Nav Mesh remainingDistance).
|
||||
/// </summary>
|
||||
static bool WithinStoppingDistanceOfTarget(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
return (EmeraldComponent.m_NavMeshAgent.remainingDistance <= EmeraldComponent.m_NavMeshAgent.stoppingDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the AI is within stopping distance of its current target (using Vector3.Distance).
|
||||
/// </summary>
|
||||
static bool WithinDistanceOfTarget(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
return (EmeraldComponent.CombatComponent.DistanceFromTarget <= EmeraldComponent.m_NavMeshAgent.stoppingDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the height between the AI and its current target (only calculating using the Y axis).
|
||||
/// </summary>
|
||||
public static float GetTargetHeight (EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
Vector3 m_TargetPos = EmeraldComponent.CombatTarget.position;
|
||||
m_TargetPos.x = 0;
|
||||
m_TargetPos.z = 0;
|
||||
Vector3 m_CurrentPos = EmeraldComponent.transform.position;
|
||||
m_CurrentPos.x = 0;
|
||||
m_CurrentPos.z = 0;
|
||||
return Vector3.Distance(m_TargetPos, m_CurrentPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the probability for the Random Pick Type.
|
||||
/// </summary>
|
||||
public static float GenerateProbability(float[] probs)
|
||||
{
|
||||
float total = 0;
|
||||
|
||||
foreach (float elem in probs)
|
||||
{
|
||||
total += elem;
|
||||
}
|
||||
|
||||
float randomPoint = Random.value * total;
|
||||
|
||||
for (int i = 0; i < probs.Length; i++)
|
||||
{
|
||||
if (randomPoint < probs[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
randomPoint -= probs[i];
|
||||
}
|
||||
}
|
||||
return probs.Length - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current angle from the passed Transform.
|
||||
/// </summary>
|
||||
public static float TransformAngle(EmeraldSystem EmeraldComponent, Transform Target)
|
||||
{
|
||||
if (Target == null)
|
||||
return 180;
|
||||
|
||||
Vector3 Direction = new Vector3(Target.position.x, 0, Target.position.z) - new Vector3(EmeraldComponent.transform.position.x, 0, EmeraldComponent.transform.position.z);
|
||||
float angle = Vector3.Angle(EmeraldComponent.transform.forward, Direction);
|
||||
float RotationDifference = EmeraldComponent.transform.localEulerAngles.x;
|
||||
RotationDifference = (RotationDifference > 180) ? RotationDifference - 360 : RotationDifference;
|
||||
float AdjustedAngle = Mathf.Abs(angle) - Mathf.Abs(RotationDifference);
|
||||
return AdjustedAngle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current angle from the AI's target.
|
||||
/// </summary>
|
||||
public static float TargetAngle(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.CombatTarget == null)
|
||||
return 360;
|
||||
Vector3 Direction = new Vector3(EmeraldComponent.CombatTarget.position.x, 0, EmeraldComponent.CombatTarget.position.z) - new Vector3(EmeraldComponent.transform.position.x, 0, EmeraldComponent.transform.position.z);
|
||||
float angle = Vector3.Angle(EmeraldComponent.transform.forward, Direction);
|
||||
float RotationDifference = EmeraldComponent.transform.localEulerAngles.x;
|
||||
RotationDifference = (RotationDifference > 180) ? RotationDifference - 360 : RotationDifference;
|
||||
return Mathf.Abs(angle) - Mathf.Abs(RotationDifference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance between this AI and its current target.
|
||||
/// </summary>
|
||||
public static float GetDistanceFromTarget(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.CombatTarget == null) return 0;
|
||||
|
||||
Vector3 CurrentTargetPos = EmeraldComponent.CombatTarget.position;
|
||||
CurrentTargetPos.y = 0;
|
||||
Vector3 CurrentPos = EmeraldComponent.transform.position;
|
||||
CurrentPos.y = 0;
|
||||
return Vector3.Distance(CurrentTargetPos, CurrentPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance between this AI and its current look at target.
|
||||
/// </summary>
|
||||
public static float GetDistanceFromLookTarget(EmeraldSystem EmeraldComponent)
|
||||
{
|
||||
if (EmeraldComponent.LookAtTarget == null) return 0;
|
||||
|
||||
Vector3 CurrentTargetPos = EmeraldComponent.LookAtTarget.position;
|
||||
CurrentTargetPos.y = 0;
|
||||
Vector3 CurrentPos = EmeraldComponent.transform.position;
|
||||
CurrentPos.y = 0;
|
||||
return Vector3.Distance(CurrentTargetPos, CurrentPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Scripts/Managers/EmeraldCombatManager.cs.meta
Normal file
11
Runtime/Scripts/Managers/EmeraldCombatManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a170756b5fab6e3498360d17874d265b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
614
Runtime/Scripts/Managers/EmeraldEventsManager.cs
Normal file
614
Runtime/Scripts/Managers/EmeraldEventsManager.cs
Normal file
@@ -0,0 +1,614 @@
|
||||
using UnityEngine;
|
||||
using EmeraldAI.Utility;
|
||||
using static UnityEngine.GraphicsBuffer;
|
||||
|
||||
namespace EmeraldAI
|
||||
{
|
||||
public class EmeraldEventsManager : MonoBehaviour
|
||||
{
|
||||
EmeraldSystem EmeraldComponent;
|
||||
EmeraldMovement MovementComponent;
|
||||
EmeraldUI EmeraldUI;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
EmeraldComponent = GetComponent<EmeraldSystem>();
|
||||
MovementComponent = GetComponent<EmeraldMovement>();
|
||||
EmeraldUI = GetComponent<EmeraldUI>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a sound clip according to the Clip parameter.
|
||||
/// </summary>
|
||||
public void PlaySoundClip(AudioClip Clip)
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlaySoundClip(Clip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random attack sound based on your AI's Attack Sounds list. Can also be called through Animation Events.
|
||||
/// </summary>
|
||||
public void PlayIdleSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayIdleSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random attack sound based on your AI's Attack Sounds list. Can also be called through Animation Events.
|
||||
/// </summary>
|
||||
public void PlayAttackSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayAttackSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random attack sound based on your AI's Attack Sounds list. Can also be called through Animation Events.
|
||||
/// </summary>
|
||||
public void PlayWarningSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayWarningSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random block sound based on your AI's Block Sounds list.
|
||||
/// </summary>
|
||||
public void PlayBlockSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayBlockSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random injured sound based on your AI's Injured Sounds list.
|
||||
/// </summary>
|
||||
public void PlayInjuredSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayInjuredSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random death sound based on your AI's Death Sounds list. Can also be called through Animation Events.
|
||||
/// </summary>
|
||||
public void PlayDeathSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayDeathSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a footstep sound from the AI's Footstep Sounds list to use when the AI is walking. This should be setup through an Animation Event.
|
||||
/// </summary>
|
||||
public void WalkFootstepSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.WalkFootstepSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a footstep sound from the AI's Footstep Sounds list to use when the AI is running. This should be setup through an Animation Event.
|
||||
/// </summary>
|
||||
public void RunFootstepSound()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.RunFootstepSound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a random sound effect from the AI's General Sounds list.
|
||||
/// </summary>
|
||||
public void PlayRandomSoundEffect()
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlayRandomSoundEffect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a sound effect from the AI's General Sounds list using the Sound Effect ID as the parameter.
|
||||
/// </summary>
|
||||
public void PlaySoundEffect(int SoundEffectID)
|
||||
{
|
||||
EmeraldComponent.SoundComponent.PlaySoundEffect(SoundEffectID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantly kills this AI.
|
||||
/// </summary>
|
||||
public void KillAI()
|
||||
{
|
||||
if (!EmeraldComponent.AnimationComponent.IsDead)
|
||||
{
|
||||
EmeraldComponent.GetComponent<IDamageable>().Damage(9999999);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually sets the AI's next Idle animation instead of being generated randomly. This is useful for functionality such as playing a particular idle animation
|
||||
/// at a certain location such as for an AI's schedule. Note: The animation numbers are from 1 to 6 and must exist in your AI's Idle Animation list. You must call
|
||||
/// DisableOverrideIdleAnimation() to have idle animations randomly generate again and to disable this feature.
|
||||
/// </summary>
|
||||
public void OverrideIdleAnimation(int IdleIndex)
|
||||
{
|
||||
EmeraldComponent.AnimationComponent.m_IdleAnimaionIndexOverride = true;
|
||||
EmeraldComponent.AIAnimator.SetInteger("Idle Index", IdleIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the OverrideIdleAnimation feature.
|
||||
/// </summary>
|
||||
public void DisableOverrideIdleAnimation()
|
||||
{
|
||||
EmeraldComponent.AnimationComponent.m_IdleAnimaionIndexOverride = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the AI's Wander Type
|
||||
/// </summary>
|
||||
public void ChangeWanderType(EmeraldMovement.WanderTypes NewWanderType)
|
||||
{
|
||||
EmeraldComponent.MovementComponent.ChangeWanderType(NewWanderType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all ignored targets from the static EmeraldAISystem IgnoredTargetsList.
|
||||
/// </summary>
|
||||
public void ClearAllIgnoredTargets()
|
||||
{
|
||||
EmeraldDetection.IgnoredTargetsList.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified ignored target to the static EmeraldAISystem IgnoredTargetsList.
|
||||
/// </summary>
|
||||
public void SetIgnoredTarget(Transform TargetTransform)
|
||||
{
|
||||
if (!EmeraldDetection.IgnoredTargetsList.Contains(TargetTransform))
|
||||
{
|
||||
EmeraldDetection.IgnoredTargetsList.Add(TargetTransform);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified ignored target from the static EmeraldAISystem IgnoredTargetsList.
|
||||
/// </summary>
|
||||
public void ClearIgnoredTarget(Transform TargetTransform)
|
||||
{
|
||||
if (!EmeraldDetection.IgnoredTargetsList.Contains(TargetTransform))
|
||||
{
|
||||
Debug.Log("The TargetTransform did not exist in the EmeraldAISystem IgnoreTargetsList list.");
|
||||
return;
|
||||
}
|
||||
|
||||
EmeraldDetection.IgnoredTargetsList.Remove(TargetTransform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current distance between the AI and their current target (Returns -1 if the Current Target is null).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public float GetDistanceFromTarget ()
|
||||
{
|
||||
if (EmeraldComponent.CombatTarget != null)
|
||||
{
|
||||
return EmeraldComponent.CombatComponent.DistanceFromTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("This AI's Current Target is null");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the AI's current target.
|
||||
/// </summary>
|
||||
public Transform GetCombatTarget()
|
||||
{
|
||||
return EmeraldComponent.CombatTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a specified combat target for your AI to attack within the AI's Detection Radius. Note: Targets outside of an AI's Detection Radius will be ignored. If you want no distance limitations, use OverrideCombatTarget(Transform Target).
|
||||
/// </summary>
|
||||
public void SetCombatTarget(Transform Target)
|
||||
{
|
||||
if (EmeraldComponent.BehaviorsComponent.CurrentBehaviorType != EmeraldBehaviors.BehaviorTypes.Aggressive) return;
|
||||
|
||||
if (Target != null)
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.SetDetectedTarget(Target);
|
||||
EmeraldComponent.m_NavMeshAgent.ResetPath();
|
||||
EmeraldComponent.m_NavMeshAgent.stoppingDistance = EmeraldComponent.CombatComponent.AttackDistance;
|
||||
EmeraldComponent.m_NavMeshAgent.destination = Target.position;
|
||||
}
|
||||
else if (Target == null)
|
||||
{
|
||||
Debug.Log("The SetCombatTarget paramter is null. Ensure that the target exists before calling this function.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a specified combat target for your AI to attack ignoring any distance limitations. If the target is not within attacking range, the AI will move to the target's position and attack based on its attack distance.
|
||||
/// </summary>
|
||||
public void OverrideCombatTarget(Transform Target)
|
||||
{
|
||||
if (EmeraldComponent.BehaviorsComponent.CurrentBehaviorType != EmeraldBehaviors.BehaviorTypes.Aggressive) return;
|
||||
|
||||
if (Target != null)
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.SetDetectedTarget(Target);
|
||||
EmeraldComponent.m_NavMeshAgent.ResetPath();
|
||||
EmeraldComponent.m_NavMeshAgent.stoppingDistance = EmeraldComponent.CombatComponent.AttackDistance;
|
||||
EmeraldComponent.m_NavMeshAgent.destination = Target.position;
|
||||
EmeraldComponent.BehaviorsComponent.InfititeChase = true;
|
||||
}
|
||||
else if (Target == null)
|
||||
{
|
||||
Debug.Log("The OverrideCombatTarget paramter is null. Ensure that the target exists before calling this function.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes an AI flee from the specified target by overiding their behavior.
|
||||
/// </summary>
|
||||
public void FleeFromTarget(Transform FleeTarget)
|
||||
{
|
||||
if (FleeTarget != null)
|
||||
{
|
||||
EmeraldComponent.BehaviorsComponent.CurrentBehaviorType = EmeraldBehaviors.BehaviorTypes.Coward;
|
||||
EmeraldComponent.CombatTarget = FleeTarget;
|
||||
EmeraldComponent.DetectionComponent.GetTargetInfo(EmeraldComponent.CombatTarget, true);
|
||||
EmeraldComponent.m_NavMeshAgent.ResetPath();
|
||||
EmeraldCombatManager.ActivateCombatState(EmeraldComponent);
|
||||
}
|
||||
else if (FleeTarget == null)
|
||||
{
|
||||
Debug.Log("The FleeTarget paramter is null. Ensure that the target exists before calling this function.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a new follow target for an AI to follow.
|
||||
/// </summary>
|
||||
public void SetFollowerTarget(Transform Target)
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.SetTargetToFollow(Target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tames the AI to become the Target's companion. Note: The tameable AI must have a Cautious Behavior Type and
|
||||
/// a Brave or Foolhardy Confidence Type. The AI must be tamed before the AI turns Aggressive to be successful.
|
||||
/// </summary>
|
||||
public void TameAI(Transform Target)
|
||||
{
|
||||
EmeraldComponent.CombatComponent.ClearTarget();
|
||||
EmeraldComponent.DetectionComponent.SetTargetToFollow(Target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the transform that last attacked the AI.
|
||||
/// </summary>
|
||||
public Transform GetLastAttacker ()
|
||||
{
|
||||
return EmeraldComponent.CombatComponent.LastAttacker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's Health Bar color
|
||||
/// </summary>
|
||||
public void UpdateUIHealthBarColor(Color NewColor)
|
||||
{
|
||||
if (EmeraldUI.AutoCreateHealthBars == YesOrNo.Yes)
|
||||
{
|
||||
GameObject HealthBarChild = EmeraldUI.HealthBar.transform.Find("AI Health Bar Background").gameObject;
|
||||
UnityEngine.UI.Image HealthBarRef = HealthBarChild.transform.Find("AI Health Bar").GetComponent<UnityEngine.UI.Image>();
|
||||
HealthBarRef.color = NewColor;
|
||||
UnityEngine.UI.Image HealthBarBackgroundImageRef = HealthBarChild.GetComponent<UnityEngine.UI.Image>();
|
||||
HealthBarBackgroundImageRef.color = EmeraldUI.HealthBarBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's Health Bar Background color
|
||||
/// </summary>
|
||||
public void UpdateUIHealthBarBackgroundColor(Color NewColor)
|
||||
{
|
||||
if (EmeraldUI.AutoCreateHealthBars == YesOrNo.Yes)
|
||||
{
|
||||
GameObject HealthBarChild = EmeraldUI.HealthBar.transform.Find("AI Health Bar Background").gameObject;
|
||||
UnityEngine.UI.Image HealthBarBackgroundImageRef = HealthBarChild.GetComponent<UnityEngine.UI.Image>();
|
||||
HealthBarBackgroundImageRef.color = NewColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's Name color
|
||||
/// </summary>
|
||||
public void UpdateUINameColor(Color NewColor)
|
||||
{
|
||||
if (EmeraldUI.AutoCreateHealthBars == YesOrNo.Yes && EmeraldUI.DisplayAIName == YesOrNo.Yes)
|
||||
{
|
||||
EmeraldUI.AINameUI.color = NewColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's Name text
|
||||
/// </summary>
|
||||
public void UpdateUINameText(string NewName)
|
||||
{
|
||||
if (EmeraldUI.AutoCreateHealthBars == YesOrNo.Yes && EmeraldUI.DisplayAIName == YesOrNo.Yes)
|
||||
{
|
||||
EmeraldUI.AINameUI.text = NewName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's dynamic wandering position to the AI's current positon.
|
||||
/// </summary>
|
||||
public void UpdateDynamicWanderPosition()
|
||||
{
|
||||
MovementComponent.StartingDestination = this.transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the AI's dynamic wandering position to the position of the Destination transform.
|
||||
/// This is useful for functionality such as custom AI schedules. Note: This will automatically change
|
||||
/// your AI's Wander Type to Dynamic.
|
||||
/// </summary>
|
||||
public void SetDynamicWanderPosition(Transform Destination)
|
||||
{
|
||||
MovementComponent.ChangeWanderType(EmeraldMovement.WanderTypes.Dynamic);
|
||||
MovementComponent.StartingDestination = Destination.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the AI's starting position to the AI's current position.
|
||||
/// </summary>
|
||||
public void UpdateStartingPosition()
|
||||
{
|
||||
MovementComponent.StartingDestination = this.transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the AI's destination using the transform's position.
|
||||
/// </summary>
|
||||
public void SetDestination(Transform Destination)
|
||||
{
|
||||
//EmeraldComponent.MovementComponent.SetDestinationPosition(Destination.position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the AI's destination using a Vector3 position.
|
||||
/// </summary>
|
||||
public void SetDestinationPosition(Vector3 DestinationPosition)
|
||||
{
|
||||
//EmeraldComponent.MovementComponent.SetDestinationPosition(DestinationPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a new position to move to within the specified radius based on the AI's current position.
|
||||
/// </summary>
|
||||
public void GenerateNewWaypointCurrentPosition(int Radius)
|
||||
{
|
||||
Vector3 NewDestination = transform.position + new Vector3(Random.insideUnitSphere.y, 0, Random.insideUnitSphere.z) * Radius;
|
||||
RaycastHit HitDown;
|
||||
if (Physics.Raycast(new Vector3(NewDestination.x, NewDestination.y + 5, NewDestination.z), -transform.up, out HitDown, 10, MovementComponent.DynamicWanderLayerMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
UnityEngine.AI.NavMeshHit hit;
|
||||
if (UnityEngine.AI.NavMesh.SamplePosition(NewDestination, out hit, 4, EmeraldComponent.m_NavMeshAgent.areaMask))
|
||||
{
|
||||
EmeraldComponent.m_NavMeshAgent.SetDestination(NewDestination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a waypoint to an AI's Waypoint List.
|
||||
/// </summary>
|
||||
public void AddWaypoint(Transform Waypoint)
|
||||
{
|
||||
MovementComponent.WaypointsList.Add(Waypoint.position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a waypoint from the AI's Wapoint List according to the specified index.
|
||||
/// </summary>
|
||||
public void RemoveWaypoint(int WaypointIndex)
|
||||
{
|
||||
MovementComponent.WaypointsList.RemoveAt(WaypointIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all of an AI's current waypoints. Note: When an AI's waypoints are cleared, it will be set to the Stationary wander type to avoid an error.
|
||||
/// If you want the AI to follow newly created waypoints, you will need to set it's Wander Type back to Waypoint with the ChangeWanderType functio (located within the EmeraldAIEventsManager script).
|
||||
/// </summary>
|
||||
public void ClearAllWaypoints()
|
||||
{
|
||||
MovementComponent.WanderType = EmeraldMovement.WanderTypes.Stationary;
|
||||
MovementComponent.WaypointsList.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops an AI from moving. This is useful for functionality like dialogue.
|
||||
/// </summary>
|
||||
public void StopMovement()
|
||||
{
|
||||
EmeraldComponent.m_NavMeshAgent.isStopped = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resumes an AI's movement after using the StopMovement function.
|
||||
/// </summary>
|
||||
public void ResumeMovement()
|
||||
{
|
||||
EmeraldComponent.m_NavMeshAgent.isStopped = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a Companion AI from moving.
|
||||
/// </summary>
|
||||
public void StopFollowing()
|
||||
{
|
||||
EmeraldComponent.m_NavMeshAgent.isStopped = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a Companion AI to resume following its follower.
|
||||
/// </summary>
|
||||
public void ResumeFollowing()
|
||||
{
|
||||
EmeraldComponent.m_NavMeshAgent.isStopped = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a Companion AI to guard the assigned position.
|
||||
/// </summary>
|
||||
public void StartCompanionGuardPosition(Vector3 PositionToGuard)
|
||||
{
|
||||
EmeraldComponent.MovementComponent.DefaultMovementPaused = true;
|
||||
EmeraldComponent.m_NavMeshAgent.SetDestination(PositionToGuard);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a Companion AI from guarding and returns it to their current follower.
|
||||
/// </summary>
|
||||
public void CancelCompanionGuardPosition()
|
||||
{
|
||||
EmeraldComponent.MovementComponent.DefaultMovementPaused = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a new target within the AI's Attacking Range clostest to the AI.
|
||||
/// </summary>
|
||||
public void SearchForClosestTarget()
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.SearchForTarget(PickTargetTypes.Closest);
|
||||
EmeraldComponent.DetectionComponent.SetDetectedTarget(EmeraldComponent.CombatTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a new random target within the AI's Attacking Range.
|
||||
/// </summary>
|
||||
public void SearchForRandomTarget()
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.SearchForTarget(PickTargetTypes.Random);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the relation of the given faction. Note: The faction must be available in the AI's faction list.
|
||||
/// </summary>
|
||||
/// <param name="Faction"> The name of the faction to change.</param>
|
||||
/// <param name="FactionLevel">The level to set the faction to typed as a string. The options are Enemy, Neutral, or Friendly</param>
|
||||
public void SetFactionLevel(string Faction, RelationTypes RelationType)
|
||||
{
|
||||
EmeraldFactionData FactionData = Resources.Load("Faction Data") as EmeraldFactionData;
|
||||
|
||||
for (int i = 0; i < EmeraldComponent.DetectionComponent.FactionRelationsList.Count; i++)
|
||||
{
|
||||
if (EmeraldComponent.DetectionComponent.FactionRelationsList[i].FactionIndex == FactionData.FactionNameList.IndexOf(Faction))
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.FactionRelationsList[i].RelationType = RelationType;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("The faction '" + Faction + "' does not exist in the AI's Faction Relations list. Please add it using the Faction Settings Foldout through the Emerald Detection editor of this AI.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Faction and Faction Relation to the AI's Faction Relations List. Note: The faction must exist within the Emerald Faction Manager's Faction List.
|
||||
/// </summary>
|
||||
/// <param name="Faction"> The name of the faction to change.</param>
|
||||
/// <param name="FactionLevel">The level to set the faction to typed as a string. The options are Enemy, Neutral, or Friendly</param>
|
||||
public void AddFactionRelation(string Faction, RelationTypes RelationType)
|
||||
{
|
||||
EmeraldFactionData FactionData = Resources.Load("Faction Data") as EmeraldFactionData;
|
||||
|
||||
if (!EmeraldDetection.FactionData.FactionNameList.Contains(Faction))
|
||||
{
|
||||
Debug.Log("The faction '" + Faction + "' does not exist in the Faction Manager. Please add it using the Emerald Faction Manager.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < EmeraldComponent.DetectionComponent.FactionRelationsList.Count; i++)
|
||||
{
|
||||
if (EmeraldComponent.DetectionComponent.FactionRelationsList[i].FactionIndex == FactionData.FactionNameList.IndexOf(Faction))
|
||||
{
|
||||
Debug.Log("This AI already contains the faction '" + Faction + "'. If you would like to modify an AI's existing faction, please use SetFactionLevel(string Faction, RelationTypes RelationType) instead.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EmeraldComponent.DetectionComponent.FactionRelationsList.Add(new FactionClass(FactionData.FactionNameList.IndexOf(Faction), (int)RelationType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relation of the EmeraldTarget with this AI in the form of a string (Enemy, Neutral, or Friendly). If a faction cannot be found, or if it is not a valid target, you will receive a value of Invalid Target.
|
||||
/// </summary>
|
||||
public string GetTargetRelation(Transform Target)
|
||||
{
|
||||
return EmeraldComponent.DetectionComponent.GetTargetFactionRelation(Target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the AI's faction. (Note: The FactionName must exists within the Faction Manager's Current Faction list)
|
||||
/// </summary>
|
||||
public void ChangeFaction(string FactionName)
|
||||
{
|
||||
EmeraldFactionData FactionData = Resources.Load("Faction Data") as EmeraldFactionData;
|
||||
|
||||
if (FactionData.FactionNameList.Contains(FactionName))
|
||||
{
|
||||
EmeraldComponent.DetectionComponent.CurrentFaction = FactionData.FactionNameList.IndexOf(FactionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Faction not Found");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the player is currently within the AI's detection radius by returning true or false (this can be true even if the player is an enemy).
|
||||
/// </summary>
|
||||
public bool CheckForPlayerDetection ()
|
||||
{
|
||||
return EmeraldComponent.CombatTarget != null && EmeraldComponent.CombatTarget.CompareTag(EmeraldComponent.DetectionComponent.PlayerTag) || EmeraldComponent.LookAtTarget != null && EmeraldComponent.LookAtTarget.CompareTag(EmeraldComponent.DetectionComponent.PlayerTag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the faction name of the passed AI target. The AI's own transform can also be passed to get its own faction name.
|
||||
/// </summary>
|
||||
public string GetTargetFactionName(Transform Target)
|
||||
{
|
||||
return EmeraldComponent.DetectionComponent.GetTargetFactionName(Target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Debug logs a message to the Unity Console for testing purposes.
|
||||
/// </summary>
|
||||
public void DebugLogMessage (string Message)
|
||||
{
|
||||
Debug.Log(Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the passed gameobject.
|
||||
/// </summary>
|
||||
public void EnableObject(GameObject Object)
|
||||
{
|
||||
Object.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the passed gameobject.
|
||||
/// </summary>
|
||||
public void DisableObject(GameObject Object)
|
||||
{
|
||||
Object.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets an AI to its default state. This is useful if an AI is being respawned.
|
||||
/// </summary>
|
||||
public void ResetAI()
|
||||
{
|
||||
EmeraldComponent.ResetAI();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Scripts/Managers/EmeraldEventsManager.cs.meta
Normal file
11
Runtime/Scripts/Managers/EmeraldEventsManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d2472edfc3c5e54bbd46570a008113b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user