0

我被要求在我的问题的先前版本中描述我的用例。因此,这篇文章非常详细,并详细介绍了我正在尝试做的事情。如果您遇到这个问题,希望它能帮助您解决自己的问题,这里有一个快速的 TL:DR,看看是否值得进一步研究:

TL:DR:我在我的项目中使用可编写脚本的对象,并希望找到将脚本附加到这些对象的最佳方法。我使用字符串在其他地方调用方法的想法是可能的,但被认为昂贵且容易出错。如果您想阅读为什么它被认为是这样,这里是我原来的问题的链接。

完整的问题

我目前正在创建自己的纸牌游戏(单人游戏,所以我不需要担心多人游戏或服务器的东西)。我创建了一个可编写脚本的对象 (CardAsset) 来处理我所有填充的卡片都需要的一些字段。像这样的东西:

  • 卡名(字符串)
  • 成本(整数)
  • 图像(图像)
  • 描述文本(字符串)

现在我的原型的所有其他东西都完成了(比赛场地、套牌、我可以实例化从我的 CardAsset 填充的卡片等),我需要开始为我的卡片实际制作效果并对其进行编程。

由于每张卡都不同,但有些效果是相同的(例如,“抽更多卡”和“对角色造成伤害”很常见),我想我可以通过将可编写脚本的对象 CardAsset 更改为节省大量时间包括一些基本的脚本和变量:

  • 卡名(字符串)

  • 成本(整数)

  • 图像(图像)

  • 描述文本(字符串)

  • 效果1(脚本)

  • Effect1Amount (int)

  • 效果2(脚本)

  • Effect2Amount (int)

  • 效果3(脚本)

  • Effect3Amount (int)

这样,我可以创建最多具有 3 种效果的 CardAssets(例如“对角色造成 X 伤害”或“绘制 Y 牌”),然后在我的游戏中使用一些东西来调用这些带有相关变量的脚本玩家打出该卡(将任何不需要的插槽保留为空)。虽然任何真正独特的卡都可能需要它自己的脚本,但我认为这会大大缩短时间(而不是单独编程每张卡)。

我的问题是我无法将脚本附加到我的 CardAsset,所以我想一个合适的解决方法是让我在 CardAsset 上的 Effect1/Effect2/Effect3 插槽中输入方法的名称(DealDamage、DrawCards)(将它们更改为一个字符串),然后让一些东西读取它们并在其他地方调用相关的方法。

我知道这个解决方案容易出错并且维护/更改可能会很痛苦,那么在这个特定项目中我可以有更好的方法吗?不幸的是,作为初学者,我目前缺乏元语言来轻松找到此问题的解决方案,因此任何指针都是最有益的。我很乐意自己做更多的研究,我只需要指出正确的方向。

4

2 回答 2

0

您可以为此使用抽象类(我建议只使用一两个函数)。

喜欢:

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()
    {
    
    }
}
于 2020-09-20T11:18:56.943 回答
0

我会建议一个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);
    }
}
于 2020-09-21T06:53:16.170 回答