Проблема в том, что ваши Ball
объекты сталкиваются друг с другом
=> поэтому Ball1
находится в списке Ball2
, а наоборот
=> таксообщение DestroySameColor
перебрасывается между ними, потому что они запускают foreach, включая компонент, который первоначально произвел весь вызов ... пока вы не достигнете StackOverflow.
Прежде всего, я быфактически добавьте правильное enum
поле цвета к самому компоненту Ball
и с самого начала собирайте только те коллизии того же цвета, что и
//all the colours you have
public enum BallColor
{
Blue,
Red,
// ...
}
public class Ball: MonoBehaviour
{
// set this in the inspector or while instantiating etc
public BallColor color;
//...
}
, теперь вы можете просто сделать
void OnCollisionEnter(Collision c)
{
var ball = c.gameObject.GetComponent<Ball>();
// if no Ball component or different color ignore
if (!ball || ball.color != color) return;
// skip if already contains this ball
if(collidingBalls.Contains(ball)) return;
// ad to list
collidingBalls.Add(ball);
}
void OnCollisionExit(Collision c)
{
var ball = c.gameObject.GetComponent<Ball>();
// if no Ball component ignore
if (!ball) return;
// if not contains ignore
if(!collidingBalls.Contains(ball)) return;
// remove from the list
collidingBalls.Remove(ball);
}
Затем в часть уничтожения
- Сделать весь процесс уничтожения обработанным только одним единственным объектом.Поэтому убедитесь, что
DestroySameColorColliding
вызывается только одним мячом! - Перед тем, как что-либо уничтожить, соберите все объекты, которые нужно уничтожить, в один список, чтобы убедиться, что нет двойников или неожиданностей - особенно убедитесь, что один главныйобъекта нет в списке
- уничтожить все объекты в списке
- уничтожить основной объект как последний
В целом что-то вроде этого
public class Ball: MonoBehaviour
{
//all the colors you have
public enum BallColor
{
Blue,
Red,
// ...
}
private readonly List<Ball> _collidingBalls = new List<Ball>();
// set this in the inspector or while instantiating etc
public BallColor Color;
private IEnumerable<Ball> GatherSubcollidingBalls(List<Ball> ignoreThose)
{
var output = new List<Ball>();
// add yourself to the ignored list
var newIgnoreThose = ignoreThose;
newIgnoreThose.Add(this);
// filter out only references that are not in ignoreThose
var withoutIgnored = _collidingBalls.Except(ignoreThose);
// add the filtered references to the output
output.AddRange(withoutIgnored);
// iterate your collidingBalls but ignore the once from ignoreThose
foreach (var ball in _collidingBalls.Except(ignoreThose))
{
// get this balls collisions ignoring the newIgnoreThose
var coll = ball.GatherSubcollidingBalls(newIgnoreThose);
// filter out only Ball references that are not in output already
var filtered = coll.Except(output).ToList();
// especially remove this reference which is the main object of the call
if (filtered.Contains(this)) filtered.Remove(this);
// add the filtered objects
output.AddRange(filtered);
}
return output;
}
private void OnCollisionEnter(Collision c)
{
var ball = c.gameObject.GetComponent<Ball>();
// if no Ball component or different color ignore
if (!ball || ball.Color != Color) return;
// skip if already contains this ball
if (_collidingBalls.Contains(ball)) return;
// ad to list
_collidingBalls.Add(ball);
}
private void OnCollisionExit(Collision c)
{
var ball = c.gameObject.GetComponent<Ball>();
// if no Ball component ignore
if (!ball) return;
// if not contains ignore
if (!_collidingBalls.Contains(ball)) return;
// remove from the list
_collidingBalls.Remove(ball);
}
public void DestroySameColorColliding()
{
// get all collisions ignoring yourself
var toDestroy = GatherSubcollidingBalls(new List<Ball> { this });
// destroy all other objects of the same color and in same collision chain
foreach (var ball in toDestroy)
{
Destroy(ball.gameObject);
}
// finally destroy yourself
Destroy(gameObject);
}
}