在C# Unity中,可以将脚本设置为函数的参数吗?

huangapple go评论83阅读模式
英文:

Can a script be set as a parameter of a function in c# Unity?

问题

我有两个脚本中都有相同的函数 - Attack(),我想从一个具有该函数的类中继承它们。所以我的问题是,在C# Unity中,能否将脚本设置为函数的参数?

public void Attack()
{
    _animator.SetTrigger("Attack");

    Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
    foreach (Collider2D enemy in hitEnemies)
    {
        enemy.GetComponent<PlayerAnimator>().TakeDamage(_attackDamage);// 在第二个脚本中使用<EnemyAnimator>
    }
}
英文:

I have the same function - Attack() in two scripts and i would like to inherit them from a class which has this function. So my question is can a script be set as a parameter of a function in c# Unity?

public void Attack()
{
    _animator.SetTrigger(&quot;Attack&quot;);

    Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
    foreach (Collider2D enemy in hitEnemies)
    {
        enemy.GetComponent&lt;PlayerAnimator&gt;().TakeDamage(_attackDamage);// in second script there is &lt;EnemyAnimator&gt;
    }
}

答案1

得分: 1

以下是您要翻译的代码部分:

That's OOP basics - In such a case you have mainly two options:

You should have 

- an interface if it is only about having the same method signatures but completely different behavior

   public interface IDamageable
   {
       void TakeDamage(float damage);
   }

   And then

   public class PlayerAnimator : MonoBehaviour, IDamageable
   {
       public void TakeDamage(float damage)
       {
           Debug.Log("I'm a player and do a certain thing when I take {damage} amount of damage");
       }
   }

   public class EnemyAnimator : MonoBehaviour, IDamageable
   {
       public void TakeDamage(float damage)
       {
           Debug.Log("I'm an enmy and do something completely different when I take {damage} amount of damage");
       }
   }

- go through inheritance and have a common parent class which already implements common behavior and can then be extended - if needed at all

   // Careful with this naming in that case!
   public class Damageable : MonoBehaviour
   {
       virtual void TakeDamage(float damage)
       {
           Debug.Log("This is the common default behavior for my inheritors - except they overwrite or extend it");
       }
   }

   and

   public class PlayerAnimator : Damageable 
   {
       public override void TakeDamage(float damage)
       {
           // First call the default implementation
           base.TakeDamage(damage);

           Debug.Log("I'm a player and do some additional stuff");
       }
   }

   public class EnemyAnimator : Damageable 
   {
       public override void TakeDamage(float damage)
       {
           Debug.Log("I'm an enmy and decided to not do the default thing but something completely different");
       }
   }

Here if you don't need any different behavior between `EnemyAnimator` and `PlayerAnimator` the answer would be easy: Just stick to a single class then!

And then I would go

public void Attack()
{
    _animator.SetTrigger("Attack");

    var hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);

    foreach (Collider2D enemy in hitEnemies)
    {
        // OPTION A
        if(enemy.TryGetComponent<IDamageable>(out var damageable))
        // or OPTION B
        // if(enemy.TryGetComponent<Damageable>(out var damageable))
        {
            damageable.TakeDamage(_attackDamage)
        }
    }
}
英文:

That's OOP basics - In such a case you have mainly two options:

