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

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

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

问题

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

  1. public void Attack()
  2. {
  3. _animator.SetTrigger("Attack");
  4. Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
  5. foreach (Collider2D enemy in hitEnemies)
  6. {
  7. enemy.GetComponent<PlayerAnimator>().TakeDamage(_attackDamage);// 在第二个脚本中使用<EnemyAnimator>
  8. }
  9. }
英文:

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?

  1. public void Attack()
  2. {
  3. _animator.SetTrigger(&quot;Attack&quot;);
  4. Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
  5. foreach (Collider2D enemy in hitEnemies)
  6. {
  7. enemy.GetComponent&lt;PlayerAnimator&gt;().TakeDamage(_attackDamage);// in second script there is &lt;EnemyAnimator&gt;
  8. }
  9. }

答案1

得分: 1

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

  1. That's OOP basics - In such a case you have mainly two options:
  2. You should have
  3. - an interface if it is only about having the same method signatures but completely different behavior
  4. public interface IDamageable
  5. {
  6. void TakeDamage(float damage);
  7. }
  8. And then
  9. public class PlayerAnimator : MonoBehaviour, IDamageable
  10. {
  11. public void TakeDamage(float damage)
  12. {
  13. Debug.Log("I'm a player and do a certain thing when I take {damage} amount of damage");
  14. }
  15. }
  16. public class EnemyAnimator : MonoBehaviour, IDamageable
  17. {
  18. public void TakeDamage(float damage)
  19. {
  20. Debug.Log("I'm an enmy and do something completely different when I take {damage} amount of damage");
  21. }
  22. }
  23. - go through inheritance and have a common parent class which already implements common behavior and can then be extended - if needed at all
  24. // Careful with this naming in that case!
  25. public class Damageable : MonoBehaviour
  26. {
  27. virtual void TakeDamage(float damage)
  28. {
  29. Debug.Log("This is the common default behavior for my inheritors - except they overwrite or extend it");
  30. }
  31. }
  32. and
  33. public class PlayerAnimator : Damageable
  34. {
  35. public override void TakeDamage(float damage)
  36. {
  37. // First call the default implementation
  38. base.TakeDamage(damage);
  39. Debug.Log("I'm a player and do some additional stuff");
  40. }
  41. }
  42. public class EnemyAnimator : Damageable
  43. {
  44. public override void TakeDamage(float damage)
  45. {
  46. Debug.Log("I'm an enmy and decided to not do the default thing but something completely different");
  47. }
  48. }
  49. 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!
  50. And then I would go
  51. public void Attack()
  52. {
  53. _animator.SetTrigger("Attack");
  54. var hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
  55. foreach (Collider2D enemy in hitEnemies)
  56. {
  57. // OPTION A
  58. if(enemy.TryGetComponent<IDamageable>(out var damageable))
  59. // or OPTION B
  60. // if(enemy.TryGetComponent<Damageable>(out var damageable))
  61. {
  62. damageable.TakeDamage(_attackDamage)
  63. }
  64. }
  65. }
英文:

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

    1. public interface IDamageable
    2. {
    3. void TakeDamage(float damage);
    4. }

    And then

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

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

    and

    1. public class PlayerAnimator : Damageable
    2. {
    3. public override void TakeDamage(float damage)
    4. {
    5. // First call the default implementation
    6. base.TakeDamage(damage);
    7. Debug.Log($&quot;I&#39;m a player and do some additional stuff&quot;);
    8. }
    9. }
    10. public class EnemyAnimator : Damageable
    11. {
    12. public override void TakeDamage(float damage)
    13. {
    14. Debug.Log($&quot;I&#39;m an enmy and decided to not do the default thing but something completely different&quot;);
    15. }
    16. }

    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

  1. public void Attack()
  2. {
  3. _animator.SetTrigger(&quot;Attack&quot;);
  4. var hitEnemies = Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange, _enemyLayer);
  5. foreach (Collider2D enemy in hitEnemies)
  6. {
  7. // OPTION A
  8. if(enemy.TryGetComponent&lt;IDamagable&gt;(out var damageable))
  9. // or OPTION B
  10. //if(enemy.TryGetComponent&lt;Damagable&gt;(out var damageable))
  11. {
  12. damageable.TakeDamage(_attackDamage)
  13. }
  14. }
  15. }

答案2

得分: 0

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

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

  1. public static class AttackManager
  2. {
  3. public static void Attack<T>(Animator attackerAnimator, Transform attacker, float attackRange, float damage, int targetLayer) where T : AnimatorBase
  4. {
  5. attackerAnimator.SetTrigger("Attack");
  6. Collider2D[] hitTargets = Physics2D.OverlapCircleAll(attacker.position, attackRange, targetLayer);
  7. foreach (Collider2D target in hitTargets)
  8. {
  9. target.GetComponent<T>().TakeDamage(damage);
  10. }
  11. }
  12. }

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

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

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

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

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

或者

  1. AttackManager.Attack<EnemyAnimator>(attackerAnimator: animator,
  2. attacker: player,
  3. attackRange: range,
  4. damage: _attackDamage,
  5. 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.

  1. public static class AttackManager
  2. {
  3. public static void Attack&lt;T&gt;(Animator attackerAnimator, Transform attacker, float attackRange, float damage, int targetLayer) where T : AnimatorBase
  4. {
  5. attackerAnimator.SetTrigger(&quot;Attack&quot;);
  6. Collider2D[] hitTargets = Physics2D.OverlapCircleAll(attacker.position, attackRange, targetLayer);
  7. foreach (Collider2D target in hitTargets)
  8. {
  9. target.GetComponent&lt;T&gt;().TakeDamage(damage);
  10. }
  11. }
  12. }

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:

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

Now you would just call the function as:

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

or

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

答案3

得分: -1

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

  1. public void Attack(EnemyAnimator enemyAnimator)
  2. {
  3. _animator.SetTrigger("Attack");
  4. Collider2D[] hitEnemies =
  5. Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange,
  6. _enemyLayer);
  7. foreach (Collider2D enemy in hitEnemies)
  8. {
  9. enemy.GetComponent<PlayerAnimator>().TakeDamage(_attackDamage);
  10. }
  11. }
英文:

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 :

  1. public void Attack(EnemyAnimator enemyAnimator)
  2. {
  3. _animator.SetTrigger(&quot;Attack&quot;);
  4. Collider2D[] hitEnemies =
  5. Physics2D.OverlapCircleAll(_attackPoint.position, _attackRange,
  6. _enemyLayer);
  7. foreach (Collider2D enemy in hitEnemies)
  8. {
  9. enemy.GetComponent&lt;PlayerAnimator&gt;().TakeDamage(_attackDamage);
  10. }
  11. }

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:

确定