如何使 Physics2D.OverlapBoxAll() 活动 x 时间,并且只与它碰撞的新对象交互?

Posted

技术标签:

【中文标题】如何使 Physics2D.OverlapBoxAll() 活动 x 时间,并且只与它碰撞的新对象交互?【英文标题】:How to make Physics2D.OverlapBoxAll() Active for x time, and only interact with the new objects it collides with? 【发布时间】:2019-02-23 03:11:30 【问题描述】:

我有一个非常简单的 2D 游戏,其中玩家位于左侧,而敌人则位于右侧并朝玩家移动。

玩家会用剑攻击敌人,我现在有这个功能:

[SerializeField] int damage = 1;
[SerializeField] Transform attackPos;
[SerializeField] LayerMask whatIsEnemy;
[SerializeField] float attackRangeX;
[SerializeField] float attackRangeY;

private void Update()

    if (Input.GetKeyDown(KeyCode.A))
    
        DamageEnemies();
    


private void DamageEnemies()

    Collider2D[] enemiesToDamage = Physics2D.OverlapBoxAll(attackPos.position, new Vector2(attackRangeX, attackRangeY), 0, whatIsEnemy);
    for (int i = 0; i < enemiesToDamage.Length; i++)
    
        enemiesToDamage[i].GetComponent<EnemyController>().TakeDamage(damage);
    

attackPos 游戏对象只是放置在玩家面前。 AttackRangeX 和 Y 就像玩家面前的一个正方形。

这个设置的问题是,当攻击键被按下时,它只会在那个时间点与里面的敌人互动,这是一个单帧。

我怎样才能让碰撞激活 5 秒,但只对个敌人一次造成伤害。我不希望它继续伤害它已经伤害的敌人。

【问题讨论】:

【参考方案1】:

您可以将已经损坏的敌人存储在一个列表中,如果您已经损坏了它们,则跳过它们。

对于更长的主动伤害,有些人更喜欢使用 Coroutines (IEnumerator),但现在为了保持简单,我会使用一个简单的计时器。

[SerializeField] int damage = 1;
[SerializeField] Transform attackPos;
[SerializeField] LayerMask whatIsEnemy;
[SerializeField] float attackRangeX;
[SerializeField] float attackRangeY;

// Adjust here how long the damage should take on (in seconds)
[SerializeField] float damageDuration = 5;

// This is the countdown timer for the damage
private float damageTimer;

// Flag that controls the damage mode
private bool isDamaging;

// Here you will store the already damaged enemies to skip them later
private List<Collider2D> alreadyDamagedEnemies = new List<Collider2D>();

private void Start()

    // Initialize the timer
    damageTimer = damageDuration;


private void Update()

    // only take keyboard input if not already damaging
    // to prevent a peanant press
    if(!isDamaging)
    
        // Hint: If you want you could with a second timer at this point add
        // a cooldown so you can not directly atack again right after the damage duration
        // Just thought it might be interesting for you so I leave it as a homework ;) 

        if (Input.GetKeyDown(KeyCode.A))
        
            // Only activate the damaging
            isDamaging = true;
        
    
    else // We are currently in damage mode
    
        // If end of timer reset and leave damage mode
        if(damageTimer <= 0)
        
            // Reset the timet
            damageTimer = damageDuration;

            // Switch off damage mode
            isDamaging = false;

            // Reset the list
            alreadyDamagedEnemies.Clear();
        
        // else make damage and redue the timer
        else
        
            DamageEnemies();

            // Reduce the timer by the time passed since last frame
            damageTimer-= Time.deltaTime;
        
    


private void DamageEnemies()

    Collider2D[] enemiesToDamage = Physics2D.OverlapBoxAll(attackPos.position, new Vector2(attackRangeX, attackRangeY), 0, whatIsEnemy);

    foreach (var currentEnemy in enemiesToDamage)
    

        // Skip if you already damaged this enemy
        if(alreadyDamagedEnemies.Contains(currentEnemy) continue;

        currentEnemy.GetComponent<EnemyController>().TakeDamage(damage);

        // Add the damaged enemy to the list
        alreadyDamagedEnemies.Add(currentEnemy);
    

这个例子假设你按下 A 一次,但如果同时松开按键,则会造成整整 5 秒的伤害。

如果您希望它只在按键期间发生并且最多持续 5 秒,我相信您可以使用 if(Input.GetKey(KeyCode.A)) ... 自行解决。

【讨论】:

非常感谢您的解决方案!完全按预期工作! :) 如果我要在协程中执行此操作,是否需要将 DamageEnemies() 设为 IEnumenator 并在 while 循环中包含所有内容,其中 yield return null;直到计时器等于或小于零? 欢迎来到 SO!我很高兴能帮上忙!

以上是关于如何使 Physics2D.OverlapBoxAll() 活动 x 时间,并且只与它碰撞的新对象交互?的主要内容,如果未能解决你的问题,请参考以下文章

如何使 RelativeLayout 半透明但不使活动

如何用word使图片上下居中

如何使图像自动调整大小,使宽度为 100% 并相应调整高度?

如何使 UISegmentedcontrol 透明?

如何使 textarea 填充 div 块?

如何使 UITableViewCell 显示为禁用?