gpt4 book ai didi

unity-game-engine - 如何使用自定义编辑器属性从任何地方获取屏幕点击输入?

转载 作者:行者123 更新时间:2023-12-02 14:21:02 25 4
gpt4 key购买 nike

TL;DR:如何使用矢量实现 Unity 的“屏幕颜色”功能?

Image of Unity's color picker

<小时/>

好吧,对于我想要做的事情来说,标题已经相当简化了:

  1. 让用户单击一个按钮,然后单击屏幕上的一个位置,将该[世界]位置保存为矢量。 - 这基本上是有效的,只是它不会检测到检查器之外的左键单击。

  2. 禁用统一编辑器上其他所有内容的左键单击(因此,当您单击某个位置时,它不会将焦点更改为另一个游戏对象)。 - 这是主要问题。

跟踪鼠标并获取世界位置非常简单,如果正在跟踪鼠标,则保存一个 bool 值,并保存一个 SerializedProperty 来保存鼠标位置要保存到的值。

这是我的属性:

public class VectorPickerAttribute : PropertyAttribute {
readonly bool relative;

/// <summary>
/// Works a lot like the color picker, except for vectors.
/// </summary>
/// <param name="relative">Make the final vector relative to the transform?</param>
public VectorPickerAttribute(bool relative = false) {
this.relative = relative;
}
}

这是 PropertyDrawer:

[CustomPropertyDrawer(typeof(VectorPickerAttribute))]
public class VectorPickerDrawer : PropertyDrawer {
bool trackMouse = false;
SerializedProperty v;

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
if(property.propertyType == SerializedPropertyType.Vector2) {
Rect button = new Rect(position);
button.x = position.width - 2;
button.width = position.height;
bool pressed = GUI.Button(button, "");
if(pressed) {
trackMouse = true;
v = property;
}
else if(Input.GetMouseButtonDown(0)) trackMouse = false;

bool tracking = trackMouse && v.propertyPath == property.propertyPath;
if(tracking) {
property.vector2Value =
Camera.main.ScreenToWorldPoint(
GUIUtility.GUIToScreenPoint(
Event.current.mousePosition
));
}

GUI.enabled = !tracking;
EditorGUI.Vector2Field(position, label.text, property.vector2Value);
GUI.enabled = true;

EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}
}

这是迄今为止它所做的事情:

Image of VectorPickerAttribute in action.

您单击右侧的按钮,它会将矢量更新为鼠标位置,直到使用 Input.GetMouseButtonDown(0) 检测到左键单击。

问题:

  • 它只会检测实际位于检查器窗口上的点击。

  • 当您在检查器窗口外部单击时,它要么不会更改任何内容,要么会选择其他内容,因此它将关闭检查器(但由于它每次 OnGUI() 都会保存鼠标位置你点击的那个点将被保存到向量中,所以我想它可以工作??)。

我尝试用空白窗口覆盖屏幕,但无法让 GUI.WindowGUI.ModalWindow 在 PropertyDrawer 中执行任何操作。我也尝试过使用 GUI.UnfocusWindow(),但要么它在 PropertyDrawer 中不起作用,要么它仅适用于 Unity 的窗口或其他东西。

最佳答案

核心方面:

  • 覆盖 SceneView.onSceneGUIDelegate 以捕获 SceneView 上的任何鼠标事件

  • 使用ActiveEditorTracker.sharedTracker.isLocked锁定和解锁检查器以防止失去焦点(这将导致OnGUI不再被调用)

  • 使用Selection.activeGameObject并将其设置为抽屉所在的游戏对象,以防止失去对游戏对象的焦点(尤其是在ActiveEditorTracker. SharedTracker.isLocked 设置为 false 似乎会自动清除 Selection.activeGameObject)

  • 允许使用Escape键将值恢复为之前的值

  • 使用Event.current.Use();和/或Event.current = null;(我只是想非常确定)以防止事件传播并被其他人处理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Attribute for setting a vector by clicking on the screen in editor mode.
