如何在Unity中将一个碰撞体挡在另一个碰撞体后面

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

How to block one collider behind another in Unity

问题

Is there a way to block one collider object behind the other in a 2D Unity game?
我可以尝试帮助您解决这个问题。

I have colliders for enemies walking on screen but if the enemies walk behind barriers (which have their own colliders), the enemy colliders still respond to the player bullets.
我有敌人的碰撞体在屏幕上移动,但是如果敌人走到障碍物的后面(障碍物有它们自己的碰撞体),敌人的碰撞体仍然会对玩家的子弹做出响应。

So basically i just want to expose the enemies only when they are not behind any barriers.
所以基本上我只想在敌人不在任何障碍物后面时暴露它们。

Currently my bullets go through the barriers and detect the enemies.
目前我的子弹会穿过障碍物并检测到敌人。

I have placed enemies and barriers on separate UI panels and layers but that is just distinguishing them visually. From collision point of view how do i make one (barrier) take priority over the other (enemy)?
我已经将敌人和障碍物放在不同的UI面板和层上,但这只是在视觉上区分它们。从碰撞的角度来看,我如何使一个(障碍物)优先于另一个(敌人)?

英文:

Is there a way to block one collider object behind the other in a 2D Unity game?
I have colliders for enemies walking on screen but if the enemies walk behind barriers (which have their own colliders), the enemy colliders still respond to the player bullets. So basically i just want to expose the enemies only when they are not behind any barriers.
Currently my bullets go through the barriers and detect the enemies.
I have placed enemies and barriers on separate UI panels and layers but that is just distinguishing them visually. From collision point of view how do i make one (barrier) take priority over the other (enemy)?

答案1

得分: 0

You could use Raycast methods. You can cast a ray from the player position to the enemy position, if it detects a wall in the middle you don't allow them to shoot.

public LayerMask wallsLayer;

bool HasPathFreeOfWalls(Transform player, Transform enemy, float reticleRadius)
{
  Vector2 direction = enemy.position - player.position;
  Vector2 origin = player.position + (direction.normalized) * reticleRadius;
  float distance = direction.magnitude - reticleRadius;

  RaycastHit2D rayHit = Physics2D.Raycast(origin, direction, distance, wallsLayer);

  return rayHit.transform == null; // if does not hit anything, it means that the path is free of walls
}

Edit to match game description

One way of doing this is offsetting all the colliders to be above the pivot points of their respective gameObjects, like this.

Then you cast a CircleCast around the reticle to detect all objects there and check which one has the lowest Y position, meaning the one that is in the front. Like this:

public LayerMask enemysAndWallsLayers;

bool HasEnemyInFront(Vector2 reticlePosition, float reticleRadius)
{
    RaycastHit2D[] enemysAndWallsHitted; // holds the results of the circle cast
    ContactFilter2D collisionFilter = new ContactFilter2D(); // filter user by the circleCast
    collisionFilter.layerMask = enemysAndWallsLayers;
    collisionFilter.useLayerMask = true;

    Physics2D.CircleCast(reticlePosition, reticleRadius, Vector2.zero, collisionFilter, enemysAndWallsHitted, 0);

    GameObject closestGameObject = null;
    float closestY = Mathf.Infinity;

    foreach(RaycastHit2D objHitted in enemysAndWallsHitted)
    {
        if(objHitted.transform.position.y < closestY)
        {
            closestY = objHitted.transform.position.y;
            closestGameObject = objHitted.transform?.gameObject;
        }
    }

    return closestGameObject != null && IsGameObjectEnemy(closestGameObject );
}

The IsGameObjectEnemy() function has to be implemented by you in a way that you can tell if it is an enemy or a wall.

英文:

You could use Raycast methods. You can cast a ray from the player position to the enemy position, if it detects a wall in the middle you don't allow them to shoot.

public LayerMask wallsLayer;

bool HasPathFreeOfWalls(Transform player, Transform enemy, float reticleRadius)
{
  Vector2 direction = enemy.position - player.position;
  Vector2 origin = player.position + (direction.normalized) * reticleRadius;
  float distance = direction.magnitude - reticleRadius;

  RaycastHit2D rayHit = Physics2D.Raycast( origin , direction, distance, wallsLayer);

  return rayHit.transform == null; // if does not hit anything, it means that the path is free of walls
}

Edit to match game description

One way of doing this is offsetting all the colliders to be above the pivot points of their respectivelly gameObjects, like this.

Then you cast a CircleCast around the reticle to detect all objects there and checks which one has the lowest Y position, meaning the one that is in the front. Like this:

public LayerMask enemysAndWallsLayers;

bool HasEnemyInFront(Vector2 reticlePosition, float reticleRadius)
{
    RaycastHit2D[] enemysAndWallsHitted; // holds the results of the circle cast
    ContactFilter2D collisionFilter = new ContactFilter2D(); // filter user by the circleCast
    collisionFilter.layerMask = enemysAndWallsLayers;
    collisionFilter.useLayerMask = true;

    Physics2D.CircleCast(reticlePosition, reticleRadius, Vector2.zero, collisionFilter, enemysAndWallsHitted, 0);

    GameObject closestGameObject = null;
    float closestY = Mathf.Infinity;

    foreach(RaycastHit2D objHitted in enemysAndWallsHitted)
    {
        if(objHitted.transform.position.y &lt; closestY)
        {
            closestY = objHitted.transform.position.y;
            closestGameObject = objHitted.transform?.gameobject;
        }
    }

    return closestGameObject != null &amp;&amp; IsGameObjectEnemy(closestGameObject );
}

The IsGameObjectEnemy() fuction have to be implemented by you in a way that you can tell if it is an enemy or a wall

答案2

得分: 0

我已翻译以下内容:

我已经注释了这部分内容,但我认为这可能会解决你的问题,所以我将它提供为答案。

在检查碰撞的函数中,将障碍物检查放在敌人检查之前。例如:

if (collider.gameObject.tag == "Barrier") {//销毁对象} 
else if (collider.gameObject.tag == "Enemy") {//伤害敌人}

这样它会先检查是否撞到了障碍物。如果撞到了障碍物,就不会从敌人那里扣除生命值。

希望这有所帮助。愉快编码!

英文:

I commented this, but I thought as it might solve your problem, I would offer it as an answer.

On the function that checks the collisions, place the barrier check before the enemy check. E.g:

if(collider.gameObejct.tag == &quot;Barrier&quot;) {//Destroy object} 
else if(collider.gameObject.tag == &quot;Enemy&quot;) {//Damage enemy}

This way it will check if it hits the barrier first. If it hits the barrier, no life will be taken from the enemy.

Hope this helps. Happy Coding!

huangapple
  • 本文由 发表于 2023年4月13日 20:25:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76005417.html
匿名

发表评论

匿名网友

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

确定