将 Unity 可编写脚本的对象附加到 GameObject
Attaching Unity Scriptable Object to GameObject
我已经做了大约两周的 Unity 教程,并且做了一些非常基础的东西。对不起,如果这是一个超级简单的问题,但我花了最后 5 个小时来解决这个问题并决定来这里。
到目前为止我做了什么:
我制作了一款非常基础的纸牌游戏,纸牌具有攻击力和生命值等属性。这些值存储在每张卡片的 ScriptableObject 中。
[CreateAssetMenu(fileName = "New Card", menuName = "Card")]
public class Card : ScriptableObject
{
public int instanceNumber;
public bool isDefaultCard = false;
public string cardName;
public string description;
public string flavorText;
public Sprite cardArt;
public int manaCost;
public int health;
public int attack;
public int characterClass;
public void printCardData()
{
Debug.Log(cardName + ": " + description);
}
}
现在我在 Unity 上还有一个卡片预制件 UI 我可以将预制件拖到场景中,然后将我的 ScriptableObject 拖到卡片显示脚本中,它会更新卡片并显示一切正常!
虽然我一辈子都想不出如何用代码来做到这一点。我不知道如何从代码访问我的 ScriptableObjects。
我知道我可以用这样的东西实例化预制件:
Instantiate(myPrefab, new Vector3(0, 0, 0), Quaternion.identity);
但是我该如何添加我的卡数据呢?我希望游戏在战斗结束时有奖励,每次都会显示随机选择的卡片,你可以选择一张。所以这意味着创建 3 个卡片对象,它们附加了一个随机的 ScriptableObject。但同样,无论我尝试什么,我似乎都无法找到从代码访问我的 ScriptableObjects。
如果这太简单了,再次抱歉,但我很迷茫。我需要一种方法来随时创建我的任何 ScriptableObjects 卡的对象。玩家将有很多次将更多卡牌添加到他们的牌组中,但他们的牌组一次只能有 20-30 张牌。
会有一些猜测(因此这里的术语很可能与代码中实际使用的术语不同),但作为示例。
您想在示例脚本中使用 Card 对象:
public class CardUser : MonoBehaviour
{
public Card cardScriptableObject {get;set;}
}
可能的方法很少,最简单的方法是使用单例(卡片的全球供应商),但通常有效的是在实例化预制件后始终分配引用,这是一个非常粗略的例子:
public class CardUserInstancer : MonoBehaviour
{
public CardUser cardUserPrefab;
public Card mainCard;
void Instantiate()
{
CardUser thisUser = Instantiate(cardUserPrefab, Vector3.zero, Quaternion.identity);
thisUser.card = mainCard;
}
}
或者,如果您不想在检查器中使用引用,您可以列出资源文件夹中的可用对象并选择一个
var cards= Resources.FindObjectsOfTypeAll(typeof(Card)) as Card[];
我最近 运行 遇到了类似的情况。我所做的是创建一个新的可编写脚本的对象 "list" 数据类型。例如,您可以创建一个 CardList class,它是一个可编写脚本的对象,其中包含一个卡片数组(或列表)。 (具有 AddCard、RemoveCard、GetCard(int index) 和 GetRandomCard 的方法。)
然后您可以创建一个新的 CardList,并在检查器中将所有可能的奖励卡添加到列表中。将 CardList 添加到卡片显示脚本中,现在您可以访问列表中的任何卡片。
附带说明一下,我会为玩家的套牌、解锁的牌、墓地等创建一个 CardList。然后您可以轻松地执行类似
的操作
unlockedCardList.AddCard(rewardCardsList.GetRandomCard());
所以你有一个 Monobehaviour CardDisplayScript 和一个 ScriptableObject Card。在此之前的其他 2 条评论提到了单例和奖励卡列表。虽然它们都可以解决问题,但我使用略有不同的设计取得了成功。我的方法允许敌人共享一个“共同的奖励池”,并在死亡时从那个共同的池中撤出。对于boss或者稀有敌人,我喜欢打造一个略有不同的“Boss奖励池”。
此设计需要 4 个脚本:
卡片 ScriptableObject
正如你的问题:
[CreateAssetMenu(fileName = "New Card", menuName = "Card")]
public class Card : ScriptableObject
{
public int instanceNumber;
public bool isDefaultCard = false;
public string cardName;
public string description;
public string flavorText;
public Sprite cardArt;
public int manaCost;
public int health;
public int attack;
public int characterClass;
public void printCardData()
{
Debug.Log(cardName + ": " + description);
}
}
CardRewardPool ScriptableObject
[CreateAssetMenu(fileName = "DefaultRewardPool", menuName = "Card Reward Pool")]
public class CardRewardPool : ScriptableObject
{
public List<Card> cards;
public Card GetRandomCard()
{
if(cards.Count > 0)
return cards[Random.Range(0,cards.Count)];
else
return null;
}
}
如果奖励池可能为空,您应该先检查列表中是否有卡片来处理(如上面的代码所示)。如果奖励池永远不应该为空,那么您可以删除“if”检查并适当处理列表为空时的错误。
CardDisplayScript 单一行为
public class CardDisplayScript : Monobehaviour
{
public Card card;
//... other properties
public void UpdateCard(Card _card)
{
card = _card;
//... reload in game attributes using the new card
}
}
LootableEntity 单一行为
这个脚本可以附加到任何可以奖励卡片的东西上(敌人、奖励宝箱、故事奖励等)。在这种情况下,如果战斗结束,那么你刚刚击败的任何敌人都应该附加一个“LootableEntity”(或者你喜欢的其他名称),它直接引用可以产生奖励的奖励池。
public class LootableEntity : Monobehaviour
{
public CardRewardPool cardRewardPool;
public Card GetBattleReward()
{
return cardRewardPool.GetRandomCard();
}
}
最后的评论
此设计允许您为每个敌人分配一个独特的奖励池,或者让一些敌人共享一个奖励池。如果您不想将您的卡片分成不同的奖励池,您可以拥有一个具有相同设计的通用奖励池。此外,您不再需要在每个实体上保留一个 ScriptableObjects 列表,因此您确实节省了一些内存。
我已经做了大约两周的 Unity 教程,并且做了一些非常基础的东西。对不起,如果这是一个超级简单的问题,但我花了最后 5 个小时来解决这个问题并决定来这里。
到目前为止我做了什么:
我制作了一款非常基础的纸牌游戏,纸牌具有攻击力和生命值等属性。这些值存储在每张卡片的 ScriptableObject 中。
[CreateAssetMenu(fileName = "New Card", menuName = "Card")]
public class Card : ScriptableObject
{
public int instanceNumber;
public bool isDefaultCard = false;
public string cardName;
public string description;
public string flavorText;
public Sprite cardArt;
public int manaCost;
public int health;
public int attack;
public int characterClass;
public void printCardData()
{
Debug.Log(cardName + ": " + description);
}
}
现在我在 Unity 上还有一个卡片预制件 UI 我可以将预制件拖到场景中,然后将我的 ScriptableObject 拖到卡片显示脚本中,它会更新卡片并显示一切正常!
虽然我一辈子都想不出如何用代码来做到这一点。我不知道如何从代码访问我的 ScriptableObjects。
我知道我可以用这样的东西实例化预制件:
Instantiate(myPrefab, new Vector3(0, 0, 0), Quaternion.identity);
但是我该如何添加我的卡数据呢?我希望游戏在战斗结束时有奖励,每次都会显示随机选择的卡片,你可以选择一张。所以这意味着创建 3 个卡片对象,它们附加了一个随机的 ScriptableObject。但同样,无论我尝试什么,我似乎都无法找到从代码访问我的 ScriptableObjects。
如果这太简单了,再次抱歉,但我很迷茫。我需要一种方法来随时创建我的任何 ScriptableObjects 卡的对象。玩家将有很多次将更多卡牌添加到他们的牌组中,但他们的牌组一次只能有 20-30 张牌。
会有一些猜测(因此这里的术语很可能与代码中实际使用的术语不同),但作为示例。
您想在示例脚本中使用 Card 对象:
public class CardUser : MonoBehaviour
{
public Card cardScriptableObject {get;set;}
}
可能的方法很少,最简单的方法是使用单例(卡片的全球供应商),但通常有效的是在实例化预制件后始终分配引用,这是一个非常粗略的例子:
public class CardUserInstancer : MonoBehaviour
{
public CardUser cardUserPrefab;
public Card mainCard;
void Instantiate()
{
CardUser thisUser = Instantiate(cardUserPrefab, Vector3.zero, Quaternion.identity);
thisUser.card = mainCard;
}
}
或者,如果您不想在检查器中使用引用,您可以列出资源文件夹中的可用对象并选择一个
var cards= Resources.FindObjectsOfTypeAll(typeof(Card)) as Card[];
我最近 运行 遇到了类似的情况。我所做的是创建一个新的可编写脚本的对象 "list" 数据类型。例如,您可以创建一个 CardList class,它是一个可编写脚本的对象,其中包含一个卡片数组(或列表)。 (具有 AddCard、RemoveCard、GetCard(int index) 和 GetRandomCard 的方法。)
然后您可以创建一个新的 CardList,并在检查器中将所有可能的奖励卡添加到列表中。将 CardList 添加到卡片显示脚本中,现在您可以访问列表中的任何卡片。
附带说明一下,我会为玩家的套牌、解锁的牌、墓地等创建一个 CardList。然后您可以轻松地执行类似
的操作unlockedCardList.AddCard(rewardCardsList.GetRandomCard());
所以你有一个 Monobehaviour CardDisplayScript 和一个 ScriptableObject Card。在此之前的其他 2 条评论提到了单例和奖励卡列表。虽然它们都可以解决问题,但我使用略有不同的设计取得了成功。我的方法允许敌人共享一个“共同的奖励池”,并在死亡时从那个共同的池中撤出。对于boss或者稀有敌人,我喜欢打造一个略有不同的“Boss奖励池”。
此设计需要 4 个脚本:
卡片 ScriptableObject
正如你的问题:
[CreateAssetMenu(fileName = "New Card", menuName = "Card")]
public class Card : ScriptableObject
{
public int instanceNumber;
public bool isDefaultCard = false;
public string cardName;
public string description;
public string flavorText;
public Sprite cardArt;
public int manaCost;
public int health;
public int attack;
public int characterClass;
public void printCardData()
{
Debug.Log(cardName + ": " + description);
}
}
CardRewardPool ScriptableObject
[CreateAssetMenu(fileName = "DefaultRewardPool", menuName = "Card Reward Pool")]
public class CardRewardPool : ScriptableObject
{
public List<Card> cards;
public Card GetRandomCard()
{
if(cards.Count > 0)
return cards[Random.Range(0,cards.Count)];
else
return null;
}
}
如果奖励池可能为空,您应该先检查列表中是否有卡片来处理(如上面的代码所示)。如果奖励池永远不应该为空,那么您可以删除“if”检查并适当处理列表为空时的错误。
CardDisplayScript 单一行为
public class CardDisplayScript : Monobehaviour
{
public Card card;
//... other properties
public void UpdateCard(Card _card)
{
card = _card;
//... reload in game attributes using the new card
}
}
LootableEntity 单一行为
这个脚本可以附加到任何可以奖励卡片的东西上(敌人、奖励宝箱、故事奖励等)。在这种情况下,如果战斗结束,那么你刚刚击败的任何敌人都应该附加一个“LootableEntity”(或者你喜欢的其他名称),它直接引用可以产生奖励的奖励池。
public class LootableEntity : Monobehaviour
{
public CardRewardPool cardRewardPool;
public Card GetBattleReward()
{
return cardRewardPool.GetRandomCard();
}
}
最后的评论
此设计允许您为每个敌人分配一个独特的奖励池,或者让一些敌人共享一个奖励池。如果您不想将您的卡片分成不同的奖励池,您可以拥有一个具有相同设计的通用奖励池。此外,您不再需要在每个实体上保留一个 ScriptableObjects 列表,因此您确实节省了一些内存。