我正在制作一个简单的套牌/卡片项目。我的项目有一个卡片预制件,带有一个Canvas子,它包含套装/卡片信息的UI版本。信息可以用一张卡显示,但在涉及多张卡时会出现问题。我对此问题的理论归结于GameObject.FindGameObjectWithTag(string)查找保存标记的gameObject的第一个实例。所以,当我画多张牌时,它只会重写第一张牌,而不是在其他牌上画画。
我已经尝试过Transform.Find(string),但是尽管在编辑器中设置了游戏对象,但它已经显示为null。一种解决方案是在名称后面使用带有数字的多个标签,ala topNum1,topNum2等,但每次执行3次52次不同的数字听起来非常重复,令人沮丧。有更好的方法吗?
以下卡片代码:
func imgtagAction(sender: UIButton!) {
let face = self.faces[selectedItem]
}
非常感谢任何和所有帮助。谢谢你的时间。
编辑:由于有几个人要求它,我编辑了这个问题,以包含调用此类的代码:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
/// <summary>
/// This class sets up the card object, which can be used in a variety of games.
/// </summary>
public class Card: MonoBehaviour
{
int cardNum;//The Value of the card.
int cardType;//The ‘Suit’ of the card. Hearts, Spades, Diamonds, Clubs
//Text topText;
Text botText;
Text faceText;
Text topText;
//Creates a default card.
public Card()
{
cardNum = -1;
cardType = -1;
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
//topText = gameObject.transform.FindChild("Canvas").gameObject.transform.FindChild("TopCardValue").GetComponent<Text>();
botText = GameObject.FindGameObjectWithTag("botText").GetComponent<Text>();
faceText = GameObject.FindGameObjectWithTag("faceText").GetComponent<Text>();
}
//Creates a custom card, with the provided values.
public Card(int cN, int cT)
{
cardNum = cN;
cardType = cT;
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
//topText = gameObject.transform.FindChild("Canvas").gameObject.transform.FindChild("TopCardValue").GetComponent<Text>();
botText = GameObject.FindGameObjectWithTag("botText").GetComponent<Text>();
faceText = GameObject.FindGameObjectWithTag("faceText").GetComponent<Text>();
}
//returns the card’s value.
public int getCardNum()
{
return cardNum;
}
//returns the card’s suit.
public int getCardType()
{
return cardType;
}
//Sets the card’s value.
public void setCardNum(int newNum)
{
cardNum = newNum;
}
//Sets the card’s suit.
public void setCardType(int newType)
{
cardType = newType;
}
//Checks if the card’s value is a face card (Jack, Queen, King, or Ace)
public bool checkIfFace()
{
if (getCardNum() > 10 && getCardNum() < 15 || getCardNum() == 0)
return true;
else
return false;
}
//Checks if the card is a valid card.
public bool checkifValid()
{
if (getCardType() < -1 || getCardType() > 4)
{
Debug.LogError("Error: Card Type not valid. Card type is: " + getCardType());
return false;
}
if (getCardNum() < 0 || getCardNum() > 15)
{
Debug.LogError("Error: Card Value not valid. Card value is : " + getCardNum());
return false;
}
return true;
}
//Prints out the card information.
public void printOutCardInfo()
{
string value = "";
string suit = "";
if (getCardNum() == 1)
value = (" Ace");
else if (getCardNum() > 1 && getCardNum() < 11)
value = (getCardNum().ToString());
else if (getCardNum() == 11)
value = ("Jack");
else if (getCardNum() == 12)
value = ("Queen");
else if (getCardNum() == 13)
value = ("King");
else
Debug.LogError("Error: No Num Found! The number in question is: " + getCardNum());
switch(getCardType())
{
case 0:
suit = ("Hearts");
break;
case 1:
suit = ("Spades");
break;
case 2:
suit = ("Diamonds");
break;
case 3:
suit = ("Clubs");
break;
default:
Debug.LogError("Error: Suit not found.");
break;
}
topText.text = value;
botText.text = value;
faceText.text = suit;
}
}
答案 0 :(得分:1)
这是因为您从构造函数调用Unity API函数。基本上,您在反序列化期间和另一个Thread
中执行此操作。
在 Unity 5.3.4f1 及以下版本中,Find
函数在从构造函数调用时会无声地失败并且您不会知道。这是Unity中难以跟踪的错误之一。
在 Unity 5.4 及更高版本中,Unity decided添加错误消息以提醒您此问题。您现在还没有看到它,因为您仍在使用 5.3 。错误就是同伴:
不允许从MonoBehaviour调用FindGameObjectWithTag 构造函数(或实例字段初始化程序),在Awake或Start中调用它 代替。来自MonoBehaviour&#39; Card&#39;在游戏对象&#39; Cube&#39;。
在构造函数中调用Find
函数时,会出现类似的错误消息。
继续阅读更多描述性信息和解决方案:
MonoBehaviour
vs不继承自MonoBehaviour
继承MonoBehaviour
:
1 。您可以将脚本附加到GameObject。
2 。您无法使用new
关键字创建从MonoBehaviour
继承的脚本的新实例。由于deckOfCards.Add(new Card(i, j));
继承自Card
,因此MonoBehaviour
在这种情况下出错了。
3.您使用gameobject.AddComponent<Card>()
或Instantiate
(克隆预制)功能来创建新的脚本实例。最后有一个例子。
在Unity中使用构造函数的规则:
1 。不要在继承自MonoBehaviour
的脚本中使用构造函数,除非您了解Unity内部的内容。
2 。如果您要使用构造函数,请不要从MonoBehaviour
继承脚本。
3 。如果您违反规则#2,请不要在继承自MonoBehaviour
的类的构造函数中使用任何Unity API。
为什么?
您无法从其他线程调用Unity API。它会失败。您将获得异常,否则它将无声地失败。
这与线程有什么关系?
从Unity中的另一个Thread
调用构造函数。
当脚本附加到GameObject并且该脚本继承自MonoBehaviour
并且具有构造函数时,该构造函数首先从Unity的主线程调用(这很好)然后再从另一个 Thread
(非Unity的主要Thread
)。这违反了规则#3 。您不能从其他函数使用Unity API。
您可以通过运行以下代码来证明这一点:
using UnityEngine;
using System.Threading;
public class Card : MonoBehaviour
{
public Card()
{
Debug.Log("Constructor Thread ID: " + Thread.CurrentThread.ManagedThreadId);
}
void Start()
{
Debug.Log("Start() function Thread ID: " + Thread.CurrentThread.ManagedThreadId);
}
// Update is called once per frame
void Update()
{
Debug.Log("Update() function Thread ID: " + Thread.CurrentThread.ManagedThreadId);
}
}
附加到GameObject时的输出:
构造函数线程ID:1
构造函数线程ID:20
Start()函数线程ID 1
Update()函数线程ID:1
正如您所看到的,Start()
和Update()
函数是从相同的Thread
( ID 1 )调用的,这是{{1} }}。构造函数也从主Thread
调用,然后再从另一个Thread
( ID 20 )调用。
BAD代码示例:因为脚本中有一个继承自Thread
的构造函数。同样糟糕,因为使用MonoBehaviour
关键字创建了新实例。
new
然后使用public class Card : MonoBehaviour
{
Text topText;
//Bad, because `MonoBehaviour` is inherited
public Card()
{
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
}
}
关键字创建新实例:
new
//错误,因为Card card = new Card();
是继承的
好代码示例:
MonoBehaviour
然后使用public class Card : MonoBehaviour
{
Text topText;
public Awake()
{
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
}
}
函数创建新实例:
AddComponent
使用Card card = gameObject.AddComponent<Card>()
函数从预制件中克隆:
Instantiate
不继承自public Card cardPrfab;
Card card = (Card)Instantiate(cardPrfab);
:
1 。您无法将脚本附加到GameObject,但您可以从其他脚本中使用它。
2 。当您不从MonoBehaviour
继承时,您可以使用new
关键字创建新的脚本实例。
MonoBehaviour
然后,您可以使用public class Card
{
Text topText;
//Constructor
//Correct, because no `MonoBehaviour` inherited
public Card()
{
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
}
}
关键字创建新实例,例如:
new
//正确,因为没有Card card = new Card();
继承
解决方案:
1 。如果您决定继承MonoBehaviour
并且必须将脚本附加到GameObject,则必须删除所有构造函数并放置代码将它们放入MonoBehaviour
或Awake()
函数中。 Unity会自动调用Start()
和Awake()
函数,您可以使用它们初始化变量。您不必手动调用它们。 不使用Start()
关键字来创建从new
继承的脚本实例。
2 。如果您决定不继承MonoBehaviour
并且不需要将脚本附加到GameObject ,您可以使用与当前代码中相同的构造函数,并且可以在这些构造函数中使用Unity的API。您现在可以使用MonoBehaviour
关键字来创建脚本的实例,因为它没有&#39 ; t继承自new
。
答案 1 :(得分:0)
根据我对代码和问题的理解,每张卡都从同一个GameObject获取它的文本,因为它们都是按文本搜索相同的Object。 我相信如果您在每个对象的Start()方法中实际初始化要获取的值,则可能会解决您的问题。
因此,对于绘图卡而言,每个正在绘制的游戏对象都会收到相同的文本。
正如答案中所述,比我更快,尽量不要使用Find(),因为当涉及更多卡片甚至只是对象时它会变得非常慢。
尝试设置要在预制件上的脚本内部的Start()方法显示的文本,然后您可以轻松访问该对象的所有属性,如果需要,还可以使用所有卡片的数据库,从中可以将文本引用到绘制。
但是,对于你所要问的问题,我可能完全错了。 这就是我要做的,因为你说所有的帮助都是受欢迎的。)
答案 2 :(得分:0)
Transform.Find()/ Transform.FindChild()正在搜索直接儿童。 我相信,你试图找到一些subChild,这就是它返回null的原因。
所以如果你必须找到具有位置的“心脏”gameObject:room / human / body / heart 在“room”元素内,你需要执行:
Transform.Find("human/body/heart")
但不是
Transform.Find("heart")
因为“心脏”不是“房间”游戏对象的子游戏