490 lines
12 KiB
C#
490 lines
12 KiB
C#
using System;
|
|
using MyUtility;
|
|
using UnityEngine;
|
|
|
|
namespace S_SnapTools
|
|
{
|
|
public class S_SnapToObject : MonoBehaviour
|
|
{
|
|
private enum EAnimationState
|
|
{
|
|
ZOOM_IN = 0,
|
|
ZOOM_OUT = 1,
|
|
IDLE = 2
|
|
}
|
|
|
|
public static EventHandler<S_SnapToObjectBeforePlacementEventArgs> OnGlobalBeforeObjectSnapped;
|
|
|
|
public static EventHandler<S_SnapToObjectEventArgs> OnGlobalAfterObjectSnapped;
|
|
|
|
public static EventHandler<S_SnapToObjectEventArgs> OnGlobalPreviewObjectInstantiated;
|
|
|
|
private static S_SnapToObject s_openSelection;
|
|
|
|
public EventHandler<S_SnapToObjectBeforePlacementEventArgs> OnBeforeObjectSnapped;
|
|
|
|
public EventHandler<S_SnapToObjectEventArgs> OnAfterObjectSnapped;
|
|
|
|
public EventHandler<S_SnapToObjectEventArgs> OnPreviewObjectInstantiated;
|
|
|
|
private const float ZOOM_OUT_TIME = 0.4f;
|
|
|
|
private const float ZOOM_IN_TIME = 0.2f;
|
|
|
|
private readonly Vector3 MIN_SCALE = Vector3.one * 0.0001f;
|
|
|
|
[SerializeField]
|
|
private float PREVIEW_RELATIVE_SCREEN_DISTANCE = 0.3f;
|
|
|
|
[SerializeField]
|
|
private float PREVIEW_RELATIVE_SCREEN_CLICK_AREA = 0.2f;
|
|
|
|
[SerializeField]
|
|
private float PREVIEW_RELATIVE_MOVE_TO_CAMERA_DISTANCE = 0.5f;
|
|
|
|
[SerializeField]
|
|
private S_SnapToObjectPrefab[] m_prefabs = new S_SnapToObjectPrefab[1];
|
|
|
|
[SerializeField]
|
|
private bool m_isDeactivatedAfterSnap;
|
|
|
|
[SerializeField]
|
|
private bool m_isDestroyedAfterSnap = true;
|
|
|
|
[SerializeField]
|
|
private bool m_isSetNameToResourcePath = true;
|
|
|
|
[SerializeField]
|
|
private bool m_isDrawUI = true;
|
|
|
|
[SerializeField]
|
|
private Material m_uiMaterialLine;
|
|
|
|
[SerializeField]
|
|
private Material m_uiMaterialFill;
|
|
|
|
private int m_snapCounter;
|
|
|
|
private bool m_isSelectionOpen;
|
|
|
|
private EAnimationState m_animState = EAnimationState.IDLE;
|
|
|
|
private float m_zoomAnimationTime = -1f;
|
|
|
|
private Transform m_previewRoot;
|
|
|
|
private Camera m_cam;
|
|
|
|
public S_SnapToObjectPrefab[] Prefabs
|
|
{
|
|
get
|
|
{
|
|
return m_prefabs;
|
|
}
|
|
set
|
|
{
|
|
m_prefabs = value;
|
|
}
|
|
}
|
|
|
|
public bool IsDeactivatedAfterSnap
|
|
{
|
|
get
|
|
{
|
|
return m_isDeactivatedAfterSnap;
|
|
}
|
|
set
|
|
{
|
|
m_isDeactivatedAfterSnap = value;
|
|
}
|
|
}
|
|
|
|
public bool IsDestroyedAfterSnap
|
|
{
|
|
get
|
|
{
|
|
return m_isDestroyedAfterSnap;
|
|
}
|
|
set
|
|
{
|
|
m_isDestroyedAfterSnap = value;
|
|
}
|
|
}
|
|
|
|
public bool IsSetNameToResourcePath
|
|
{
|
|
get
|
|
{
|
|
return m_isSetNameToResourcePath;
|
|
}
|
|
set
|
|
{
|
|
m_isSetNameToResourcePath = value;
|
|
}
|
|
}
|
|
|
|
public bool IsDrawUI
|
|
{
|
|
get
|
|
{
|
|
return m_isDrawUI;
|
|
}
|
|
set
|
|
{
|
|
m_isDrawUI = value;
|
|
}
|
|
}
|
|
|
|
public Material UImaterialLine
|
|
{
|
|
get
|
|
{
|
|
return m_uiMaterialLine;
|
|
}
|
|
set
|
|
{
|
|
m_uiMaterialLine = value;
|
|
}
|
|
}
|
|
|
|
public Material UImaterialFill
|
|
{
|
|
get
|
|
{
|
|
return m_uiMaterialFill;
|
|
}
|
|
set
|
|
{
|
|
m_uiMaterialFill = value;
|
|
}
|
|
}
|
|
|
|
public int SnapCounter
|
|
{
|
|
get
|
|
{
|
|
return m_snapCounter;
|
|
}
|
|
}
|
|
|
|
public bool IsPrefabSelectionOpen
|
|
{
|
|
get
|
|
{
|
|
return m_isSelectionOpen;
|
|
}
|
|
}
|
|
|
|
public Transform PreviewRoot
|
|
{
|
|
get
|
|
{
|
|
if (m_previewRoot == null)
|
|
{
|
|
m_previewRoot = new GameObject("S_SnapToObject Preview Root").transform;
|
|
m_previewRoot.position = base.transform.position;
|
|
}
|
|
return m_previewRoot;
|
|
}
|
|
}
|
|
|
|
private Camera Cam
|
|
{
|
|
get
|
|
{
|
|
if (m_cam == null)
|
|
{
|
|
m_cam = Camera.main;
|
|
if (m_cam == null)
|
|
{
|
|
Debug.LogError("S_SnapToObject: could not find main camera!");
|
|
}
|
|
}
|
|
return m_cam;
|
|
}
|
|
}
|
|
|
|
public S_SnapToObject()
|
|
{
|
|
if (m_prefabs != null && m_prefabs.Length == 1)
|
|
{
|
|
m_prefabs[0] = new S_SnapToObjectPrefab();
|
|
}
|
|
}
|
|
|
|
public void IncSnapCounter()
|
|
{
|
|
if (m_isDeactivatedAfterSnap)
|
|
{
|
|
base.gameObject.SetActive(false);
|
|
}
|
|
else if (m_isDestroyedAfterSnap)
|
|
{
|
|
UnityEngine.Object.Destroy(base.gameObject);
|
|
}
|
|
m_snapCounter++;
|
|
}
|
|
|
|
public void DecSnapCounter()
|
|
{
|
|
if (m_snapCounter > 0)
|
|
{
|
|
m_snapCounter--;
|
|
if (m_isDeactivatedAfterSnap && m_snapCounter == 0)
|
|
{
|
|
base.gameObject.SetActive(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OpenSelection()
|
|
{
|
|
if (s_openSelection != null)
|
|
{
|
|
s_openSelection.CloseSelection();
|
|
}
|
|
s_openSelection = this;
|
|
m_isSelectionOpen = true;
|
|
m_animState = EAnimationState.ZOOM_OUT;
|
|
m_zoomAnimationTime = 0f;
|
|
for (int i = 0; i < m_prefabs.Length; i++)
|
|
{
|
|
CreatePrefabPreview(m_prefabs[i]);
|
|
}
|
|
}
|
|
|
|
public void CloseSelection()
|
|
{
|
|
if (s_openSelection == this)
|
|
{
|
|
s_openSelection = null;
|
|
}
|
|
m_isSelectionOpen = false;
|
|
m_animState = EAnimationState.ZOOM_IN;
|
|
m_zoomAnimationTime = 0f;
|
|
}
|
|
|
|
public void PlacePrefab(S_SnapToObjectPrefab p_prefab)
|
|
{
|
|
if (m_isSelectionOpen)
|
|
{
|
|
DestroyAllPreviews();
|
|
if (s_openSelection == this)
|
|
{
|
|
s_openSelection = null;
|
|
}
|
|
m_isSelectionOpen = false;
|
|
m_animState = EAnimationState.IDLE;
|
|
m_zoomAnimationTime = -1f;
|
|
}
|
|
UnityEngine.Object obj = Resources.Load(p_prefab.PrefabResourcePath);
|
|
if (obj != null)
|
|
{
|
|
GameObject gameObject = (GameObject)UnityEngine.Object.Instantiate(obj);
|
|
if (m_isSetNameToResourcePath)
|
|
{
|
|
gameObject.name = p_prefab.PrefabResourcePath;
|
|
}
|
|
gameObject.transform.localRotation = base.transform.rotation;
|
|
gameObject.transform.Rotate(p_prefab.LocalEulerRotation);
|
|
gameObject.transform.localScale = Vector3.Scale(p_prefab.LocalScale, base.transform.lossyScale);
|
|
gameObject.transform.position = base.transform.position + gameObject.transform.TransformPoint(p_prefab.LocalPosition);
|
|
IncSnapCounter();
|
|
if (OnGlobalAfterObjectSnapped != null)
|
|
{
|
|
OnGlobalAfterObjectSnapped(this, new S_SnapToObjectEventArgs(this, gameObject));
|
|
}
|
|
if (OnAfterObjectSnapped != null)
|
|
{
|
|
OnAfterObjectSnapped(this, new S_SnapToObjectEventArgs(this, gameObject));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("S_SnapToObject: PlacePrefabInternal: could not find prefab in resources at '" + p_prefab.PrefabResourcePath + "'!");
|
|
}
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
UtilityClickTouchDetector utilityClickTouchDetector = base.gameObject.AddComponent<UtilityClickTouchDetector>();
|
|
utilityClickTouchDetector.m_onClick = (Action)Delegate.Combine(utilityClickTouchDetector.m_onClick, new Action(OnClicked));
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (IsPrefabSelectionOpen)
|
|
{
|
|
PreviewRoot.position = base.transform.position;
|
|
}
|
|
else if (m_previewRoot != null && m_animState == EAnimationState.IDLE)
|
|
{
|
|
UnityEngine.Object.Destroy(m_previewRoot.gameObject);
|
|
m_previewRoot = null;
|
|
}
|
|
if (m_animState == EAnimationState.ZOOM_OUT)
|
|
{
|
|
float p_fade = UpdateAnimationTimeFadeState(0.4f);
|
|
UpdateZoomAnimation(p_fade);
|
|
}
|
|
else if (m_animState == EAnimationState.ZOOM_IN)
|
|
{
|
|
float num = UpdateAnimationTimeFadeState(0.2f);
|
|
if (num < 1f)
|
|
{
|
|
UpdateZoomAnimation(1f - num);
|
|
}
|
|
else
|
|
{
|
|
DestroyAllPreviews();
|
|
}
|
|
}
|
|
else if (IsPrefabSelectionOpen)
|
|
{
|
|
UpdateZoomAnimation(1f);
|
|
}
|
|
}
|
|
|
|
private void DestroyAllPreviews()
|
|
{
|
|
for (int i = 0; i < m_prefabs.Length; i++)
|
|
{
|
|
UnityEngine.Object.Destroy(m_prefabs[i].m_currentInstance);
|
|
}
|
|
}
|
|
|
|
private float UpdateAnimationTimeFadeState(float p_animTime)
|
|
{
|
|
m_zoomAnimationTime += Time.deltaTime;
|
|
if (m_zoomAnimationTime >= p_animTime)
|
|
{
|
|
m_animState = EAnimationState.IDLE;
|
|
m_zoomAnimationTime = -1f;
|
|
return 1f;
|
|
}
|
|
return m_zoomAnimationTime / p_animTime;
|
|
}
|
|
|
|
private void UpdateZoomAnimation(float p_fade)
|
|
{
|
|
if (!(Cam != null))
|
|
{
|
|
return;
|
|
}
|
|
Vector3 vector = Cam.WorldToScreenPoint(base.transform.position);
|
|
float num = 360f / (float)m_prefabs.Length;
|
|
for (int i = 0; i < m_prefabs.Length; i++)
|
|
{
|
|
GameObject currentInstance = m_prefabs[i].m_currentInstance;
|
|
if (currentInstance != null && m_prefabs[i].m_currentInstancePreviewScript != null)
|
|
{
|
|
Vector3 vector2 = (float)Screen.height * PREVIEW_RELATIVE_SCREEN_DISTANCE * new Vector3(Mathf.Cos((float)Math.PI / 180f * num * (float)i * p_fade), Mathf.Sin((float)Math.PI / 180f * num * (float)i * p_fade), 0f);
|
|
vector2 = Cam.ScreenToWorldPoint(vector + vector2);
|
|
vector2 = vector2 * (1f - PREVIEW_RELATIVE_MOVE_TO_CAMERA_DISTANCE) + Cam.transform.position * PREVIEW_RELATIVE_MOVE_TO_CAMERA_DISTANCE;
|
|
currentInstance.transform.localScale = Vector3.Lerp(MIN_SCALE, m_prefabs[i].PreviewScale * m_prefabs[i].m_currentInstancePreviewScript.WorldRadius, p_fade);
|
|
currentInstance.transform.position = Vector3.Lerp(base.transform.position, vector2, p_fade);
|
|
Quaternion rotation = Quaternion.LookRotation(Cam.transform.position - currentInstance.transform.position, Vector3.up);
|
|
rotation *= Quaternion.Euler(m_prefabs[i].PreviewEulerRotation);
|
|
currentInstance.transform.rotation = rotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnClicked()
|
|
{
|
|
if (!m_isSelectionOpen)
|
|
{
|
|
OpenSelection();
|
|
}
|
|
else
|
|
{
|
|
CloseSelection();
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (m_previewRoot != null)
|
|
{
|
|
UnityEngine.Object.Destroy(m_previewRoot.gameObject);
|
|
m_previewRoot = null;
|
|
}
|
|
DestroyAllPreviews();
|
|
if (s_openSelection == this)
|
|
{
|
|
s_openSelection = null;
|
|
}
|
|
m_isSelectionOpen = false;
|
|
m_animState = EAnimationState.IDLE;
|
|
m_zoomAnimationTime = -1f;
|
|
}
|
|
|
|
private void CreatePrefabPreview(S_SnapToObjectPrefab p_prefab)
|
|
{
|
|
if (p_prefab.m_currentInstance != null)
|
|
{
|
|
Debug.LogWarning("S_SnapToObject: CreatePrefabPreview: there was already an instance of this prefab '" + p_prefab.m_currentInstance.name + "'! Old instance will be destroyed...");
|
|
UnityEngine.Object.Destroy(p_prefab.m_currentInstance);
|
|
}
|
|
if (p_prefab.PrefabResourcePath != null)
|
|
{
|
|
UnityEngine.Object obj = Resources.Load(p_prefab.PrefabResourcePath);
|
|
if (obj != null)
|
|
{
|
|
p_prefab.m_currentInstance = (GameObject)UnityEngine.Object.Instantiate(obj);
|
|
p_prefab.m_currentInstance.transform.parent = PreviewRoot;
|
|
p_prefab.m_currentInstance.transform.localPosition = Vector3.zero;
|
|
p_prefab.m_currentInstance.transform.localScale = Vector3.one;
|
|
if (p_prefab.IsCollidersDisabledInPreview)
|
|
{
|
|
Collider[] componentsInChildren = p_prefab.m_currentInstance.GetComponentsInChildren<Collider>();
|
|
for (int i = 0; i < componentsInChildren.Length; i++)
|
|
{
|
|
componentsInChildren[i].enabled = false;
|
|
}
|
|
}
|
|
p_prefab.m_currentInstance.transform.localScale = MIN_SCALE;
|
|
p_prefab.m_currentInstancePreviewScript = p_prefab.m_currentInstance.AddComponent<S_SnapToObjectPreview>();
|
|
p_prefab.m_currentInstancePreviewScript.Init(m_isDrawUI, PREVIEW_RELATIVE_SCREEN_CLICK_AREA, delegate
|
|
{
|
|
PlacePrefabInternal(p_prefab);
|
|
}, m_uiMaterialLine, m_uiMaterialFill);
|
|
if (OnGlobalPreviewObjectInstantiated != null)
|
|
{
|
|
OnGlobalPreviewObjectInstantiated(this, new S_SnapToObjectEventArgs(this, p_prefab.m_currentInstance));
|
|
}
|
|
if (OnPreviewObjectInstantiated != null)
|
|
{
|
|
OnPreviewObjectInstantiated(this, new S_SnapToObjectEventArgs(this, p_prefab.m_currentInstance));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("S_SnapToObject: CreatePrefabPreview: could not find prefab in resources at '" + p_prefab.PrefabResourcePath + "'!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("S_SnapToObject: CreatePrefabPreview: no prefab assigned!");
|
|
}
|
|
}
|
|
|
|
private void PlacePrefabInternal(S_SnapToObjectPrefab p_prefab)
|
|
{
|
|
S_SnapToObjectBeforePlacementEventArgs e = new S_SnapToObjectBeforePlacementEventArgs(this, p_prefab);
|
|
if (OnGlobalBeforeObjectSnapped != null)
|
|
{
|
|
OnGlobalBeforeObjectSnapped(this, e);
|
|
}
|
|
if (OnBeforeObjectSnapped != null)
|
|
{
|
|
OnBeforeObjectSnapped(this, e);
|
|
}
|
|
if (!e.IsDelayedPlacePrefab)
|
|
{
|
|
PlacePrefab(p_prefab);
|
|
}
|
|
}
|
|
}
|
|
}
|