/// </summary>
public class VectorPickerAttribute : PropertyAttribute {
public readonly bool relative;

/// <summary>Works a lot like the color picker, except for vectors.</summary>
/// <param name="relative">Make the final vector relative the transform?</param>
public VectorPickerAttribute(bool relative = false) {
this.relative = relative;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(VectorPickerAttribute))]
public class VectorPickerDrawer : PropertyDrawer {
#region Variables
bool _trackMouse;
SerializedProperty _property;
MonoBehaviour script;

///<summary>Keep the currently selected object to avoid loosing focus while/after tracking</summary>
GameObject _mySelection;

///<summary>For reverting if tracking canceled</summary>
Vector2 _originalPosition;

///<summary>Flag for doing Setup only once</summary>
bool _setup;

/// <summary>Mouse position from scene view into the world.</summary>
Vector2 worldPoint;
#endregion

/// <summary>
/// Catch a click event while over the SceneView
/// </summary>
/// <param name="sceneView">The current scene view => might not work anymore with multiple SceneViews</param>
private void UpdateSceneView(SceneView sceneView) {

Camera cam = SceneView.lastActiveSceneView.camera;
worldPoint = Event.current.mousePosition;
worldPoint.y = Screen.height - worldPoint.y - 36.0f; // ??? Why that offset?!
worldPoint = cam.ScreenToWorldPoint(worldPoint);

VectorPickerAttribute vectorPicker = attribute as VectorPickerAttribute;
if(script != null && vectorPicker.relative) worldPoint -= (Vector2)script.transform.position;

// get current event
var e = Event.current;

// Only check while tracking
if(_trackMouse) {
if((e.type == EventType.MouseDown || e.type == EventType.MouseUp) && e.button == 0) {
OnTrackingEnds(false, e);
}
else {
// Prevent losing focus
Selection.activeGameObject = _mySelection;
}
}
else {
// Skip if event is Layout or Repaint
if(e.type == EventType.Layout || e.type == EventType.Repaint) return;

// Prevent Propagation
Event.current.Use();
Event.current = null;

// Unlock Inspector
ActiveEditorTracker.sharedTracker.isLocked = false;

// Prevent losing focus
Selection.activeGameObject = _mySelection;

// Remove SceneView callback
SceneView.onSceneGUIDelegate -= UpdateSceneView;

}
}

/// <summary>
/// Called when ending Tracking
/// </summary>
/// <param name="revert">flag whether to revert to previous value or not</param>
/// <param name="e">event that caused the ending</param>
/// <returns>Returns the vector value of the property that we are modifying.</returns>
private Vector2 OnTrackingEnds(bool revert, Event e) {
e.Use();
Event.current = null;
//Debug.Log("Vector Picker finished");

if(revert) {
// restore previous value
_property.vector2Value = _originalPosition;
//Debug.Log("Reverted");
}

// disable tracking
_trackMouse = false;

// Apply changes
_property.serializedObject.ApplyModifiedProperties();

return _property.vector2Value;
}

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
script = (MonoBehaviour)property.serializedObject.targetObject;

if(property.propertyType != SerializedPropertyType.Vector2) {
EditorGUI.HelpBox(position, "This Attribute requires Vector2", MessageType.Error);
return;
}

var e = Event.current;

if(!_setup) {
// store the selected Object (should be the one with this drawer active)
_mySelection = Selection.activeGameObject;
_property = property;

_setup = true;
}


// load current value into serialized properties
_property.serializedObject.Update();

//specific to the ONE property we are updating
bool trackingThis = _trackMouse && property.propertyPath == _property.propertyPath;

GUI.enabled = !trackingThis;
EditorGUI.PropertyField(position, property, label);
GUI.enabled = true;


// Write manually changed values to the serialized fields
_property.serializedObject.ApplyModifiedProperties();



if(!trackingThis) {
var button = new Rect(position) {
x = position.width - 2,
width = position.height
};

// if button wasn't pressed do nothing
if(!GUI.Button(button, "")) return;

// store current value in case of revert
_originalPosition = _property.vector2Value;

// enable tracking
_property = property;
_trackMouse = true;

// Lock the inspector so we cannot lose focus
ActiveEditorTracker.sharedTracker.isLocked = true;

// Prevent event propagation
e.Use();

//Debug.Log("Vector Picker started");
return;
}

// <<< This section is only reached if we are in tracking mode >>>

// Overwrite the onSceneGUIDelegate with a callback for the SceneView
SceneView.onSceneGUIDelegate = UpdateSceneView;

// Set to world position
_property.vector2Value = worldPoint;

// Track position until either Mouse button 0 (to confirm) or Escape (to cancel) is clicked
var mouseUpDown = (e.type == EventType.MouseUp || e.type == EventType.MouseDown) && e.button == 0;
if(mouseUpDown) {
// End the tracking, don't revert
property.vector2Value = OnTrackingEnds(false, e);
}
else if(e.type == EventType.KeyUp && _trackMouse && e.keyCode == KeyCode.Escape) {
// Cancel tracking via Escape => revert value
property.vector2Value = OnTrackingEnds(true, e);
}

property.serializedObject.ApplyModifiedProperties();

//This fixes "randomly stops updating for no reason".
EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}

我试图解释评论中的所有内容。当然这仍然有一些缺陷,在某些特殊情况下可能不起作用,但我希望它能朝着正确的方向发展。

关于unity-game-engine - 如何使用自定义编辑器属性从任何地方获取屏幕点击输入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53120463/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com