如何让 Physics2D.OverlapBoxAll() 激活 x 时间,并且只与它碰撞的新对象交互?
How to make Physics2D.OverlapBoxAll() Active for x time, and only interact with the new objects it collides with?
我有一个非常简单的 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 秒,但只对 new 个敌人造成伤害 一次。我不希望它继续伤害它已经伤害过的敌人。
您可以将已经损坏的敌人存储在一个列表中,如果您已经损坏了它们,则跳过它们。
有些人更喜欢使用协程 (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)){ ... }
.
我有一个非常简单的 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 秒,但只对 new 个敌人造成伤害 一次。我不希望它继续伤害它已经伤害过的敌人。
您可以将已经损坏的敌人存储在一个列表中,如果您已经损坏了它们,则跳过它们。
有些人更喜欢使用协程 (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)){ ... }
.