寻找比在 Scriptable Objects 中使用字符串在别处调用特定方法更好的解决方案
Posted
技术标签:
【中文标题】寻找比在 Scriptable Objects 中使用字符串在别处调用特定方法更好的解决方案【英文标题】:Looking for a better solution than using strings within Scriptable Objects to call specific methods elsewhere 【发布时间】:2021-01-06 11:24:32 【问题描述】:我被要求在我的问题的先前版本中描述我的用例。因此,这篇文章非常详细,并详细介绍了我正在尝试做的事情。如果您遇到这个问题是希望它能帮助您解决自己的问题,这里有一个快速的 TL:DR,看看是否值得进一步研究:
TL:DR: 我在我的项目中使用可编写脚本的对象,并希望找到将脚本附加到这些对象的最佳方法。我使用字符串在其他地方调用方法的想法是可能的,但被认为昂贵且容易出错。如果您想了解为什么会这样认为,这里是link to my original question。
完整问题
我目前正在创建自己的纸牌游戏(单人游戏,所以我不需要担心多人游戏或服务器的问题)。我创建了一个可编写脚本的对象 (CardAsset) 来处理我所有填充的卡片都需要的一些字段。像这样的东西:
卡名(字符串) 成本(整数) 图片(图片) 描述文本(字符串)现在我的原型的所有其他东西都完成了(比赛场地、套牌、我可以实例化从我的 CardAsset 填充的卡片等),我需要开始为我的卡片实际制作效果和对它们进行编程。
由于每张牌都不同,但有些效果是相同的(例如,“抽更多牌”和“对角色造成伤害”很常见),我想我可以通过更改我的脚本来节省大量时间对象 CardAsset 以包含一些基本脚本和变量:
卡名(字符串)
成本(整数)
图片(图片)
描述文本(字符串)
效果1(脚本)
Effect1Amount (int)
Effect2(脚本)
Effect2Amount (int)
Effect3(脚本)
Effect3Amount (int)
这样,我可以创建最多具有 3 种效果的 CardAssets(例如“对角色造成 X 伤害”或“抽 Y 牌”),然后在我的游戏中使用某些东西来调用这些脚本,并带有相关的玩家打出该卡时的变量(将任何不需要的插槽保留为空)。虽然任何真正独特的卡都可能需要它自己的脚本,但我认为这会大大缩短时间(而不是单独对每张卡进行编程)。
我的问题是我无法将脚本附加到 CardAsset,因此我认为合适的解决方法是在 CardAsset 的 Effect1/Effect2/Effect3 插槽中输入方法名称(DealDamage、DrawCards)(将它们更改为字符串),然后读取它们并在其他地方调用相关方法。
我了解此解决方案容易出错并且维护/更改可能会很痛苦,那么在这个特定项目中是否有更好的方法可以做到这一点?不幸的是,作为初学者,我目前缺乏元语言来轻松找到此问题的解决方案,因此任何指针都是最有益的。我很乐意自己做更多的研究,我只需要指出正确的方向。
【问题讨论】:
您可以阅读的内容可能会对您有所帮助。 1. 您可以使用装饰器或复合图案将多种效果组合成一个。这样您就不需要多种效果方法,也不会局限于三种效果。 2. 您可以阅读有关 C# 委托的信息。它们允许您将方法作为参数传递给另一个方法。我从 Unity/C# 开始学到的第一课 - 当字符串与显示文本无关时,忘记字符串的存在。 3. 或者您可以将效果设为可编写脚本的对象,并以您想要的任何组合将它们附加到 CardAssets。 不如使用接口并始终调用相同的方法呢?就像interface ICardEffect void RunEffect()
一样,您可以附加任意数量的效果并运行它们......
【参考方案1】:
您可以为此使用抽象类(我建议只使用一两个函数)。
喜欢:
public abstract class Effect
public virtual void runeffect(Card target)
public virtual void runeffect()
然后,对于您想要的每个效果,您将编写一个使用抽象效果类(继承)的脚本,并将您想要的效果添加到主脚本中。 所以你的主要卡片脚本将是这样的:
public Effect[] cardeffects; //add effects you want here
//like --> public Effect cardeffects=new Drawcard(2);
public void playthecard()
foreach(Effect e in cardeffects)
e.runeffect;
还有一个效果例子:
public class DrawCard : Effect
public override void runeffect()
【讨论】:
那么他会怎么做add them into your card in Unity.
? ;)
你是对的。我在想别的东西我正在编辑它。谢谢 :)【参考方案2】:
我会建议像 abstract
这样的课程
public abstract class CardEffect : ScriptableObject
// The GameManager reference might be useful later when you need e.g. StartCoroutine
// or a transform reference where to spawn objects to etc
// or simply to Invoke your different behaviours on
public abstract void RunEffect(GameManager behaviour);
然后您可以得出不同的效果及其行为,例如
[CreateAssetMenu]
public class DamageCardEffect : CardEffect
[SerializeField] private float damageAmount = 1.5f;
public override void RunEffect(GameManager gameManager)
gameManager.SelectCard(DealDamageToSelectedCard);
private void DealDamageToSelectedCard (Card card)
Card.DealDamage(damageAmount);
或者以同样的方式
[CreateAssetMenu]
public class DrawCardsEffect : CardEffect
[SerializeField] private int drawAmount = 3;
public override void RunEffect (GameManager gameManager)
gameManager.DrawCards(drawAmount);
等等……
那么在你的卡片里你就可以拥有
[CreateAssetMenu]
public class Card : ScriptableObject
[SerializeField] private CardEffect [] effects;
public void PlayCardEffects(GameManager gameManager)
foreach(var effect in effects)
effect.RunEffect(gameManager);
最后,在您的 GameManager 主控制器类中,您将实现相应的方法并引用不同的 Card
资产作为例如卡片组,例如
public class GameManager : MonoBehaviour
// Reference assets via Inspector or edit it on runtime
public List<Card> deck;
public List<Card> hand;
public void DrawCards(int amount)
for(var i = 0; i < amount; i ++)
if(deck.Count <= 0) return;
var card = deck[0];
deck.RemoveAt(0);
hand.Add(card);
public void SelectCard(Action<Card> onSelected)
StartCoroutine(SelectCardRoutine (onSelected));
private IEnumerator SelectCardRoutine(Action<Card> onSelected)
// Somehow wait until User selects a Card
// then Invoke the callback
onSelected?.Invoke(theSelectedCard);
【讨论】:
以上是关于寻找比在 Scriptable Objects 中使用字符串在别处调用特定方法更好的解决方案的主要内容,如果未能解决你的问题,请参考以下文章
Unity零基础到进阶 | Unity中Scriptable Object介绍学习
Unity零基础到进阶 | Unity中Scriptable Object介绍学习
为啥在大型数据库上查找具有特定 ID 的对象的 Django 查询比在较小数据库上要慢?