You should have

  • an interface if it is only about having the same method signatures but completely different behavior

    public interface IDamageable
    {
    void TakeDamage(float damage);
    }
    

    And then

    public class PlayerAnimator : MonoBehaviour, IDamageable
    {
    public void TakeDamage(float damage)
    {
    Debug.Log($&quot;I&#39;m a player and do a certain thing when I take {damage} amount of damage&quot;);
    }
    }
    public class EnemyAnimator : MonoBehaviour, IDamageable
    {
    public void TakeDamage(float damage)
    {
    Debug.Log($&quot;I&#39;m an enmy and do something completely different when I take {damage} amount of damage&quot;);
    }
    }
    
  • go through inheritance and have a common parent class which already implements common behavior and can then be extended - if needed at all

    // Careful with this naming in that case!
    public class Damageable : MonoBehaviour
    {
    virtual void TakeDamage(float damage)
    {
    Debug.Log(&quot;This is the common default behavior for my inheritors - except the overwrite or extend it&quot;);
    }
    }
    

    and

    public class PlayerAnimator : Damageable 
    {
    public override void TakeDamage(float damage)
    {
    // First call the default implementation
    base.TakeDamage(damage);
    Debug.Log($&quot;I&#39;m a player and do some additional stuff&quot;);
    }
    }
    public class EnemyAnimator : Damageable 
    {
    public override void TakeDamage(float damage)
    {
    Debug.Log($&quot;I&#39;m an enmy and decided to not do the default thing but something completely different&quot;);
    }
    }
    

    Here if you don't need any different behavior between EnemyAnimator and PlayerAnimator the answer would be easy: Just stick to a single class then!

And then I would go

public void Attack()
{
_animator.SetTrigger(&quot;Attack&quot;);
var hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
foreach (Collider2D enemy in hitEnemies)
{
// OPTION A
if(enemy.TryGetComponent&lt;IDamagable&gt;(out var damageable))
// or OPTION B
//if(enemy.TryGetComponent&lt;Damagable&gt;(out var damageable))
{
damageable.TakeDamage(_attackDamage)
}
}
}

答案2

得分: 0

答案是是的,但有一些限制。如果您想要在C# Unity中将脚本设置为函数的参数,您需要像以下示例中所示执行一些操作:

要将一个“Type”作为参数传递,您可以使您的方法接受一个泛型类型并将其限制为您的基类。

public static class AttackManager
{
    public static void Attack<T>(Animator attackerAnimator, Transform attacker, float attackRange, float damage, int targetLayer) where T : AnimatorBase
    {
        attackerAnimator.SetTrigger("Attack");

        Collider2D[] hitTargets = Physics2D.OverlapCircleAll(attacker.position, attackRange, targetLayer);
        foreach (Collider2D target in hitTargets)
        {
            target.GetComponent<T>().TakeDamage(damage);
        }
    }
}

请注意,我将您的“Attack()”方法转换为了一个接受攻击者和目标信息的泛型静态方法。

这个类型假设您的“PlayerAnimator”和“EnemyAnimator”都派生自一个基类,例如:

//我将其设为抽象类,您可以以其他方式做,如@derHugo的回答中所述
public abstract class AnimatorBase : MonoBehaviour
{
    public abstract void TakeDamage(float damage);
}
public class PlayerAnimator : AnimatorBase
{
    public override void TakeDamage(float damage)
    {
        Debug.Log("玩家受到伤害");
    }
}
public class EnemyAnimator : AnimatorBase
{
    public override void TakeDamage(float damage)
    {
        Debug.Log("敌人受到伤害");
    }
}

现在,您可以像这样调用函数:

AttackManager.Attack<PlayerAnimator>(attackerAnimator: animator,
                                 attacker: enemy,
                                 attackRange: range,
                                 damage: _attackDamage,
                                 targetLayer: LayerMask.GetMask("player"));

或者

AttackManager.Attack<EnemyAnimator>(attackerAnimator: animator,
                                 attacker: player,
                                 attackRange: range,
                                 damage: _attackDamage,
                                 targetLayer: LayerMask.GetMask("enemy"));
英文:

To answer:
> can a script be set as a parameter of a function in c# Unity?

The answer is yes, but with some constraints. If you want to use the GetComponent&lt;ScriptParameter&gt;() you would need do do something like generic method:

To pass a Type as a parameter you could make your method accept a generic type and constrain it to your base class.

public static class AttackManager
{
    public static void Attack&lt;T&gt;(Animator attackerAnimator, Transform attacker, float attackRange, float damage, int targetLayer) where T : AnimatorBase
    {
        attackerAnimator.SetTrigger(&quot;Attack&quot;);

        Collider2D[] hitTargets = Physics2D.OverlapCircleAll(attacker.position, attackRange, targetLayer);
        foreach (Collider2D target in hitTargets)
        {
            target.GetComponent&lt;T&gt;().TakeDamage(damage);
        }
    }
}

Notice I transformed your Attack() method into a generic static method that takes attacker and target info

The type assumes your PlayerAnimator and EnemyAnimator derive from a base class for example:

//I made it abstract you could do it other ways, such as mentioned in @derHugo answer
public abstract class AnimatorBase : MonoBehaviour
{
    public abstract void TakeDamage(float damage);
}
public class PlayerAnimator : AnimatorBase
{
    public override void TakeDamage(float damage)
    {
        Debug.Log(&quot;player took damage&quot;);
    }
}
public class EnemyAnimator : AnimatorBase
{
    public override void TakeDamage(float damage)
    {
        Debug.Log(&quot;Enemy took damage&quot;);
    }
}

Now you would just call the function as:

AttackManager.Attack&lt;PlayerAnimator&gt;(attackerAnimator: animator,
                                     attacker: enemy,
                                     attackRange: range,
                                     damage: _attackDamage,
                                     targetLayer: LayerMask.GetMask(&quot;player&quot;));

or

AttackManager.Attack&lt;EnemyAnimator&gt;(attackerAnimator: animator,
                                     attacker: player,
                                     attackRange: range,
                                     damage: _attackDamage,
                                     targetLayer: LayerMask.GetMask(&quot;enemy&quot;));

答案3

得分: -1

是的,可以将脚本设置为Unity C#函数的参数。您只需要首先提供脚本的名称,然后是您想要调用它的方式。就像这样:

public void Attack(EnemyAnimator enemyAnimator)
{
    _animator.SetTrigger("Attack");

    Collider2D[] hitEnemies = 
    Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, 
    _enemyLayer);
    foreach (Collider2D enemy in hitEnemies)
    {
        enemy.GetComponent<PlayerAnimator>().TakeDamage(_attackDamage);
    }
}
英文:

Yes, a script can be set as a parameter of a function in unity c#. All you need to do is first of all the script's name, followed by what you want to call it. Like this :

public void Attack(EnemyAnimator enemyAnimator)
{
_animator.SetTrigger(&quot;Attack&quot;);
Collider2D[] hitEnemies = 
Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, 
_enemyLayer);
foreach (Collider2D enemy in hitEnemies)
{
enemy.GetComponent&lt;PlayerAnimator&gt;().TakeDamage(_attackDamage);
}
}

huangapple
  • 本文由 发表于 2023年3月3日 18:49:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/75626088.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定