Есть ли способ заблокировать один объект коллайдера за другим в 2D-игре Unity? У меня есть коллайдеры для врагов, идущих по экрану, но если враги проходят за барьерами (у которых есть свои собственные коллайдеры), вражеские коллайдеры все равно реагируют на пули игрока. Так что в основном я просто хочу разоблачать врагов только тогда, когда они не находятся за какими-либо барьерами. В настоящее время мои пули проходят через барьеры и обнаруживают врагов. Я разместил врагов и барьеры на отдельных панелях и слоях пользовательского интерфейса, но это просто их визуальное различение. С точки зрения столкновения, как мне сделать так, чтобы один (барьер) имел приоритет над другим (врагом)?
Вы можете использовать методы Raycast. Вы можете бросить луч из позиции игрока в позицию врага, если он обнаружит стену посередине, вы не позволите им стрелять.
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
}
Изменить, чтобы соответствовать описанию игры
Один из способов сделать это — сместить все коллайдеры так, чтобы они были выше опорных точек их соответствующих игровых объектов, например это.
Затем вы запускаете CircleCast вокруг сетки, чтобы обнаружить все объекты там, и проверяете, какой из них имеет самое низкое положение Y, то есть тот, который находится впереди. Так:
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 );
}
Функция IsGameObjectEnemy() должна быть реализована вами таким образом, чтобы вы могли определить, враг это или стена.
Извините, я не упомянул, но я использую сетку, чтобы указать на врагов. Так что, если я использую raycast, он будет проходить через все, что находится между игроком и прицельной сеткой.
Я думаю, что сетка не будет проблемой, если вы убедитесь, что в wallLayer есть только стены. В любом случае, я изменил код, чтобы raycast начинался за пределами сетки.
Мне нужно искать столкновения только вокруг сетки. Таким образом, даже если луч проходит через кучу стен, которые не окружают сетку, он не должен влиять на луч. Это шутер, и цели находятся вокруг прицельной сетки. Таким образом, если прицельная сетка указывает на врага, а перед врагом есть стена (так что здесь прицельная сетка указывает и на стену, и на врага), то стена должна закрывать врага. Если стена не окружает прицел и не закрывает врага, то она не должна влиять на луч. Я не уверен, как ваш модифицированный код справляется с этим. Пожалуйста, дополните. Спасибо!
Предположим, что сетка представляет собой круг вокруг игрока, вам нужно передать радиус сетки в качестве аргумента, чтобы можно было вычислить, что исходная точка raycast находится за пределами сетки.
Это статичный шутер от третьего лица, похожий на рассказы о стрелках, с сеткой (перекрестием), управляемой игроком. Таким образом, сетка перемещается повсюду на экране. Я устанавливаю сетку и стреляю. Все, что находится под прицелом, является целью. Если я установлю исходную точку луча рядом с игроком, он заденет все на пути (между игроком и прицельной сеткой).
Теперь я понимаю! Я отредактировал свой ответ, чтобы он соответствовал этому описанию
Я использую OverlapCircle вместо CircleCast, и вместо того, чтобы искать позицию y, я просто вручную устанавливаю маску слоя для вражеского контейнера как слой контейнера препятствия. Спасибо за руководство.
Я прокомментировал это, но подумал, что, поскольку это может решить вашу проблему, я бы предложил это в качестве ответа.
В функции, которая проверяет столкновения, поместите проверку барьера перед проверкой врага. Например:
if (collider.gameObejct.tag == "Barrier") {//Destroy object}
else if (collider.gameObject.tag == "Enemy") {//Damage enemy}
Таким образом, он проверит, попал ли он в барьер первым. Если он попадет в барьер, у врага не будет отнята жизнь.
Надеюсь это поможет. Удачного кодирования!
Просто используйте оператор if, чтобы уничтожить (или деактивировать при объединении) объект, если он сталкивается с барьером, прежде чем проверять, является ли он врагом. ---В вашей функции столкновения вы можете указать if (collider.gameObejct.tag == "Barrier") {//Уничтожить объект} else if (collider.gameObject.tag == "Enemy") {//Нанести урон врагу } .