我已经从事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[];
我最近遇到了类似情况。我所做的是创建一个新的可编写脚本的对象“列表”数据类型。例如,您可以创建一个CardList类,该类是一个可编写脚本的对象,其中包含一个Cards数组(或List)。 (具有AddCard,RemoveCard,GetCard(int index)和GetRandomCard的方法。)
然后您可以创建一个新的CardList,并在检查器中将所有潜在的奖励卡添加到列表中。将CardList添加到Card Display Script,现在您可以访问列表中的任何卡。
在旁注中,我将为玩家的牌组,解锁的卡,坟墓场等创建CardList。然后,您可以轻松地执行类似的操作
unlockedCardList.AddCard(rewardCardsList.GetRandomCard());
因此,您具有Monobehaviour CardDisplayScript和ScriptableObject Card。在此之前的其他2条评论提到了Singleton和奖励卡列表。尽管它们都可以解决问题,但我已经成功使用了稍有不同的设计。我的方法允许敌人共享“公共奖励池”,并在死亡时退出该公共池。对于老板或稀有敌人,我喜欢创建一个稍有不同的“老板奖励池”。
正如您所提出的问题:
[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);
}
}
[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”检查,并适当地处理列表为空时的错误。
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”(或您愿意使用的其他名称),该实体直接引用可以产生奖励的奖励池。
public class LootableEntity : Monobehaviour
{
public CardRewardPool cardRewardPool;
public Card GetBattleReward()
{
return cardRewardPool.GetRandomCard();
}
}
此设计使您可以为每个敌人分配一个唯一的奖励池,或者让一些敌人共享奖励池。如果您不希望将卡分成不同的奖励池,则可以使用具有相同设计的通用奖励池。另外,您不再需要在每个实体上保留ScriptableObjects列表,因此您确实节省了一些内存。