Unity 防止通过脚本编辑变量
Unity preventing editing of variable through script
我试图在将鼠标悬停在按钮上时弹出工具提示。我在从单独的线程调用主线程时遇到了一些问题,因为主线程是您可以从中编辑游戏对象的唯一线程。所以我决定使用布尔值和更新函数来获得所需的结果。但是,我 运行 遇到了一个我以前从未见过的问题。我将我的布尔值 showToolTip 设置为 true,这应该会触发创建弹出功能。但是,当我的更新函数运行时,showTooltip 始终为 false。我知道当我将它设置为 true 时,它就变成了 true,因为我在 requestShowTooltip 函数中使用调试语句来看到这一点。但是,更新功能运行时它始终为 false。我通过将 showToolTip 和 removeToolTip 更改为 public 布尔值来进行一些测试。我可以在 运行 应用程序时在编辑器中更改它们,当我通过编辑器手动更改它们时,它们按预期 100% 工作,并且工具提示将在我更改相关布尔值时出现和隐藏。但是,当它们从脚本中更改时,它们将保留为 false。我也试过让它们未初始化并在开始时将它们初始化为 false,但我仍然遇到同样的问题。我什至尝试将 "showTooltip = true;" 从线程中取出并让函数立即执行,但我仍然遇到同样的问题。调用更新函数时,showTooltip 始终为 false。我通过在报告 showTootip 和 removeTooltip 布尔值的更新函数中添加调试语句来确定这一点。此外,无论如何, "Calling create tooltip" 永远不会出现在控制台中。除了当我制作布尔值 public 并通过编辑器更改它们时。
关于我如何设置的进一步解释。我制作了一个工具提示游戏对象,它是 canvas 的子项,其中包含我想要工具提示的按钮。我将工具提示脚本添加到工具提示对象。我在按钮游戏对象中创建了事件触发器。 Pointer Enter 触发器调用 requestShowTooltip() 函数,Pointer Exit 触发器调用 requestHideTooltip() 函数。
工具提示背景和工具提示文本游戏对象也是 canvas 的子对象。
public class Tooltips : MonoBehaviour
{
private GameObject toolTipBackground;
private GameObject toolTipText;
private GameObject inButtonObject;
private bool showTooltip = false;
private bool removeTooltip = false;
void Start()
{
toolTipBackground = GameObject.Find("Tooltip background");
toolTipText = GameObject.Find("Tooltip Text");
//inButton = GameObject.Find("Aft Button");
toolTipBackground.SetActive(false);
toolTipText.SetActive(false);
//Debug.Log("Tool Tip Start");
}
void Update()
{
// show the tooltip when the appropriate boolean has been set
// to true
if(removeTooltip)
{
hideTooltip();
}
// this if statement is always false, because showTooltip is
// always false when the update function is called??
if(showTooltip)
{
Debug.Log("Calling create tooltip");
createTooltip();
}
}
// A timed request for a tooltip to show. The tooltip should
// currently show one second after the request has been made. The
// text of the tooltip will be equal to the passed object's name
public void requestShowTooltip(GameObject inButtonObject)
{
Thread thread = new Thread(delegate ()
{
System.Threading.Thread.Sleep(1000);
this.inButtonObject = inButtonObject;
// I know this runs, but showTooltip is always returning
// to false when the update function is called??
showTooltip = true;
removeTooltip = false;
Debug.Log("Request function completed");
});
thread.start();
}
public void createTooltip()
{
toolTipText.SetActive(true);
toolTipBackground.SetActive(true);
string labelString = inButtonObject.name;
Button inButton = inButtonObject.GetComponent<Button>();
int width = labelString.Length * 10;
int height = 35;
Vector2 size = new Vector2(width, height);
Vector3 position = new Vector3(inButton.transform.x,
inButton.transform.position.y,
inButton.transform.position.z + 1);
toolTipBackground.GetComponent<RectTransform>().
sizeDelta = size;
toolTipText.GetComponent<RectTransform>().sizeDelta = size;
toolTipBackground.transform.position = position;
toolTipText.transform.position = position;
toolTipText.GetComponent<Text>().text = labelString;
showTooltip = false;
}
public void requestHideTooltip()
{
removeTooltip = true;
showTooltip = false;
}
public void hideTooltip()
{
Vector2 hide = new Vector2(0, 0);
toolTipBackground.GetComponent<RectTransform>().
sizeDelta = hide;
toolTipText.GetComponent<RectTransform>().sizeDelta = hide;
toolTipText.setActive(false);
toolTopBackground.SetActive(false);
removeTooltip = false;
showTooltip = false;
}
}
由于基本上 Unity 中的所有内容都需要在主线程中调用(极少数例外),我建议使用 ConcurrentQueue<Action>
and TryDequeue
更改您的代码,让主线程处理所有响应线程。
// a queue for storing actions that shall be handled by the main thread
// a ConcurrentQueue in specific is thread-save
private ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
private void Update()
{
// invoke all actions from the threads in the main thread
while(!actions.IsEmpty)
{
// TryDequeue writes the first entry to currentAction
// and at the same time removes it from the queue
// if it was successfull invoke the action otherwise do nothing
if(actions.TryDequeue(out var currentAction))
{
currentAction?.Invoke();
}
}
if(removeTooltip)
{
hideTooltip();
}
if(showTooltip)
{
Debug.Log("Calling create tooltip");
createTooltip();
}
}
然后在你的 Thread
中,不是让线程直接执行这些东西本身,而是使用 actions
队列将它传回主线程,并添加一个新的 Action
到结束使用 Enqueue
public void requestShowTooltip(GameObject inButtonObject)
{
Thread thread = new Thread(delegate()
{
System.Threading.Thread.Sleep(1000);
// add an Action to the end of the queue
// e.g. as lambda-expression
actions.Enqueue(()=>
{
this.inButtonObject = inButtonObject;
// I know this runs, but showTooltip is always returning
// to false when the update function is called??
showTooltip = true;
removeTooltip = false;
Debug.Log("Request function completed");
});
});
thread.start();
}
您可能想顺便考虑一下使 inButtonObject
成为 Button
类型 - 首先是为了确保传递的对象始终具有 a/is 和 Button
,其次您可以摆脱性能紧张的 GetComponent
调用。
我宁愿以同样的方式存储
RectTransform toolTipBackgroundRectTransform;
Text toolTipText;
RectTransform toolTipTextrectTransform;
因为这些是您可以在 Start
中获得 一次 的组件,然后您可以 re-use 始终使用相同的引用并进一步删除所有内容GetComponent
调用。
我试图在将鼠标悬停在按钮上时弹出工具提示。我在从单独的线程调用主线程时遇到了一些问题,因为主线程是您可以从中编辑游戏对象的唯一线程。所以我决定使用布尔值和更新函数来获得所需的结果。但是,我 运行 遇到了一个我以前从未见过的问题。我将我的布尔值 showToolTip 设置为 true,这应该会触发创建弹出功能。但是,当我的更新函数运行时,showTooltip 始终为 false。我知道当我将它设置为 true 时,它就变成了 true,因为我在 requestShowTooltip 函数中使用调试语句来看到这一点。但是,更新功能运行时它始终为 false。我通过将 showToolTip 和 removeToolTip 更改为 public 布尔值来进行一些测试。我可以在 运行 应用程序时在编辑器中更改它们,当我通过编辑器手动更改它们时,它们按预期 100% 工作,并且工具提示将在我更改相关布尔值时出现和隐藏。但是,当它们从脚本中更改时,它们将保留为 false。我也试过让它们未初始化并在开始时将它们初始化为 false,但我仍然遇到同样的问题。我什至尝试将 "showTooltip = true;" 从线程中取出并让函数立即执行,但我仍然遇到同样的问题。调用更新函数时,showTooltip 始终为 false。我通过在报告 showTootip 和 removeTooltip 布尔值的更新函数中添加调试语句来确定这一点。此外,无论如何, "Calling create tooltip" 永远不会出现在控制台中。除了当我制作布尔值 public 并通过编辑器更改它们时。
关于我如何设置的进一步解释。我制作了一个工具提示游戏对象,它是 canvas 的子项,其中包含我想要工具提示的按钮。我将工具提示脚本添加到工具提示对象。我在按钮游戏对象中创建了事件触发器。 Pointer Enter 触发器调用 requestShowTooltip() 函数,Pointer Exit 触发器调用 requestHideTooltip() 函数。
工具提示背景和工具提示文本游戏对象也是 canvas 的子对象。
public class Tooltips : MonoBehaviour
{
private GameObject toolTipBackground;
private GameObject toolTipText;
private GameObject inButtonObject;
private bool showTooltip = false;
private bool removeTooltip = false;
void Start()
{
toolTipBackground = GameObject.Find("Tooltip background");
toolTipText = GameObject.Find("Tooltip Text");
//inButton = GameObject.Find("Aft Button");
toolTipBackground.SetActive(false);
toolTipText.SetActive(false);
//Debug.Log("Tool Tip Start");
}
void Update()
{
// show the tooltip when the appropriate boolean has been set
// to true
if(removeTooltip)
{
hideTooltip();
}
// this if statement is always false, because showTooltip is
// always false when the update function is called??
if(showTooltip)
{
Debug.Log("Calling create tooltip");
createTooltip();
}
}
// A timed request for a tooltip to show. The tooltip should
// currently show one second after the request has been made. The
// text of the tooltip will be equal to the passed object's name
public void requestShowTooltip(GameObject inButtonObject)
{
Thread thread = new Thread(delegate ()
{
System.Threading.Thread.Sleep(1000);
this.inButtonObject = inButtonObject;
// I know this runs, but showTooltip is always returning
// to false when the update function is called??
showTooltip = true;
removeTooltip = false;
Debug.Log("Request function completed");
});
thread.start();
}
public void createTooltip()
{
toolTipText.SetActive(true);
toolTipBackground.SetActive(true);
string labelString = inButtonObject.name;
Button inButton = inButtonObject.GetComponent<Button>();
int width = labelString.Length * 10;
int height = 35;
Vector2 size = new Vector2(width, height);
Vector3 position = new Vector3(inButton.transform.x,
inButton.transform.position.y,
inButton.transform.position.z + 1);
toolTipBackground.GetComponent<RectTransform>().
sizeDelta = size;
toolTipText.GetComponent<RectTransform>().sizeDelta = size;
toolTipBackground.transform.position = position;
toolTipText.transform.position = position;
toolTipText.GetComponent<Text>().text = labelString;
showTooltip = false;
}
public void requestHideTooltip()
{
removeTooltip = true;
showTooltip = false;
}
public void hideTooltip()
{
Vector2 hide = new Vector2(0, 0);
toolTipBackground.GetComponent<RectTransform>().
sizeDelta = hide;
toolTipText.GetComponent<RectTransform>().sizeDelta = hide;
toolTipText.setActive(false);
toolTopBackground.SetActive(false);
removeTooltip = false;
showTooltip = false;
}
}
由于基本上 Unity 中的所有内容都需要在主线程中调用(极少数例外),我建议使用 ConcurrentQueue<Action>
and TryDequeue
更改您的代码,让主线程处理所有响应线程。
// a queue for storing actions that shall be handled by the main thread
// a ConcurrentQueue in specific is thread-save
private ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
private void Update()
{
// invoke all actions from the threads in the main thread
while(!actions.IsEmpty)
{
// TryDequeue writes the first entry to currentAction
// and at the same time removes it from the queue
// if it was successfull invoke the action otherwise do nothing
if(actions.TryDequeue(out var currentAction))
{
currentAction?.Invoke();
}
}
if(removeTooltip)
{
hideTooltip();
}
if(showTooltip)
{
Debug.Log("Calling create tooltip");
createTooltip();
}
}
然后在你的 Thread
中,不是让线程直接执行这些东西本身,而是使用 actions
队列将它传回主线程,并添加一个新的 Action
到结束使用 Enqueue
public void requestShowTooltip(GameObject inButtonObject)
{
Thread thread = new Thread(delegate()
{
System.Threading.Thread.Sleep(1000);
// add an Action to the end of the queue
// e.g. as lambda-expression
actions.Enqueue(()=>
{
this.inButtonObject = inButtonObject;
// I know this runs, but showTooltip is always returning
// to false when the update function is called??
showTooltip = true;
removeTooltip = false;
Debug.Log("Request function completed");
});
});
thread.start();
}
您可能想顺便考虑一下使 inButtonObject
成为 Button
类型 - 首先是为了确保传递的对象始终具有 a/is 和 Button
,其次您可以摆脱性能紧张的 GetComponent
调用。
我宁愿以同样的方式存储
RectTransform toolTipBackgroundRectTransform;
Text toolTipText;
RectTransform toolTipTextrectTransform;
因为这些是您可以在 Start
中获得 一次 的组件,然后您可以 re-use 始终使用相同的引用并进一步删除所有内容GetComponent
调用。