Skip to content

Instantly share code, notes, and snippets.

@lordlycastle
Last active January 31, 2023 13:47
Show Gist options
  • Save lordlycastle/82e4523aba6e4fd272e890e41ab68522 to your computer and use it in GitHub Desktop.
Save lordlycastle/82e4523aba6e4fd272e890e41ab68522 to your computer and use it in GitHub Desktop.
Casts a ray from the scene view camera and draws a line from transform to the hits. Can be used for selection in scene view.
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
using System.Linq;
using Sirenix.Serialization;
public class RaycastPlacer : MonoBehaviour
{
[Serializable]
public class RaycastPlacerModel
{
[TitleGroup("Hit Objects")]
[ShowInInspector]
public RaycastHit raycastHit;
[ShowInInspector, HideLabel, LabelWidth(0)]
public Transform HitTransform => raycastHit.transform;
// [ShowInInspector]
private GameObject HitGameObject => raycastHit.transform.gameObject;
[ShowInInspector]
public Vector3 HitPoint => raycastHit.point;
private Transform _transform;
public RaycastPlacerModel(RaycastHit raycastHit, Transform transform)
{
this.raycastHit = raycastHit;
_transform = transform;
}
[ButtonGroup, Button]
public void AlignX()
{
var transformPosition = _transform.position;
transformPosition.x = raycastHit.point.x;
ChangeTransformPosition(transformPosition);
}
[ButtonGroup]
public void AlignY()
{
var transformPosition = _transform.position;
transformPosition.y = raycastHit.point.y;
ChangeTransformPosition(transformPosition);
}
[ButtonGroup]
public void AlignZ()
{
var transformPosition = _transform.position;
transformPosition.z = raycastHit.point.z;
ChangeTransformPosition(transformPosition);
}
[ButtonGroup]
public void AlignXYZ()
{
ChangeTransformPosition(raycastHit.point);
}
public void ChangeTransformPosition(Vector3 transformPosition)
{
Undo.RecordObject(_transform, "Move Transform");
_transform.position = transformPosition;
}
private Editor editor;
[OnInspectorGUI]
private void OnInspectorGUI()
{
if (editor == null || editor.target != HitGameObject)
{
editor = HitGameObject == null ? null : Editor.CreateEditor(HitGameObject);
}
Rect rect = GUILayoutUtility.GetRect(100, 100);
editor?.DrawPreview(rect);
}
}
[ShowInInspector]
public static LayerMask raycastLayers = int.MaxValue;
public float maxDistance = 100f;
[HideInInspector]
public bool isRaycasting;
[OdinSerialize, ListDrawerSettings(HideAddButton = true, DraggableItems = false, ShowPaging = false), PropertyOrder(99)]
public List<RaycastPlacerModel> raycastHits = new List<RaycastPlacerModel>();
private Ray mouseRay;
[Button(ButtonSizes.Medium)]
public void ToggleRaycasting() => isRaycasting = !isRaycasting;
private void UpdateRaycast(SceneView sceneView)
{
Vector3 mousePosition = Event.current.mousePosition;
mousePosition.y = SceneView.currentDrawingSceneView.camera.pixelHeight - mousePosition.y;
mousePosition = SceneView.currentDrawingSceneView.camera.ScreenToWorldPoint(mousePosition);
mousePosition.y = -mousePosition.y;
mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
raycastHits = Physics.RaycastAll(mouseRay, maxDistance)
.Select(hit => new RaycastPlacerModel(hit, transform))
.ToList();
if (raycastHits.Count > 0)
{
// Debug.Log($"Hit {raycastHits.Count} objects.");
DrawRays(transform.position, raycastHits);
}
SceneView.onSceneGUIDelegate -= UpdateRaycast;
}
private void DrawRays(Vector3 origin, List<RaycastPlacerModel> objects)
{
Handles.color = Color.magenta;
foreach (var o in objects)
{
Handles.DrawLine(origin, o.raycastHit.point);
}
}
private void OnDrawGizmosSelected()
{
if (isRaycasting == false) return;
SceneView.onSceneGUIDelegate += UpdateRaycast;
Gizmos.color = Color.yellow;
// Debug.Log($"Ray: {mouseRay.origin} dir: {mouseRay.direction}");
}
}
[CustomEditor(typeof(RaycastPlacer))]
class RaycastPlacerEditor : OdinEditor
{
private void OnSceneGUI()
{
if (target is RaycastPlacer raycastPlacer)
{
// Retrieve the control Id
int controlId = GUIUtility.GetControlID(FocusType.Passive);
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && raycastPlacer.isRaycasting)
{
raycastPlacer.isRaycasting = false;
// Tell the UI your event is the main one to use, it override the selection in the scene view
GUIUtility.hotControl = controlId;
Event.current.Use();
// Debug.Log("Event Used.");
}
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment